更新记录(仅首版,2026年08月20日 发布)
- 首次发布
引用本文(DOI(已登记存档): 10.5281/zenodo.22176469)
以下 DOI 指向先前登记的存档,内容可能与当前正文不同。引用当前正文时,请使用本页网址。
Go Komura(2026)。《日文字体与字符的陷阱——业务应用如何处理 JIS2004、异体字选择符与外字》。小村软件有限公司。 https://comcomponent.com/zh-CN/blog/japanese-fonts-jis2004-ivs-gaiji-business-apps/
- DOI(已登记存档)
- 10.5281/zenodo.22176469
- DOI(上次登记版本)
- 10.5281/zenodo.22176470
“明明是同一个姓名,屏幕和报表上字的形状却不一样”“换了电脑之后,以前能显示的字变成了 □”——在处理日文的业务系统里,经常会遇到这类咨询。
首先要确认的是:字符的数据变了,还是同一份数据只有外观变了。 不做这个区分就当成“乱码”去修,调查的入口就会走错。
本文以两个咨询为入口:客户名册里的“葛”在屏幕上和打印出来的报表上看起来不一样;提交给政府部门的文件里的姓名,在更换电脑之后无法显示。
这里讨论的两个咨询,和编码不一致引起的乱码是不同的问题。第一个是数据一个比特都没变、只有外观变了;第二个是只存在于那台电脑上的“外字”丢失了。
flowchart TB
accTitle: 常见的两个咨询的真实原因
accDescr: 屏幕和报表上葛的形状不同这一咨询,是数据没有变化而只有外观变了,换电脑之后字变成方框这一咨询,是只存在于那台电脑上的外字丢失了,两者都与编码不一致引起的乱码不同
c1["咨询 1:屏幕和报表上形状不同"] --> r1["数据不变,只有外观变化"]
c2["咨询 2:更换电脑后变成 □"] --> r2["只属于那台电脑的外字丢失了"]
r1 --> diff["与编码引起的乱码是两回事"]
r2 --> diff
图1:常被叫作“乱码”的两个咨询,都和编码不一致是两回事。
只要把字符编码(数据层)和字体(外观层)分开考虑,日文的文字问题大半都能理清。 本文面向业务系统的开发者和信息系统部门负责人,依次讲解症状的排查、JIS2004、IVS、外字的机制、接受的字符范围,以及报表和 PDF 的设计。
Shift_JIS 与 UTF-8 转换本身产生的乱码已有专门文章讲解,因此本文集中讨论“编码往返都正确,外观或能否显示却出现偏差”的问题。
1. 先说结论
“保存哪一串字节”是数据设计的问题,“它看起来是什么样”是字体设计的问题。 决定处理方式时,要把下面三点分开考虑。
先确认数据是否相同
乱码是错误解释字节序列的数据层问题。而同一个 Unicode 码位,字体不同,字形也会不同。JIS2004 变更了“葛”“辻”“飴”等 168 个字的示例字形,Windows 的 MS Gothic/MS Mincho 也从 Vista 起以 JIS2004 字形为默认。12
不要把字形的指定和外字的携带混为一谈
把字形作为数据来指定的标准手段是 IVS。但它需要支持的字体和支持的应用程序,在不支持的环境里,忽略选择符、显示基字符的默认字形才是正确行为。看上去的一个字在 UTF-16 里最多占 4 个码元,因此受影响的不只是显示,还有字符数统计和截取。34
外字(EUDC)是另一回事,专用区的编号没有全球通用的含义。eudc.tte 里的字形不会随数据一起交给对方,因此迁移时需要做排查并建立到替代字符的对应关系。56
把接受的字符和输出环境写进规格
处理姓名的系统要确定并明示接受的字符集合。与政府部门对接时,还要关注以户籍统一文字和文字信息基础为底座的行政事务标准文字的动向。789 报表和 PDF 的基本做法是与屏幕使用同一字体,确认许可后嵌入。有长期保存需求时考虑 PDF/A。1011
另外,不要轻易对姓名的原始数据做 NFKC 规范化。 因为全角半角和兼容字符被替换后,本该保留的区别会丢失。12
按症状和目的选择阅读位置
| 遇到的问题、要决定的事 | 首先确认什么 | 阅读位置 |
|---|---|---|
| 分不清是乱码还是字形差异 | 码位是否相同。“�”和“□”的区别是什么 | 第 2 章:数据与外观 |
| 迁移前后、或屏幕与报表之间,“葛”“辻”等字的形状不同 | JIS90 与 JIS2004 的字形差异、所用字体 | 第 3 章:JIS2004、第 7 章:报表与 PDF |
| 想连姓名的字形都用数据区分 | 显示、打印、对接方是否都支持 IVS | 第 4 章:IVS、第 4.2 节:对实现的影响 |
| 有些字只有旧电脑上才显示 | 专用区的使用位置,以及原来的外字字体 | 第 5 章:外字、第 5.1 节:迁移步骤 |
| 想决定姓名接受到什么范围 | 对接方的规定,以及范围外字符的处理 | 第 6 章:字符集合的设计 |
| 只有一部分字换了字体,或者变成 □ | 指定字体和回退目标里是否有该字形 | 第 8 章:回退 |
| 想检查实现或迁移有无遗漏 | 输入、规范化、保存、显示、打印、对接各层 | 第 9 章:检查清单 |
想整体理解就从第 2 章按顺序读,只想查眼前的症状就从表中对应的小节读起。实施对策时,最后请用第 9 章确认对其他层的影响。
图中实线表示始终成立的关系,虚线表示带条件的关系(成立条件写在详情页各关系的说明中)。关系的完整列表(共 14 条,附依据与确信度)以及主要概念的定义,汇总在知识地图详情页(日文)。数据:JSON-LD / Turtle
2. 把数据和外观分开考虑——码位与字形
编号和画出来的形状是两回事
在 Unicode 中,字符用码位(code point)这个编号表示。“葛”是 U+845B,这个编号在任何电脑上都相同。
而这个编号在屏幕或纸上画成什么样,由字体持有的字形(glyph)决定。同样是 U+845B,字体 A 和字体 B 的细节形状不同,是正常行为。
按症状区分要调查的层
以这两层为前提,现场的症状可以这样排查。
| 层 | 会出的问题 | 典型症状 | 主要处理 |
|---|---|---|---|
| 数据层(字符编码) | 编码解释错误、转换时丢失 | 出现“縺ッ”这样的乱码、被替换成 ? 或 〓、U+FFFD(�) |
定位并修复转换路径 |
| 外观层(字体) | 字体造成的字形差异、缺少字形 | 数据相同但形状不同、显示成 □(豆腐块) | 统一或更换字体、嵌入字体 |
作为排查线索,记住“�”和“□”的区别会很方便。
“�”是转换失败留下的痕迹。 一旦被替换成 U+FFFD(REPLACEMENT CHARACTER),原来的字符就已经丢失了。这时要调查数据层。
“□”在多数情况下表示找不到字形。 如果数据还在、只是字体里没有这个字形,换一种字体就有可能显示出来。这时要调查外观层。
flowchart TB
accTitle: 用 � 和 □ 排查症状
accDescr: 字显示不正确时,� 是数据层转换失败、原字符已经丢失的痕迹,而 □ 是数据还在、只是字体里没有字形,换一种字体就有可能显示出来
symptom["字显示不正确"] --> which{"看到的是什么?"}
which -->|看到 �| datalayer["数据层的问题"]
datalayer -.-> lost["转换失败的痕迹(原字符已丢失)"]
which -->|看到 □| viewlayer["外观层的问题"]
viewlayer -.-> noglyph["只是字体里没有字形"]
noglyph --> fixable["换字体就有可能显示"]
图2:� 是数据层、□ 是外观层的问题信号,调查的入口随之改变。
编码本身的基础(CP932 与 UTF-8、BOM、换行符)在理清 Windows 的文字编码 - 乱码为什么会发生,尤其是和 Linux 搭配时容易出问题的地方和Windows 的文字编码与换行符 - 乱码与 CRLF/LF 的基础中讲解。以下讨论的是外观层,以及发生在两层边界上的问题。
3. 从 JIS90 到 JIS2004——编码不变,字形变了
开头那句“屏幕和报表上‘葛’的形状不同”,真实原因多半就在这里。
变了的部分:字体的默认字形
依据 2000 年日本国语审议会答申的《表外汉字字体表》,2004 年修订的 JIS X 0213:2004(通称 JIS2004)把 168 个汉字的示例字形改成了接近康熙字典体的印刷标准字体。“葛”“辻”“飴”“芦”“溢”“餅”等是代表例子。1
Windows 随之在 Windows Vista 之后的 MS Gothic/MS Mincho(以及新增的 Meiryo)中把 JIS2004 字形设为默认。现在的 MS Gothic 默认字形同样基于 JIS2004,通过 OpenType 的 jp90 特性可以访问 JIS90 时代的字形。21
flowchart TB
accTitle: 现在 MS Gothic 的字形构成
accDescr: Vista 之后的 MS Gothic 以 JIS2004 字形为默认,通过 OpenType 的 jp90 特性可以访问 JIS90 时代的字形
msg["MS Gothic(Vista 之后)"] --> def["默认字形:基于 JIS2004"]
msg --> feat["经由 jp90 特性"]
feat --> old["JIS90 时代的字形"]
图3:现在的 MS Gothic 默认是 JIS2004 字形,用 jp90 特性可以切换到 JIS90 字形。
没变的部分:字符的码位
这里的关键是,变的只有字体,数据一点都没变。
- “葛”的码位在 XP 上和 Windows 11 上都是 U+845B
- XP(JIS90 字形)显示为“勹”中简化成“ヒ”的形状,Vista 之后(JIS2004 字形)显示为里面连“人”都写出来的形状
- 因此,旧系统打印的报表扫描件和新电脑屏幕上的显示,字的形状会对不上。而数据比较完全一致
“辻”的走之旁是一点还是两点、“飴”的食字旁形状等等也是同样的情况。不了解这段历史,调查就容易朝“迁移把数据弄坏了”的错误方向走。
调查顺序是:比较迁移前后的码位 → 一致就怀疑字体的字形差异。不要仅凭外观不同,就判定数据已经损坏。
flowchart TB
accTitle: 同一码位在不同字体下字形会变
accDescr: 葛的码位 U+845B 在 XP 上和 Windows 11 上都不变,只是 JIS90 字形的字体和 JIS2004 字形的字体显示出的形状不同,数据比较完全一致
cp["码位 U+845B(葛)"] --> f90["JIS90 字形的字体(XP)"]
cp --> f04["JIS2004 字形的字体(Vista 之后)"]
f90 --> g90["勹 中简化成 ヒ 的形状"]
f04 --> g04["里面连 人 都写出来的印刷标准字体"]
g90 -.-> same["数据比较完全一致"]
g04 -.-> same
图4:变的只有字体,码位 U+845B 在任何环境下都保持不变。
另外,由于字的规范形体本身并没有变,两种字形都是“同一个字”。不过在人名上,本人或政府部门有时会坚持某一特定形状,满足这种“作为数据加以区分”需求的,就是下面要讲的 IVS。
4. 异体字选择符(IVS)——用数据指定字形
本章分开来看指定字形的机制、能够显示的条件、对实现的影响。
IVS(Ideographic Variation Sequence)是在汉字的正后方放一个叫“异体字选择符”的不可见码位,从而把字形的变体作为数据来指定的机制。 选择符使用 U+E0100~U+E01EF(VS17~VS256)。3
字形的对应由 IVD 的登记决定
哪一个“基字符 + 选择符”的序列指向哪一个字形,由 Unicode 联盟管理的 IVD(Ideographic Variation Database) 这一登记簿决定。
主要的集合如下。13
| 集合 | 登记 | 来源与用途 |
|---|---|---|
| Adobe-Japan1 | 2007 年 | Adobe 的日文字符集合。商用字体异体字切换的底座 |
| Hanyo-Denshi(汎用电子) | 2010 年 | 源自通用电子信息交换环境建设项目。对应户籍、住民基本台账等行政用字 |
| Moji_Joho | 2014 年 | 对应文字信息基础(MJ)。用于 IPAmj 明朝。2026 年 8 月也有追加登记 |
例如 Microsoft 的文档举了这样的例子:单独的 U+845B“葛”用于西葛西站的写法,U+845B+U+E0100(VS17)用于奈良县葛城市的写法。同样是“葛”,也能用数据区分是哪一种字形。3
flowchart TB
accTitle: 用 IVS 把同一个葛作为数据区分的例子
accDescr: 单独的 U+845B 葛用于西葛西站的写法,U+845B 正后方放上 VS17 的序列用于葛城市的写法,哪个序列指向哪个字形由 IVD 这一登记簿决定
seq1["U+845B 单独"] --> gl1["西葛西站写法的字形"]
seq2["U+845B + VS17"] --> gl2["葛城市写法的字形"]
ivd["IVD(登记簿)"] -.-> gl1
ivd -.-> gl2
图5:同样是“葛”,靠有没有选择符就能把两种字形作为数据区分开。
4.1. 在不支持的环境中的行为
字体一侧用 OpenType 的 cmap 表(format 14)实现 IVS 与字形的对应。4 支持的字体(如 IPAmj 明朝)和支持的应用程序都具备时,就会显示出指定的字形。
不具备时,指定的字形不一定能保住。 要把下面两种情况分开考虑。
- 规格上的正确行为:选择符被忽略,按基字符的默认字形显示(选择符本身不可见)
- 旧应用程序和部分绘制系统:选择符被当作独立的未知字符,多显示出一个 □
也就是说,IVS 的设计是“即使降级,基字符仍然可读”,但“一定按指定的字形显示”的保证取决于接收方的环境。
政府的居民记录和户籍类系统会使用文字信息基础系字体加 IVS 的组合,但一般业务系统如果轻易接受,字形会在显示、打印或对接方的某一环掉落。
flowchart TB
accTitle: 带 IVS 的数据会被怎样显示
accDescr: 支持的字体和支持的应用程序都具备时按指定的字形显示,不具备时选择符被忽略而显示基字符的默认字形,旧应用程序和部分绘制系统会把选择符当作未知字符而多显示出一个方框
ivs["基字符 + 异体字选择符"] --> env{"支持的字体和应用是否齐备?"}
env -->|是| ok["按指定的字形显示"]
env -->|否| ignore["选择符被忽略,按默认字形显示"]
env -->|旧应用或部分绘制系统| tofu["多显示出一个 □"]
ignore -.-> spec["规格上这才是正确行为"]
图6:IVS 即使降级基字符仍然可读,但能否按指定字形显示取决于接收方的环境。
4.2. 实现上的注意事项——“一个字”最多 4 个码元
IVS 的选择符 U+E0100 以后都是辅助平面的码位,在 UTF-16 里始终是代理对(2 个码元)。如果基字符本身是辅助平面的汉字(例如 JIS2004 追加的 𠮟(U+20B9F)),仅基字符就占 2 个码元,于是用户认作“一个字”的序列在 UTF-16 里最多 4 个码元、在 UTF-8 里最多 8 字节。
字符数与截取:不要把看上去的一个字切断
在 C# 中,"葛󠄀"(葛+VS17)的 string.Length == 3。用 Substring 或按固定长度截取,有把基字符和选择符拆散的风险。
字符数的校验和截取要按字素簇而不是码元进行(使用 StringInfo 等 API)。
保存:确认列长度的单位
SQL Server 的 nvarchar(n) 以 UTF-16 码元计。如果要接受 IVS,数据库的列长度要按看上去的字符数的 2~4 倍预留。
检索与比较:决定是否区分选择符
有没有选择符,会成为不同的字符串。用“葛”检索时是否命中“葛+VS17”,需要作为需求定下来再实现。
flowchart TB
accTitle: 带 IVS 的一个字与 UTF-16 码元
accDescr: 用户认作一个字的基字符与异体字选择符的序列中,选择符始终是代理对,如果基字符是辅助平面的汉字就还要再占两个码元,在 UTF-16 里最多是四个码元
one["用户认作的一个字"] --> base["基字符"]
one --> vs["异体字选择符"]
base -.-> bnote["辅助平面的汉字则占 2 个码元"]
vs -.-> vnote["始终是代理对(2 个码元)"]
base --> total["UTF-16 里最多 4 个码元"]
vs --> total
total -.-> risk["按固定长度截取会把它拆散"]
图7:带 IVS 的一个字在 UTF-16 里最多 4 个码元,按码元截取很危险。
5. 外字(EUDC)——只在那台电脑上才显示的字
外字是用户自己把字形分配到 Unicode 专用区(PUA:U+E000~U+F8FF 等)码位上的机制。 专用区的码位没有全球通用的含义,同样是 U+E000,不同电脑、不同组织也可能分配了不同的字。5
字形留在外字字体里,数据里只剩编号
在 Windows 上,用专用字符编辑程序(eudcedit.exe)创建字形,保存到名为 eudc.tte 的字体文件里。
这个文件作为隐藏字体安装,并通过 HKEY_CURRENT_USER\EUDC 注册表与各字体关联。6 Shift_JIS(CP932)时代 0xF040~0xF9FC 是外字区,转换到 Unicode 后对应到专用区。
数据里有编号,不代表对方持有同样的字形。 这个机制会带来下面的问题。
- eudc.tte 属于那台电脑(那个用户),不会随数据一起交给对方
- 一旦传到邮件、PDF、Web 或其他系统,就会变成 □,或者显示成对方那边的另一个外字
- 操作系统迁移或更换电脑时忘记搬走 eudc.tte,就会出现“以前的电脑上能显示的字现在显示不出来”
开头第二个咨询的真实原因就是这个。
flowchart TB
accTitle: 外字为什么只在那台电脑上才显示
accDescr: 用专用字符编辑程序创建的字形保存在 eudc.tte 并由那台电脑的注册表与字体关联,因此只有专用区的编码传到邮件或 PDF 或其他系统时会变成方框或看起来像别的字
edit["用专用字符编辑程序创建字形"] --> tte["保存到 eudc.tte"]
tte --> reg["注册表中与字体关联"]
reg --> local["在那台电脑上能显示"]
tte -.-> stay["eudc.tte 不会随数据一起传出去"]
send["只有专用区的编码传给对方"] --> dest["邮件、PDF、其他系统"]
dest --> broken["变成 □ 或看起来像别的字"]
图8:字形留在 eudc.tte 里,数据里只剩专用区的编号,因此外字一离开这台电脑就会坏掉。
5.1. 已经接手了外字的系统的现实解法
麻烦的是从遗留系统继承来的数据里已经混入了外字。我们在迁移项目中推荐的是排查 → 认定 → 替换 → 阻断这四个阶段。
1. 排查:收集在用的编号和原来的字形
用专用区(U+E000~U+F8FF)的正则表达式扫描数据库和文件,列出在用的外字编码及其数量。从各站点的电脑上回收 eudc.tte,确认字形。
2. 认定:制作替代字符对应表
对每个外字逐一调查“能否用正规的 Unicode 字符表示”“能否用 IVS 表示”“文字信息基础(MJ)里有没有对应的字符”,制作替代字符对应表。实际上大多数情况,只是把旧字体用 JIS 外字做了出来而已。
3. 替换:按对应表替换数据
按对应表替换数据。只有在实在没有对应字符时,才以图像形式保留,或者给该记录加上注记。
4. 阻断:不再增加新的外字
新系统用校验拦截专用区的输入,不再新建外字。
flowchart TB
accTitle: 含外字数据的迁移步骤
accDescr: 用扫描专用区和回收 eudc.tte 列出在用的外字,制作替代字符对应表并做替换,新系统用校验拦截专用区的输入,不再新建外字
st1["排查:扫描专用区"] --> st2["认定:制作替代字符对应表"]
st2 --> st3["替换:按对应表替换"]
st3 --> st4["阻断:不新建外字"]
st1 -.-> tte["从各站点回收 eudc.tte"]
st2 -.-> nomap["只有没有对应时才用图像或注记"]
图9:外字的迁移按排查、认定、替换、阻断四个阶段推进,不再新建外字。
政府一侧的方向也相同:已经提出方针,把地方自治体各自制作的外字(据说全国约有 200 万个)唯一地认定到后面要讲的行政事务标准文字,并停止使用。9 “不再增加外字,认定到标准化的字符集合”正在成为公私两边迁移的通行做法。
6. 政府的文字基础——从户籍统一文字到行政事务标准文字
在设计处理姓名的系统时,了解政府一侧的文字基础,可以作为“接受到什么范围”的判断依据。
先分清文字基础的名称与职责
| 名称 | 管理方 | 概要 |
|---|---|---|
| 户籍统一文字 | 法务省 | 为户籍电子化整理出的约 5 万 6 千字。可在法务省的网站上检索7 |
| 住基网统一文字 | 地方公共团体信息系统机构 | 住民基本台账网络使用的约 2 万 1 千字 |
| 文字信息基础(MJ) | 文字信息技术促进协议会 | 整理了行政事务使用的约 6 万字。以 MJ 文字图形名管理,公开了 IPAmj 明朝字体和 MJ 文字信息一览表。最初作为 IPA 的项目建设,现已移交该协议会8 |
| 行政事务标准文字(MJ+) | 数字厅 | 在文字信息基础之上,加入无法认定到 MJ 的户籍用字等扩展而成的字符集。符合标准的系统中姓名等使用这套字符集,字符编码使用 JIS X 0221:20209 |
区分政府内部的对接和一般外部环境的对接
在地方自治体的核心业务系统(符合标准的系统)中,标准规格采用两级做法:姓名等信息的对接使用行政事务标准文字,与智能手机等没有统一对接规定的外部系统,则在 JIS X 0213:2012 的范围内对接。9
“内部保留较宽的字符集合,对外只在一般环境能显示的范围内交换”这个结构本身,对民营系统也有参考价值。
flowchart TB
accTitle: 符合标准的系统的两级对接
accDescr: 地方自治体的符合标准的系统在姓名等信息的对接上使用行政事务标准文字,与没有统一对接规定的智能手机等外部系统则在 JIS X 0213:2012 的范围内对接
sys["地方自治体的符合标准的系统"] --> renkei["姓名等信息的对接"]
sys --> gaibu["与外部系统的对接"]
renkei --> mjp["行政事务标准文字"]
gaibu --> jis["JIS X 0213:2012 的范围"]
gaibu -.-> sumaho["智能手机等没有规定的对象"]
mjp -.-> naibu["内部保留较宽的字符集合"]
图10:政府内的对接用行政事务标准文字,没有规定的外部对接用 JIS X 0213:2012,分成两级。
决定自己的系统接受的范围
对一般业务系统,实务上建议如下。
- 确定接受的字符集合,并在规格书和输入校验两边都写明。例如“到 JIS X 0213:2012 的范围为止”“不接受专用区和组合字符”“不接受 IVS(或者接受,但只在 IPAmj 明朝环境保证显示)”等
- 不要无限制地接受。“反正是 Unicode,什么都能放进来”的设计,一定会在显示、打印或对接的某一环崩掉
- 事先定好范围外字符的处理方式。替换成替代写法(新字体、片假名)的规则,以及向本人解释的措辞,都属于系统规格
- 政府、金融等对接方有字符集合规定时,以其为准来对齐
flowchart TB
accTitle: 接受字符集合的设计与运维
accDescr: 确定接受的字符集合并在规格书和输入校验两边写明,范围内的字符予以接受,范围外的字符则连同替换成替代写法的规则和向本人解释的措辞一起把处理方式定好
decide["确定接受的字符集合"] --> spec["在规格书中写明"]
decide --> valid["在输入校验中写明"]
valid --> range{"是否在范围内?"}
range -->|是| ok["接受"]
range -->|否| alt["替换成替代写法"]
alt -.-> word["向本人解释的措辞也是规格"]
图11:接受字符集合要在规格书和输入校验两边写明,并把范围外的处理方式也定好。
7. 字体的选型与嵌入——让屏幕和报表一致
这里按确认使用环境里有没有这个字体 → 让屏幕和报表一致 → 确认许可后嵌入 PDF 的顺序来考虑。
7.1. 常用字体的性格
| 字体 | 收录 | 性格与适用场合 |
|---|---|---|
| MS Gothic / MS Mincho | Windows 标配 | 为低分辨率屏幕设计的老将。默认字形基于 JIS20042。为保持与遗留报表的兼容性至今仍在服役 |
| Meiryo | Vista 之后 | 以 ClearType 为前提的现代屏幕用字体。与 Vista 一代的 JIS2004 迁移同时登场1 |
| Yu Gothic / Yu Mincho | Windows 8.1 之后 | Windows 与 macOS 两边都有收录的系列,容易让文档的外观保持一致 |
| BIZ UDGothic / BIZ UDMincho | Windows 10 1809 之后 | 森泽出品的通用设计字体。在重视报表和屏幕可读性的项目中是首选14 |
| Noto Sans JP | 需另行安装 | 以开源方式提供,便于随服务器或 Linux 环境一起打包以及做 Web 分发 |
不只看字体名,还要确认使用环境
选型时重要的不是字体的喜好,而是“与显示、打印、PDF 生成有关的所有环境里,是否都存在这个字体”。
Windows 10/11 的日语补充字体(BIZ UD 等)在某些配置下并没有安装;在服务器端生成 PDF 的架构里,服务器上有没有字体会直接产生影响。
flowchart TB
accTitle: 字体选型要确认的环境
accDescr: 字体选型比起字体的喜好,更重要的是与显示和打印和 PDF 生成有关的所有环境里是否都存在这个字体,补充字体的配置和服务器上有没有字体会直接产生影响
cand["候选字体"] --> exist["是否在所有环境都存在"]
exist --> scr["显示的环境"]
exist --> prn["打印的环境"]
exist --> srv["PDF 生成服务器"]
scr -.-> hojo["补充字体视配置可能没有"]
srv -.-> eikyo["服务器上有没有字体直接产生影响"]
图12:选字体看的不是喜好,而是显示、打印、PDF 生成的所有环境里是否都有。
7.2. 报表设计的基本——统一,然后嵌入
让屏幕和报表使用同一字体
在屏幕和报表上指定同一种字体。 字体不同,同样的数据也会看起来字形不同,就会引出开头那样的投诉。“屏幕用 Meiryo,报表用 MS Mincho”这样的组合,至少要确认 JIS2004 那 168 个字有没有字形差异。
PDF 要在确认许可后嵌入
PDF 里要嵌入字体。 不嵌入的话,阅读方会用手头的字体替代绘制,不只字形,连版面都可能改变。
能否嵌入由许可决定。 OpenType 字体用 fsType 字段声明嵌入权限(Installable / Restricted / Preview & Print / Editable、禁止子集化等),不得嵌入未获嵌入许可的字体。10 商用字体必须确认合同。
以子集嵌入为基本做法。 只嵌入用到的字符的字形,就不必背上整套日文字体(数 MB 到数十 MB)。
长期保存要考虑 PDF/A
有长期保存要求就用 PDF/A。 PDF/A(ISO 19005)是把显示所需的资源全部收进文件内部的规范,必须嵌入字体。11 这也是防止“十年后打开时字形变了”最可靠的办法。
flowchart TB
accTitle: 字体嵌入的判断流程
accDescr: 在 PDF 里嵌入字体之前先确认 fsType 的嵌入许可,有许可就以子集嵌入为基本做法,有长期保存要求时就考虑必须嵌入字体的 PDF/A
emb["在 PDF 里嵌入字体"] --> lic{"fsType 是否许可嵌入?"}
lic -->|有许可| sub["以子集嵌入为基本"]
lic -->|无许可| ng["不得嵌入"]
sub -.-> gly["只嵌入用到的字符的字形"]
sub -->|有长期保存要求| pdfa["考虑 PDF/A"]
pdfa -.-> must["必须嵌入字体"]
图13:嵌入的前提是确认 fsType 的许可,子集嵌入和 PDF/A 是基本路线。
打印和 PDF 输出的实现手段如何选,在Windows 业务应用程序的打印与 PDF 输出中有详细讲解。
8. 字体链接与回退——“混进了别的字体”这一现象
指定字体里没有字形的字符,不会什么都不显示,而是用别的字体替代绘制,这是现代绘制系统的默认行为。
在 GDI 中由注册表(FontLink\SystemLink)定义的“字体链接”负责,在 DirectWrite、WPF 和浏览器中则由“字体回退”负责。15
flowchart TB
accTitle: 字体链接与回退的流程
accDescr: 指定字体里有字形就直接显示,没有就用字体链接或回退目标的字体替代绘制,哪里都没有字形就显示方框,但数据多半还活着
disp["显示字符"] --> has{"指定字体里有字形吗?"}
has -->|有| draw["用指定字体显示"]
has -->|没有| fb{"链接目标或回退目标里有吗?"}
fb -->|有| alt["用别的字体替代绘制"]
alt -.-> mixed["字体风格看起来混在一起的原因"]
fb -->|没有| tofu["显示 □(豆腐块)"]
tofu -.-> alive["数据多半还活着"]
图14:□ 是回退失败的痕迹,替代绘制成功与否决定了是“混杂”还是“豆腐块”。
从症状读出替代绘制的结果
知道这个机制,就能解释下面这些常见现象。
- 英数字和日文的字体风格不一样:因为把西文字体指定在了最前面,只有日文部分是用链接目标或回退目标的日文字体绘制的
- 日文句子里只有汉字变成中文风格的字形:回退目标被解析成了中文字体。常见于没有正确传递语言信息(lang 属性或区域设置)的网页和应用程序
- 出现豆腐块(□):指定字体和回退目标里都没有字形。也就是说,□ 是回退“失败的痕迹”,数据多半还活着
回退不能代替设计
回退是补救装置,不能代替一开始就选对字体。15
在业务应用中,把它定位成“主要的显示和打印路径只用设计好的字体就能走完,回退只是对意料之外字符的保险”比较健康。多语言 UI 下的字体选择思路,也请参考WinForms/WPF应用的多语言化。
9. 业务应用的实现检查清单
最后把从输入到对接的各层要确认的要点汇总成表。不要只看屏幕上能不能显示就结束,请把保存、打印、对接都确认一遍。
| 层 | 典型的问题 | 设计与实现的要点 |
|---|---|---|
| 输入 | 从输入法进来环境依赖字符、带 IVS 的字符、专用区的字符 | 确定接受的字符集合并做校验。范围外的不报错,而是给出引导(提示替代写法),柜台业务才转得动 |
| 规范化 | NFKC 把“㈱”变成“(株)”、合并全角半角、①→1 等非预期的转换。即使是 NFC,CJK 兼容汉字(例:U+FA19 的“神”)也会被替换成统一汉字 U+795E | 不要对姓名和地址做 NFKC。规范化限定用途(如生成检索键),原始数据按输入原样保存12 |
| 保存 | 代理对和 IVS 导致列长度不够、按码元被截断 | 以 UTF-8/UTF-16 保存,列长度按码元留出余量。截取按字素簇进行 |
| 显示 | 字体里没有字形而显示 □、回退导致字形改变 | 明确指定能显示目标字符集合的字体,并确认目标操作系统的标配收录情况 |
| 打印与 PDF | 屏幕和报表的字形差异、阅读方的替代绘制 | 让屏幕和报表使用同一字体,PDF 在确认许可之后做子集嵌入10 |
| 与其他系统对接 | Shift_JIS(CP932)转换会把 JIS X 0213 的追加汉字、IVS、外字变成 ? 或 〓 |
在对接规格中写明字符编码和字符集合。如果还保留 CP932 对接,就实现无法转换字符的检测和替代规则 |
把原始数据的保存和检索用的加工分开
规范化尤其典型:出于好意加上的处理,会把异体字和全角半角的区别抹平,正是本文主题本身的陷阱。原则是原始数据保持原样,加工放到副本上。
CSV 对接的字符编码问题,在 CSV 不是“纯文本” 中有详细讲解。
flowchart TB
accTitle: 原始数据保持原样而加工放到副本上
accDescr: 输入进来的字符串作为原始数据按输入原样保存,仅限生成检索键等用途对副本做规范化,对原始数据做 NFKC 会失去异体字和全角半角的区别
input["输入进来的字符串"] --> orig["原始数据:按输入原样保存"]
input --> copy["副本:限定用途做规范化"]
copy -.-> use["生成检索键等"]
orig -.-> ng["对原始数据做 NFKC 会抹平区别"]
图15:规范化限定用途对副本做,原始数据按输入原样保存。
10. 总结
调查:把数据和外观分开
- 文字方面的问题,先分成“数据层(字符编码)”和“外观层(字体)”。� 是数据层、□ 是外观层的问题信号。
- JIS X 0213:2004 变更了 168 个字的示例字形,Windows 从 Vista 起以 JIS2004 字形为默认。“葛”“辻”“飴”的形状因环境而异,不是数据损坏,而是字体的历史。
设计:定好字形的指定方式和接受范围
- 用数据固定字形的标准手段是 IVS,但支持的字体和支持的应用程序不齐,就会退回默认字形。也别忘了一个字最多占 4 个 UTF-16 码元这一实现上的影响。
- 外字(EUDC)是那台电脑特有的资产,无法随数据一起旅行。现实解法是迁移时先盘点,用到正规字符或 IVS 的对应表做替换,并停止新建。
- 处理姓名的系统要确定并明示接受的字符集合。政府正以户籍统一文字和文字信息基础为底座,向行政事务标准文字推进标准化,与之对接的系统需要跟进这一动向。
实现与输出:守住原始数据,统一显示路径
- 报表和 PDF 的基本做法是“与屏幕使用同一字体,确认许可后嵌入”。有长期保存需求时请考虑 PDF/A。
- NFKC 规范化、按码元截取、CP932 转换,是悄悄破坏异体字和外字的三大环节。请把保存原始数据和按字素簇处理作为原则。
下次有人说“字不一样”,请先这样反问:码位是相同,还是不同?相同就是字体的问题,不同就是数据的问题。有这一步,就不会走错调查的入口。
flowchart TB
accTitle: 决定调查入口的第一个问题
accDescr: 有人说字不一样时先比较码位是相同还是不同,相同就作为字体的问题、不同就作为数据的问题开始调查
said["有人说字不一样"] --> cmp{"码位是否相同?"}
cmp -->|相同| fontp["字体的问题"]
cmp -->|不同| datap["数据的问题"]
图16:码位相同就按字体的问题、不同就按数据的问题开始调查。
相关文章
- 理清 Windows 的文字编码 - 乱码为什么会发生,尤其是和 Linux 搭配时容易出问题的地方
- Windows 的文字编码与换行符 - 乱码与 CRLF/LF 的基础
- Windows 业务应用程序的打印与 PDF 输出 —— System.Drawing.Printing / WPF / 报表库的取舍选型
- WinForms/WPF应用的多语言化 —— resx、附属程序集与区域文化切换的实务
- CSV 不是“纯文本” —— C# 业务应用中的 CSV 实务(字符编码、Excel 兼容性、注入防范)
- Windows 应用无障碍入门 —— 为 UI Automation 与合理便利做准备
相关咨询领域
小村软件有限公司承接业务系统中与文字相关的设计与调查。从“屏幕和报表上字不一样”“迁移之后姓名变成 □”这类症状的原因排查,到从遗留系统迁移时的外字盘点与替代字符表制作、处理姓名的系统的接受字符集合设计,再到报表和 PDF 的字体嵌入方案评审,我们从编码层和字体层两方面一起处理。
参考链接
-
株式会社森泽, JIS X 0213:2004(JIS2004)|字体术语集。关于依据表外汉字字体表,JIS X 0213:2004 把 168 个汉字的示例字形改成印刷标准字体(即所谓康熙字典体),以及 Windows Vista 标配了支持 JIS2004 的字体。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, MS Gothic font family。关于 MS Gothic 字体家族的默认字形基于 JIS2004,以及可通过 OpenType 的 ‘jp90’ 特性访问 JIS90 的遗留字形。 ↩ ↩2 ↩3
-
Microsoft Learn, The Unicode standard。关于变体序列由基字符加异体字选择符(VS1~VS256,U+FE00~U+FE0F 以及 U+E0100~U+E01EF)构成,U+845B“葛”与 U+845B+U+E0100(VS17)的区分用法示例(西葛西站与葛城市),以及显示需要支持的字体。 ↩ ↩2 ↩3
-
Microsoft Learn, cmap — Character to Glyph Index Mapping Table (OpenType spec)。关于 OpenType 字体用 cmap 子表 format 14 实现 Unicode Variation Sequence、default 与 non-default UVS 的区别,以及在支持 JIS2004 的字体中的使用示例。 ↩ ↩2
-
Microsoft Learn, End-User-Defined and Private Use Area Characters。关于外字(EUDC)与专用区(PUA)字符由用户或团体各自定义,同一码位在不同计算机上的分配可能不同并发生冲突。 ↩ ↩2
-
Microsoft Learn, Character Sets and Fonts。关于 Unicode 的 EUDC 用途使用 PUA(U+E000~U+F8FF 等)、用专用字符编辑程序创建字形,以及 EUDC 字体作为 .tte 文件隐藏安装并通过 HKEY_CURRENT_USER\EUDC 注册表与字体关联。 ↩ ↩2
-
日本法务省, 户籍统一文字信息 检索条件输入。法务省提供的户籍统一文字官方检索网站。关于可检索户籍所用文字的字形、读音和相关信息。 ↩ ↩2
-
一般社团法人文字信息技术促进协议会, 文字信息基础建设事业。关于由 IPA 在经济产业省等支持下建设的、面向行政事务约 6 万汉字的文字信息基础(MJ 文字图形、MJ 文字信息一览表、IPAmj 明朝字体),现已移交该协议会并对外公开。 ↩ ↩2
-
日本数字厅, 关于地方公共团体信息系统文字要求运用的研讨会报告书(令和 6 年 7 月)。关于地方自治体使用的外字据说约有 200 万个,把扩展了文字信息基础的“行政事务标准文字”(通称 MJ+)作为符合标准的系统中姓名等的字符集、字符编码采用 JIS X 0221:2020,姓名等信息对接使用行政事务标准文字、与智能手机等的对接使用 JIS X 0213:2012,以及把原有外字唯一认定到行政事务标准文字并停止使用的方针。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, OS/2 — OS/2 and Windows Metrics (OpenType spec)。关于字体的 fsType 字段定义嵌入许可(Installable / Restricted License / Preview & Print / Editable、禁止子集化位等),以及应用程序不得嵌入未获嵌入许可的字体。 ↩ ↩2 ↩3
-
PDF Association, PDF/A Basics。关于长期保存用的 PDF/A(ISO 19005)要求把显示文档所需的要素包含在文件内部,其代表就是必须嵌入字体。 ↩ ↩2
-
Microsoft Learn, Using Unicode Normalization to Represent Strings。关于 Unicode 规范化的 NFC/NFD/NFKC/NFKD 四种形式,以及 KC/KD 形式会合并全角半角字符等兼容字符而丢失信息,因此一般不适合作为字符串的正式保存形式。 ↩ ↩2
-
Unicode Consortium, Ideographic Variation Database。基于 UTS #37 的 IVS 登记簿。关于已登记 Adobe-Japan1(2007 年)、Hanyo-Denshi(2010 年)、Moji_Joho(2014 年)等集合,以及 2026 年 8 月版仍在向 Moji_Joho 集合追加登记。 ↩
-
Microsoft Learn, BIZ UDGothic font family。关于森泽出品的通用设计字体 BIZ UDGothic 自 Windows 10 版本 1809 起作为日语补充字体收录。 ↩
-
Microsoft Learn, Fonts (Globalization documentation)。关于字体回退的机制、GDI 的字体链接(FontLink\SystemLink 注册表)、默认字形(豆腐块)的含义,以及字体链接不能代替正确的字体选择。 ↩ ↩2
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
Windows 应用的深色模式与对比度主题支持 ── DWM 的深色标题栏、WinForms/WPF 跟随系统主题、高对比度下的绘制
讲解如何让 WinForms/WPF 应用跟随 Windows 11 的深色模式与对比度主题。梳理 DWM 的深色标题栏、.NET 9/10 的 SetColorMode 与 ThemeMode、主题切换的检测,以及高对比度下用系统颜色绘制的做法。
Windows 打印驱动程序停止提供 ── 业务应用的报表与标签打印如何应对
Microsoft 正在分阶段推进 v3/v4 打印驱动程序的停止提供,从 2026 年 7 月起会优先选择 IPP 类驱动程序。本文梳理 Windows protected print mode 下会消失什么,并用判断表整理业务应用程序的报表、标签打印中依赖点的盘点方法与...
睡眠恢复后就出故障的应用——电源事件机制与扛得住恢复的业务应用设计
打开笔记本电脑时业务应用的通信已经断开——原因是设计没有考虑睡眠。本文依据一手资料讲解 WM_POWERBROADCAST 的通知流程、Modern Standby 的行为、断开与重连的设计、睡眠抑制以及调查命令。
Windows 应用无障碍入门——为 UI Automation 与合理便利义务化做准备
以 2024 年 4 月施行的修订版残障者歧视消除法为背景,围绕屏幕阅读器读取 Windows 应用的机制 UI Automation,从实务角度梳理 WinForms/WPF 的命名、键盘操作、对比度与验证工具。
OneDrive“按需文件”与业务应用——占位符打破的前提与对策
桌面上的 CSV 读不了,导入处理以“找不到文件”中断——原因可能是 OneDrive 的 KFM 与按需文件。本文讲解占位符的机制与属性判断,以及应用端和信息系统部门各自的对策。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
常见问题
汇总了咨询这一主题时常见的问题。
- 同样是“葛”这个字,为什么在不同电脑或不同报表上形状看起来不一样?
- 这多半不是乱码,而是字体字形的差异。JIS X 0213:2004(JIS2004)把 168 个汉字的示例字形改成了印刷标准字体,Windows 也从 Vista 起让 MS Gothic/MS Mincho 等以 JIS2004 字形为默认。“葛”“辻”“飴”是其中的代表例子:Unicode 的码位(数据)没变,只有字体持有的字形(外观)变了。因此比较数据会完全一致,XP 时代打印的报表影像与新电脑屏幕上的形状对不上,是符合规格的行为。若连字形也要统一,就在屏幕和报表上使用同一种字体,或者用异体字选择符指定字形。
- 用了异体字选择符(IVS),人名的字形问题是不是就全部解决了?
- 并不能解决。IVS 是在基字符的正后方加上 U+E0100 之后的选择符、把字形作为数据来指定的机制,只有支持的字体(如 IPAmj 明朝)和支持的应用程序都具备时,才会按指定显示。在不支持的环境里,忽略选择符、按基字符的默认字形显示才是正确行为,有些环境还会把选择符显示成 □。此外,带 IVS 的一个字在 UTF-16 里最多占 4 个码元,因此也会影响字符数统计、截取和数据库列长度的设计。要引入时,请先确认显示、打印以及对接方的支持范围再使用。
- 用外字(EUDC)登记的字符,在别的电脑或 PDF 上也能显示吗?
- 原则上不能。外字是用户把字形登记到那台电脑的 eudc.tte 文件、并占用 Unicode 专用区(U+E000 起)码位的机制,同一个码位在别的电脑上要么未定义,要么是另一个字形。因此一旦传到邮件、PDF 或其他系统,注定会变成 □ 或看起来像另一个字。如果已经接手了包含外字的数据,现实做法是在迁移时盘点专用区的使用位置,制作到正规 Unicode 字符或异体字选择符的对应表再做替换。新系统则应避免新建外字。
- 业务系统对姓名里的字符应该接受到什么范围?
- 首要的是“确定接受的字符集合,并作为规格写明”。户籍中有约 5 万 6 千字的户籍统一文字,符合标准的政务系统方针是使用在文字信息基础之上扩展的行政事务标准文字,但一般业务系统并没有义务无限制地接受同一水平的字符。现实的设计是划定范围,例如“到 JIS X 0213 的范围为止”“不接受异体字选择符和专用区”,在输入时校验,范围外的字符用告警或替代写法来处理。只有与政务系统或地方自治体对接的系统,才需要跟进行政事务标准文字以及基于 JIS X 0221 的对接要求的动向。
- 要让报表和 PDF 上显示出和屏幕相同的字,应该怎么做?
- 基本做法是先让屏幕和报表使用同一种字体,并把字体嵌入 PDF。字体不同,同样的数据字形也可能变;阅读方的电脑上如果没有该字体,就会用替代字体绘制,外观会走样。能否嵌入由字体的许可(OpenType 的 fsType)决定,因此不要全交给报表库,自己要确认。只嵌入用到的字符的子集嵌入还能压住文件体积。如果有长期保存要求,就考虑必须嵌入字体的 PDF/A。