Arabic / Persian / Urdu 塑形支持

生产端塑形流程 (v2.85.0 - v2.119.68)

 

OpenType GSUB Engine  CFF / OpenType Subsetting

HotPDF 在生产端运行塑形流程,在 PDF 文本输出期间把 Unicode 输入的 Arabic、Persian 和 Urdu 文本段折叠为对应的 Arabic Presentation Forms,因此消费端阅读器会收到可直接渲染的位置字形和连字字形,不需要自带 Harfbuzz 级别的塑形器

 

流程内容

位置成形(v2.85.0):每个阿拉伯基础字母最多有四种位置形式,即 isolated(isol)、initial(init)、medial(medi)和 final(fina)。HotPDF 会检查每个阿拉伯字符的 joining class 及其相邻字符的 joining class,然后在输出前将输入 codepoint 映射到合适的 Arabic Presentation Forms-B(U+FE70 - U+FEFC)glyph。不连接字母、连接符、透明字符(combining marks)以及 Tatweel kashida 都按 Unicode Arabic Shaping 算法处理

 

LAM-ALEF 强制连字(v2.119.32):Arabic Unicode Shaping 规范要求任何 LAM(U+0644)后紧跟 ALEF(U+0627 裸形式、U+0622 带 madda、U+0623 带 hamza above、U+0625 带 hamza below)时折叠为单个连字 glyph(U+FEFB - U+FEFC isolated / final 形式,带相应的 hamza / madda 变体)。这是阿拉伯排版中少数非可选连字之一,也是正确性的要求;若渲染为分离 glyph,母语读者会立即识别为错误。HotPDF 在输出期间执行折叠,因此调用方无需在自己的代码路径中使用 Harfbuzz 级成形器

 

Persian / Urdu 核心 9 个字母(v2.119.35):波斯语和乌尔都语扩展了阿拉伯字母,包含不在 Arabic Presentation Forms-B block(U+FE70 - U+FEFC)中的字母。最常用的 9 个字母——包括 PEH(U+067E)、TCHEH(U+0686)、JEH(U+0698)、KEH(U+06A9 / U+06AF)、GAF(U+06AF)、NOON GHUNNA(U+06BA)、HEH DOACHASHMEE(U+06BE)、YEH BARREE(U+06D2)和 HEH GOAL(U+06C1)——在 Arabic Presentation Forms-A(U+FB50 - U+FDFF)中有 presentation forms。HotPDF 现在会在 positional shaping 期间将这些字母映射到对应 Forms-A glyph,使波斯语和乌尔都语文本在任何消费端阅读器中都能以正确的 init / medi / fina / isol 形式渲染

 

Arabic Extended-A + Supplement(v2.119.52、v2.119.56):joining-class 表现在覆盖剩余 Arabic Extended-A 字符(ALEF WASLA / NOON GHUNNA / HEH 变体)、Arabic Supplement U+0750-U+077F 以及更高的 Arabic Extended-A U+08A0-U+08FF 范围。带静态 Presentation Forms-A 编码的字符会通过现有四位置 shaper 映射;没有 Presentation Forms-A 编码的多数 Extended-A 字符只获得 joining-class 分类,使相邻字符即便在该字符原样传递时也能正确成形。v2.119.56 修正了 v2.119.52 引入的两个错误 Forms-A 映射(U+06C2 / U+06C3)

 

Persian / Urdu Form-B 全覆盖(v2.119.57):joining-class 表扩展到完整 U+0672-U+06D5 范围,约 80 个字符,覆盖 REH / DAL / SEEN / SAD / TAH / AIN / FEH / QAF / KAF / GAF / LAM / NOON / HEH / WAW / YEH 变体,并新增 26 个 Presentation Forms-A 映射(15 个 D-class 四形式 + 11 个 R-class 双形式)。尤其是乌尔都语标准 'h' HEH DOACHASHMEE(U+06BE → FBAA-FBAD)和乌尔都语词尾 yeh YEH BARREE(U+06D2 → FBAE-FBAF)现在会正确映射到此前被 v2.119.52 ALEF WASLA / NOON GHUNNA 临时修复误用的 Forms-A 槽位。静态 Forms-A 覆盖现在超过 40 个源字符

 

YEH-HAMZA + 元音连字后处理(v2.119.58):在 positional shaping 之后加入 _ApplyArabicShaping 的 post-pass,覆盖 Forms-A block U+FBEA-U+FBFB 中的 8 个连字对:YEH-HAMZA + ALEF / AE / WAW / U / OE / YU / E / ALEF MAKSURA。每对会输出 isolated form(FBEA / FBEC / FBEE / FBF0 / FBF2 / FBF4 / FBF6 / FBF9)以及 base+1 final form。starting / medial form 槽位 FBF8 / FBFB 留给通过 sfArabicGSUB opt-in 的 GSUB engine。该实现建立在 v2.119.32 LAM-ALEF 的相同骨架上

 

Allah 连字(v2.119.60):四字符序列 ALEF + LAM + LAM + HEH 会折叠为 U+FDF2 ARABIC LIGATURE ALLAH ISOLATED FORM。实现为 _ApplyArabicShaping 链中位于 v2.119.32 LAM-ALEF 和 v2.119.58 YEH-HAMZA 之后的 codepoint-level static post-pass

 

Bismillah 短语连字(v2.119.62):标准 22-codepoint Bismillah 短语 “بسم الله الرحمن الرحيم” 会折叠为单个 glyph U+FDFD ARABIC LIGATURE BISMILLAH AR-RAHMAN AR-RAHEEM。它作为 _ApplyArabicShaping 中最早的 pre-pass 运行(早于 LAM-ALEF),因此折叠后的 Bismillah glyph 会以单个 codepoint 进入后续管线,不会被下游替换再次处理

 

公开查询辅助函数

GetArabicJoiningClass 返回静态 Arabic shaper 使用的 Unicode joining class。GetArabicPosition 将 run 中的一个字符解析为 isolated、initial、medial 或 final 位置,使调用方可在输出文本前检查或复用相同的成形决策

 

调用方式

用 PDF.AutoShapeArabic:= True 一次性把管线打开;之后它会在 Unicode 文本输出方法内部自动运行,不需要显式调用“shape this”。将 Unicode 输入的 Arabic / Persian / Urdu 字符串传给 THPDFPage.TextOut、THPDFPage.UnicodeTextOut 或 THPDFPage.RtLTextOut,HotPDF 会在写入 PDF text-showing operator 前把每个输入 codepoint 映射到对应 presentation form。用页面字体绘制的 free text annotation 也按同样的方式成形。原始 Unicode 字节也会捕获到 FUnicodeUsedCps 中以生成 ToUnicode CMap,因此阅读器侧复制 / 粘贴和屏幕阅读器仍能看到原始 Unicode 载荷

 

字体要求

通过 RegisterUnicodeTTF 注册的字体,必须同时包含 Arabic Presentation Forms-B 区块(U+FE70 - U+FEFC)的字形,以及(用于波斯语 / 乌尔都语时)相应的 Arabic Presentation Forms-A 范围(U+FB50 - U+FDFF)。下面这些字体自带完整字形集,推荐使用:

 

Noto Sans Arabic (Latin + Arabic; Forms-A + Forms-B + Arabic Extended-A).

Noto Naskh Arabic, Noto Naskh Arabic UI.

Amiri (traditional Naskh, full Forms-A + Forms-B coverage).

Scheherazade New(SIL 出品,为穆斯林世界的各种语言而设计)

Microsoft Arabic Typesetting / Tahoma / Times New Roman (Windows-bundled, full Forms-B; some have Forms-A coverage).

 

当注册字体缺少派生 presentation form 的 glyph 时,HotPDF 会回退到基础 Unicode codepoint 并原样输出;消费端阅读器随后可能尝试自己的成形(Acrobat、Foxit),也可能渲染 .notdef.

 

典型工作流

 

PDF.RegisterUnicodeTTF('NotoArab', 'NotoSansArabic-Regular.ttf');

PDF.AutoShapeArabic:= True;

PDF.BeginDoc;

PDF.CurrentPage.SetFont('NotoArab', [], 14);

PDF.CurrentPage.RtLTextOut(100, 700, 0,

  UnicodeString(#$0645#$0631#$062D#$0628#$0627));  // "marhaba" (hello)

PDF.EndDoc;

 

自动 Phase 8 流程集成 (v2.119.59 - v2.119.68)

v2.119.59 引入了可选启用的 ShapingFeatures: THPDFShapingFeatures 属性和 THPDFShapingFeature 枚举,让 shaping 流程不再局限于静态的后处理折叠(post-pass folding),而是升级为由 GSUB 驱动、按字体自动完成的字形替换。设置 PDF.ShapingFeatures := [sfArabicGSUB] 后,会应用所注册字体自带的阿拉伯文 init、medi、fina、isol、rlig、calt 和 rclt lookup,并输出得到的上下文字形 ID;加上 sfCursiveAttachment,会对该 shaped run 应用 GPOS cursive 的入口/出口锚点;加上 sfStandardLigatures,启用拉丁文标准连字(FB00-FB06 ff / fi / fl / ffi / ffl / ſt / st,通过 v2.119.65 的 ApplyLatinLigatureRefinement);加上 sfIndicShaping,启用天城文预处理重排(v2.119.67)。默认值 [] 可保证依赖静态流程的调用方得到逐字节相同的输出

 

设置 sfArabicGSUB 时,不再走静态 Unicode Presentation Forms shaper,改用字体自身的 GSUB 规则。这些 glyph 无法经字体 cmap 到达时,HotPDF 通过合成 codepoint 输出原生上下文 glyph ID,保持嵌入子集封闭,并为复制粘贴写出 ToUnicode 反向映射。如果调用方希望静态 shaper 继续覆盖字体 GSUB 未声明的 codepoint,应保持 sfArabicGSUB 关闭并依赖静态 post-pass 链

 

ToUnicode CMap 连字反向映射 (v2.119.61, v2.119.62, v2.119.65)

RegisterUnicodeTTF 生成的 Adobe-Identity-UCS ToUnicode CMap,为 post-pass 链可能产生的每一个连字码位都带有 bfchar 反向映射条目。v2.119.61 新增 27 条(8 个 LAM-ALEF、18 个 YEH-HAMZA 系列和 1 个 Allah);v2.119.62 新增 Bismillah(U+FDFD);v2.119.65 新增 7 条拉丁标准连字(FB00-FB06)。在阅读器中复制粘贴时,任何连字字形都能还原为原始码位序列,因此生成的 PDF 对无障碍访问依然友好

 

PUA 合成码位输出 (v2.119.68)

AssignSyntheticCodepointForGID(GID; out CP): Boolean + GetSyntheticCodepointForGID(GID): Word 允许 producer code 输出没有自然 Unicode codepoint 可通过字体 cmap 到达的 GSUB substitute GIDs。分配器会在 Private Use Area(U+E000 - U+F8FF,6400 个槽位)中分配 codepoint,并将该分配同步到 FUnicodeCpToGid(使 /CIDToGIDMap 在消费端阅读器处把合成 CP 解析回目标 GID)、FAcroFormUnicodeAdvances(使 v2.65 word-wrap 找到正确的 em-fraction)以及 per-GID 反向查找表(使重复分配请求保持幂等)。适用于 Devanagari cluster shapes、stylistic alternates,以及 GSUB 引入但 cmap 无法到达的 CJK ideographic variation sequences

 

与 OpenType GSUB 引擎的关系

上述 static post-pass shaper(v2.85.0 + v2.119.32 / 58 / 60 / 62)默认独立于 OpenType GSUB engine。通过 ShapingFeatures 启用 sfArabicGSUB 后,GSUB engine 成为生产端输出路径的一部分:HotPDF 查询 cmap 构建基础 GID 数组,对该 glyph run 应用原生 Arabic positional 与 contextual GSUB lookup,为没有 Unicode Presentation Form 的 substitute glyph ID 分配合成 codepoint,并调用 MarkUnicodeGlyphUsed 把 substitute glyph 保留在嵌入字体子集中

 

对于没有 Unicode codepoint 的 substitute glyph,调用方将 ShapingFeatures 与 v2.119.68 PUA synthetic-codepoint allocator 结合,使 substitute GID 仍可通过标准 hex pipeline 到达。对于字体 GSUB 声明未覆盖的 codepoint,静态 post-pass 链仍是默认回退

 

Scope and limitations

BiDi 算法仍不在本页范围内:混合 LTR 和 RTL 文本段仍需调用方自行排序,或使用独立 BiDi 库。GPOS、Indic/Tibetan/Mongolian、Hebrew、N'Ko、Adlam、Thai/Lao 和 Javanese 支持现在分别记录在可选塑形开关与 API 页面中,因此本页只聚焦静态和 GSUB 驱动的 Arabic 塑形

 

另请参阅: OpenType GSUB Substitution Engine, Automatic Shaping Pipeline (Phase 8), Syriac / Mongolian / Devanagari Shaping, THotPDF.AssignSyntheticCodepointForGID, THPDFPage.RtLTextOut, THPDFPage.UnicodeTextOut, CFF / OpenType Font Subsetting