Best way to insert a custom TOC in officer - officer

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")

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")

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.

Related

KableExtra html table not collapsing rows

I have a simple Table that I want to visualize in an html format using kableExtra. This table has a few repeated cells in the first column and I would like to collapse these cells into one. Only problem is that the package isn't letting me do that. How can I solve this?
This is my data:
df <- data.frame( Vegitation = c("Tree", "Tree", "Tree" , "Fruit", "Fruit", "Water"),
Non_sense_var1 = c(17,14,1,20,21,0),
Non_sense_var2 = c(15,1,11,2,2.1,60),
Non_sense_var3 = c(4,6,14,2,7,7)
)
And this is the code for my table:
header_line <- c("Vegitation", "Value 1", "Value 2", "Value 3")
kbl(df, escape = F, align = 'lcccc')%>%
add_header_above( header_line, bold = T, line = F, font_size = 11) %>%
kable_styling(full_width = T, font_size = 10, html_font = 'arial') %>%
kable_classic() %>%
column_spec(1, width = "2.2cm", bold = TRUE ) %>%
column_spec(2, width = "2.2cm") %>%
column_spec(c(3:4), width = "2.2cm", color = '#FF7F0E') %>%
collapse_rows(1, valign = "top")
And when I try to run this code, this is what I get:
EDIT: Currently (the date being Sept. 27 2022), KableExtra has issues when collapsing rows in similar scenarios as to mentioned here. There is no official production fix in yet. You can try the fix via github update but what that did for me was mess up other formatting of my table. You can also try another package for your use case. As of now, those seem like the possible available options.
Given this issue seems to have been persistent with the kbl (https://github.com/haozhu233/kableExtra/issues/624), you may consider another package such as reactable, huxatable, or gt
a couple of examples:
df <- data.frame( Vegitation = c("Tree", "Tree", "Tree" , "Fruit", "Fruit", "Water"),
Non_sense_var1 = c(17,14,1,20,21,0),
Non_sense_var2 = c(15,1,11,2,2.1,60),
Non_sense_var3 = c(4,6,14,2,7,7)
)
header_line <- c("Vegitation", "Value 1", "Value 2", "Value 3")
library(reactable)
reactable(df,
columns = list(
Vegitation = colDef(
style = JS("function(rowInfo, column, state) {
const firstSorted = state.sorted[0]
// Merge cells if unsorted or sorting by school
if (!firstSorted || firstSorted.id === 'Vegitation') {
const prevRow = state.pageRows[rowInfo.viewIndex - 1]
if (prevRow && rowInfo.values['Vegitation'] === prevRow['Vegitation']) {
return { visibility: 'hidden' }
}
}
}"))))
library(gt)
df <- df %>%
group_by(Vegitation)
gt(df)

Several lines with different style in Caption in both html and docx - flextable

I need to show data caption, computer name and period in the header of table.
I have also requirements: zebra theme, merging cells if needed. That's why I chose flextable.
Here is my code:
library(officer) # border settings library
library(flextable) # drawing tables library
library(dplyr)
Caption <- "<b><big>Computer01.domain.com</big></b><br>Network Interface<br>Gbit Total/sec<br><small>2021-05-14 18:04 to 2021-05-25 13:29</small>"
bold_border <- fp_border(color="gray", width =2)
std_border <- fp_border(color="gray")
stub <- "2021-05-14 01:40 to 2021-05-17 08:26"
table_data <- data.frame (
Instance = c("Intel[R] Ethernet 10G",
"Intel[R] Ethernet Converged Network Adapter _1",
"Intel[R] Ethernet Converged Network Adapter _2",
"Intel[R] Ethernet 10G",
"Intel[R] Gigabit"),
Max = c(2.45, 2.41, 2.29, 2.17, 0),
Avg = c(0.15, 0.15, 0.15, 0.17, 0)
)
table <- table_data %>% flextable() %>%
set_caption(caption = Caption , html_escape = F) %>%
bg(bg = "#579FAD", part = "header") %>%
color(color = "white", part = "header") %>%
theme_zebra(
odd_header = "#579FAD",
odd_body = "#E0EFF4",
even_header = "transparent",
even_body = "transparent"
) %>%
set_table_properties(width = 1, layout = "autofit") %>%
hline(part="all", border = std_border ) %>%
vline(part="all", border = std_border ) %>%
border_outer( border = bold_border, part = "all" ) %>%
fix_border_issues() %>%
set_header_labels(
values = list(Instance = InstanceName ) ) %>%
flextable::font (part = "all" , fontname = "Calibri")
save_as_docx( table, path = file.path("c:\\temp", "test01.docx") )
save_as_html (table, path = file.path("c:\\temp", "test01.html"))
Here is what I got in html which is okay for me:
But in docx format my header style is not applied:
How can I create header like I did for html that can be saved to both html and docx?
If I have to create separate tables - one for html, other for docx - it's not so good but acceptable options. That case my question how to create header I made in html but for docx format?

how to get background color in html variable for a shiny app in r?

I am creating a shiny app and trying to get top two fields (Name & location) of the html popup to have a orange background color.
library(shiny)
library(shinydashboard)
library(tidyverse)
library(leaflet)
# library(htmlwidgets)
# library(htmltools)
library(readxl)
library(RCurl)
URL <- "https://www.mohfw.gov.in/pdf/PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx"
download.file(URL, destfile = "../../timesnow_PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx",method = "curl")
# Data
ind_vaccination_leaflet <- readxl::read_xlsx(path = "../../timesnow_PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx",
sheet = 1)
# Creating variable with html tags & background doesn't work
ind_vaccination_leaflet <- ind_vaccination_leaflet %>%
mutate(label_display = paste(
"<body style='background-color:orange;'>",
"<h2>", "Center: ", ind_vaccination_leaflet$`Name of the Vaccination Site*`, "</h2>",
"<h3>",ind_vaccination_leaflet$`District*`, ", ", ind_vaccination_leaflet$`State*`, "</h3>",
"</body>",
"<p>", "Address: ", ind_vaccination_leaflet$Address, ", ", ind_vaccination_leaflet$`PinCode*`, "</p>",
"<p>", "Mobile: ", ind_vaccination_leaflet$`Mobile Number`, "</p>",
"<p>", "Contact Person: ", ind_vaccination_leaflet$`Contact Person`, "</p>"
)
)
Issue: When I plot it using below code then I don't get the orange background in the first two rows of the popup:
m <- leaflet() %>%
setView(lat = 26.64510, lng = 80.17012, zoom = 5) %>%
addTiles(group = "OSM") %>%
addProviderTiles(providers$CartoDB.DarkMatter, group = "Dark") %>%
addProviderTiles(providers$CartoDB.Positron, group = "Light") %>%
addProviderTiles("Stamen.Terrain", group = "Terrain") %>%
addProviderTiles("Esri.WorldImagery", group = "WorldImagery") %>%
addLayersControl(baseGroups = c("OSM","WorldImagery","Dark","Light","Terrain"))
m %>%
addCircleMarkers(
lng = ind_vaccination_leaflet$lon,
lat = ind_vaccination_leaflet$lat,
label = lapply(ind_vaccination_leaflet$label_display, htmltools::HTML),
color = "midnightblue",
weight = 1,
radius = 8
)%>%
addMiniMap(tiles = providers$OpenStreetMap, width = 120, height=80)
I am not really a coder nor ui/html person so not sure where is it going wrong.
Try switching body to div.
You can experiment with HTML code much easier in an online editor, e.g. this one

How can I pass a CSS selector with IDs or class names in R html_nodes?

I'm trying to extract the names of parliament members from the homepage of the German parliament, however, regardless which css selector or xpath I'm trying it returns nothing.
https://www.bundestag.de/ausschuesse/a11#
#names <- landing_page_AS %>%
#html_nodes("main > div") %>%
#extract2(7) %>%
#html_nodes("h3") %>%
#html_text()
names <- landing_page_AS %>%
html_nodes(".bt-teaser-person-text h3") %>%
#html_nodes(xpath = "//*[(#id = "bt-collapse-538348")]//h3") %>%
#html_nodes(xpath = "//*[contains(concat( " ", #class, " " ),
concat( " ", "bt-teaser-person-text", " " ))]//h3") %>%
html_text()
I was able to extract the list of names from German parliament website using selenium. The problem could be that the server denies access to your bot without using a headless browser.
If you use selenium, this is the code and xpath you could use, which worked for me :
from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
driver = webdriver.Chrome(r"your_webdriver_address", chrome_options = chrome_options)
#OPEN NEW BROWSER
driver.set_page_load_timeout(10)
driver.get('https://www.bundestag.de/en/members')
button = driver.find_elements_by_xpath("//*[contains(#class, 'icon-list-bullet')]")
button = button[0]
button.click()
time.sleep(3)
GE_MEMBERS_NAMES = driver.find_elements_by_xpath("//*[contains(#class, 'bt-teaser-person-text')]/h3")
for item in GE_MEMBERS_NAMES:
name = item.text
print (name)

Embedding flextable in outlook with rdcomclient

I am facing the following issue: I created a beautiful flextable from a dataframe in R, which I would like to send via email. I use htmltools_value to get the HTML code of the flextable. Now I am able to embed this as htmlbody in my email which works in a sense that I succesfully send the email. However, the email is losing all the colors and borders with rest of the formatting still as defined in the flextable. Anyone faced similar issues or has an idea what could be the problem?
require(flextable)
require(RDCOMClient)
header_col2 <- c("","","", "", "2nd header", "2nd header","More headers", "More headers", "More headers", "More headers")
dfTest <- mtcars[c(1:6),c(1:10)]
ft <- flextable(dfTest)
ft <- add_header_row(ft,values = header_col2,top = T,colwidths = c(rep(1,10))) ft <- merge_h(ft, part = "header")
ft <-bold(ft, bold=T, part="header")
ft <-theme_zebra(ft,odd_header = 'red', even_header = 'grey', odd_body = 'lightblue', even_body = "white")
ft <- color(ft, color = "white", part = "header")
ft <- bold(ft, bold = TRUE, part = "header")
ft <- fontsize(ft, size = 11, part = "header")
std_border = fp_border(color="white", width = 1.5)
big_border = fp_border(color="gray", width = 1)
ft <- border_outer(ft, part="all", border = big_border )
ft <- border_inner_v(ft, part="header", border = std_border )
body <- htmltools_value(ft)
# or body <- format(ft, type = "html")
OutApp <- COMCreate("Outlook.Application")
outMail = OutApp$CreateItem(0)
outMail[["To"]] = "test#test.com"
outMail[["subject"]] = "TEST"
outMail[["HTMLbody"]] = body
outMail$Send()