Chapter 5 officer for RTF

5.1 Why RTF?

RTF (Rich Text Format) is a plain text format readable by virtually every word processor: Microsoft Word, LibreOffice, WordPad, and many legacy editorial pipelines (pharma, clinical, regulatory submissions). It has no macros, no embedded OLE objects.

The ‘officer’ package produces RTF documents from R with an API that mirrors the one used for Word: you create a document object, add content with rtf_add(), and write the result to a file with print().

5.2 A minimal document

A working RTF document fits in four lines:

library(officer)

doc <- rtf_doc()
doc <- rtf_add(doc, "Hello RTF", style = "heading 1")
doc <- rtf_add(doc, "This is a first paragraph.")

print(doc, target = "static/office/rtf-hello.rtf")
# [1] "static/office/rtf-hello.rtf"

rtf_doc() returns an empty document and pre-registers four paragraph styles: "Normal", "heading 1", "heading 2" and "heading 3". The heading names follow Word’s own convention, so the same call works identically against a Word or an RTF document.

5.3 What you can add

The rtf_add() function dispatches on the class of its second argument. The table below lists what is supported.

  • character, factor, double vectors (one paragraph per element)
  • fpar() paragraphs and block_list() of paragraphs (see chapter 8)
  • block_toc() for a table of contents
  • block_section() to open a new section
  • ggplot objects and plot_instr() for base R plots
  • tables built with the ‘flextable’ package
  • any run object: ftext(), hyperlink_ftext(), run_word_field(), run_autonum(), run_reference(), run_pagebreak(), run_columnbreak(), run_linebreak(), external_img()

Formatting properties (fp_text(), fp_par(), borders, shading, tab stops) are shared with the Word workflow and described in chapter 7.

5.3.1 Paragraphs and titles

Use the style argument of rtf_add() to pick a paragraph style. The built-in heading styles carry an outline level, which means Word will list them in the navigation pane and pick them up in the table of contents field.

doc <- rtf_doc()
doc <- rtf_add(doc, "Introduction", style = "heading 1")
doc <- rtf_add(doc, "First section", style = "heading 2")
doc <- rtf_add(doc, "Some body text.")
print(doc, target = "static/office/rtf-titles.rtf")
# [1] "static/office/rtf-titles.rtf"

For richer paragraphs, build an fpar() and add it the same way:

def_text <- fp_text_lite(color = "#006699", bold = TRUE)

doc <- rtf_doc()
doc <- rtf_add(doc, fpar(
  ftext("Mixed ", prop = def_text),
  ftext("formatting in a single paragraph."),
  fp_p = fp_par(text.align = "center")
))
print(doc, target = "static/office/rtf-fpar.rtf")
# [1] "static/office/rtf-fpar.rtf"

5.3.2 Tables

The recommended way to add a table is through the ‘flextable’ package, which renders to RTF the same way as to Word, PowerPoint, HTML and PDF.

library(flextable)
ft <- qflextable(head(iris))

doc <- rtf_doc()
doc <- rtf_add(doc, "Iris dataset", style = "heading 2")
doc <- rtf_add(doc, ft)
print(doc, target = "static/office/rtf-table.rtf")
# [1] "static/office/rtf-table.rtf"

5.3.3 Plots

ggplot objects and plot_instr() are rendered to PNG and embedded as images.

library(ggplot2)
gg <- ggplot(iris, aes(Sepal.Length, Petal.Length, colour = Species)) +
  geom_point()

doc <- rtf_doc()
doc <- rtf_add(doc, gg, width = 5, height = 3)
print(doc, target = "static/office/rtf-plot.rtf")
# [1] "static/office/rtf-plot.rtf"

5.3.4 Table of contents

block_toc() inserts a Word TOC field. The field is computed by Word when the document is opened (or when the user presses F9). LibreOffice will not populate it automatically.

doc <- rtf_doc()
doc <- rtf_add(doc, block_toc(level = 3))
doc <- rtf_add(doc, "Chapter 1", style = "heading 1")
doc <- rtf_add(doc, "Some content.")
doc <- rtf_add(doc, "Chapter 2", style = "heading 1")
doc <- rtf_add(doc, "More content.")
print(doc, target = "static/office/rtf-toc.rtf")
# [1] "static/office/rtf-toc.rtf"

5.4 Paragraph styles

rtf_doc() ships with four registered paragraph styles. You can list them with rtf_styles_info():

rtf_styles_info(rtf_doc())

style_id

style_name

type

rtf_index

based_on

rtf

character

character

character

integer

character

character

Normal

Normal

paragraph

1

\sl240\slmult1\ql\sb0\sa0\fi0\li0\ri0 %font:Arial%\fs22%ftcolor:black%

heading 1

Heading 1

paragraph

2

Normal

\sl240\slmult1\ql\sb120\sa240\fi0\li0\ri0 \outlinelevel0\b\fs36%ftcolor:#1F4E79%

heading 2

Heading 2

paragraph

3

Normal

\sl240\slmult1\ql\sb80\sa160\fi0\li0\ri0 \outlinelevel1\b\fs28%ftcolor:#2E75B6%

heading 3

Heading 3

paragraph

4

Normal

\sl240\slmult1\ql\sb60\sa120\fi0\li0\ri0 \outlinelevel2\b\fs24%ftcolor:#5B9BD5%

n: 4

To override the look of a built-in style or to register a new one, use rtf_set_paragraph_style(). The style_id is the value you will pass to rtf_add(..., style = ...).

doc <- rtf_doc()
doc <- rtf_set_paragraph_style(
  doc,
  style_id = "heading 1",
  fp_p = fp_par(text.align = "center", padding = 12),
  fp_t = fp_text_lite(bold = TRUE, font.size = 24, color = "#000000"),
  outline_level = 1L
)
doc <- rtf_add(doc, "Centered heading 1", style = "heading 1")
print(doc, target = "static/office/rtf-custom-style.rtf")
# [1] "static/office/rtf-custom-style.rtf"

The outline_level argument is what makes a paragraph eligible for the navigation pane and the table of contents. Set it on any paragraph style you want to behave like a heading.

5.5 Sections

Sections control page orientation, margins, columns, and per-section headers and footers. The RTF section model is the only place where the API differs from the Word one.

In Word, body_end_block_section() applies to the content that precedes the call. In RTF, rtf_add(block_section(...)) applies to the content that follows the call.

The first section of the document is configured with the def_sec argument of rtf_doc(). Each subsequent rtf_add(block_section(...)) opens a new section, inherited by everything added afterwards until the next block_section.

header_default <- function(label) {
  block_list(fpar(ftext(label, fp_text_lite(bold = TRUE, color = "#006699"))))
}
footer_default <- block_list(fpar(
  "Page ", run_word_field(field = "PAGE \\* MERGEFORMAT")
))

doc <- rtf_doc(
  def_sec = prop_section(
    header_default = header_default("Default section"),
    footer_default = footer_default
  )
)
doc <- rtf_add(doc, "Portrait section", style = "heading 1")
doc <- rtf_add(doc, "Some content in portrait orientation.")

# switch to landscape for what follows
doc <- rtf_add(doc, block_section(prop_section(
  type = "nextPage",
  page_size = page_size(orient = "landscape"),
  header_default = header_default("Landscape section"),
  footer_default = footer_default
)))
doc <- rtf_add(doc, "Landscape section", style = "heading 1")
doc <- rtf_add(doc, "Wider content fits here.")

print(doc, target = "static/office/rtf-sections.rtf")
# [1] "static/office/rtf-sections.rtf"

Multi-column layouts are configured with section_columns() inside prop_section(). Column breaks are inserted with run_columnbreak().

5.6 Headers, footers and page numbers

Headers and footers are attached to a section through the header_default / footer_default arguments of prop_section(). They accept a block_list() of paragraphs. Page numbers are produced with the standard Word field PAGE:

fpar("Page ", run_word_field(field = "PAGE \\* MERGEFORMAT"))

Other useful fields: DATE, TIME, NUMPAGES, STYLEREF. They are passed verbatim to run_word_field() and resolved by Word at open time.

5.7 Known limitations

  • No macros, no OLE embedding, no Microsoft Charts.
  • Tables of contents and other Word fields are computed at document open by Word.
  • Fonts are resolved by the reader’s installed font set: stick to commonly available families if the file will be opened on other machines.
  • There is no equivalent of the Word read_docx() template mechanism: styles are registered programmatically with rtf_set_paragraph_style().

5.8 Going further

  • Chapter 7 covers formatting properties (fp_text(), fp_par(), borders, shading) shared by the Word, PowerPoint and RTF outputs.
  • Chapter 8 covers paragraph construction with fpar() and block_list().
  • ?rtf_add lists every supported input class.
  • ?rtf_set_paragraph_style and ?rtf_styles_info document the style API.
  • ?rtf_doc documents the document constructor and the section model.