Skip to contents

Overview

This document is for developers who would like to understand the internal of the r2rtf package.

The r2rtf package is developed to standardize the approach to generate highly customized tables, listings and figures (TLFs) in RTF format and provide flexibility to customize table appearance for table title, subtitle, column header, footnote, and data source.

The r2rtf package is designed to enables pipes (%>%), the first argument are all tbl except rtf_read_figure() and write_rtf(). A minimal example summarized the major steps in using r2rtf package to create a table or listing after a data frame is ready.

head(iris) %>%
  rtf_body() %>% # Step 1 Add attributes
  rtf_encode() %>% # Step 2 Convert attributes to RTF encode
  write_rtf(file = "rtf/ex-tbl.rtf") # Step 3 Write to a .rtf file

Similar step is used to create a figure. First we generate a figure in png format, then use rtf_read_figure() to read in the png file, and proceed to rtf generation.

fig <- c("fig/fig1.png")
fig %>% rtf_read_figure() %>% # Step 1 Read in PNG file
  rtf_figure() %>% # Step 2 Add attributes
  rtf_encode(doc_type = "figure") %>% # Step 3 Convert attributes to RTF encode
  write_rtf(file = "rtf/ex-fig.rtf") # Step 4 Write to a .rtf file

Data Structure and Attributes

We explore rtf_page() to illustrate how attributes of a data frame is used. rtf_page() is a function to define the feature of a page in rtf document. For example, we need to set the page orientation, width, height, margin etc. The information is attached to an data frame using attributes.

Therefore, the core part of rtf_page() is to assign attributes to the input data frame (tbl) as below. Necessary input checking using check_arg(), stopifnot() and match.arg(), is provided in the function to provide informative error message with unexpected input.

attr(tbl, "page")$orientation <- orientation
attr(tbl, "page")$width <- width
attr(tbl, "page")$height <- height
attr(tbl, "page")$margin <- margin
attr(tbl, "page")$border_color_first <- border_color_first

All attributes is saved in a logical way and summarized in the table below.

tbl <- head(iris) %>% rtf_page()

To access the attributes, attr() or attributes() function can be used. For example, to get page attributes in default setting, we can run code below.

data.frame(value = unlist(attr(tbl, "page")))
#>                  value
#> width              8.5
#> height              11
#> orientation   portrait
#> margin1           1.25
#> margin2              1
#> margin3           1.75
#> margin4           1.25
#> margin5           1.75
#> margin6        1.00625
#> nrow                40
#> col_width         6.25
#> border_first    double
#> border_last     double
#> page_title         all
#> page_footnote     last
#> page_source       last
#> use_color        FALSE

The table body attributes are more complicated than page attributes. Take rtf_body() as an example, it meets the user’s need to take fully control of table appearance through parameters to customize table size, space, border type (e.g., single, double, dash, dot, etc.), color (e.g., 657 different colors named in color() function), line width, column width, row height, text format (e.g., bold, italics, strikethrough, underline and any combinations), font size, text color, alignment (e.g., left, right, center, decimal), etc. Format control can be at the cell, row, column, or table level.

With the help of functions obj_rtf_text() and obj_rtf_border(), it is straightforward to pass the user inputs to text/border attributes. For example, parameter text_justification = "c" sets all text in the cells to be center adjusted and text_justification = c("c", rep("l", 4)) sets the text in the first column to be center adjusted and the texts in the remaining 4 columns to be left adjusted for a table with five columns.

In addition, rtf_body() initiates “page” attributes when color is used in any of table elements (border, text, etc.).

head(iris) %>% rtf_body()
#>   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#> 1          5.1         3.5          1.4         0.2  setosa
#> 2          4.9           3          1.4         0.2  setosa
#> 3          4.7         3.2          1.3         0.2  setosa
#> 4          4.6         3.1          1.5         0.2  setosa
#> 5            5         3.6          1.4         0.2  setosa
#> 6          5.4         3.9          1.7         0.4  setosa

Similarly to rtf_body(), the package also includes rtf_colheader(), rtf_footnote(), rtf_source(), rtf_subline(), rtf_text(), rtf_title() and rtf_figure() to assign attributes to the certain component of the output. Below is a simple example of using the functions together for table/figure.

head(iris) %>% # Step 1 Read in data frame
  rtf_title("iris") %>% # Step 2 Add title
  rtf_colheader("A | B | C | D | E") %>% # Step 3 Add column header
  rtf_body() %>% # Step 4 Add attributes
  rtf_footnote("This is a footnote") %>% # Step 5 Add footnote
  rtf_source("Source: xxx") # Step 6 Add data source
"fig/tmp-fig.png" %>%
  rtf_read_figure() %>% # Step 1 Read PNG files from the file path
  rtf_title("title", "subtitle") %>% # Step 2 Add title or subtitle
  rtf_footnote("footnote") %>% # Step 3 Add footnote
  rtf_source("[datasource: mk0999]") %>% # Step 4 Add data source
  rtf_figure() # Step 5 Add figure attributes

RTF Encoding

rtf_encode() wraps up internal functions in r2rtf package to translate all attributes attached to a data frame into RTF syntax. A good reference is RTF Pocket Guide.

All of the internal encoding functions in the r2rtf package start with a prefix as_rtf_. For a table, r2rtf:::rtf_encode_table() is used internally to translate attribute to RTF syntax by using a set of as_rtf_ functions.

Likewise, rtf_encode_list() is used when we have multiple data frame with different data structures to be stacked, this is often seen in efficacy analysis. And rtf_encode_figure() is used for figure outputs.

r2rtf:::as_rtf_colheader() define the column header in a table or listing.

head(iris) %>%
  rtf_body() %>%
  r2rtf:::as_rtf_colheader() %>%
  cat()

r2rtf:::as_rtf_color() define the color to be used in text or border.

head(iris) %>%
  rtf_body(text_color = "red") %>%
  r2rtf:::as_rtf_color() %>%
  cat()

r2rtf:::as_rtf_end() define the end of rtf encode string.

r2rtf:::as_rtf_end() %>% cat()

r2rtf:::as_rtf_font() define the font encode.

r2rtf:::as_rtf_font() %>% cat()

r2rtf:::as_rtf_footnote() define the footnote encode.

head(iris) %>%
  rtf_footnote("example") %>%
  r2rtf:::as_rtf_footnote() %>%
  cat()
#> \trowd\trgaph108\trleft0\trqc
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrr\brdrs\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx9000
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\ql\fs18{\f0 example}\cell
#> \intbl\row\pard

r2rtf:::as_rtf_init() initiates rtf encode with ‘English’ as the default language.

r2rtf:::as_rtf_init() %>% cat()

r2rtf:::as_rtf_margin() define a page margin.

head(iris) %>%
  rtf_page() %>%
  r2rtf::as_rtf_margin() %>%
  cat()

r2rtf:::as_rtf_new_page() initiates new page encode.

r2rtf::as_rtf_new_page() %>% cat()

r2rtf:::as_rtf_page() define a page width (\paperw) and height(\paperh).

head(iris) %>%
  rtf_page() %>%
  r2rtf:::as_rtf_page() %>%
  cat()

r2rtf:::as_rtf_pageby() define a page_by encoding when argument page_by is not NULL in rtf_body().

iris %>%
  rtf_body(page_by = "Species") %>%
  r2rtf:::as_rtf_pageby() %>%
  cat()

r2rtf:::as_rtf_paragraph() define a title/text attribute as paragraph.

r2rtf:::as_rtf_paragraph(attr(head(iris) %>% rtf_title("example"), "rtf_title")) %>% cat()

r2rtf:::as_rtf_source() define the source encode.

head(iris) %>%
  rtf_source("example") %>%
  r2rtf:::as_rtf_source() %>%
  cat()

r2rtf:::as_rtf_subline() define the subline encode.

head(iris) %>%
  rtf_subline("example") %>%
  r2rtf:::as_rtf_subline() %>%
  cat()

r2rtf:::as_rtf_table() define the table encode.

head(iris) %>%
  rtf_title("example") %>%
  r2rtf:::as_rtf_table() %>%
  cat()

r2rtf:::as_rtf_title() define the title encode.

head(iris) %>%
  rtf_title("example") %>%
  r2rtf:::as_rtf_title() %>%
  cat()

In a minimal example, we can create an RTF file by combining different pieces of RTF code. For simplicity, we only show the first two rows of iris data

tbl <- iris[1:2, ] %>% rtf_body()

paste(
  r2rtf:::as_rtf_init(),
  r2rtf:::as_rtf_font(),
  r2rtf:::as_rtf_page(tbl),
  r2rtf:::as_rtf_margin(tbl),
  r2rtf:::as_rtf_table(tbl),
  "}",
  sep = "\n"
) %>% cat()
#> {\rtf1\ansi
#> \deff0\deflang1033
#> {\fonttbl{\f0\froman\fcharset161\fprq2 Times New Roman;}
#> {\f1\froman\fcharset161\fprq2 Times New Roman Greek;}
#> {\f2\fswiss\fcharset161\fprq2 Arial Greek;}
#> {\f3\fswiss\fcharset161\fprq2 Arial;}
#> {\f4\fswiss\fcharset161\fprq2 Helvetica;}
#> {\f5\fswiss\fcharset161\fprq2 Calibri;}
#> {\f6\froman\fcharset161\fprq2 Georgia;}
#> {\f7\ffroman\fcharset161\fprq2 Cambria;}
#> {\f8\fmodern\fcharset161\fprq2 Courier New;}
#> {\f9\ftech\fcharset161\fprq2 Symbol;}
#> }
#> 
#> \paperw12240\paperh15840
#> 
#> \margl1800\margr1440\margt2520\margb1800\headery2520\footery1449
#> 
#> \trowd\trgaph108\trleft0\trqc
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrs\brdrw15\clbrdrb\brdrw15\clvertalt\cellx1800
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrs\brdrw15\clbrdrb\brdrw15\clvertalt\cellx3600
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrs\brdrw15\clbrdrb\brdrw15\clvertalt\cellx5400
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrs\brdrw15\clbrdrb\brdrw15\clvertalt\cellx7200
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrs\brdrw15\clbrdrr\brdrs\brdrw15\clbrdrb\brdrw15\clvertalt\cellx9000
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 5.1}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 3.5}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 1.4}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 0.2}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 setosa}\cell
#> \intbl\row\pard
#> } {\rtf1\ansi
#> \deff0\deflang1033
#> {\fonttbl{\f0\froman\fcharset161\fprq2 Times New Roman;}
#> {\f1\froman\fcharset161\fprq2 Times New Roman Greek;}
#> {\f2\fswiss\fcharset161\fprq2 Arial Greek;}
#> {\f3\fswiss\fcharset161\fprq2 Arial;}
#> {\f4\fswiss\fcharset161\fprq2 Helvetica;}
#> {\f5\fswiss\fcharset161\fprq2 Calibri;}
#> {\f6\froman\fcharset161\fprq2 Georgia;}
#> {\f7\ffroman\fcharset161\fprq2 Cambria;}
#> {\f8\fmodern\fcharset161\fprq2 Courier New;}
#> {\f9\ftech\fcharset161\fprq2 Symbol;}
#> }
#> 
#> \paperw12240\paperh15840
#> 
#> \margl1800\margr1440\margt2520\margb1800\headery2520\footery1449
#> 
#> \trowd\trgaph108\trleft0\trqc
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx1800
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx3600
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx5400
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx7200
#> \clbrdrl\brdrs\brdrw15\clbrdrt\brdrw15\clbrdrr\brdrs\brdrw15\clbrdrb\brdrs\brdrw15\clvertalt\cellx9000
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 4.9}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 3}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 1.4}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 0.2}\cell
#> \pard\hyphpar0\sb15\sa15\fi0\li0\ri0\qc\fs18{\f0 setosa}\cell
#> \intbl\row\pard
#> }

If we save the RTF code into an .rtf file. Microsoft Word or other RTF Viewer software can display the file properly.

Save RTF File

After everything has been translated into RTF code using rtf_encode(). It is a simple step to save all RTF code into an .rtf file.

write_rtf() is a simple wrapper of the write() function, which exports a single RTF string into the output file.

A simple example is as in the overview section.

head(iris) %>%
  rtf_body() %>% # Step 1 Add attributes
  rtf_encode() %>% # Step 2 Convert attributes to RTF encode
  write_rtf(file = "tmp.rtf") # Step 3 Write to a .rtf file

Dictionary and Conversion

As mentioned earlier, functions in the dictionary.R file contains the most commonly used font types, formats, border types, etc. All these attributes are mapped in a data frame which contains the element names and corresponding RTF encode. User is only allowed to key in whatever is defined in dictionary for the rtf_ functions, or error message will be provided as a result from match_arg().

r2rtf:::font_type()
#>    type                  name     style rtf_code    family
#> 1     1       Times New Roman  \\froman     \\f0     Times
#> 2     2 Times New Roman Greek  \\froman     \\f1     Times
#> 3     3           Arial Greek  \\fswiss     \\f2   ArialMT
#> 4     4                 Arial  \\fswiss     \\f3   ArialMT
#> 5     5             Helvetica  \\fswiss     \\f4 Helvetica
#> 6     6               Calibri  \\fswiss     \\f5   Calibri
#> 7     7               Georgia  \\froman     \\f6   Georgia
#> 8     8               Cambria \\ffroman     \\f7   Cambria
#> 9     9           Courier New \\fmodern     \\f8   Courier
#> 10   10                Symbol   \\ftech     \\f9     Times

Superscripts, under scripts are taken care of by using convert() function to translate them into LaTeX code and Unicode. For example:

r2rtf:::convert(c("^", "<="))
#> [1] "\\super "       "\\uc1\\u8804* "

Pageby

It is commonly seen a variable displayed in headline as group category (e.g. baseline characteristic table). To achieve this, user first need to sort input data frame by page_by variable then define it in rtf_body(). Border and text attributes are controlled together with other variables in the vectors.

iris %>%
  arrange(Species) %>%
  rtf_colheader("Sepal Length | Sepal Width | Petal Length | PetalWidth", ) %>%
  rtf_body(
    page_by = c("Species"),
    border_top = c(rep("", 4), c("single")),
    border_bottom = c(rep("", 4), c("single")),
    text_justification = c(rep("c", 4), c("l")),
    new_page = TRUE
  ) %>%
  rtf_encode() %>%
  write_rtf("pageby.rtf")

Utility functions

  • check_args(): Function for argument checking for types, length or dimension, this function is used for all export functions except write_rtf().
  • match_arg(): Function for argument verification on input values to see whether they match dictionary() defined values, this function is used for all export functions except write_rtf().
  • footnote_source_space(): Function to derive space adjustment, whose results could be used in rtf_footnote() and rtf_source() when indentation is defined by user.