[R Markdown] 为什么我放弃了用LaTex写作业文档 [20210914 更新]

发布于 2020-03-25  847 次阅读


在长久以来用 LaTex 写作业的日子里, 我逐渐意识到了一个问题. 在我写作业文档的过程中, 绝大部分时候是在写数学公式和插入图片, 而不是玩弄 LaTex 的排版技巧. 那么, 为什么我需要为了最长不过十余页的作业文档不停地敲击复杂的, 不直观的, 无法直接预览的LaTex文件呢? 杀鸡焉用牛刀? 为什么不用一些简单但是更高效的工具呢? 抱着这种怀疑, 我搜索到了R Markdown.

为什么我需要 R Markdown?

LaTex的确很强, 功能很完善, 但是"功能强大"也同时意味着它复杂. 即使有很优秀的自动补全(比如VSC里面的LaTex Workshop), 我仍然需要输入大量额外的字符. 并且LaTex插入图片尤其烦人, 图片的插入和缩放都非常折磨人. 再者, LaTex的编译系统不是很友好, 报错完全报不准. 在长期的使用过程中我感觉到使用LaTex编写小文档是一件实际上很浪费时间的事情. 在相当多的情形下, 我宁愿放弃一些对于排版的控制能力, 来节省排版的时间, 尽快输出想要的结果.

其实之前我就考虑过使用Markdown来写作业, 因为它语法简介直观, 而且大部分Markdown编辑器都可以实时预览. 但是我在Markdown上的尝试受到了两个强大的阻力:

  • Markdown(尤其是带了数学公式的 Markdown) 转 PDF 文件不方便
  • 页眉页脚不方便自定义

对于这两个问题, 我曾经试图使用过Pandoc, 我之前的文章也提到过, Pandoc参数设置复杂, 简单直接的命令行调用带来的文档转换效果并不理想. 但是, 总归是有人早就想到了这些问题, 于是有了 R Markdown.

R Markdown也是利用Pandoc来进行文档转换的, 它的语法以Markdown语法为基础, 支持一些扩展语法, 同时支持直接内嵌 LaTex (可以让hardcore LaTex Player 愉♂悦地玩弄他们那些可有可无的排版技巧). 经过我这几天的实际尝试, 效果非常好, 而且在 Visual Studio Code 中可以直接利用 Markdown 的实时预览功能.

安装 R Markdown 环境

在本文第一次发布的一年多的时间里, 我最终发现, 作为 Windows 用户, 其实把整个工具链(R, Pandoc, LaTex)放到 WSL 中, 才是最方便的. 而至于各个工具要什么版本的, 其实都是其次.

注意! 原本的(推荐用 scoop 安装 MikTex 的)方法经过我自己一段时间的使用之后, 出现了编译异常, 原因在于 MikTex 在下载时有时会彻底卡死, 这一问题既是切换到国内源也依然存在. 在经过多方尝试之后, 转向 tinytex, 这是一个精简版的 TexLive, 很小巧 (150Mb on macOS/Linux and 220Mb on Windows). 当然了, 用 TexLive 也完全没有问题.

对于 Linux 平台(包括 WSL), 通过包管理器安装上述的三个工具即可. 例如在 Ubuntu, 你需要做的是.

sudo apt install r-base pandoc texlive-full

当然其中的 texlive-full 也可以不安装,由 r 的包 tinytex 来代替(安装方法参加下文)。总之保持系统中只有一套 LaTex 环境即可。

对于Windows平台, 如果你使用scoop, 你可以直接在 Power Shell 中运行如下的命令

scoop install r pandoc

这会分别安装 R, Pandoc. (什么? 你不用scoop? 那你可以手动去网站上下载安装)

安装好之后需要打开 R 的交互式 Terminal(如果通过scoop安装, 在Power Shell中需要使用r.ps1来启动, 因为r在PS里面是显示上一条命令), 安装rmarkdown 与 tinytex.

install.packages('rmarkdown')
install.packages('tinytex')
tinytex::install_tinytex()

虽然 R Markdown 的官网上竭尽一切向你推荐 R Studio, 但是我实际使用的体验就是, 那玩意很不好用. 所以我还是选择了我最爱的 Visual Studio Code.

在VSC中安装如下的两个插件:

其中第一个用于调用 R, Pandoc 和 LaTex 来生成PDF文档, 第二个用于Markdown的语法高亮和 Tab 补全. 第三个插件是最近新出的, 支持代码块插入, 交叉引用等功能. 据说还有 RMD 的实时预览?

使用效果展示

其实在一段时间的使用之后积累了蛮多技巧, 我考虑过段时间再单独写一篇博客介绍, 本文还是只介绍最基本的功能.

比如这样一个常见的文件.

---
title: "测试文件"
header-includes:
  - \usepackage{ctex}
output:
  pdf_document:
    latex_engine: xelatex
    number_sections: true
    keep_tex: true
---

# 这是一个中文标题

这是一段py代码

```Python
if __name__ == "__main__":
    print("Hello World!")
```

这是一段C代码

```C
#include 

int main(int argc, char **argv){
    if(argc == 1){
        printf("One!");
    }
    return 0;
}
```

## 这是一个子标题

这是Markdown数学$e^{i\pi}$环境

$$
\begin{aligned}
    test &= \Omega \Delta \Gamma\cdot \beta_{\phi_{0}} \\
         &= f(0) \\
         &= something
\end{aligned}
$$

# This is an English heading

接下来是一段内嵌LaTex

\begin{align*}  
    \alpha &= \delta_{\beta} + \Omega \\  
           &= RUA  
\end{align*}  

## 接下来是图片

![测试图片$\beta$](Snipaste_2020-03-25_23-05-45.png)

接下来是一些废话

- Bullet List
- RUA!
- RUA~
- RUE~~

1. Numbered List
2. RUA!
   1. RUE~
3. Another One!
上面的文件在VSC编辑时的实时预览效果

可以看到内嵌的LaTex无法实时预览, 但是 Markdown 数学环境可以. 另外就是图片不要指定网络图片(尽管Markdown支持, 但是LaTex不支持), caption 会在产生PDF文件时自动生成, 当然也可以指明不要caption.

关于这个预览功能还得多说几句. 这个预览功能是 vsc 针对普通 md 文件的预览, 简单的 rmd 文件如果后缀写成 md, 既可以在这里预览也可以被正确编译. 但是需要注意, 具有复杂功能的 rmd 文件(例如包含了 r 代码用于生成内容), 以 md 作为后缀时是会出问题的, 会显示代码本体, 而不是代码运行结果.

编译该文档, 直接按 Ctrl+Shift+P 调出VSC命令, 输入r:knit rmd to pdf 找到对应的命令(输了前几个字母就能看见了), 然后等待执行就可以得到PDF文件.

一些小技巧

修改后缀名为 .Rmd

如果还把后缀写成 .md, 相当多的功能都是正常的, 但是有的 r 代码块会运行出错, 这一点上面已经提到了.

显示中文

正常显示中文, 需要在最开始的注释里面写明

header-includes:
  - \usepackage{ctex}

PDF 图片交叉引用

用 bookdown::pdf_document2 替代原本的 pdf_document 格式作为输出, 会解决很多奇奇怪怪的图片交叉引用的问题.

图片名字

插入的图片名字不能太诡异, 例如我曾经使用过的3.29.dfa.png就会转换异常, 估计是点点太多了.

强制图片出现位置与插入代码位置相同

如果图片插入错位了, 而你希望所有图片都出现在它在文字中出现的位置, 可以想办法让所有的LaTex图片环境增加[h]参数. 该方法来自于StackOverflow上的某个答案. 新建一个.tex文件, 假设名为preamble-latex.tex, 它包含以下内容

\usepackage{float}
\let\origfigure\figure
\let\endorigfigure\endfigure
\renewenvironment{figure}[1][2] {
    \expandafter\origfigure\expandafter[H]
} {
    \endorigfigure
}

然后把.md文件开头的注释中output部分修改如下

output:
  pdf_document:
    latex_engine: xelatex
    number_sections: true
    includes:  
      in_header: preamble-latex.tex

至于添加自定义的页眉页脚什么的, R Markdown 都可以利用 LaTex 轻松地做到, 自己搜搜就OK了.


终有一日, 仰望星空