在之前的文章中,我们讲到了Mathjax来在Hexo中输入公式。
由于Hexo的默认渲染使用hexo-renderer-marked,在渲染Mathjax的过程中存在一些问题。
这里我使用了hexo-renderer-pandoc渲染器来替代了默认的渲染器。
安装
依赖安装
使用hexo-renderer-pandoc依赖于pandoc,所以需要先安装pandoc,参见pandoc官网
https://pandoc.org/installing.html
替换渲染器
在Hexo站点根目录执行以下命令
1 | npm uninstall hexo-renderer-marked # 删除默认渲染器 |
修改主题配置
在主题配置文件中修改下列参数 1
2
3
4
5
6
7
8
9
10
11
12# Math Formulas Render Support
math:
# Default (true) will load mathjax / katex script on demand.
# That is it only render those page which has `mathjax: true` in Front-matter.
# If you set it to false, it will load mathjax / katex srcipt EVERY PAGE.
per_page: true
# hexo-renderer-pandoc (or hexo-renderer-kramed) required for full MathJax support.
mathjax:
enable: true
# See: https://mhchem.github.io/MathJax-mhchem/
mhchem: false
使用
正如之前的文章所述,Markdown是针对方便书写阅读的目标而设计的,Markdown的创立者John Gruber说过:
一份Markdown格式的文件应该要能以纯文字形式直接发表,并且一眼看过去不存在任何标记用的标签或格式指令。
这项原则同样也是pandoc 在制订表格、脚注以及其他扩充的语法时,所依循的规范。
然而,pandoc 的目标与原始markdown 的最初目标有着方向性的不同。在markdown 原本的设计中,HTML 是其主要输出对象;然而pandoc 则是针对多种输出格式而设计。因此,虽然pandoc 同样也允许直接嵌入HTML 标签,但并不鼓励这样的作法,取而代之的是pandoc 提供了许多非HTML 的方式,来让使用者输入像是定义清单、表格、数学公式以及脚注等诸如此类的重要文件元素。
因此,pandoc与默认的Markdown语法存在着很大的区别,这里摘录一部分pandoc不同的规则放在这里,具体可以参见pandoc官网的介绍。
换行
在pandoc中,换行并非简单的回车,而是需要使用一个或多个空行来将一行或多行文本分开,一个换行符(即一个回车)将被视为空格。
如果需要硬换行,在一行的结尾使用两个或两个以上的空格。
标题
标题支持两种格式:Setext以及ATX格式
Setext格式即一行文字下面接着一行=
(一级标题)或-
(二级标题)
1 | 一级标题 |
ATX模式即Markdown默认的标题格式,使用1-6个#来表达一至六级标题。
## 二级标题
### 三级标题
标题文字可以包含行内格式,例如强调。 # 一级标题带有链接 and 强调
原始markdown语法在标题之前并不需要预留空行。但是Pandoc需要在标题之前预留空行,除了文章开头第一行的标题。这是因为以#符号开头的情况在一般文字段落中相当常见,这会导致非预期的标题。例如下面的例子:
I like several of their flavors of ice cream:
#22, for example, and #5.
HTML, LaTeX与ConTex的标题识别符
在标题文字所在行的行尾,可以使用以下语法为标题加上属性:
1
{#identifier .class .class key=value key=value}
虽然这个语法也包含加入类别(class)以及键/值形式的属性(attribute),但目前只有识别符(identifier/ID)在输出时有实际作用(且只在部分格式的输出,包括:HTML,
LaTeX, ConTeXt, Textile,
AsciiDoc)。举例来说,下面是将标题加上foo识别符的几种方法:
1
2
3
4# My header {#foo}
## My header ## {#foo}
My other header {#foo}
---------------
(此语法与PHP Markdown Extra相容。)
具有unnumbered
类别的标题将不会被编号,即使--number-sections
的选项是开启的。单一连字符号(
-
)等同于.unnumbered
,且更适用于非英文文件中。因此,
# My header {-}
与下面这行是等价的
# My header {.unnumbered}
没有明确指定ID(识别符)的标题将会依据其标题文字,自动指派一个独一无二的ID。由标题文字推导ID 的规则如下:
- 移除所有格式,连结等。
- 移除所有标点符号,除了底线、连字符号与句号。
- 以连字符号取代所有空格与换行字元。
- 将所有英文字母转为小写。
- 移除第一个字元前的所有内容(ID 不能以数字或标点符号开头)。
- 如果剩下为空字串,则使用section作为ID。 以下是一些范例,
HeaderIdentifier
Header identifiers in HTML
header-identifiers-in-html
Dogs ?–in my house?
dogs--in-my-house
[HTML], [S5], or [RTF]?
html-s5-or-rtf
Applications
applications
33
section
在大多数情况下,这些规则应该让人能够直接从标题文字推导出ID。唯一的例外是当有多个标题具有同样文字的情况;在这情况下,第一个标题的ID仍旧是透过以上规则推导而得;第二个则是在同样ID后加上-1;第三个加上-2;以此类推。
在开启--toc|--table-of-contents
的选项时,这些ID是用来产生目录(Table
of
Contents)所需的页面连结。此外,这些ID也提供了一个简便的方式来输入跳到指定章节的连结。一个以ID产生的连结,其使用的语法看起来就像下面的例子:
See the section on
[header identifiers](#header-identifiers-in-html-latex-and-context).
然而要注意的一点是,只有在以HTML、LaTeX 与ConTeXt 格式输出时,才能以这种方式产生对应的章节连结。
如果指定了--section-divs
选项,则每一个小节都会以div
标签包住(或是section
标签,如果有指定--html5
选项的话),并且ID会被附加在用来包住小节的<div >
(或是<section>
)标签,而非附加在标题上。这使得整个小节都可以透过javascript
来操作,或是采用不同的CSS设定。
Pandoc 假设每个标题都定义了其参考连结,因此,相较于以下的连结语法
[header identifiers](#header-identifiers-in-html)
你也可以单纯只写
[header identifiers]
或
[header identifiers][]
或
[the section on header identifiers][header identifiers]
如果有多个标题具有同样文字,对应的参考只会连结到第一个符合的标题,这时若要连结到其他符合的标题,就必须以先前提到的方式,明确指定连结到该标题的ID 。
与其他一般参考连结不同的是,这些参考连结是大小写有别的。
注意:如果你有明确定义了任何一个标题的标示符,那么选项implicit_header_references
就没有作用。
区块引言
这里与Markdown的习惯类似,一个引言区块可以由一或多个段落或其他的区块元素(如清单或标题)组成,并且其行首均是由一个>符号加上一个空格作为开头。(>符号不一定要位在该行最左边,但不能缩进超过三个空格)。
1 | > This is a block quote. This |
如果想偷懒的话,可以只在每个段落的第一行行首输入>
,后面的行首可以忽略
1 | > This is a block quote. This |
区块引言是可以嵌套多层的,例如
1 | > This is a block quote. |
代码区块
缩进代码区块
一段以四个空格(或一个tab)缩进的文字区块会被视为字面区块(Verbatim Block):换句话说,特殊字元并不会转换为任何格式,单纯只以字面形式呈现,而所有的空格与换行也都会被保留。例如,
if (a > 3) {
moveShip(5 * gravity, DOWN);
}
位于行首的缩排(四个空格或一个tab)并不会被视为字面区块的一部分,因此在输出时会被移除掉。
注意:在字面文字之间的空行并不需要也以四个空格字元做开头。
围栏代码区块
除了 标准的缩进代码区块外,Pandoc也支援了围栏 ( fenced )代码区块的语法。这区块需以包含三个以上波浪线( ~ )或反引号( ``` )的一行作为开始,并以同样符号且至少同样长度的一行作为结束。所有介于开始与结束之间的文字行都会视为代码。不需要额外的缩进:
1 | ~~~~~~~ |
如同一般的代码区块,围栏代码区块与其前后的文字之间必须以空行作间隔。
如果代码本身也包含了一整行的波浪线或反引号,那么只要在区块首尾处使用更长的波浪线或反引号即可:
1 | ~~~~~~~~~~~~~~~~ |
要取消所有语法高亮,使用--no-highlight选项。要设定语法高亮的配色,则使用--highlight-style
清单
无序清单
无序清单是以项目符号作列举的清单。每条项目都以项目符号( * , +或-
)作开头。下面是个简单的例子: 1
2
3* one
* two
* three
这会产生一个「紧凑」清单。如果你想要一个「宽松」清单,也就是说以段落格式处理每个项目内的文字内容,那么只要在每个项目间加上空行即可:
1
2
3
4
5* one
* two
* three
项目符号不能直接从行首最左边处输入,而必须以一至三个空格字元作缩进。项目符号后必须跟着一个空格字元。
清单项目中的接续行,若与该项目的第一行文字对齐(在项目符号之后),看上去会较为美观:
1 | * here is my first |
但markdown 也允许以下「偷懒」的格式: 1
2
3* here is my first
list item.
* and my second.1
2
3
4
5* First paragraph.
Continued.
* Second paragraph. With a code block, which must be indented
eight spaces:
{ code }1
2
3
4
5
6
7
8
9* fruits
+ apples
- macintosh
- red delicious
+ pears
+ peaches
* vegetables
+ brocolli
+ chard1
2
3
4
5
6+ A lazy, lazy, list
item.
+ Another one; this looks
bad but is legal.
Second paragraph of second
list item.
在markdown syntax guide中并未明确表示「四个空格规则」是否一体适用于所有位于清单项目里的区块元素上;规范文件中只提及了段落与代码区块。但文件暗示了此规则适用于所有区块等级的内容(包含嵌套清单),并且pandoc以此方向进行解读与实作。
有序清单
有序清单与无序清单相类似,唯一的差别在于清单项目是以列举编号作开头,而不是项目符号。
在原始markdown
中,列举编号是阿拉伯数字后面接着一个句点与空格。数字本身代表的数值会被忽略,因此下面两个清单并无差别:
1
2
31. one
2. two
3. three1
2
35. one
7. two
1. three
除了清单标记外,Pandoc
也能判断清单的起始编号,这两项资讯都会保留于输出格式中。举例来说,下面的输入可以产生一个从编号9
开始,以单括号为编号标记的清单,底下还跟着一个小写罗马数字的子清单:
1
2
3
4
5
6 9) Ninth
10) Tenth
11) Eleventh
i. subone
ii. subtwo
iii. subthree1
2
3
4(2) Two
(5) Three
1. Four
* Five1
2
3#. one
#. two
#. three
Pandoc支持定义清单,其语法的灵感来自于PHP Markdown
Extra以及reStructuredText:2。
1
2
3
4
5
6Term 1
: Definition 1
Term 2 with *inline markup*
: Definition 2
{ some code, part of Definition 2 }
Third paragraph of definition 2.
如果你在定义内容后面留下空行(如同上面的范例),那么该段定义会被当作段落处理。在某些输出格式中,这意谓著成对的专有名词与定义内容间会有较大的空格间距。在定义与定义之间,以及定义与下个专有名词间不要留空行,即可产生一个比较紧凑的定义清单:
1
2
3
4
5Term 1
~ Definition 1
Term 2
~ Definition 2a
~ Definition 2b
这个特别的清单标记@
可以用来产生连续编号的范例清单。清单中第一个以@标记的项目会被编号为’1’,接着编号为’2’,依此类推,直到文件结束。范例项目的编号不会局限于单一清单中,而是文件中所有以@为标记的项目均会次序递增其编号,直到最后一个。举例如下:
1
2
3
4(@) My first example will be numbered (1).
(@) My second example will be numbered (2).
Explanation of examples.
(@) My third example will be numbered (3).1
2(@good) This is a good example.
As (@good) illustrates, ...
紧凑与宽松清单
在与清单相关的「边界处理」上,Pandoc与Markdown.pl有着不同的处理结果。考虑如下代码:1 | + First |
标签),而markdown则会在“Second”与“Third” (但不包含“ First”)里面置入
标签,这是因为“Third”之前的空行而造成的结果。Pandoc依循着一个简单规则:如果文字后面跟着空行,那么就会被视为段落。既然“Second”后面是跟着一个清单,而非空行,那么就不会被视为段落了。至于子清单的后面是不是跟着空行,那就无关紧要了。(注意:即使是设定为markdown_strict格式,Pandoc仍是依以上方式处理清单项目是否为段落的判定。这个处理方式与markdown官方语法规范里的描述一致,然而却与Markdown.pl的处理不同。)
结束一个清单
如果你在清单之后放入一个缩排的代码区块,会有什么结果? 1
2
3- item one
- item two
{ my code block }
要在item
two之后「切断」清单,你可以插入一些没有缩排、输出时也不可见的内容,例如HTML的注解:
1
2
3
4- item one
- item two
<!-- end of list -->
{ my code block }1
2
3
4
5
6
71. one
2. two
3. three
<!-- -->
1. uno
2. dos
3. tres1
2* * * *
---------------
所有介于两个\(字元之间的内容将会被视为TeX数学公式处理。**开头的\)右侧必须立刻接上任意文字,而结尾$的左侧同样也必须紧挨着文字**。这样一来,$20,000 and $30,000就不会被当作数学公式处理了。
如果基于某些原因,有必须使用\(符号将其他文字括住的需求时,那么可以**在\)前使用反斜线跳脱字元**,这样$就不会被当作数学公式的分隔符。
参考资料
https://daringfireball.net/projects/markdown/syntax#philosophy
https://pandoc.org/MANUAL.html#pandocs-markdown
https://www.bookstack.cn/read/mba811-Writing/Pandoc-01.md#%E6%97%A0%E5%BA%8F%E6%B8%85%E5%8D%95