Contents

List of Tables

List of Figures

1 Introduction

When talking about PDF and printing, we often think of tools like LaTeX and Microsoft Word. When talking about HTML and CSS, we may have never imagined their possible off-screen use such as printing to PDF.

Can we print a book with HTML and CSS? W3C published the first working draft on “Paged Media Properties for CSS(3)”, which was last updated in 2013. Although the working draft has been there for nearly two decades, it is still not common to see authors write or print books with HTML and CSS. The main reason is that the W3C specs are still in the draft mode, so most web browsers have not really implemented them.

HTML and CSS still cannot beat other dominating tools like Word or LaTeX when it comes to typesetting content under the constraint of “pages”. You may be disappointed by a lot of typesetting details on a paged HTML page. However, HTML and CSS can be extremely powerful and flexible in other aspects, especially when combined with the power of JavaScript. By the way, HTML works almost anywhere because it only requires a web browser.

Although most web browsers have not implemented the W3C specs for Paged Media, a JavaScript polyfill library named “paged.js” is currently being developed to fill the gap. This library is still experimental and has many rough edges, but looks promising enough to us, so we created an R package pagedown (Xie et al. 2024) based on this JavaScript library to paginate the HTML output of R Markdown documents. You can install the package from Github:

remotes::install_github('rstudio/pagedown')

To learn more about paged.js and CSS for Paged Media, you may check out the documentation of paged.js.

The pagedown package contains output formats for paged HTML documents, letters, resumes, posters, business cards, and so on. Usually there is an R Markdown template for each output format, which you can access from RStudio’s menu File -> New File -> R Markdown -> From Template.

2 Paged HTML documents

To create a paged HTML document, you can use the output format pagedown::html_paged, e.g.,

output:
  pagedown::html_paged: 
    toc: true
    number_sections: false

2.1 Preview paged HTML documents

This format is based on paged.js. Some other formats in this package are extensions of html_paged, such as html_letter and html_resume. Please note that you need a web server to view the output pages of these formats, because paged.js requires a web server. The web server could be either a local server or a remote one. When you compile an R Markdown document to HTML in RStudio, RStudio will display the HTML page through a local web server, so paged.js will work in RStudio Viewer. However, when you view such pages in a real web browser, you will need a separate web server. The easiest way to preview these HTML pages in a web browser may be through the RStudio addin “Infinite Moon Reader”, which requires the xaringan package (Xie 2024b). Or equivalently, you can call the function xaringan::inf_mr(). This will launch a local web server via the servr package (Xie 2024a).

Please note that the layout of the pages is very sensitive to the zoom level in your browser. Elements on a page are often not zoomed strictly linearly, e.g., as you zoom out, certainly elements may start to collapse into each other. The 100% zoom level usually gives the best result (press Ctrl + 0 or Command + 0). You are strongly recommended to use this level when printing the page to PDF.

2.2 The CSS overriding mechanism

We have provided a few default CSS stylesheets in this output format:

output:
  pagedown::html_paged: 
    css:
      - default-fonts
      - default-page
      - default

To find the actual CSS files behind these names, use pagedown:::list_css(). For example, default-fonts means the file resources/css/default-fonts.css in the installation directory of pagedown. The stylesheet default-fonts defines the typefaces of the document, default-page defines some page properties (such as the page size, running headers and footers, and rules for page breaks), and default defines the style of various elements (such as the table of contents).

If you do not like any of these default stylesheets, you can use a subset of them, or override certain CSS rules. For example, if you do not like the default typeface, you may create a CSS file my-fonts.css (assuming it is under the same directory of your Rmd file):

body {
  font-family: 
    "Palatino Linotype", "Book Antiqua",
    Palatino, serif;
}

Then include this CSS file via the css option:

output:
  pagedown::html_paged: 
    css:
      - my-fonts.css
      - default-page
      - default

Note that it is possible to use Sass, by installing the Sass package (install.packages("sass")) and including a file with the .sass or .scss extension in the css option. Moreover, this overriding mechanism also works for other output formats in pagedown.

3 Applications

3.1 Resume

Currently pagedown has one resume format: pagedown::html_resume. See https://pagedown.rbind.io/html-resume/ for an example.

The HTML resume in pagedown.

Figure 3.1: The HTML resume in pagedown.

The R Markdown source document should contain two parts titled “Aside” (for the sidebar) and “Main” (for the main body), respectively. Each part can contain any numbers of sections (e.g., education background and working experience). Each section can contain any numbers of subsections, and you can write more content in a subsection.In case you are not very familiar with the Markdown syntax for section headings, a series of = under a heading is equivalent to # before a heading, and a series of - is equivalent to ##. See Pandoc’s manual for details: https://pandoc.org/MANUAL.html#headers. Below is a quick example:

---
title: "Someone's resume"
author: Your Name
output: pagedown::html_resume
---


Aside
====================================

Picture, etc.

Contact info
------------------------------------

Email, phone number, ...

Skills
------------------------------------

- One
- Two
- Three

Disclaimer
------------------------------------

A footer in the sidebar.


Main
====================================

Your Name {#title}
------------------------------------

Arbitrary content.

Education {data-icon=graduation-cap}
------------------------------------

### University One

Title

Location

Year

Arbitrary content

### University Two

...

The “Aside” part will be displayed in the right sidebar. This part can contain arbitrary content (not necessarily structured). For example, you can include a picture in the beginning. The “Disclaimer” section will be placed at the bottom of the first page. All icons for this resume template are from Font Awesome. For example, <i class="fa fa-envelope"></i> will generate an envelope icon. You can look up all icon names on the Font Awesome website if you want to use other icons.

For the “Main” part, all sections must follow a specific structure except the first section. The first section usually shows your name and information that you want to highlight. For the rest of sections, they should contain a title and a number of subsections. You can specify an icon for a section title via the attribute data-icon, e.g., {data-icon=graduation-cap} means an icon of a graduation cap (which can be used as a symbol for education). For each subsection, it should contain a title, followed by at least three paragraphs:

  • The first paragraph is a brief description of the subsection.

  • The second paragraph is the location.

  • The third paragraph is the time period. If this subsection has both a starting and ending time, separate them by a dash, e.g., 2014 - 2015 or 2014/07/27 - 2015/07/23.

The description, location, and time period can each be N/A if the relevant information is not available.

You can write arbitrary content after the third paragraph (e.g., more paragraphs, bullet lists, and so on). If you want to write content in two columns, you may use a “concise” block, e.g.,

::: concise
- Taught R language to beginners. 
- Wrote Shiny app demos.
- Converted statistical tutorials from SPSS to R language.
:::

If you want to write a side note, use an “aside” block, e.g.,

Section
---------------

### Subsection

Title

Location

Year

More info

::: aside
Some notes in the sidebar.
:::
An example of side notes.

Figure 3.2: An example of side notes.

There is a caveat about page breaks. By default, we allow page breaks within a subsection. Sometimes this may lead to odd output like the example in Figure 3.3. The first bullet should not be split into two columns, and the rest of bullets should have a larger left margin.

Allow page breaks in a subsection.

Figure 3.3: Allow page breaks in a subsection.

If you want to avoid layout problems like this, you may disallow page breaks via CSS:

.blocks {
  break-inside: avoid;
}

However, this may lead to a new issue demonstrated in Figure 3.4: there may be a large bottom margin on the previous page. At this point, you may start to miss your old friends, Word and LaTeX.

Do not allow page breaks in a subsection.

Figure 3.4: Do not allow page breaks in a subsection.

3.2 Poster

You can create a poster with the output format pagedown::poster_relaxed or pagedown::poster_jacobs. See https://pagedown.rbind.io/poster-relaxed/ and https://pagedown.rbind.io/poster-jacobs/ for examples.

We do not have time to document the poster formats yet, but here is a caveat: the layout of poster sections is hardcoded in CSS styleseets, which means you cannot add/delete sections unless you know CSS (in particular, CSS Grid Layout). If you are interested in learning CSS Grid Layout, you may take a look at the CSS of poster_jacobs.

3.2.1 The ReLaXed style

3.2.2 The Jacobs University style

3.3 Business card

To create a simple business card, you can use the pagedown::business_card format. See https://pagedown.rbind.io/business-card/ for an example.

3.3.1 Single card

A single business card can be created with the following R Markdown file (this file contains only a YAML header).

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
output: pagedown::business_card
---

You can repeat the card on multiple pages using the repeat variable. The following example produces as many pages as cards (12).

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
repeat: 12
output: pagedown::business_card
---

In order to print the cards, you may prefer a layout with several cards on the same page: you can adjust the paper size with the paperwidth and paperheight variables and define a grid layout with the cols and rows variables (you may test some combinations of these parameters to find the most appropriate one).

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
repeat: 12
paperwidth: 8.5in
paperheight: 11in
cols: 4
rows: 3
output: pagedown::business_card
---

You also can use markdown to define a card. Be aware to use the slot attributes as follows.

---
logo: "logo.png"
paperwidth: 8.5in
paperheight: 11in
cols: 4
rows: 3
output: pagedown::business_card
---

::::: {.wrapper data-repeat="12"}
[Jane Doe]{slot="name"}
[Miss Nobody]{slot="title"}
[+1 123-456-7890]{slot="phone"}
[jane.doe@example.com]{slot="email"}
[www.example.com]{slot="url"}

::: {.address slot="address"}
2020 South Street
Sunshine, CA 90000
:::
:::::

3.3.2 Different cards with shared informations

You can produce business cards for members of an organization sharing some informations (address, website…).
Common informations are declared as top level variables in the YAML header. Custom cards are defined using the person variable: each key: value pair of a person block overrides the corresponding top level pair.

---
phone: "+1 123-456-7890"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
person:
  - name: Jane Doe
    title: Miss Nobody
    email: "jane.doe@example.com"
    repeat: 6
  - name: John Doe
    title: Mister Nobody
    phone: "+1 777-777-7777" # overrides the default phone
    email: "john.doe@example.com"
    repeat: 6
paperwidth: 8.5in
paperheight: 11in
cols: 4
rows: 3
output: pagedown::business_card
---

If you prefer, you can use markdown to create a card as follows.

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
repeat: 6
paperwidth: 8.5in
paperheight: 11in
cols: 4
rows: 3
output: pagedown::business_card
---

::: {.wrapper data-repeat="6"}
[John Doe]{slot="name"}
[Mister Nobody]{slot="title"}
[+1 777-777-7777]{slot="phone"}
[john.doe@example.com]{slot="email"}
[my.domain.com]{slot="url"}
:::

3.3.3 Styling business cards

3.3.3.1 Fonts

You can change the text font with the mainfont and/or googlefonts top level YAML variables:

  • mainfont will use the local font installed on your computer, e.g.

    ---
    name: Jane Doe
    title: Miss Nobody
    phone: "+1 123-456-7890"
    email: "jane.doe@example.com"
    url: www.example.com
    address: |
      2020 South Street
      Sunshine, CA 90000
    logo: "logo.png"
    mainfont: Arial
    output: pagedown::business_card
    ---
  • googlefonts to use fonts from https://fonts.google.com, e.g.

    ---
    name: Jane Doe
    title: Miss Nobody
    phone: "+1 123-456-7890"
    email: "jane.doe@example.com"
    url: www.example.com
    address: |
      2020 South Street
      Sunshine, CA 90000
    logo: "logo.png"
    googlefonts: ["Roboto Condensed", "Raleway"]
    output: pagedown::business_card
    ---

3.3.3.2 Card sizing

You can modify the card size with the cardwidth and cardheight variables. You can get a landscape card with:

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
cardwidth: 3.5in
cardheight: 2in
output: pagedown::business_card
---

If you render this card, you will see that the default style does not suit well with a landscape card. Read the next section to find an example of a landscape card with a better style.

3.3.3.3 CSS

Finally, you can modify the style of the card using CSS rules.
The markup of a card can be represented as followsThis is technically incorrect since the card template use a shadow DOM.:

<div class="wrapper" data-repeat="1">
  <img class="logo" src="logo.png" alt="Logo" />
  <div class="me">
    <div class="name"><span>Jane Doe</span></div>
    <div class="title"><span>Miss Nobody</span></div>
    <div class="coordinates">
      <p class="phone"><span>+1 123-456-7890</span></p>
      <p class="contact-email"><span>jane.doe@example.com</span></p>
      <p class="website"><span>www.example.com</span></p>
      <div class="address">2020 South Street Sunshine, CA 90000</div>
    </div>
  </div>
</div>

You can use these built-in classes to style your card with CSS.
A landscape card could be styled like this:

---
name: Jane Doe
title: Miss Nobody
phone: "+1 123-456-7890"
email: "jane.doe@example.com"
url: www.example.com
address: |
  2020 South Street
  Sunshine, CA 90000
logo: "logo.png"
cardwidth: 3.5in
cardheight: 2in
output: pagedown::business_card
---

```{css}
.logo {
  display: block;
  height: 20%;
  margin-right: .3in;
  padding: .3in 0 0;
  float: right;
}
.name {
  margin-top: .1in;
}
```

3.4 Letter

You can write a letter with the pagedown::html_letter format. See https://pagedown.rbind.io/html-letter/ for an example.

3.5 Thesis

You can write a thesis with the pagedown::thesis_paged format created by Brent Thorne. See https://pagedown.rbind.io/thesis-paged/ for an example.

3.6 A Journal of Statistical Software article

You can write an article for the Journal of Statistical Sofware. See https://pagedown.rbind.io/jss-paged/ for an example.

4 Miscellaneous features

4.1 Lists of tables and figures

Lists of tables and/or figures can be inserted in the document using the lot and lof variables in the YAML metadata. You also can customize their titles with the lot-title and lof-title variables. By default, theses lists are referenced in the table of contents, if any. You can use the lot-unlisted and lof-unlisted options to remove them. For instance:

---
title: "A document with lists of tables and figures"
output: pagedown::html_paged
toc-title: Contents
lot: true
# default: "List of Tables"
lot-title: "Tables"
# uncomment to remove from the TOC:
#lot-unlisted: true
lof: true
# default: "List of Figures"
lof-title: "Figures"
# uncomment to remove from the TOC:
#lof-unlisted: true
---

4.2 List of abbreviations

A list of abbreviations is automatically built if the document contains at least one HTML abbr element.

For instance, if the R Markdown document contains <abbr title="Cascading Style Sheets">CSS</abbr>, a list of abbreviations is built with the CSS definition.

List of abbreviations example

CSS
Cascading Style Sheets

The title of this list of abbreviations can be customized using the loa-title field in the YAML header.

4.3 Front matter

By default, the front matter is composed of the cover, the table of contents and the lists of figures, tables and abbreviations if any. The only difference between the front matter pages and the main content pages is the style of the page numbers.

You can add extra sections to the front matter using the front-matter class. For instance, if you want to add a preface to the front matter, you need to write:

# Preface {.front-matter .unnumbered}

4.4 Chapters prefix

The word “Chapter” can be prepended to chapter numbers in chapter titles using the chapter class :

# A prefixed chapter {.chapter}

4.4.1 Internationalization

The chapter title prefix can be customized using the chapter_name field in the YAML header or in _bookdown.yml filesee https://bookdown.org/yihui/bookdown/internationalization.html

When defined in the YAML header the chapter_name field is parsed by Pandoc. Therefore, special characters like spaces have to be escapedsee https://pandoc.org/MANUAL#backslash-escapes. For instance, if you want to use 'CHAPTER ' as a chapter title prefix, you have to write:

---
title: "Custom chapter title prefix"
output: pagedown::html_paged
chapter_name: "CHAPTER\\ "
---

# A chapter with a custom prefix {.chapter}

A suffix string can be added after the chapter number. For instance, to add a dot (.) after the chapter number, use the following value for the chapter_name field:

---
title: "Custom chapter title prefix"
output: pagedown::html_paged
chapter_name: ["CHAPTER\\ ", "."]
---

# A chapter with a custom prefix {.chapter}

If defined in _bookdown.yml file, the chapter_name field will override a chapter_name field declared in the YAML header.
Note that contrary to the bookdown HTML formats, a custom function is not allowed as a value for the chapter_name field.

4.6 Footnotes

The default behavior of pagedown is to render notes as endnotes because Paged.js does not natively support footnotes for now. However, we introduced an experimental support for footnotes. You can test it by including paged-footnotes: true in the YAML header of your document. Please, note that the paged-footnotes option only supports inline contentsee https://github.com/rstudio/pagedown/issues/156. If you get any trouble with this experimental feature, please open an issue in the pagedown repository on GitHub.

If you need to override the default footnotes style, you should use an important rule on elements of class footnote. For example,

.footnote {
  font-size: 20px !important;
  color: red !important;
}

4.7 Custom running headers

Sometimes a section title may be too long to fit the header or footer area. In this case, you can specify a shorter title for the running headers/footers via the attribute data-short-title after a title, e.g.,

## The actual long long long title {data-short-title="An alternative title"}

4.8 Covers

Covers images can be added using the front_cover and back_cover parameters of pagedown::html_paged(). You can pass any path to a file or an url.

---
output:
  pagedown::html_paged:
    front_cover: !expr system.file("img", "Rlogo.png", package="png")
    back_cover: https://www.r-project.org/Rlogo.png
---

# Content

Several paths or links can be passed to the front_cover and back_cover parameters. For each image declared in the front_cover or back_cover parameter, a CSS variable is created: --front-cover, --back-cover, --front-cover-2, --back-cover-2, etc.
They can be used as value for the background-image CSS property:

@page chapter:first {
  background-image: var(--front-cover-2);
}

You also can add textual content on the front and back covers using two special divs of classes front-cover and back-cover:

---
output:
  pagedown::html_paged:
    front_cover: !expr system.file("img", "Rlogo.png", package="png")
    back_cover: https://www.r-project.org/Rlogo.png
---

:::front-cover
# My great book about
:::

:::back-cover
### Written with
:::

# Content

If the background properties of the default template does not suit your needs, here is a small hack to modify them.
The following lines are used to position the pagedown hex logo on the front page of the current document:

/* position the hex logo on the first page */
.pagedjs_page.pagedjs_first_page {
  background-size: 40%;
  background-position: center 80%;
}

4.9 Page references

Internal links will be followed by page numbers by default. For example, you can refer to another section using either [Section Title] or [Link Text](#section-id) (where section-id is the ID of the section header).

Do you still remember Paged.js that we mentioned earlier?

4.10 Line numbering

For templates built on top of html_paged, line numbering can be added using the top-level YAML parameter number-lines. For example:

---
output: pagedown::thesis_paged
number-lines: true
---

The line numbers can be reset on each page using the reset-page option:

---
output: pagedown::thesis_paged
number-lines:
  reset-page: true
---

You also can select the HTML elements by passing a CSS selector to the selector parameter. To number the lines of all the paragraphs and headers, you need to write:

---
output: pagedown::thesis_paged
number-lines:
  selector: "p, h1, h2, h3, h4, h5, h6"
---

The default CSS selector is ".level1:not(.front-matter) h1, .level1 h2, .level1 h3, .level1 p". Line numbering is deactivated for display math environments.

Be aware that the value "normal" of the CSS line-height property is not supported: elements with a normal line height are not numbered. Since "normal" is the default value for the line-height property, the CSS stylesheets must define a different value. If your template relies on custom CSS files, you can add for example:

html {
  line-height: 1.3;
}

You can modify the horizontal positioning and the font size of the lines numbers using two CSS variables: --line-numbers-padding-right (default value 10px) and --line-numbers-font-size (default value 8pt). For further customisation, you can modify the style of the elements of class maintextlinenumbers. Here is an example:

.maintextlinenumbers {
  --line-numbers-padding-right: 25px;
  --line-numbers-font-size: 10pt;
  font-weight: bold;
  font-family: monospace;
}

Please note that this feature is sensitive to elements which break the vertical rythm of the text like inline maths.

4.11 Page breaks

There are two ways to force a page break:

  • with the \newpage \(\LaTeX\) command (\pagebreak also works)

  • using one of these two CSS classes: page-break-before or page-break-after
    For example, to force a page break before a given section, use:

    ### New section {.page-break-before}

4.12 MathJax

The following test comes from http://www.cs.toronto.edu/~yujiali/test/mathjax.html.

Some RBM stuff:

\[ E(\mathbf{v}, \mathbf{h}) = -\sum_{i,j}w_{ij}v_i h_j - \sum_i b_i v_i - \sum_j c_j h_j \]

Multiline equations:

\[ \begin{align} p(v_i=1|\mathbf{h}) & = \sigma\left(\sum_j w_{ij}h_j + b_i\right) \\ p(h_j=1|\mathbf{v}) & = \sigma\left(\sum_i w_{ij}v_i + c_j\right) \end{align} \]

Here is an example of an inline expression: \(p(x|y) = \frac{p(y|x)p(x)}{p(y)}\).

4.13 Figures and tables

Table 4.1:

knitr::kable(head(iris[, -5]), caption = 'An example table.')
Table 4.1: An example table.
Sepal.Length Sepal.Width Petal.Length Petal.Width
5.1 3.5 1.4 0.2
4.9 3.0 1.4 0.2
4.7 3.2 1.3 0.2
4.6 3.1 1.5 0.2
5.0 3.6 1.4 0.2
5.4 3.9 1.7 0.4

Bibliography

Xie, Yihui. 2024a. Servr: A Simple HTTP Server to Serve Static Files or Dynamic Documents. https://github.com/yihui/servr.
———. 2024b. Xaringan: Presentation Ninja. https://github.com/yihui/xaringan.
Xie, Yihui, Romain Lesur, Brent Thorne, and Xianying Tan. 2024. Pagedown: Paginate the HTML Output of r Markdown with CSS for Print. https://github.com/rstudio/pagedown.