Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing YAML parameters as macros within external LaTeX files

I'm looking for ways to add variables (or LaTeX macros) to the YAML header or soon after such that they can be used in external .tex files that are a part of my (modularised) report.

My .rmd file

---
output:
  pdf_document:
    latex_engine: xelatex
    includes:
      before_body: some.tex
params:
  cat: "Felix"
  numb: 14
---

# chapter
Oh my \textbf{`r params$cat`}. 
$x = `r 2*params$numb`^2$

<!-- Trying again to get the parameter -->
\input{some.tex}

My some.tex file:

`r params\$cat`

Output

enter image description here

Hoped-for output

I want to be able to somehow pass the variables from the YAML header (or even just below it) to be used by LaTeX so that all important and regularly updated parameters can be viewed and changed in one place.

like image 664
sindri_baldur Avatar asked Oct 08 '18 15:10

sindri_baldur


3 Answers

If you are looking for something which perhaps is most in keeping with the R Markdown workflow, you can customise the template which is used to build the LaTeX output and add all the extra LaTeX code directly to this.

1. Copying Template

Firstly, we must make a copy of the template used by R Markdown. The following code will create this in your current working directory:

file.copy(system.file("rmd/latex/default-1.17.0.2.tex",
          package = "rmarkdown"), "template.tex")

2. Adding Variables

With our copy, we can define our own pandoc variables which will be inserted into the output document. This allows us to specify parameters in the YAML section of the document and they will be updated in the output format. It is exactly the same mechanism which allows us to add title, author, date and for them to be added to the output format.

I have added some code to the front matter of the document at lines 253-255. The exact location doesn't matter, but I also tend to put my customisations before the \begin{document} argument:

\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhead[LO, LE]{$params.value$}
\fancyhead[RO, RE]{$yourParam$}

3. Calling Template from R Markdown

We can reference the custom template to our R Markdown document as explained here. Here is my minimal example:

---
output:
  pdf_document:
    template: template.tex
params:
  value: Text
yourParam: "`r Sys.Date()`"
---

`r params$value`

The two parameters will be added to the output replacing the $params.value$ and $yourParam$, and result in the output below:

enter image description here

The example highlights how the YAML parameters don't have to be nested within the params argument, as specified in your original question. Having them specified within the parameters mainly has benefits if you want to build a parameterized report

Note: the approach of replacing variables using the pandoc notation $variable$ is only possible for the main template file defined under the template option. It won't work for any of the includes arguments or any other external LaTeX files. See here for more details.

like image 199
Michael Harper Avatar answered Nov 15 '22 23:11

Michael Harper


This comes only half-way. Still no file as header-input...

Maybe this answer will give someone else an idea to build on..

---
output:
  pdf_document:
    latex_engine: xelatex
params:
  cat: "Felix"
  numb: 14
header-includes:
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \fancyhead[CO,CE]{`r params$cat`}
---

# CHAPTER 1
Oh my \textbf{`r params$cat`}. 
$x = `r 2*params$numb`^2$

```{r child = 'some.tex'}
```

screenshot pdf

enter image description here

like image 45
Wimpel Avatar answered Nov 15 '22 22:11

Wimpel


I'm sure you can code something together using lua-filters.

First come up with your own include-mechanism (since this needs to happen before variable-substitution, so you cannot use latex's \input), but e.g. this filter:

function Para (elem)
  if #elem.content == 1 and elem.content[1].t == "Image" then
    local img = elem.content[1]
    if img.classes[1] == "markdown" then
      local f = io.open(img.src, 'r')
      local blocks = pandoc.read(f:read('*a')).blocks
      f:close()
      return blocks
    end
  end
end

Then do the variable substitution with e.g. this filter:

local vars = {}

function get_vars (meta)
  for k, v in pairs(meta) do
    if v.t == 'MetaInlines' then
      vars["$" .. k .. "$"] = {table.unpack(v)}
    end
  end
end

function replace (el)
  if vars[el.text] then
    return pandoc.Span(vars[el.text])
  else
    return el
  end
end

return {{Meta = get_vars}, {Str = replace}}

This should then work like:

---
output:
  pdf_document:
    latex_engine: xelatex
    pandoc_args:
      - '--lua-filter=include.lua'
      - '--lua-filter=substitution.lua'
name: Samuel
---

Look, I can include files:

![](include.md){.markdown}

And in include.md:

Look, I can use variables: \$name\$
like image 20
mb21 Avatar answered Nov 16 '22 00:11

mb21