印刷侧的默认实现

印刷器这边的默认实现要薄得多,因为成品页面没有交互,不需要按钮、面板和快捷键。 它提供的主要是一套排版部件和一份配置,让不同渲染器写出来的东西在版面上协调一致。

DefaultPrinterComponent

这个组件做三件事:套上印刷配置的上下文、铺一层带正文字体的背景纸、渲染印刷器组件。 它接受的参数是:

参数作用
printer印刷器核心
root要渲染的文档树
config部分印刷配置,只写想改的项
onUpdateCache每次预处理完成后收到整份缓存,交叉引用的目标选择器可以据此构建

onUpdateCache 在教程第 6 章出现过,当时用它把缓存打到控制台以确认引用名确实写进去了。 在真实应用里它的用途是给编辑侧提供数据:想做一个「选择要引用哪个定理」的下拉框, 候选项就来自这份缓存。

印刷配置

印刷配置也是部分覆盖式的,分成边距和字体两块:

字段内容
marginsparagraph(段落间距)、special(特殊元素前后的间距)、colon(一个小间隔)、level(缩进一级的宽度)、structure(结构性文字的横向偏移)
fontsbodytitlestructuredisplayweaken

字体比编辑器那边多两种:display 用于展示性文字,比如整段引文; weaken 用于弱化文字,比如证明的正文。中文排版里这两种通常要换字体, 比如引文用楷体、弱化用仿宋。

level 这一项决定缩进一级有多宽,它同时被下面几个部件使用, 所以改一处就能让全文的层级缩进统一变化。

<DefaultPrinterComponent
    printer={printer}
    root={tree}
    config={{
        margins: {paragraph: "0.6rem", level: "2.5rem"},
        fonts  : {display: {fontFamily: "KaiTi"}, weaken: {fontFamily: "FangSong"}},
    }}
/>

排版部件

写印刷渲染器时,应当用下面这些部件而不是裸的 divspan。 原因是它们会自动从印刷配置里取字体和间距,从而保证同一种角色的文字在全文各处长得一样; 用裸标签则等于把间距和字体硬编码在某一个渲染器里,改配置时改不动它。

部件用途
PrinterPartBox一个逻辑段落块,subtitle_like 打开时用标题字体
PrinterStructureBoxText结构性文字,比如「定理 一」这样的题头
PrinterParagraphBox普通段落容器,带标准的段间距
PrinterDisplayText展示性文字,比如引文
PrinterWeakenText弱化文字,比如证明的正文
PrinterNewLevelBox向内缩进一级
PrinterOldLevelBox占住一级缩进的宽度,用来在左侧空出编号的位置
PrinterDivider分隔线,带印刷配置里的特殊元素间距
AutoStack按内容自动选择横排或纵排的容器

最后两个缩进部件是一对,配合起来才能做出「编号在左、内容在右」的效果: 外层用 PrinterNewLevelBox 把内容整体推进一级, 再用 PrinterOldLevelBox 在左边那一级的宽度里放编号。 教程第 6 章给条目加编号时用的就是这个组合。

教程第 4 章和第 5 章写定理渲染器时其实已经在用这些部件了, 当时只是把它们当作普通的布局组件;现在可以理解为,它们是印刷配置在渲染器里的出口。

渲染器工厂

印刷侧也有一组渲染器工厂,和编辑侧对应: get_default_group_rendererget_default_inline_rendererget_default_structure_rendererget_default_abstract_renderer, 以及不属于任何具体类型的 auto_renderer。 它们的选项(outerinnerpre_elementcontexters 等)在教程第 4 章和第 6 章讲过,那里是按使用顺序逐个引入的, 比在这里列一张表更容易理解,所以不再重复。

另外还有一组 useless_renderer_blockuseless_renderer_inlineuseless_renderer_text,作用和编辑侧的 get_default_editors() 一样: 填 Printer 构造函数的默认渲染器位,保证没注册渲染器的节点也能显示出来。