bookdown下Latex pdf格式输出技巧清单

bookdown编写LaTex书籍中的注意事项。

1 配置及相关设置

1.1 yaml信息的设置

只有index.Rmd具有设置yaml信息的地位!

index.Rmd: This is the only Rmd document to contain a YAML frontmatter as described within Chapter 2, and is the first book chapter. —R Markdown: The Definitive Guide

2 表格的HTML和LaTeX“和谐共存”

2.1 注意bookdown的渲染方式

表格排版还是需要kable和kableExtra的结合。kableExtra的latex参数请参看Zhuhao的awesome_table_in_pdf

zhuhao提到了kableExtra如何实现多形式输出的办法

  • 首先要采用“M-K”的图书渲染方式,也即先合并(merge)再编译(knit)。具体的实现是在文件_bookdown.yml设置new_session: true
# Example _bookdown.yml
book_filename: "bookdown_example"
delete_merged_file: true
new_session: true
language:
  ui:
    chapter_name: "Chapter "
  • 采用“M-K”图书渲染方式后,图书每一章的.Rmd文件在代码运行时都会强制使用new session,也即意味着各章的packagesdata都相互独立。所以每一章的.Rmd文件最开始,需要分别设置options及加载packages

2.2 环境参数的全局性影响

需要注意的是环境参数设置的变化。一种情形是,在Rstudio中点击knitr(或biuld),都会自动识别knitr.table.format =是latex环境还是html环境。另一种情形就是在console中手动运行比如render_preview_chapter(),这时就需要手动写入执行环境是latex或hetml。

  • 自动配置情形下,可以全局设置options(knitr.table.format = "latex")。如果不想自动配置,则可以禁用它options(kableExtra.auto_format = FALSE)

  • 手动配置情形下,则需要在code chunk里对kable()函数设定format="latex"

函数kable()处理的数据类型应该是dataframe(或tibble)。不然booktab=Tkable_styling(latex_options = c("striped"))将会不起作用。请参看在线问答R markdown KableExtra latex table booktabs not working

调用latex包的先后顺序很有影响。例如涉及到颜色的使用,可能出现报错Option clash for package xcolor。这个时候latex包的先后顺序会带来各种问题。参看在线问答Option clash for package xcolor

(https://stackoverflow.com/questions/16507191/automatically-adjust-latex-table-width-to-fit-pdf-using-knitr-and-rstudio)

2.3 表格中插入有图片

有时候我们会独立地展示图片(.png之类),有时候我们还希望在表格中展示图片(.png之类)或图标(icon之类)。

如下R代码块(R code chunk)情况下,是可以正确输出html,但是却无法正确输出Latex:

  • 动图是不行的。knitr::include_graphics('local.gif')

  • 在线图片是不行的。knitr::include_graphics('https://commonmark.org/images/markdown-mark.png')

此外,在kable环境下,data.frame中出现如下value,也是可以正确输出html,但是却无法正确输出Latex:

  • Rmarkdown语言,![](picture/object/Alpha,png)

  • HTML语言,<image source="picture/object/Alpha,png">

yihuixie指出了这种混合表格不能兼得两种输出,同时也为latex输出指明了出路——使用//includgraphics。然而此//includgraphics并非彼include_graphics()

错误的include_graphics()。我开始一直认为是使用R code chunk下的include_graphics()函数。当然是无功而返,无论怎么做都无法实现正确的latex输出。

正确的//includgraphics。进一步googlew问答add and resize a local image to a .Rmd file in RStudio that will produce a pdf,终于明白了其中玄机。原来yihuixie所说的//includgraphics是一种原生的latex语法。——略加调整,问题果真迎刃而解!

  • 使用latex语法正确处理data.frame的单元格value(此处为字符串):"\\includegraphics{picture/object/Alpha.png}"。如果需要调整图片属性(如大小),则可以处理data.frame的单元格value为:"\\includegraphics[width=0.9\linewidth]{picture/object/Alpha.png}"

  • 设置kable参数:kable(dat, escape = FALSE,booktab=T)。(假定环境配置为自动判别latex还是html输出,此处为latex输出)

  • if函数上场,实现latex和html并行输出。根据输出格式(output)来设定data.frame的value值。而且再knitr1.18版本开始,可以直接使用两个函数来自动判明输出格式:

knitr::is_html_output()

knitr::is_latex_output()

2.3.1 latex宏包longtable的使用

如果latex表格很长,一般需要调用longtable参数,但是kableExtra()函数下自动缩放大小latex_options = c( "scale_down")就会失效。并提示:Longtable cannot be resized。但是可以利用kableextra的函数column_sep()来设定各列的宽度。

kable_styling(full_width = F,
  latex_options = c( "scale_down","striped")) %>%
  column_spec(5, width = "15em")

表格中显示图标(icon)。则可以参看add a fontawesome icon to a table in Rmarkdown

2.4 表格中字符格式(如加粗)

建议尽量对整个单元格进行latex参数操作。

单元格部分字符加粗,则必须使用latex语法

dat <- data.frame(
  country = c('Canada', 'United Kindom'),
  abbr = c("Alpha","Coef"),
  var1 = c(1, 2),
  var2 = rnorm(2),
  var3 =c("\\textbf{加粗}","*斜体*")
)

2.5 表格中显示数学公式

kable如何显示数学公式。

数学公式可以参看在线问答math in table-header not working

3 block的使用

3.1 定理和证明block

Bookdown说明书2.2.2 Theorems and proofs

定理环境(theorem environment)下的block类型有:

  • theorem

  • lemma

  • corollary

  • proposition

  • conjecture

  • definition

  • example

  • exercise

证明环境(proof environments)下目前支持的block类型包括:

  • proof

  • remark

  • solution

3.2 个性化blocks

Bookdown说明书里指出了个性化blocks2.7 Custom blocks

如果是html输出,则需要去设置。bookdown内置的block类型包括:

  • rmdcomment

  • rmdlist

  • rmdnotes

也可以参考yihuixie的案例CSS

3.3 注意事项

  • 注意block参数的设置
\BeginKnitrBlock{theorem}\iffalse{-91-39640-26031-45-39532-23572-21487-22827-23450-29702-40-71-97-117-115-115-45-77-97-114-107-111-118-32-84-104-101-111-114-101-109-41-93-}\fi{}<div class="theorem"><span class="theorem" id="thm:BLUE"><strong>(\#thm:BLUE)  \iffalse (高斯-马尔可夫定理(Gauss-Markov Theorem)) \fi{} </strong></span>在正态经典线性回归模型假设(N-CLRM)下,采用普通最小二乘法(OLS),得到的估计量$\hat{\beta}$,是真实参数$\beta$最优的、线性的、无偏估计量(BLUE)。记为:
\xrightarrow[\text{N-CLRM}]{\text{OLS}}\hat{\beta} \xrightarrow[\text{}]{\text{BLUE}} \beta</div>\EndKnitrBlock{theorem}
  • 所有类型的block都需要给出echo=TRUE的参数设置才能正常显示

  • html输出和latex输出的block类型支持是不一样的,前者应该更多

4 Latex输出各种“梗”

4.1 fig.cap

不要在R code chunk的options中出现特殊字符。比如:fig.cap=中出现了特殊符号&,则会报错。

fig.cap="绘制Line & Symbol图"

图题中的数学公式符号,使用货币符号表达是需要注意LaTeX的规范写法(特定命令符需要加双斜杠\\alpha)。例如:fig.cap="判定系数$R^2$和调整判定系数$\bar{R}^2$",则LaTex会报错! Text line contains an invalid character.。正确的写法应该是(参看解决方案):

fig.cap="判定系数$R^2$和调整判定系数$\\bar{R}^2$"

4.2 latex list

正文内容中列表(list)层级太多,则会报错:LaTeX Error: Too deeply nested.。如果非要那么多层级,就需要加载LaTex的enumitem package(让tinytex自动加载去吧),并在preamble.tex中调用它(参看在线文达jgm的 解决方案):

\usepackage{enumitem}
   \setlistdepth{9}

   \setlist[itemize,1]{label=$\bullet$}
   \setlist[itemize,2]{label=$\bullet$}
   \setlist[itemize,3]{label=$\bullet$}
   \setlist[itemize,4]{label=$\bullet$}
   \setlist[itemize,5]{label=$\bullet$}
   \setlist[itemize,6]{label=$\bullet$}
   \setlist[itemize,7]{label=$\bullet$}
   \setlist[itemize,8]{label=$\bullet$}
   \setlist[itemize,9]{label=$\bullet$}
   \renewlist{itemize}{itemize}{9}

   \setlist[enumerate,1]{label=$\arabic*.$}
   \setlist[enumerate,2]{label=$\alph*.$}
   \setlist[enumerate,3]{label=$\roman*.$}
   \setlist[enumerate,4]{label=$\arabic*.$}
   \setlist[enumerate,5]{label=$\alpha*$}
   \setlist[enumerate,6]{label=$\roman*.$}
   \setlist[enumerate,7]{label=$\arabic*.$}
   \setlist[enumerate,8]{label=$\alph*.$}
   \setlist[enumerate,9]{label=$\roman*.$}
   \renewlist{enumerate}{enumerate}{9}

4.3 段落首行缩进

段落首行缩进。在preamble.tex中条用如下包[解决方案(https://stackoverflow.com/questions/29460112/first-line-paragraph-indenting-in-pdfs-using-r-markdown)]

% first-line paragraph indenting
\usepackage{indentfirst}
\setlength\parindent{24pt}

4.4 页边距调整

页边距调整。需要在output.yaml文件中进行设置(参看解决方案

geometry: [a4paper, tmargin=2.5cm, bmargin=2.5cm, lmargin=2.5cm, rmargin=2.5cm]

4.5 正确设置章序号

正确设置章序号。如果修改了documentclass:,那么就需要告诉Pandoc你是按chapter来编码的,而不是section(默认)。在output.yaml文件下设置pandoc_args: --top-level-division=chapter。(参看解决方案

documentclass: ctexbook

bookdown::pdf_book:
  includes:
    in_header: latex/preamble.tex
    before_body: latex/before_body.tex
    after_body: latex/after_body.tex
  keep_tex: yes
  dev: "cairo_pdf"
  latex_engine: xelatex
  citation_package: natbib
  template: latex/template.tex
  pandoc_args: "--top-level-division=chapter"
  toc_depth: 4
  toc_unnumbered: no
  toc_appendix: yes
  quote_footer: ["\\begin{flushright}", "\\end{flushright}"]

4.6 正确处理页码序号

正确处理页码序号。其实bookdown电子书 6.3 Publishers专门提到了这一点,只是我一直没有细看。另外也可以参看在线问答Page Numbering in R Bookdown。 总体意思就是要让Pandoc知道哪些部分属于\frontmatter,\mainmatter,\backmatter。所以需要做如下几件事情:

在preamble.tex文件下加入代码行\frontmatter

在before_body.tex文件下加入代码行\frontmatter

在(序言)index.Rmd文件第一行加入代码行\frontmatter

在(第一章)01-introduction.Rmd文件第一行加入代码行\mainmatter

在after_body.tex文件下加入代码行\backmatter

总之,这些标记代码可以插入到相应的章节,合适的位置。跟word的分节符很像。

5 诡异现象

5.1 编译报错但删除临时文件又好了

  1. Latex错误提示:! File ended while scanning use of \@writefile.。突然之间,Latex无法编译,反复查看yaml代码,感觉都没有问题,大半个下午解决不了(包括更新bookdown和Rstudio;多次重启。)。google搜索,最后采用了David Carlisle的建议,把.aux等日志文件删除,重新Latex编译,最后消除了不能编译的错误。——最终还是没有明白原因。

6 blogdown相关话题(hugo academic)

6.1 通过静态页面的使用

6.2 直接引用本地数据和图片文件

既然blogdown下就能直接编写.Rmd文件,那就可以直接在里面进行数据分析、可视化等操作。问题的关键是如何引用本地数据(“data/”目录下)和图片文件(“pic/”目录下)。

Adding and reading local data files in R Markdown posts

Hu Huaping
Hu Huaping
PhD on Agricultural Economic and Management

My research interests include Data Science, Statistics, Agricultural Economics and Management.

Related