简体   繁体   中英

render a markdown file to pdf with rmarkdown::render() and adjust page margins and font

I'd like to render a simple markdown file, that has been created by another process before, into a pdf file.

The command:

rmarkdown::render(input = "my_report.md", 
                  output_format = rmarkdown::pdf_document(latex_engine = "xelatex"))

just does this job.

However I would like to change the margins and the main font. With an .Rmd file one would define these settings in the Yaml header like this:

---
output: 
  pdf_document:
    latex_engine: xelatex
mainfont: LiberationSans
geometry: "left=5cm,right=3cm,top=2cm,bottom=2cm"
---

But the markdown files I'd like to convert don't have a Yaml header. Is there a way to pass these Yaml options to the render function as function parameters or in an indirect way?

It's possible, but only using some trickery, and it might not work for all possible arguments. It does seem to work for mainfont and geometry .

The way those settings are passed to LaTeX is as Pandoc variables which are inserted into the template. The trouble is, the pdf_document driver hardcodes margins of 1in if you don't have them in your YAML, and those are added to the Pandoc command line right near the end. You need to override that setting even later.

Here's one way to do it. Create your own output format that is based on pdf_document , but which adds its own variable settings right at the end. For example,

my_pdf_document <- function(geometry, mainfont, ...) {
  force(geometry)
  force(mainfont)
  result <- rmarkdown::pdf_document(...)
  oldpreproc <- result$pre_processor
  result$pre_processor <- function(...) {
    c(oldpreproc(...),
      "--variable",
      paste0("geometry:", geometry),
      "--variable",
      paste0("mainfont:", mainfont))
  }
  result
}

When you want to render using this preprocessor, give the arguments you would have given to pdf_document as well as the ones being set using Pandoc variables, eg

rmarkdown::render("test.md",
              output_format = my_pdf_document(
                geometry = "left=5cm,right=3cm,top=2cm,bottom=2cm", 
                mainfont = "Helvetica", 
                latex_engine="xelatex")
             )

When I make the call above, I see a Pandoc command that looks like this:

/Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/pandoc
  +RTS -K512m -RTS test.md --to latex --from
  markdown+autolink_bare_uris+tex_math_single_backslash 
  --output test.tex 
  --lua-filter /Library/Frameworks/R.framework/Versions/4.2/Resources/library/rmarkdown/rmarkdown/lua/pagebreak.lua 
  --lua-filter /Library/Frameworks/R.framework/Versions/4.2/Resources/library/rmarkdown/rmarkdown/lua/latex-div.lua 
  --embed-resources --standalone --highlight-style tango 
  --pdf-engine xelatex --variable graphics 
  --variable 'geometry:margin=1in' 
  --variable 'geometry:left=5cm,right=3cm,top=2cm,bottom=2cm' 
  --variable 'mainfont:Helvetica' 

(though it's all on one long line, not wrapped so neatly). The 3rd last line is the problematic margin setting, and the last two lines are the result of our special output format.

EDITED to add:

An easier, less hacky approach to this would be to read the entire Markdown file and write it out again with a YAML header. Then you can put whatever you like in the header, and it'll work if a hard coded version would work.

Option 01

geometry , mainfont are Pandoc variables and you can pass them to rmarkdown::pdf_document via pandoc_args argument.

And for convenience, you can use rmarkdown::pandoc_variable_arg to specify name-value pair of pandoc arguments. And you need to use template=NULL so that rmarkdown default template is not used (otherwise whatever variable value passed to pandoc_arg get appended by geometry:margin=1in , so yours specified geometry value wiil be overwritten by rmarkdown defaults).

library(rmarkdown)

render(
  input = "input.md",
  output_format = rmarkdown::pdf_document(
    latex_engine = "xelatex",
    template = NULL,
    pandoc_args = c(
      pandoc_variable_arg("geometry", "left=5cm,right=3cm,top=2cm,bottom=2cm"),
      pandoc_variable_arg("mainfont", "LiberationSans")
    )
  )
)

And we can see that the variables are passed to pandoc correctly,

 /usr/lib/rstudio-server/bin/quarto/bin/tools/pandoc +RTS -K512m -RTS input.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output input.tex --lua-filter /cloud/lib/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /cloud/lib/x86_64-pc-linux-gnu-library/4.2/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --highlight-style tango --pdf-engine xelatex --variable geometry=left=5cm,right=3cm,top=2cm,bottom=2cm --variable mainfont=LiberationSans

Option 02

Alternatively, you can pass those YAML options from a yaml file (located in the same directory as the input markdown file) via --metadata-file pandoc args.

So specify the pandoc variables name and value in a yaml file like as following,

main.yml

author: Jphn Doe
mainfont: LiberationSans
geometry:
  - left=5cm
  - top=2cm
  - right=3cm
  - bottom=2cm
linestretch: 1.5
pagestyle: headings

and then run the rmarkdown::render , (here you still need to use template=NULL , other wise the geometry you have specified in main.yml will be overwritten by rmardown default geometry:margin=1in )

library(rmarkdown)

render(
  input = "test_yaml.md",
  output_format = rmarkdown::pdf_document(
    latex_engine = "xelatex",
    template = NULL,
    pandoc_args = c("--metadata-file", "main.yml")
  )
)

input.md (markdown file used to test the code)

# Rmarkdown

Hello from rmarkdown.

Lorem ipsum dolor sit amet, odio nec ornare tempor semper eget. Ligula lorem,
torquent ante mauris torquent feugiat dis finibus vitae.

Output for 1st option

pdf输出

Output from 2nd option

pdf输出

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM