I'd like to create a template with tags which are highlighted. The tags should be replaced with some data with R
. I have highlighted text in the template file and want a blank background in the output file.
I've tried officer
because that seems to be the most mature R
-package for manipulating word
documents. I've tried the body_replace_all_text
and the body_replace_text_at_bkm
function but neither seems to provide options to change the formatting.
I am completely open to a different approach. The hard requirements are that the template and the output file must be editable by non-technical co-worker (thus a word document) and the input fields in the template must be somehow highlighted. That means if a co-worker manually edit the template he wouldn't miss any tags.
Code
library(officer)
library(magrittr)
template <- read_docx("template.docx")
pattern <- "<tags>"
replacement <- "tags"
template <- template %>%
body_replace_all_text(pattern, replacement)
pattern <- "<color.*?/color>"
replacement <- "yellow"
template <- template %>%
body_replace_all_text(pattern, replacement)
print(template, target = "output.docx")
template.docx
output.docx
desired_output.docx
Edit
Solved that problem by monkey-patching the officer
package. It's kind of hacky so I'm still open for a cleaner solution.
replace_all_text.custom = function( oldValue, newValue, onlyAtCursor=TRUE, warn = TRUE, ... ) {
oldValue <- enc2utf8(oldValue)
newValue <- enc2utf8(newValue)
replacement_count <- 0
base_node <- if (onlyAtCursor) self$get_at_cursor() else self$get()
# For each matching text node...
for (text_node in xml_find_all(base_node, ".//w:t")) {
# ...if it contains the oldValue...
if (grepl(oldValue, xml_text(text_node), ...)) {
replacement_count <- replacement_count + 1
# Replace the node text with the newValue.
xml_text(text_node) <- gsub(oldValue, newValue, xml_text(text_node), ...)
# remove background color
xml_remove(xml_find_all(text_node, "..//w:highlight"))
}
}
# Alert the user if no replacements were made.
if (replacement_count == 0 && warn) {
search_zone_text <- if (onlyAtCursor) "at the cursor." else "in the document."
warning("Found 0 instances of '", oldValue, "' ", search_zone_text)
}
self
}
docx_part.custom <- officer:::docx_part
docx_part.custom$public_methods$replace_all_text <- replace_all_text.custom
assignInNamespace("docx_part", docx_part.custom, ns = "officer")