Table and Figure cross-reference officer R - r

I would like to be able to cross-reference a table or figure in a word document using the officer R package.
I have come across these materials so far but they do not seem to have a solution:
https://davidgohel.github.io/officer/articles/word.html#table-and-image-captions
and a similar question
add caption to flextable in docx
In both of these I can only insert a caption as a level 2 header and not a true table caption.
What I want to be able to do in Word is Insert -> Cross-reference and go to Reference type: Table and see my caption there. Right now I can only see the caption under Numbered item.
Does this functionality exist in officer or anywhere else?

In word, the table numbers use the { SEQ \\# arabic } pattern, but references to them use { REF bookmark \h }. We can use this to make new code which can reference a SEQ field.
code:
ft <- regulartable(head(iris)) # create flextable
str <- paste0(' REF ft \\h ') # create string to be used as reference to future bookmark
doc <- read_docx() %>%
body_add_par('This is my caption' , style = 'Normal') %>% # add caption
slip_in_seqfield(str = "SEQ Table \\# arabic",
style = 'Default Paragraph Font',
pos = "before") %>% # add number for table
body_bookmark('ft') %>% # add bookmark on the number
slip_in_text("Table ",
style = 'Default Paragraph Font',
pos = "before") %>% # add the word 'table'
body_add_flextable(value = ft, align = 'left') %>% # add flextable
body_add_break() %>% # insert a break (optional)
slip_in_text('As you can see in Table',
style = 'Default Paragraph Font',
pos = 'after') %>% # add the text you want before the table reference
slip_in_seqfield(str = str,
style = 'Default Paragraph Font',
pos = 'after') %>% # add the reference to the table you just added
slip_in_text(', there are a lot of iris flowers.',
style = 'Default Paragraph Font',
pos = 'after') %>% # add the rest of the text
print('Iris_test.docx') # print
Hope this helps :)

Just for the record, you can do this a bit easier now by using some helper functions from the {crosstable} package.
Disclaimer: I am the developer of that package and these functions were highly inspired by #morgan121's answer. Thanks Morgan!
Here is an example:
library(officer)
library(crosstable)
library(ggplot2)
options(crosstable_units="cm")
ft = regulartable(head(iris))
my_plot = ggplot(data = iris ) +
geom_point(mapping = aes(Sepal.Length, Petal.Length))
doc = read_docx() %>%
body_add_title("Dataset iris", 1) %>%
body_add_normal("Table \\#ref(table_iris) displays the 6 first rows of the iris dataset.") %>%
body_add_flextable(ft) %>%
body_add_table_legend("Iris head", bookmark="table_iris") %>%
body_add_normal("Let's add a figure as well. You can see in Figure \\#ref(fig_iris) that sepal length is somehow correlated with petal length.") %>%
body_add_figure_legend("Relation between Petal length and Sepal length", bookmark="fig_iris") %>%
body_add_gg2(my_plot, w=14, h=10, scale=1.5)
print(doc , 'Iris_test.docx')
More info on https://danchaltiel.github.io/crosstable/articles/crosstable-report.html.
As with morgan121's code, you have to select all the text in MS Word and press F9 twice for the numbers to update properly.

Related

Modify xml_document in officer in R

I have an rdocx and I want to manipulate something in the xml code. That's my document:
library(officer)
doc <- read_docx() %>%
body_add_par("centered text", style = "centered") %>%
slip_in_seqfield("STYLEREF 1 \\s") %>%
slip_in_text("\u2011") %>%
slip_in_seqfield(sprintf("SEQ %s \\* ARABIC \\s 1", "Table")) %>%
slip_in_text(str_c(": ", "My Caption")) %>%
body_bookmark("my_bookmark")
With doc$doc_obj$get() I can get the xml code with classes xml_document and xml_node. Now I want to replace some code, in detail I want the part with w:bookmarkEnd to appear later so the bookmarked part gets bigger. How can I achieve this? If I could achieve this with str_replace it would be awesome.
You can use run_bookmark() as in the following example (the manual does not state that lists are supported, I'll add that info soon):
library(officer)
bkm <- run_bookmark(
bkm = "test",
list(
run_word_field(field = "SEQ tab \\* ARABIC \\s"),
ftext(" Table", prop = fp_text_lite(color = "red"))
)
)
doc <- read_docx()
doc <- body_add_fpar(
x = doc,
value = fpar(bkm)
)
# how to make a reference to the bkm
doc <- body_add_fpar(
x = doc,
value = fpar(run_reference("test"))
)
print(doc, "zz.docx")

Using officer in R to hyperlink to another slide within a flextable cell

Using officer in R, I've used ph_slidelink() to hyperlink a text box to another slide in the presentation, and I've used compose() and hyperlink_text() to hyperlink a cell within a flextable. My question is: is there a way to combine these, and to hyperlink to another slide in the presentation within the cell of a flextable?
Here's a very simple example of code I'd like to transform:
library(officer)
library(flextable)
library(magrittr)
ft <- data.frame(slide_number = seq(3)) %>%
flextable() %>%
width(width = 3)
doc <- read_pptx() %>%
add_slide() %>%
ph_with("Table of Contents", location = ph_location_label("Title 1")) %>%
ph_with(ft, location = ph_location_label("Content Placeholder 2"))
for (i in seq(3)) {
doc <- doc %>%
add_slide() %>%
ph_with(paste("Slide", i), location = ph_location_label("Title 1"))
}
print(doc, target = "~/Desktop/officer_example.pptx" )
...and in this case I'd like the 1/2/3 in the table of contents (here) to link to slides 1/2/3.
Is this possible?

Setting line height in table created using flextable inserted into powerpoint using officer

I have drafted some dataframes with text inside that linebreaks are present in each cell, the dataframe is converted to flextable and then inserted into a powerpoint slide using officer. I found the line height a bit too much, I tried using the height_all function in flextable to make reduce the line height, but it is not working. Please find the sample code as below:
library(officer)
library(dplyr)
pptx.output.st00 <- read_pptx()
data(iris)
data.df <- head(iris) %>%
as_tibble %>%
mutate_all(.,as.character) %>%
mutate_all(.,~paste0(.,'\ntesting'))
pptx.tbl <- data.df %>%
flextable %>%
height_all(height = 0.01) # this line is not working
pptx.output.st01 <- pptx.output.st00 %>%
add_slide(.,layout = 'Title and Content',master = 'Office Theme') %>%
ph_with(.,value=pptx.tbl,location=ph_location(type='body'))
print(pptx.output.st01,'presentation.output.pptx')
Currently I need to manually change the paragraph settings for table as the screen capture below:
Is there a way in officer of flextable to set up line height for table? Thanks!
I didn't satisfied by this ad hoc way, but padding(padding.top = 0, padding.bottom = 0.5) and height_all(0.45) gives a bit better output.
pptx.output.st01 <- pptx.output.st00 %>%
add_slide(.,layout = 'Title and Content',master = 'Office Theme') %>%
ph_with(.,
value=pptx.tbl %>%
padding(padding.top = 0, padding.bottom = 0.5) %>%
height_all(0.45),
location=ph_location(type='body'))

Second line of text in pptx subtitle

I have added a Title Slide to a pptx object, and want to add a second line in the subtitle location.
I've tried using ph_add_fpar function with no success. I'm still new with the package so probably not using the correct functions!
mypowerpoint <- read_pptx() %>%
add_slide("Title Slide","Office Theme") %>%
ph_with("Flashy Title",ph_location_type("ctrTitle",position_right = TRUE)) %>%
ph_with("Catchy Subtitle 1",ph_location_type("subTitle")) %>%
ph_with("Catchy Subtitle 2",ph_location_type("subTitle"))
Running the above gives me two subtitles overlaid on top of each other, rather than the second subtitle text as a new line in the same objects as the first. Can anyone please tell me what I'm missing, or a better function to use for this?
ph_with support vector with length > 1
library(magrittr)
library(officer)
mypowerpoint <- read_pptx() %>%
add_slide("Title Slide","Office Theme") %>%
ph_with("Flashy Title",ph_location_type("ctrTitle",position_right = TRUE)) %>%
ph_with(c("Catchy Subtitle 1", "Catchy Subtitle 2"),ph_location_type("subTitle"))

Using dynamic popups in leaflet

I'm making a leaflet map with popups. The popups give information of the ID of the polygon being selected. The problem I have is that the name of the field use for ID can change, so the way I was originally doing doesn't work anymore.
Here is a reproducible example:
## preparing the RE:
library(maps); library(sf); library(leaflet); library(htmltools)
w = st_as_sf(map('world', plot = FALSE, fill = TRUE))
What I use to do is prepare a html string to display:
text <- paste0("<b>ID %s</b>")
Then call the leaflet and populating the popups with sprintf and htmlEscape
leaflet(data=w) %>% addTiles() %>%
addPolygons(
popup = ~sprintf(
text,
htmlEscape(ID)
)
)
This works great :
However, the field isn't always called ID, but the name is known and in a r object (here called vari):
colnames(w) <- c("geometry", "country")
vari <- "country"
text <- paste0("<b>", vari, " %s</b>")
leaflet(data=w) %>% addTiles() %>%
addPolygons(
popup = ~sprintf(
text,
htmlEscape(vari)
)
)
This doesn't work:
I've tried using as.name so it would be considered as a symbol but it doesn't work:
vari <- as.name("country")
text <- paste0("<b>", vari, " %s</b>")
leaflet(data=w) %>% addTiles() %>%
addPolygons(
popup = ~sprintf(
text,
htmlEscape(vari)
)
)
Error in sprintf(text, htmlEscape(vari)) :
invalid type of argument[1]: 'symbol'
Any idea how to fix that? BTW, my HTML is more complex than in my example (uses more variables, however, all other variable names are fixed, only the ID field change).
I'm not sure if this is what you're after, but it sounds like you want to be able to simply populate any popup with the data from a column that doesn't necessarily have the name ID, but is simply an identifier agnostic of title? So in this case country? I fear this is an ugly cheat, but given your data structure contains a data.frame where the coords are actually a list structure, i simply test the dataframe columns for class, whichever is a character, use that as the index and directly call
leaflet(data=w) %>% addTiles() %>%
addPolygons(
popup = ~sprintf('<b>ID %s</b>', w[[names(which(mapply(is.character, w)))]])
)

Resources