19 Officer

19.1 ⭐️Overview

I don’t have time to do this right today, but I do make some good notes on using officer.

Good examples to check out:

  • LEAD panel monthly report.
  • Sun Study report.
  • stroke study -> table_characteristics_by_network.Rmd.
  • L2C quarterly reports.
  • L2C Distress Tolerance Scale Paper

At least until you figure out make files, I think the best thing to do is save tables and figures as files in the docs folder. Create a single R script to compile them all together and create the report. Use comments to specify what file the table/figure is created in.

Still trying to figure out if it’s better to write text in Word and then add in tables and charts with bookmarks, or is it better to write everything in R?

In general, it seems helpful/expedient to do as much of the prose and formatting as possible directly in the Word template. Then, just add numbers, tables, and graphs from R with bookmarks.

⚠️Warning: When editing the code chunks below, you will need to add “../../” in front of file paths that retrieve Word docs from or save word docs to the examples folder if you want them to work when you run them interactively. For example, “../../examples/Word Template for Officer.docx”. However, you have to delete the “../../” from the file paths in order for build book to work.

19.2 🌎Useful websites

19.3 📦Load packages

library(dplyr, warn.conflicts = FALSE)
library(officer, warn.conflicts = FALSE)
library(flextable, warn.conflicts = FALSE)

19.4 Formatted text

Can start with a template

doc <- read_docx("officer_word_template.docx")

Or not

doc <- read_docx() %>%
  body_add_par("DETECT LEAD Panel Initial Review Report", style = "heading 1") %>%
  body_add_par(paste("Updated: ", Sys.Date())) %>%
  body_add_par("") %>%
  body_add_fpar(
    fpar(
      ftext(
        "Total Initial Assessments:",
        prop = fp_text(
          font.family = "Times New Roman", font.size = 11
        )
      )
    )
  )
print(
  doc, 
  "examples/officer_formatted_text.docx"
)

Link to Word Document on Dropbox

# Output Word document
# Update the year and month in the file name dynamically
print(
  doc, 
  paste(Sys.Date() %>% format("%Y-%m"), " Initial Review Report.docx")
)

19.5 Bulleted list

It’s possible to do. You need to make sure you create a bullet style in the template Word doc.

See here: https://github.com/davidgohel/officer/issues/262

#  Create Word doc
ss_report <- read_docx("examples/Word Template for Officer.docx") %>%
  body_add_par("Sun Study Outcomes Report", style = "heading 1") %>% 
  body_add_par("\n") %>% 
  body_add_par(paste("Updated: ", Sys.Date())) %>% 
  body_add_par("\n") %>% 
  
  # Add bulleted list
  body_add_par("item 1", style = "bullet") %>%
  body_add_par("item 2", style = "bullet") %>%
  body_add_par("item 3", style = "bullet")
# Output Word document
print(
  ss_report, 
  "examples/officer_bullets.docx"
)

Link to Word Document on Dropbox

19.6 Adding on to the end of a file.

From the Sun Study. Need to clean up when I get time.

Start with the same document we just made above.

#  Create Word doc
ss_report <- read_docx("examples/Word Template for Officer.docx") %>%
  body_add_par("Sun Study Outcomes Report", style = "heading 1") %>% 
  body_add_par("\n") %>% 
  body_add_par(paste("Updated: ", Sys.Date())) %>% 
  body_add_par("\n")
# Output Word document
print(
  ss_report, 
  "examples/officer_bullets.docx"
)

Link to Word Document on Dropbox

Now add a new section to the document.

Section 1. Report on missing data

# Load report and add to the end?
ss_report <- read_docx("examples/officer_bullets.docx") %>%
  body_add_par("Test Text")
# Output Word document
print(
  ss_report, 
  "examples/officer_add_to_end.docx"
)

Link to Word Document on Dropbox

This works. However, there was no advantage to doing it this way in the Sun Study example. In fact, what I’ve found in general is that it typically works best to do as much adding text and formatting things as possible directly in the Word template. Then, just pretty much add flextables and plots to the Word template.

19.7 Using bookmarks

Writing everything in an R script and then compiling to a Word document works alright, but wrapping everything in officer functions makes it hard to read. In this section, I’m seeing if I can type all the narrative in the Word template, then just add tables and figures into the Word template at bookmarks.

Website about bookmarks: https://davidgohel.github.io/officer/reference/body_replace_text_at_bkm.html

Have to create this with point and click in Word: http://howtomicrosoftofficetutorials.blogspot.com/2017/03/use-bookmarks-in-word-2016-for-mac.html

Use Insert > Links > Bookmark

If you don’t add other text around the bookmark, you will get this error: Error: bookmark ‘example_table’ does not end in the same paragraph (or is on the whole paragraph)

A hacky fix is to enclose bookmark text with bm text . Make sure only the “text” part is the bookmark.

I also found that you can’t continue piping the document after you’ve added a flextable at a bookmark. You have to start overwriting the Word document object. See LEAD Panel Summary Report as an example.

doc_w_bookmarks <- read_docx("examples/Word Template for Officer Bookmarks.docx") %>% 
  body_replace_flextable_at_bkm(
    bookmark = "example_table",
    value = flextable(head(mtcars))
  )

print(
  doc_w_bookmarks,
  "examples/officer_bookmarks.docx"
)

Link to Word Document on Dropbox

After figuring this out, I think it makes more sense to write in Word and replace tables and figures with bookmarks.

For ggplot graphs, it looks like you need to use ggsave to save the plot as an image file. Then, use the image file as the value to body_replace_img_at_bkm()

Don’t forget to use external_img()

Example from L2C quarterly report:

doc <- doc %>% 
  body_replace_text_at_bkm("n_phone_terminations", as.character(n_phone_terminations)) %>% 
  body_replace_img_at_bkm(
    "fig_phone_terminations", 
    external_img("fig_1.jpeg", width = 7, height = 4)
  )

For images, just add the bookmark to the end of the title without surrounding it with “bm”. For example, “Figure 1. Overall trend in sunscreen application outcomes by study p_overall” Where “p_overall is the bookmark. Otherwise, the”bm” will still be there.

For best results, the width and height used in ggsave() should match the width and height in external_img(). Deciding on the correct width and height might take some trial and error.

To add an updated date, use this:

doc_w_bookmarks <- doc_w_bookmarks %>% 
  body_replace_text_at_bkm("date", as.character(Sys.Date()))

19.7.1 Embedding cells form one table into cells of an existing Word tables at a bookmark with flextable and officer

Posting on Stack Overflow – Update when you have a solution

In Word, it is possible to copy and paste a subsection of one table into another table. For example, if I have the two Word tables below, I can copy column 2, rows 2-5 from the first table and paste them into column 2, rows 4-7 in the second table.

I can highlight the cells that I want to copy in the first table and and paste them into the cells of the second table.

I’m wondering if there is a programmatic way to do the same operation with flextable and officer. It’s probably worth mentioning the following to parameters:

  1. Creating the second table from scratch with the values from the first table included won’t work for my problem – or at least it’s a solution that I’m trying to avoid.

  2. Add the values from the first table to the cells of the second table one at a time with bookmarks won’t work for my problme – or at least it’s a solution that I’m trying to avoid.

I’ve tried the following potential solutions so far. They all begin with the following template Word document with bookmarks embedded in the table.

Additionally, here is the code I’m using to create the “First table” example data.

first_table <- tibble(
  n_items = 1:4,
  n = c(100, 40, 20, 5)
)

Method 1: Add values to a bookmark in the second table

Start by reading in the Word template document.

Then, I tried adding the values from first_table to the bookmark in the second table.

doc <- doc %>% 
  body_replace_text_at_bkm(
    "bm_add_rows_01", 
    first_table %>% pull(n) %>% as.character()
  )
## Error in body_replace_text_at_bkm(., "bm_add_rows_01", first_table %>% : is_scalar_character(value) is not TRUE

That code results in the following error:

Error in body_replace_text_at_bkm(., "bm_add_rows_01", first_table %>% : 
is_scalar_character(value) is not TRUE

So, I can’t add a vector of values to the bookmark.

Method 2: Add a single-column flextable to a bookmark in the second table

To do this, I need to first coerce first_table into a single-column flextable object.

first_table_ft <- first_table %>% 
  select(n) %>% 
  flextable() %>% 
  delete_part("header") %>% 
  border_remove()

Then, add the single-column flextable to the bookmark in the second table.

doc <- doc %>% 
  body_replace_flextable_at_bkm("bm_add_rows_01", first_table_ft)
print(
  doc, 
  "embedding_flextables.docx"
)

That returns the following result – a four-row table embedded into a single cell of the second table. This result makes sense, but it isn’t the result I’m attempting to achieve.

Method 3: Add a single-column flextable to a bookmark in the second table with cells merged vertically

For my third attempt, I tried adding the bookmark to the second table with rows 4-7 merged in column 2.

Then, adding the single-column flextable to the bookmark in the second table as before.

doc <- doc %>% 
  body_replace_flextable_at_bkm("bm_add_rows_02", first_table_ft)
print(
  doc, 
  "embedding_flextables.docx"
)

Which again returns a result that isn’t quite what I’m looking for.

Any solutions and/or constructive feedback is appreciated!