Allow interpolation of fp_text styled text with other text using glue #514
-
Problem statement: I have an R markdown report with some summary text at the top. The summary text contains calculations from a recent data set. If the results of these calculations have changed from a previous data set (also in the environment) I need to highlight the numbers that have changed. I can do this by setting a style (e.g. blue colour and bold) with colourtext <- function(filtername){
# Set style using officer -------------------------------------------------
# Set emphasise text style to RKI blue and bold:
hstyle = officer::fp_text(color = "#4F81BD",
bold = TRUE,
font.size = 10,
font.family = "Calibri")
# Structure of expected data ----------------------------------------------
# Expects a data set called 'compare' to exist in environment
# - Names to filter on are in the 'name' column
# - Values to format and print are in the 'value_current' column
# - Logical indicator of whether to format or not is in the 'change' column
# Check if 'compare' data exists:
if(!exists("compare") | (exists("compare") & !is.data.frame(compare))){
stopifnot("The source data for text to highlight does not exist or is in the
wrong format. Please create the 'compare' data and try again.")
} else {
# Extract text to format --------------------------------------------------
# Get text to highlight from data.frame:
text2colour = compare %>%
filter(name == filtername) %>%
pull(value_current)
# Get logical indicator to highlight or not from data.frame:
emphasize = compare %>%
filter(name == filtername) %>%
pull(change)
# Format text -------------------------------------------------------------
# Format the text according to 'emphasize' value:
if(emphasize == TRUE){
# Apply text style to input text:
formatted_text = officer::ftext(text2colour, hstyle)
} else {
formatted_text = text2colour
}
# Return coloured text:
return(formatted_text)
}
} The table # Create example data for compare table:
compare <- data.frame(name = c("n_total", "n_children", "n_adults"),
value_new = c(50, 0, 50),
value_old = c(45, 0, 45),
change = c(TRUE, FALSE, TRUE))
# Print example data:
> compare
name value_new value_old change
1 n_total 50 45 TRUE
2 n_children 0 0 FALSE
3 n_adults 50 45 TRUE
In this example, the total number of cases and the number which are adults have changed (increased) in the new data set, while the number of children (0) has stayed the same in both data sets. The values that have changed need to be highlighted with the style set by the There were `r colourtext('n_total')` cases in total. There were `r colourtext('n_children')` children. There were `r colourtext('n_adults')` adults. This is fine, but including a whole sentence to say that there were 0 children seems a bit much for the summary. It would be better to conditionally display each sentence depending on whether or not the value was greater than 0. This is where things get a bit tricky. I can create an ifelse statement with ```{r statements2print}
# Define show / hide statements for each sentence:
showtotal <- ifelse(n_total > 0, TRUE, FALSE)
showkids <- ifelse(n_children > 0, TRUE, FALSE)
showadults <- ifelse(n_adults > 0, TRUE, FALSE)
# Create ifelse statements:
totalcases <- ifelse(showtotal, glue("There were {colourtext('n_total')} cases in total. "), "")
kids <- ifelse(showkids glue("There were {colourtext('n_children')} children. "), "")
adults <- ifelse(showadults, glue("There were {colourtext('n_adults')} adults."), "")
```
This is a summary paragraph describing the cases by agegroup. `r totalcases``r kids``r adults`
In order to retain the conditional text formatting, I could find two options:
Both options print the sentences, supress them if the relevant value is 0 or otherwise show the sentence with the coloured text if the value has changed. The problem with the first option is that because each sentence is in a separate chunk, they are each displayed in separate paragraphs, wherease they should be in the same paragraph. I described this format and the problem in a Stack Overflow post here The problem with the second option is that any sentences that are conditionally supressed create a line break (i.e. the sentence about adults starts in a new paragraph because the sentence about children is supressed). Syntax for the second option is shown below: This is a summary paragraph describing the cases by agegroup. `r if(!showtotal) {"\\begin{comment}"}`There were `r colourtext('n_total')` cases in total. `r if(!showtotal) {"\\end{comment}"}``r if(!showkids) {"\\begin{comment}"}`There were `r colourtext('n_children')` children.`r if(!showkids) {"\\end{comment}"}``r if(!showadults) {"\\begin{comment}"}`There were `r colourtext('n_adults')` adults.`r if(!showadults) {"\\end{comment}"}`
While the second option gets me closest to what I want, it is still frustrating to have the empty line in between the two printed sentences. It is also somewhat more difficult to read. Feature request: There are a couple of different things which would be helpful here:
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
Beta Was this translation helpful? Give feedback.
-
Thanks @davidgohel this is very helpful - has given me more to go on and I fully understand not wanting to duplicate things if these features will become available in Quarto or in a POSIT package. After I wrote this, I realised that I'm also struggling to understand what causes the three different behaviours below:
It seems in the last scenario that either nothing is evaluated (first case) or the text is evaluated but not the formatting (second and third case). It would be good to understand why? For example running the below in an R markdown document: ```{r create_some_text}
# Set highlight style:
hstyle <- officer::fp_text(color = "#4F81BD",
bold = TRUE,
font.size = 10,
font.family = "Calibri")
# Create unformatted part of sentence:
thetext <- "This is a test to highlight the number "
# Apply formatting to the number:
thenumber <- ftext("32", hstyle)
# Create condition to show sentence or not:
showtext <- TRUE
```
This is a paragraph in R markdown that illustrates the problem. Instead of applying the formatting, all the tags are printed when the document with the following inline code is rendered: `r if(showtext){paste0(thetext, thenumber)}
... results in this in the rendered document: This is a test to highlight the number 32, list(font.size = 10, bold = FALSE, italic = FALSE, underlined = FALSE, color = “black”, font.family = “Calibri”, bold.cs = FALSE, font.size.cs = 10, vertical.align = “baseline”, shading.color = “#C6D9F1”, hansi.family = “Calibri”, eastasia.family = “Calibri”, cs.family = “Calibri”, lang.val = NA, lang.eastasia = NA, lang.bidi = NA)list(font.size = 10, bold = TRUE, italic = FALSE, underlined = FALSE, color = “#4F81BD”, font.family = “Calibri”, bold.cs = TRUE, font.size.cs = 10, vertical.align = “baseline”, shading.color = “#C6D9F1”, hansi.family = “Calibri”, eastasia.family = “Calibri”, cs.family = “Calibri”, lang.val = NA, lang.eastasia = NA, lang.bidi = NA). |
Beta Was this translation helpful? Give feedback.
-
As you said, it is a block output, it can't be used in an inline expression. The documentation https://ardata-fr.github.io/officeverse shows a lot of examples and provides explanations. The code you show don't work because you convert the object to string via https://ardata-fr.github.io/officeverse/officedown-for-word.html#insert-formatted-texts If you really don't want to generate the whole block with officer and create your paragraph with:
You have to:
|
Beta Was this translation helpful? Give feedback.
-
I think the issue should be closed. The discussion moves to "knitr" programming and it's out of the scope of the 'officer' package. |
Beta Was this translation helpful? Give feedback.
Hello
Sorry, I don't want to develop these features, it would take time and I don't have it. Also, I think this is a classic and frequent requirement, so these features will probably be implemented in Quarto or in a posit package (in the medium and long term), so it's wiser to not waste my free time on something that will be redone later.
Last but not least, this can easily be done with R programming. The solution lies in the use of R
list
s anddo.call()
: