0

I need to insert a custom TOC into an officer document. In this case I need to insert a list of tables that are created using the level 6 headers. The TOC field I need to insert is:

{TOC \o "6-6" \* MERGEFORMAT}

The block_toc function doesn't seem to allow me to do this. So was thinking of doing is using the internal functions within that function (e.g., run_seqfield, to_wml, etc) to accomplish this. I was wondering if anyone had any other ideas that utilized the more standard officer functions.

In the example below I'm trying to create a list of tables using the style option with block_toc. When I run this it gives me "No table of contents entries found."

library(officer)
library(flextable)
library(magrittr)


tab_seq_id = "Table"

# empty report
rpt = read_docx()

bt <- block_toc(style = "Table Caption")
out <- to_wml(bt, add_ns = TRUE)
rpt <- body_add_xml(rpt, str = out, pos = "after")

mytxt = paste(rep("The quick brown fox jumped over the lazy dog.", 30), collapse=" ")
# Making a table
ft = flextable(head(mtcars))

# Creating some sections with text
rpt = body_add_fpar(rpt, fpar("A section"), style="heading 1")

fptxt  = fpar(mytxt)

rpt = officer::body_add_fpar(rpt, fptxt)

rpt = body_add_fpar(rpt, fpar("Another section"), style="heading 1")

fptxt  = fpar("This is a cross reference to the first table (Table ",
              run_reference("my_table"), 
              ") and this is a reference to the second table (Table ",
              run_reference("my_second_table"), ")." ,
              ") and a third table in a new section (Table ",
              run_reference("my_third_table"), ")." )

long_cap = "This is my table caption. It can span many lines and take up much space on the page." 

#-------------------------------------------------------
# Normal table
run_num = officer::run_autonum(seq_id     = tab_seq_id,
                               pre_label  = "Table ",
                               post_label = ".",
                               bkm        = "my_table")

caption = officer::block_caption(long_cap,
                                 style  = "Table Caption",
                                 autonum = run_num )

rpt = officer::body_add_fpar(rpt, fptxt)


rpt  = flextable::body_add_flextable(rpt, value=ft)
rpt = officer::body_add_caption(rpt, caption)
#-------------------------------------------------------
# Table with the section number in it
runs =        list(
  run_word_field("STYLEREF 1 \\s"),
  ftext("-"),
  officer::run_autonum(pre_label = "", seq_id = tab_seq_id, post_label=""))
rb_res = run_bookmark("my_second_table", runs)

rpt  = flextable::body_add_flextable(rpt, value=ft)
rpt  = officer::body_add_fpar(rpt, fpar("Table ", rb_res, ".    ", long_cap), style = "Table Caption")


# Creating some sections with text
rpt = body_add_fpar(rpt, fpar("A third section"), style="heading 1")
#-------------------------------------------------------
# Table with the section number in it
runs =        list(
  run_word_field("STYLEREF 1 \\s"),
  ftext("-"),
  officer::run_autonum(pre_label = "", seq_id = tab_seq_id, start_at = 1, post_label=""))
rb_res = run_bookmark("my_third_table", runs)

rpt  = flextable::body_add_flextable(rpt, value=ft)
rpt  = officer::body_add_fpar(rpt, fpar("Table ", rb_res, ".    ", long_cap), style = "Table Caption")
print(rpt, "fig_sec_num.docx")
  • the `bloc_toc()`function has an argument named `style`, look at the example in the manual for an illustration. In your case, it could be something like `block_toc(style = "heading 6")`, you need to find out the correct style name, "heading 6", "Heading 6", "Titre 6", ... See `styles_info` – David Gohel Oct 31 '21 at 22:50
  • I edited the original post to provide an example where I'm trying to use the style argument to `block_toc` to get a list of figures. – John Harrold Nov 01 '21 at 03:31
  • I should say that example doesn't work for me. – John Harrold Nov 01 '21 at 03:41
  • When I look at your update with code, I can't see any reference to Heading 6 so I am not surprised! It need to be used. I will post an answer, the code can be simplified – David Gohel Nov 01 '21 at 12:08

2 Answers2

1

This should help (also remember to refresh the TOC from within Word):

library(officer)
library(flextable)
library(magrittr)

mytxt <- paste(rep("The quick brown fox jumped over the lazy dog.", 30), collapse = " ")
long_cap <- "This is my table caption. It can span many lines and take up much space on the page."
tab_seq_id <- "Table"

ft <- flextable(head(mtcars))

get_caption <- function(bookmark){
  par <- list(
    ftext("Table "),
    run_word_field("STYLEREF 1 \\s"),
    ftext("-"),
    run_word_field("SEQ Table \u005C* Arabic")
  )
  run_bookmark(bookmark, par)
}


fptxt <- fpar(
  "This is a cross reference to the first table (",
  run_reference("my_table1"),
  ") and this is a reference to the second table (",
  run_reference("my_table2"), ").",
  ") and a third table in a new section (",
  run_reference("my_table3"), ")."
)


rpt <- read_docx() %>% 
  body_add_toc(style = "Table Caption") %>% 
  body_add_par("A section", style = "heading 1") %>% 
  body_add_par(value = mytxt, style = "Normal") %>% 
  body_add_par("Another section", style = "heading 1") %>% 
  body_add_fpar(fptxt) %>% 
  body_add_flextable(value = ft) %>% 
  body_add_fpar(value = fpar(get_caption(bookmark = "my_table1"), " ", long_cap), style = "Table Caption") %>% 
  body_add_flextable(value = ft) %>% 
  body_add_fpar(value = fpar(get_caption(bookmark = "my_table2"), " ", long_cap), style = "Table Caption") %>% 
  body_add_par("A third section", style = "heading 1") %>% 
  body_add_flextable(value = ft) %>% 
  body_add_fpar(value = fpar(get_caption(bookmark = "my_table3"), " ", long_cap), style = "Table Caption")
  


print(rpt, "fig_sec_num.docx")

enter image description here

David Gohel
  • 9,180
  • 2
  • 16
  • 34
  • When I run that, open the file in Word, select all of the text, right click and select update field I still get "No table of contents entries found". If it's relevant, I'm using officer 0.4.0 and Word for Mac 16.54. – John Harrold Nov 01 '21 at 15:08
  • Same versions for me. Sorry, no idea of what is your issue. All is as expected on my machine – David Gohel Nov 01 '21 at 15:22
  • I just switched over to Windows. It has version 0.3.18 of officer (not that it should matter). I reran the script there and when I selected "update field" in the word document, it gave me this message: Creating a table of contents? Start by applying a heading style from the Styles gallery to the selected text. When I clicked OK it still gives me the "No table of contents entries found". I'm not sure what I'm doing wrong on my end, but I'm doing it consistently on two different OSs :). – John Harrold Nov 01 '21 at 15:40
0

TLDR; I need to add separator="," to body_add_toc() call above

I started looking at the TOC field codes are generated when specifying a Word Style. The example David provided worked on his computer but I couldn't get it to generate a TOC on my end. I started playing with the field codes that were generated to try to get at the problem. The field code that is generated by officer for me in Davids solution is:

{TOC \h \z \t "Table Caption;1"}

That wasn't working for me. Then I found that if I change the field code to one of the following it will generate the TOC correctly:

{TOC \h \z \t "Table Caption"}
{TOC \h \z \t "Table Caption,1"}

In the first example I'm omitting the ";1" completely and in the second example I'm changing ";" to ",". So I started googling and if I'm reading this correctly there is some kind of regional setting that may be causing this:

https://answers.microsoft.com/en-us/msoffice/forum/all/table-of-contents-word-2016-word-2013/842156d0-51dd-4726-9c36-343e57bf9f92

Then I read the documentation for body_add_toc and realized there is an option there for this very problem. I'd seen it before, but I had no idea why it was there, but that fixed everything for me.

Thanks for your patience David.