/
design-pattern.Rmd
351 lines (272 loc) · 11.1 KB
/
design-pattern.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
---
title: r2rtf Design Pattern
output:
html_document:
df_print: paged
toc: yes
toc_depth: '2'
toc_float: true
resource_files:
- pdf/*.pdf
- fig/*.png
- html/*.html
- html/*.png
- docx/*.docx
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
```{r, message=FALSE, echo=FALSE}
library(dplyr)
library(r2rtf)
```
## 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.
```{r}
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
```
```{r, out.width = "100%", out.height = "400px", echo = FALSE, fig.align = "center"}
knitr::include_graphics("pdf/ex-tbl.pdf")
```
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.
```{r}
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
```
```{r, out.width = "100%", out.height = "400px", echo = FALSE, fig.align = "center"}
knitr::include_graphics("pdf/ex-fig.pdf")
```
## 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.
```{r, eval = FALSE}
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.
```{r}
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.
```{r}
data.frame(value = unlist(attr(tbl, "page")))
```
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.).
```{r}
head(iris) %>% rtf_body()
```
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.
```{r, eval = FALSE}
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
```
```{r, eval = FALSE}
"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](https://web.archive.org/web/20221008045825/http://www.snake.net/software/RTF/RTF-Spec-1.7.pdf).
A good reference is [RTF Pocket Guide](https://www.oreilly.com/library/view/rtf-pocket-guide/9781449302047/).
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.
```{r, eval = FALSE}
head(iris) %>%
rtf_body() %>%
r2rtf:::as_rtf_colheader() %>%
cat()
```
`r2rtf:::as_rtf_color()` define the color to be used in text or border.
```{r, eval = FALSE}
head(iris) %>%
rtf_body(text_color = "red") %>%
r2rtf:::as_rtf_color() %>%
cat()
```
`r2rtf:::as_rtf_end()` define the end of rtf encode string.
```{r, eval = FALSE}
r2rtf:::as_rtf_end() %>% cat()
```
`r2rtf:::as_rtf_font()` define the font encode.
```{r, eval = FALSE}
r2rtf:::as_rtf_font() %>% cat()
```
`r2rtf:::as_rtf_footnote()` define the footnote encode.
```{r}
head(iris) %>%
rtf_footnote("example") %>%
r2rtf:::as_rtf_footnote() %>%
cat()
```
`r2rtf:::as_rtf_init()` initiates rtf encode with 'English' as the default language.
```{r, eval = FALSE}
r2rtf:::as_rtf_init() %>% cat()
```
`r2rtf:::as_rtf_margin()` define a page margin.
```{r, eval = FALSE}
head(iris) %>%
rtf_page() %>%
r2rtf::as_rtf_margin() %>%
cat()
```
`r2rtf:::as_rtf_new_page()` initiates new page encode.
```{r, eval = FALSE}
r2rtf::as_rtf_new_page() %>% cat()
```
`r2rtf:::as_rtf_page()` define a page width (`\paperw`) and height(`\paperh`).
```{r, eval = FALSE}
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()`.
```{r, eval = FALSE}
iris %>%
rtf_body(page_by = "Species") %>%
r2rtf:::as_rtf_pageby() %>%
cat()
```
`r2rtf:::as_rtf_paragraph()` define a title/text attribute as paragraph.
```{r, eval = FALSE}
r2rtf:::as_rtf_paragraph(attr(head(iris) %>% rtf_title("example"), "rtf_title")) %>% cat()
```
`r2rtf:::as_rtf_source()` define the source encode.
```{r, eval = FALSE}
head(iris) %>%
rtf_source("example") %>%
r2rtf:::as_rtf_source() %>%
cat()
```
`r2rtf:::as_rtf_subline()` define the subline encode.
```{r, eval = FALSE}
head(iris) %>%
rtf_subline("example") %>%
r2rtf:::as_rtf_subline() %>%
cat()
```
`r2rtf:::as_rtf_table()` define the table encode.
```{r, eval = FALSE}
head(iris) %>%
rtf_title("example") %>%
r2rtf:::as_rtf_table() %>%
cat()
```
`r2rtf:::as_rtf_title()` define the title encode.
```{r, eval = FALSE}
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
```{r}
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()
```
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.
```{r, eval = FALSE}
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()`.
```{r}
r2rtf:::font_type()
```
Superscripts, under scripts are taken care of by using `convert()` function to translate them into LaTeX code and Unicode. For example:
```{r}
r2rtf:::convert(c("^", "<="))
```
## 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.
```{r, eval = FALSE}
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.