Browse Source

Add line numbering

pull/25/head
Noam Ross 1 year ago
parent
commit
277d4169e1
3 changed files with 79 additions and 32 deletions
  1. +41
    -21
      R/docx_reversible.R
  2. +31
    -7
      R/officer-sections.R
  3. +7
    -4
      man/rdocx_reversible.Rd

+ 41
- 21
R/docx_reversible.R View File

@@ -7,9 +7,11 @@
#' code in the final document
#' @param wrap when round-tripping the document, at what width to wrap the
#' markdown output? See [undoc()].
#' @param margins page margin size. Can be a single value or a named
#' vector with values, `top`, `bottom`, `left`, `right`, `gutter`, `header`,
#' and `footer`. If NULL defaults to the reference document.
#' @param margins page margin size. Can be a single value or a named vector
#' with values, `top`, `bottom`, `left`, `right`, `gutter`, `header`, and
#' `footer`. If NULL defaults to the reference document.
#' @param line_numbers either TRUE or list with any of the arguments `start`,
#' `by`, `restart`, and `distance`
#' @param ... other parameters passed to [rmarkdown::word_document()]
#' @importFrom rmarkdown output_format word_document
#' @importFrom officer read_docx
@@ -19,36 +21,41 @@
#' @importFrom xfun parse_only
#' @export
rdocx_reversible <- function(highlight_outputs = FALSE, wrap = 80,
margins = NULL, ...) {

margins = NULL, line_numbers = NULL, ...) {
out <- word_document(
md_extensions = c("+fenced_divs", "+bracketed_spans"),
...)
...
)

out$knitr <- rmarkdown::knitr_options(
# Wrap code outputs in spans and divs
knit_hooks = list(
# TODO: See if its better to make empty inline output raw openxml
evaluate.inline = function(code, envir = knit_global()) {
v = withVisible(eval(parse_only(code), envir = envir))
v <- withVisible(eval(parse_only(code), envir = envir))
if (is.null(v$value) || v$value == "") v$value <- "\uFEFF"
if (v$visible) knit_print(v$value, inline = TRUE, options = opts_chunk$get())
},
inline = function(x) {
id = paste0("inline-", inline_counter())
paste0("[", x, "]{custom-style=\"", id, "\"}")},
id <- paste0("inline-", inline_counter())
paste0("[", x, "]{custom-style=\"", id, "\"}")
},
chunk = function(x, options) {
if (isFALSE(options$redoc_include)) {
# Special output for empty chunks
# TODO: move empty chunk handler to a lua filter to make more general
# TODO: test if we need special handling for other no-result chunks
paste0("```{=openxml}\n<w:p><w:pPr><w:pStyle w:val=\"chunk-",
options$label,
"\"/><w:rPr><w:vanish/></w:rPr></w:pPr></w:p>\n```")
paste0(
"```{=openxml}\n<w:p><w:pPr><w:pStyle w:val=\"chunk-",
options$label,
"\"/><w:rPr><w:vanish/></w:rPr></w:pPr></w:p>\n```"
)
} else {
paste0("::: {custom-style=\"chunk-", options$label, "\"}\n",
x,
"\n:::")
paste0(
"::: {custom-style=\"chunk-", options$label, "\"}\n",
x,
"\n:::"
)
}
}
),
@@ -66,8 +73,9 @@ rdocx_reversible <- function(highlight_outputs = FALSE, wrap = 80,
# Pre-parse, name inline chunks and save chunk contents to lookup table
out$pre_knit <- function(input, ...) {
utils::write.table(parse_rmd_to_df(input),
file = paste0(file_path_sans_ext(input), ".chunks.csv"),
sep = ",", row.names = FALSE, qmethod = "double")
file = paste0(file_path_sans_ext(input), ".chunks.csv"),
sep = ",", row.names = FALSE, qmethod = "double"
)
inline_counter(reset = TRUE)
chunk_counter(reset = TRUE)
}
@@ -79,16 +87,21 @@ rdocx_reversible <- function(highlight_outputs = FALSE, wrap = 80,
chunkfile <- paste0(file_path_sans_ext(rmd_input), ".chunks.csv")
tmpd <- tempdir()

orig_rmd <- file.path(tmpd,
paste0(file_path_sans_ext(basename(rmd_input)),
".original.Rmd"))
orig_rmd <- file.path(
tmpd,
paste0(
file_path_sans_ext(basename(rmd_input)),
".original.Rmd"
)
)
file.copy(rmd_input, orig_rmd)

roundtrip_rmd <- undoc(
output_file,
to = paste0(basename(file_path_sans_ext(rmd_input)), ".roundtrip.Rmd"),
dir = tmpd, wrap = wrap, overwrite = TRUE,
orig_chunkfile = chunkfile)
orig_chunkfile = chunkfile
)

docx <- embed_file(docx, chunkfile)
docx <- embed_file(docx, orig_rmd)
@@ -98,10 +111,17 @@ rdocx_reversible <- function(highlight_outputs = FALSE, wrap = 80,
docx <- highlight_output_styles(docx)
}

# Stuff to go to worded/officedown
if (!is.null(margins)) {
set_body_margins(docx, margins)
}

if(isTRUE(line_numbers)) {
set_body_linenumbers(docx)
} else if(is.list(line_numbers)) {
do.call(set_body_linenumbers, c(list(x = docx), line_numbers))
}

print(docx, output_file)
if (clean) {
file.remove(chunkfile)


+ 31
- 7
R/officer-sections.R View File

@@ -5,8 +5,10 @@ set_section_properties <- function(x, section = NULL, properties = NULL) {
if (is.null(section)) {
sec <- xml_find_first(doc_obj, "/w:document/w:body/w:sectPr")
if (inherits(sec, "xml_missing")) {
sec <- xml_add_child(xml_find_first(doc_obj, "/w:document/w:body"),
"w:sectPr")
sec <- xml_add_child(
xml_find_first(doc_obj, "/w:document/w:body"),
"w:sectPr"
)
}
} else {
sec <- section
@@ -23,15 +25,37 @@ set_section_properties <- function(x, section = NULL, properties = NULL) {
x
}

set_body_margins <- function(x, mar = c(top = 1, bottom = 1, left = 1,
right = 1, gutter = 0, header = 0,
footer = 0)) {
set_body_margins <- function(x, mar = c(
top = 1, bottom = 1, left = 1,
right = 1, gutter = 0, header = 0,
footer = 0
)) {
if (length(mar) == 1 && (is.null(names(mar)) || names(mar) == "all")) {
mar <- c(top = mar, bottom = mar, left = mar, right = mar,
gutter = 0, header = 0, footer = 0)
mar <- c(
top = mar, bottom = mar, left = mar, right = mar,
gutter = 0, header = 0, footer = 0
)
}
mar <- structure(as.character(mar * 1440), .Names = names(mar))
set_section_properties(x, properties = list(pgMar = mar))
x
}

set_body_linenumbers <- function(x, start = 1L, by = 1L, distance = NULL,
restart = c("page", "section", "never")) {
restart <- match.arg(restart)
lnNumType <- c(
start = as.character(as.integer(start)),
countBy = as.character(as.integer(by)),
restart = switch(restart,
page = "newPage",
section = "newSection",
never = "continuous"
)
)
if (!is.null(distance)) {
lnNumType <- c(lnNumType, distance = as.character(distance * 1440))
}
set_section_properties(x, properties = list(lnNumType = lnNumType))
x
}

+ 7
- 4
man/rdocx_reversible.Rd View File

@@ -5,7 +5,7 @@
\title{Convert to a Reversible Microsoft Word Document}
\usage{
rdocx_reversible(highlight_outputs = FALSE, wrap = 80,
margins = NULL, ...)
margins = NULL, line_numbers = NULL, ...)
}
\arguments{
\item{highlight_outputs}{whether to highlight outputs from chunks and inline
@@ -14,9 +14,12 @@ code in the final document}
\item{wrap}{when round-tripping the document, at what width to wrap the
markdown output? See \code{\link[=undoc]{undoc()}}.}

\item{margins}{page margin size. Can be a single value or a named
vector with values, \code{top}, \code{bottom}, \code{left}, \code{right}, \code{gutter}, \code{header},
and \code{footer}. If NULL defaults to the reference document.}
\item{margins}{page margin size. Can be a single value or a named vector
with values, \code{top}, \code{bottom}, \code{left}, \code{right}, \code{gutter}, \code{header}, and
\code{footer}. If NULL defaults to the reference document.}

\item{line_numbers}{either TRUE or list with any of the arguments \code{start},
\code{by}, \code{restart}, and \code{distance}}

\item{...}{other parameters passed to \code{\link[rmarkdown:word_document]{rmarkdown::word_document()}}}
}


Loading…
Cancel
Save