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
,也即意味着各章的packages
和data
都相互独立。所以每一章的.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=T
或kable_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
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{加粗}","*斜体*")
)
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 编译报错但删除临时文件又好了
- 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/”目录下)。