Skip to content

Commit 9717a1e

Browse files
committed
feat: support for hyperlink_base (see doc_properties and set_doc_properties)
fix #630
1 parent ae998af commit 9717a1e

File tree

8 files changed

+159
-2
lines changed

8 files changed

+159
-2
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: officer
33
Title: Manipulation of Microsoft Word and PowerPoint Documents
4-
Version: 0.7.3.006
4+
Version: 0.7.3.007
55
Authors@R: c(
66
person("David", "Gohel", , "david.gohel@ardata.fr", role = c("aut", "cre")),
77
person("Stefan", "Moog", , "moogs@gmx.de", role = "aut"),

NAMESPACE

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method("[",app_properties)
34
S3method("[",core_properties)
45
S3method("[",custom_properties)
6+
S3method("[<-",app_properties)
57
S3method("[<-",core_properties)
68
S3method("[<-",custom_properties)
79
S3method(as.character,fp_tab)
@@ -410,6 +412,7 @@ importFrom(xml2,xml_remove)
410412
importFrom(xml2,xml_replace)
411413
importFrom(xml2,xml_set_attr)
412414
importFrom(xml2,xml_set_attrs)
415+
importFrom(xml2,xml_set_text)
413416
importFrom(xml2,xml_text)
414417
importFrom(zip,unzip)
415418
importFrom(zip,zip)

NEWS.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44

55
- function `remove_slide()` now supports deletion of multiple slides
66
thanks to Wahiduzzaman Khan (#691).
7-
- add `cursor_reach_index()` to set the cursor at a specific index position in
7+
- add `cursor_reach_index()` to set the cursor at a specific index position in
88
the document (#574).
9+
- `set_doc_properties()` gains a new argument `hyperlink_base` to set the base
10+
URL for relative hyperlinks in Word documents. `doc_properties()` now returns
11+
the `HyperlinkBase` property when available (#630).
912

1013
## Issues
1114

R/core_properties.R

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,99 @@ drop_templatenode_from_app <- function(package_dir) {
144144
write_xml(doc, file)
145145
}
146146
}
147+
148+
149+
# app.xml properties ----
150+
151+
read_app_properties <- function(package_dir) {
152+
file <- file.path(package_dir, "docProps", "app.xml")
153+
if (!file.exists(file)) {
154+
return(NULL)
155+
}
156+
doc <- read_xml(file)
157+
ns <- xml_ns(doc)
158+
159+
hyperlink_base_node <- xml_find_first(doc, "//d1:HyperlinkBase", ns = ns)
160+
hyperlink_base <- if (inherits(hyperlink_base_node, "xml_missing")) {
161+
NA_character_
162+
} else {
163+
xml_text(hyperlink_base_node)
164+
}
165+
166+
company_node <- xml_find_first(doc, "//d1:Company", ns = ns)
167+
company <- if (inherits(company_node, "xml_missing")) {
168+
NA_character_
169+
} else {
170+
xml_text(company_node)
171+
}
172+
173+
z <- list(
174+
data = data.frame(
175+
name = c("HyperlinkBase", "Company"),
176+
value = c(hyperlink_base, company),
177+
stringsAsFactors = FALSE
178+
),
179+
file = file
180+
)
181+
class(z) <- "app_properties"
182+
z
183+
}
184+
185+
186+
#' @export
187+
`[<-.app_properties` <- function(x, i, j, value) {
188+
if (!i %in% x$data$name) {
189+
new_row <- data.frame(name = i, value = value, stringsAsFactors = FALSE)
190+
x$data <- rbind(x$data, new_row)
191+
} else {
192+
x$data[x$data$name == i, j] <- value
193+
}
194+
x
195+
}
196+
197+
198+
#' @export
199+
`[.app_properties` <- function(x, i, j) {
200+
x$data[x$data$name == i, j]
201+
}
202+
203+
#' @importFrom xml2 xml_set_text
204+
write_app_properties <- function(app_props, package_dir) {
205+
if (is.null(app_props)) {
206+
return(invisible())
207+
}
208+
209+
file <- file.path(package_dir, "docProps", "app.xml")
210+
if (!file.exists(file)) {
211+
return(invisible())
212+
}
213+
214+
doc <- read_xml(file)
215+
ns <- xml_ns(doc)
216+
217+
for (i in seq_len(nrow(app_props$data))) {
218+
prop_name <- app_props$data$name[i]
219+
prop_value <- app_props$data$value[i]
220+
221+
if (is.na(prop_value) || prop_value == "") {
222+
next
223+
}
224+
225+
xpath <- paste0("//d1:", prop_name)
226+
node <- xml_find_first(doc, xpath, ns = ns)
227+
if (inherits(node, "xml_missing")) {
228+
new_node <- read_xml(sprintf(
229+
"<%s>%s</%s>",
230+
prop_name,
231+
htmlEscapeCopy(prop_value),
232+
prop_name
233+
))
234+
xml_add_child(doc, new_node)
235+
} else {
236+
xml_set_text(node, prop_value)
237+
}
238+
}
239+
240+
write_xml(doc, file)
241+
invisible()
242+
}

R/docx_utils_funs.R

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,21 @@ doc_properties <- function(x) {
5555
stringsAsFactors = FALSE
5656
)
5757
out <- rbind(out, out_custom)
58+
59+
# Add app properties for rdocx
60+
if (inherits(x, "rdocx") && !is.null(x$app_properties)) {
61+
app_props <- x$app_properties$data
62+
app_props <- app_props[!is.na(app_props$value), , drop = FALSE]
63+
if (nrow(app_props) > 0) {
64+
out_app <- data.frame(
65+
tag = app_props$name,
66+
value = app_props$value,
67+
stringsAsFactors = FALSE
68+
)
69+
out <- rbind(out, out_app)
70+
}
71+
}
72+
5873
row.names(out) <- NULL
5974
out
6075
}
@@ -75,6 +90,8 @@ doc_properties <- function(x) {
7590
#' @param x an rdocx or rpptx object
7691
#' @param title,subject,creator,description text fields
7792
#' @param created a date object
93+
#' @param hyperlink_base a string specifying the base URL for relative hyperlinks
94+
#' in the document (only for rdocx).
7895
#' @param ... named arguments (names are field names), each element is a single
7996
#' character value specifying value associated with the corresponding field name.
8097
#' These pairs of *key-value* are added as custom properties. If a value is
@@ -99,6 +116,7 @@ set_doc_properties <- function(
99116
creator = NULL,
100117
description = NULL,
101118
created = NULL,
119+
hyperlink_base = NULL,
102120
...,
103121
values = NULL
104122
) {
@@ -175,6 +193,35 @@ set_doc_properties <- function(
175193
}
176194
}
177195

196+
if (!is.null(hyperlink_base)) {
197+
if (!inherits(x, "rdocx")) {
198+
cli::cli_warn(
199+
c(
200+
"!" = "{.arg hyperlink_base} is only supported for Word documents.",
201+
"i" = "It will be ignored."
202+
)
203+
)
204+
} else if (!is_string(hyperlink_base)) {
205+
cli::cli_warn(
206+
c(
207+
"!" = "The value for property {.val hyperlink_base} is not a string.",
208+
"i" = "It will not be set in the document properties."
209+
)
210+
)
211+
} else {
212+
if (is.null(x$app_properties)) {
213+
cli::cli_warn(
214+
c(
215+
"!" = "No app.xml file found in the document.",
216+
"i" = "{.arg hyperlink_base} will not be set."
217+
)
218+
)
219+
} else {
220+
x$app_properties["HyperlinkBase", "value"] <- hyperlink_base
221+
}
222+
}
223+
}
224+
178225
if (is.null(values)) {
179226
values <- list(...)
180227
}

R/docx_write.R

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,9 @@ print.rdocx <- function(
254254
if (nrow(x$doc_properties_custom$data) > 0) {
255255
write_custom_properties(x$doc_properties_custom, x$package_dir)
256256
}
257+
if (!is.null(x$app_properties)) {
258+
write_app_properties(x$app_properties, x$package_dir)
259+
}
257260
x <- sanitize_images(x, warn_user = FALSE)
258261
invisible(pack_folder(folder = x$package_dir, target = target))
259262
}

R/read_docx.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ read_docx <- function(path = NULL) {
8989

9090
obj$doc_properties_custom <- read_custom_properties(package_dir)
9191
obj$doc_properties <- read_core_properties(package_dir)
92+
obj$app_properties <- read_app_properties(package_dir)
9293
obj$content_type <- content_type$new(package_dir)
9394
obj$doc_obj <- body_part$new(
9495
package_dir,

man/set_doc_properties.Rd

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)