基于 js 的类

在 jsarticle 里指定「10 磅」,实际排出的日文字号只有约 9.25 磅。这不是缺陷,而是设计:奥村晴彦的 jsclassesjsarticlejsbook)是以日文印刷使用的 13 级(3.25 mm) 为基准来搭建正文的,而不是西文的 10 pt。js 系 class 保留 LaTeX 标准 class 的操作感,只替换日文真正需要的部分——和文字体度量、字号的梯级,以及缩放这些字号的机制。ltjsclasses 把这套设计带到 LuaLaTeX,BXjscls 把它带到所有引擎。本页要讲的是这三个系谱各自解决了什么,以及该怎么选。

jsclasses 相对标准 class 改动的两件事

jsclasses 附带的手册把「与标准 document class 的差异」明确列为两点:和文字体度量字号选项的处理方式。它并没有改写页边距或行距的理念,而是修好了排日文时真正坏掉的两个地方。第一,和文 TFM 不再使用旧的 min10goth10,而是采用东京书籍印刷的 小林肇 制作的 JIS 字体度量 jis.tfmjisg.tfm。第二,重做了字号选择:标准 class 只有 10pt11pt12pt 三档,而且用手册自己的话说,「除了标准的 10 磅之外,字体的平衡多少会走样」。

这两点其实同出一源。日文印刷用 级(Q,1 级 = 0.25 mm) 计量字号,正文常用 13 级即 3.25 mm。但 JIS 字体度量的全角原样是 13.527 级,所以 jsclasses 把和文字体缩放 0.961 倍(= 13 ÷ 13.527),让全角正好落在 13 级。手册把这个算式写得很清楚,并指出把 9.62216 pt 的度量再乘 0.961 之后,名义上的 10 磅正文其实「只有 9 磅出头」。这个比值保存在实数宏 \Cjascale 中,在 jsarticlejsbookjsreport 里是 0.924690(= 9.62216 pt × 0.961 ÷ 10 pt)。2018 年以后的 OTF 宏包会读取这个宏来对齐日文字号。

latex
% upLaTeX: the dvipdfmx option is a global option for graphicx/hyperref
\documentclass[uplatex,dvipdfmx,a4paper,papersize]{jsarticle}
\begin{document}
こんにちは、\LaTeX\end{document}

class 的阵容是 jsarticle(论文与报告)、jsbook(书籍)、jsreport(报告)三个,另外还捆绑了学会期刊用的 jspf 和纪要用的 kiyoujsreport 是 2017 年 2 月经论坛讨论后,把原先用 jsbookreport 选项代替的用途独立出来的 class。这套 class 最初由奥村基于 LaTeX3 Project 的 classes.dtx株式会社 ASCII 的 jclasses.dtx 编写;2009 年并入了 田中琢爾 的 upLaTeX 支持补丁,自 2016 年 7 月起由 Japanese TeX Development Community(GitHub 上的 texjporg/jsclasses)维护。它随 TeX Live 提供,无需另行安装。

选项作用
a4paper / b5j / a4var纸张。ISO 的 a4paperb5paper,JIS B 系的 b4jb5j,以及变形开本 a4var(210×283 mm)、b5var(182×230 mm)。默认 a4paper
papersize向 DVI 写入纸张尺寸的 \special。走 DVI 转 PDF 的路线时基本上是必需的
tombow / tombo / mentuke打印裁切标记。纸张四周各加 1 英寸的出血区,tombow 还会印上作业名和本次处理的日期时间
mingoth / jismingoth 把和文 TFM 退回旧的 min10goth10jis 在 pLaTeX 下显式选择 JIS 度量
disablejfam不把日文字体注册为数学字族;在数学字族用尽的文档中有用
openright / openleft / openanyjsbookjsreport 中决定章从哪一页开始;openleft 表示从左页开始

正文字号是怎么做出来的:\mag 与 nomag

jsclasses 先以 10 磅排正文,再用 TeX 的 primitive \mag 整体缩放文档以达到指定字号(11pt 为 1.095 倍,12pt 为 1.200 倍)。正因如此,它才能提供标准 class 没有的等比字号 8pt9pt14pt17pt20pt21pt25pt30pt36pt43pt,以及按级指定的 12Q14Q 和实际尺寸的 10ptj10.5ptj11ptj12ptj\mag 会把纸张、字形和线条一并放大,思路很有力;缺点是有些工具读不懂这个值,结果还取决于后续 dvipdfmxdvips 如何处理。

选项行为
usemag\mag 整体缩放文档的原始方式。jsclasses 的 默认值,也是 2016 年 7 月 8 日以前唯一的方式
nomag2016 年 7 月 8 日加入:不用 \mag,改为缩放版式中的各种尺寸
nomag*2016 年 7 月 24 日加入:在 nomag 基础上给 NFSS 打补丁,同时调整 optical size

实际操作中,从默认的 usemag 开始就好。只有当 geometry、图像定位或 PDF 后处理出现真实的尺寸不符时,再尝试 nomag*。比起一开始就堆满所有选项,更该优先保证同样的步骤能得到同样的 PDF。另外,使用 \mag 的文档必须把「带放大率」这件事传达给下游每一个 DVI 工具,所以在协作中把缩放方式和构建步骤一起定下来,可以少出事故。

jsarticle 该用 pLaTeX 还是 upLaTeX

两者都可以,因为 class 会自己判定引擎。 只写 \documentclass{jsarticle} 并用 upLaTeX 处理时,日志会出现 Class jsarticle Info: Autodetected engine: upLaTeX,日文内部编码切换为 JY2/JT2;用 pLaTeX 则是 Autodetected engine: pLaTeX。即便如此,仍要在 class 选项里写 uplatex(或 platexautodetect-engine),一是把意图留在原稿里,二是防止判定不一致时悄悄排出另一种版面。若指定与实际处理系不符,class 会直接停下:! Class jsarticle Error: Option 'platex' is specified but you are running upLaTeX.

dvipdfmx 根本不是 class 选项。jsclasses 不处理的指定会作为全局选项传给后续的宏包,graphicxcolorhyperref 读到它来选择驱动。因此只要在 \documentclass 的方括号里写一次,就不必在每个宏包上分别写——这就是常见写法 [uplatex,dvipdfmx] 的真相。

明明按 A5 排版,PDF 却出成 A4

如果写了 a5paper 而 PDF 却是 A4,原因不在 class,而在于 DVI 里没有写入纸张尺寸。在 TeX Live 2024 上,把 \documentclass[uplatex,a5paper]{jsarticle} 交给 dvipdfmx,得到的 PDF 是 595.28 × 841.89 pt,也就是 A4:DVI 文件本身没有纸张的概念,dvipdfmx 只好用自己的默认值。给 class 选项加上 papersize,就会写出 \special{papersize=...},同一份原稿随即输出为 419.53 × 595.28 pt 的 A5。若同时使用 tombow,纸张会因裁切标记而变大:A5 变成 563.53 × 739.28 pt,正好四边各加一英寸。LuaLaTeX(下一节的 ltjsclasses)直接写 PDF,不会遇到这个问题。

ltjsclasses — 移植到 LuaLaTeX 的 jsclasses

ltjsclasses 是把 jsclasses 改写为 LuaLaTeX(LuaTeX-ja) 使用的一组 class,由 LuaTeX-ja 项目维护。它提供 ltjsarticleltjsbookltjsreport(另有 ltjspfltjskiyou),正如名称所示与 jsclasses 一一对应。这里最大的差别在缩放方式。LuaTeX 官方手册明确写着 \mag 只在 DVI 输出模式下受支持,因此直接输出 PDF 的 LuaLaTeX 用不了它。于是 ltjsclasses 把 nomag* 设为默认;若指定 usemag,它会警告 This ltjsarticle cls does not support 'usemag' option, since LuaTeX does not support \mag in pdf output 并退回 nomag*

与引擎相关的选项处理也变了。传入 uplatex报错this class does not support 'uplatex' option),而 autodetect-engine 只是警告并被忽略——既然只有一种处理系,这样设计很自然。和文度量默认使用 LuaTeX-ja 的标准 jfm-ujis.lua;加上 ptexjis 选项则切换为 jsclasses 所用的同一 JIS 度量(jfm-jis.lua),mingoth 则切换为旧的 jfm-min.lua。要更换字体时配合 luatexja-fontspec,可以直接按名称指定系统里安装的 OpenType 字体。

latex
% compile with lualatex; nomag* is already the default here
\documentclass[a4paper]{ltjsarticle}
\usepackage{luatexja-fontspec}
\setmainjfont{Noto Serif CJK JP}
\setsansjfont{Noto Sans CJK JP}
\begin{document}
こんにちは、\LaTeX\end{document}

BXjscls — 同一份原稿在任意引擎上通过

BXjscls(八登崇之,通称 ZR)把 jsclasses 的设计扩展到可在任意引擎上使用,提供 bxjsarticlebxjsbookbxjsreportbxjsslide。这里最容易一开始就弄错的是引擎的写法。引擎是裸的 class 选项,而不是 engine=——可写 lualatexxelatexpdflatexplatexuplatexlatexplatex-ng,或用 autodetect-engine 自动判定。若写成 engine=lualatex,这个设定根本传不到,编译会以 ! Class bxjsarticle Error: An engine option must be explicitly given. 停下。

latex
% the engine is a bare option; ja= picks the Japanese driver
\documentclass[lualatex,ja=standard,a5paper]{bxjsarticle}
\begin{document}
こんにちは、\LaTeX\end{document}

% same body, different engine: swap the first option only
% \documentclass[uplatex,ja=standard,dvipdfmx,a5paper]{bxjsarticle}

第二个键是 ja=(旧称 jadriver),用来从 standardminimalmodernpandoc 中选择日文处理方式。真正的陷阱就在这里。省略 ja= 时,只有 (u)pLaTeX 会自动补上 standard,其他引擎都会退回 minimal,并给出警告 The option 'ja' is MISSING!! So 'ja=minimal' is assumed as fallback, but such implicit setting is now DEPRECATED!。反过来,一旦写了 ja=,就必须显式给出引擎选项。所以实用上唯一稳妥的写法,是把引擎和 ja= 永远成对写出。选择 ja=standard 时,class 会为当前引擎加载合适的日文宏包。

引擎选项ja=standard 下加载的日文处理
platex / uplatex直接使用 (u)pLaTeX 本身的日文功能;换字体用 pxchfon
lualatexluatexja;字体指定用 luatexja-fontspec / luatexja-preset
xelatexzxjatype(构建在 xeCJK 之上);字体用 zxjafont
pdflatex / latexbxcjkjatype(构建在 CJK 宏包之上);限制最多的路线

与尺寸有关的写法同时借鉴了 jsclasses 和 jlreq。西文基准字号是 base=(别名 fontsize=),日文是 jbase=(别名 jafontsize=),日文缩放比是 scale=(别名 jafontscale=),其默认值为 \jsScale = 0.924715\Cjascale 指向同一个值)。版面既可用 textwidth=number-of-lines=,也可用与 jlreq 相同拼写的 line_length=number_of_lines=。缩放方式用 magstyle= 选择 usemagnomagnomag*;从 LuaTeX v0.87 起以及在 pTeX-ng 上,默认切换为 nomag*,此时指定 magstyle=usemag 会以 ! Class bxjsarticle Error: The engine does not support 'magstyle=usemag' 停下。

jsclasses、ltjsclasses、BXjscls 该选哪个

顺序是 先决定引擎,再选择与之匹配的 class。即使要的是同样的「jsarticle 风格」,处理系一变,class 名也随之改变。反过来,只换 class 而不动引擎,日文处理本身就会失效,版面随之崩坏。

  • 使用 pLaTeX/upLaTeX 时用 jsclasses。 当既有资产或投稿规定已经限定处理系时,这是标准选择。可以从 \documentclass[uplatex,dvipdfmx,papersize]{jsarticle} 出发。
  • 以 LuaLaTeX 为主时用 ltjsclasses。 系统 OpenType 字体可直接使用,PDF 无需经过 DVI 就能写出;nomag* 是默认值,也不会丢失纸张尺寸。
  • 不想固定引擎,或要把源文件分发给别人时用 BXjscls。 只改两处——引擎名和 ja=——同一份文件就能在 pdfLaTeX、XeLaTeX、LuaLaTeX、(u)pLaTeX 之间移动。
  • 想用数值指定版面时用 jlreq。 它与 js 系是不同谱系,可以依据规范设计每行字数、行数和边距。

在日志中确认处理路线

js 系里引擎和 class 是成对确定的,因此只看 .log 开头就能知道文档是否按预期的路线排出。在协作或 CI 中,要确认的不是「生成了 PDF」,而是 PDF 是通过指定路线生成的。理想状态是构建命令、\documentclass 那一行和 README 三者指向同一个名字。

class在日志中确认什么
jsarticleAutodetected engine: 那一行是 pLaTeX 还是 upLaTeX;若加了 papersize,还要量一下 PDF 的纸张尺寸
ltjsarticle是否加载了 luatexja、字体设置是否生效,以及有没有出现 usemag 警告
bxjsarticle引擎选项和 ja= 是否都写了;若出现 ja 缺失警告,就补上