标准字体介绍

文章目录

1. 标准字体的定义

PDF 规范定义了 14 种标准字体(Base14 字体),包括:
Times 系列:Times-Roman, Times-Bold, Times-Italic, Times-BoldItalic
Helvetica 系列:Helvetica, Helvetica-Bold, Helvetica-Oblique, Helvetica-BoldOblique
Courier 系列:Courier, Courier-Bold, Courier-Oblique, Courier-BoldOblique
SymbolZapfDingbats

这些字体在 PDF 文档中广泛使用,因此对它们进行特别化处理可以显著优化性能。

标准字体字典的创建

标准字体创建的入口函数

以下是使用Mermaid语法描述的CPDF_Font::GetStockFont函数的流程图:

无效
有效
开始
将ByteStringView转换为ByteString
获取标准字体ID
字体ID是否有效?
返回nullptr
获取字体全局实例
查找是否已经存在该字体
字体是否找到?
返回找到的字体
创建新的字体字典
设置字体类型为Font
设置字体子类型为Type1
设置字体名称
设置字体编码为WinAnsi
创建字体对象
将新创建的字体存储在全局字体表中
返回字体对象

流程说明:

  1. 开始:函数开始执行。
  2. 将ByteStringView转换为ByteString:将输入的ByteStringView转换为ByteString类型。
  3. 获取标准字体ID:通过CFX_FontMapper::GetStandardFontName函数获取标准字体ID。
  4. 字体ID是否有效?:检查获取的字体ID是否有效。
    • 如果无效,直接返回nullptr
    • 如果有效,继续执行。
  5. 获取字体全局实例:通过CPDF_FontGlobals::GetInstance获取字体全局实例。
  6. 查找是否已经存在该字体:在全局字体表中查找是否已经存在该字体。
  7. 字体是否找到?:检查是否找到了该字体。
    • 如果找到,直接返回该字体。
    • 如果未找到,继续执行。
  8. 创建新的字体字典:创建一个新的字体字典对象。
  9. 设置字体类型为Font:在字体字典中设置字体类型为Font
  10. 设置字体子类型为Type1:在字体字典中设置字体子类型为Type1
  11. 设置字体名称:在字体字典中设置字体名称。
  12. 设置字体编码为WinAnsi:在字体字典中设置字体编码为WinAnsi
  13. 创建字体对象:使用创建的字体字典对象创建字体对象。
  14. 将新创建的字体存储在全局字体表中:将新创建的字体存储在全局字体表中。
  15. 返回字体对象:返回新创建的字体对象。

这个流程图清晰地展示了CPDF_Font::GetStockFont函数的执行逻辑。

创建字体字典的通用函数

以下是使用Mermaid语法描述的CPDF_Font::Create函数的流程图:

开始
获取字体子类型
子类型是否为TrueType?
获取字体名称的前4个字符
是否为中文字体?
获取字体描述字典
字体描述是否存在且包含FontFile2?
创建CID字体
继续
创建TrueType字体
子类型是否为Type3?
创建Type3字体
子类型是否为Type0?
创建CID字体
创建Type1字体
加载字体
字体加载是否成功?
返回创建的字体对象
返回nullptr

流程说明:

  1. 开始:函数开始执行。
  2. 获取字体子类型:从字体字典中获取Subtype字段的值。
  3. 子类型是否为TrueType?:检查字体子类型是否为TrueType
    • 如果是,继续处理TrueType字体。
    • 如果不是,检查其他子类型。
  4. 获取字体名称的前4个字符:从BaseFont字段中提取前4个字符。
  5. 是否为中文字体?:检查提取的字符是否匹配中文字体名称列表。
    • 如果是中文字体,继续处理。
    • 如果不是中文字体,直接创建TrueType字体。
  6. 获取字体描述字典:从字体字典中获取FontDescriptor字段。
  7. 字体描述是否存在且包含FontFile2?:检查字体描述字典是否存在且是否包含FontFile2字段。
    • 如果不存在或不包含FontFile2,创建CID字体。
    • 如果存在且包含FontFile2,继续处理。
  8. 创建CID字体:创建CPDF_CIDFont对象。
  9. 创建TrueType字体:创建CPDF_TrueTypeFont对象。
  10. 子类型是否为Type3?:检查字体子类型是否为Type3
    ◦ 如果是,创建Type3字体。
    ◦ 如果不是,继续检查其他子类型。
  11. 创建Type3字体:创建CPDF_Type3Font对象。
  12. 子类型是否为Type0?:检查字体子类型是否为Type0
    ◦ 如果是,创建CID字体。
    ◦ 如果不是,默认创建Type1字体。
  13. 创建CID字体:创建CPDF_CIDFont对象。
  14. 创建Type1字体:创建CPDF_Type1Font对象。
  15. 加载字体:调用pFont->Load()加载字体。
  16. 字体加载是否成功?:检查字体加载是否成功。
    ◦ 如果成功,返回创建的字体对象。
    ◦ 如果失败,返回nullptr

这个流程图清晰地展示了CPDF_Font::Create函数的执行逻辑,涵盖了所有子类型的处理路径。

CPDF_Font::Create 函数的主要功能是 根据字体字典中的信息创建并返回一个字体对象。它是 PDF 文档处理中字体管理的核心函数,负责解析字体字典中的属性(如字体子类型),并根据这些属性创建对应的字体对象。


函数的具体功能:

  1. 解析字体字典
    • 从传入的 CPDF_Dictionary 中提取字体子类型(Subtype)和字体名称(BaseFont)等关键信息。

  2. 创建字体对象
    • 根据字体子类型(Subtype),创建不同类型的字体对象:
    TrueType:创建 CPDF_TrueTypeFont 对象。
    Type3:创建 CPDF_Type3Font 对象。
    Type0:创建 CPDF_CIDFont 对象。
    其他:默认创建 CPDF_Type1Font 对象。

  3. 加载字体
    • 调用字体对象的 Load 方法,加载字体数据。如果加载失败,返回 nullptr

  4. 返回字体对象
    • 如果字体创建和加载成功,返回创建好的字体对象(RetainPtr<CPDF_Font>)。


Type1字体字典的解析

以下是使用Mermaid语法描述的CPDF_Type1Font::Load函数的流程图:

开始
获取Base14字体名称
是否是Base14字体?
调用通用加载方法
获取字体描述符字典
字体描述符是否存在且包含Flags?
获取字体标志
是否是符号字体?
设置为符号字体标志
设置为非符号字体标志
是否是固定宽度字体?
设置字符宽度
是否是Symbol字体?
设置为Adobe Symbol编码
是否是Dingbats字体?
设置为Zapf Dingbats编码
是否是非符号字体?
设置为标准编码
继续
调用通用加载方法
结束

流程说明:

  1. 开始:函数开始执行。
  2. 获取Base14字体名称:通过CFX_FontMapper::GetStandardFontName获取Base14字体名称。
  3. 是否是Base14字体?:检查是否是Base14字体。
    • 如果不是,直接调用通用加载方法LoadCommon
    • 如果是,继续处理。
  4. 获取字体描述符字典:从字体字典中获取FontDescriptor字段。
  5. 字体描述符是否存在且包含Flags?:检查字体描述符是否存在且是否包含Flags字段。
    • 如果存在且包含Flags,获取字体标志。
    • 如果不存在或不包含Flags,继续检查是否是符号字体。
  6. 是否是符号字体?:检查是否是符号字体。
    • 如果是,设置为符号字体标志。
    • 如果不是,设置为非符号字体标志。
  7. 是否是固定宽度字体?:检查是否是固定宽度字体。
    • 如果是,设置字符宽度为600。
    • 如果不是,继续检查字体类型。
  8. 是否是Symbol字体?:检查是否是Symbol字体。
    • 如果是,设置为Adobe Symbol编码。
    • 如果不是,继续检查是否是Dingbats字体。
  9. 是否是Dingbats字体?:检查是否是Dingbats字体。
    • 如果是,设置为Zapf Dingbats编码。
    • 如果不是,继续检查是否是非符号字体。
  10. 是否是非符号字体?:检查是否是非符号字体。
    ◦ 如果是,设置为标准编码。
    ◦ 如果不是,继续执行。
  11. 调用通用加载方法:调用LoadCommon方法完成字体加载。
  12. 结束:函数执行结束。

这个流程图清晰地展示了CPDF_Type1Font::Load函数的执行逻辑,涵盖了所有关键分支和操作。

这里对标准字体进行了特别的预处理

因为 标准字体的属性(如编码、字符宽度、标志等)是已知且固定的,不需要从字体文件中动态加载。

(1) 设置标准字体的标志
if (pFontDesc && pFontDesc->KeyExist("Flags")) {
  m_Flags = pFontDesc->GetIntegerFor("Flags");
} else if (IsSymbolicFont()) {
  m_Flags = pdfium::kFontStyleSymbolic;
} else {
  m_Flags = pdfium::kFontStyleNonSymbolic;
}

• 根据字体描述符中的 Flags 字段,设置字体的标志(如符号字体或非符号字体)。

(2) 设置字符宽度
if (IsFixedFont()) {
  std::fill(std::begin(m_CharWidth), std::end(m_CharWidth), 600);
}

• 如果字体是固定宽度字体(如 Courier),将所有字符的宽度设置为固定值(通常为 600)。

(3) 设置标准字体的编码
if (m_Base14Font == CFX_FontMapper::kSymbol) {
  m_BaseEncoding = FontEncoding::kAdobeSymbol;
} else if (m_Base14Font == CFX_FontMapper::kDingbats) {
  m_BaseEncoding = FontEncoding::kZapfDingbats;
} else if (FontStyleIsNonSymbolic(m_Flags)) {
  m_BaseEncoding = FontEncoding::kStandard;
}

• 根据标准字体的类型,设置对应的编码(如 Symbol 使用 Adobe Symbol 编码,ZapfDingbats 使用 Zapf Dingbats 编码)。


简单字体 通用的加载函数

以下是使用Mermaid语法描述的CPDF_SimpleFont::LoadCommon函数的流程图:

开始
获取字体描述符
字体描述符是否存在?
加载字体描述符
继续
加载字符宽度信息
是否有字体文件?
是否是子集化字体?
截取字体名称
继续
加载替代字体
是否是符号字体?
设置为基础编码
继续
加载PDF编码信息
加载字形映射表
清空字符名称数组
是否有字体面?
返回true
是否是全大写字体?
遍历小写字母范围
字形索引是否有效且有字体文件?
继续
计算对应的大写字母索引
设置字形索引
大写字母宽度是否有效?
设置小写字母宽度和边界框
继续
检查字体度量信息
返回true

流程说明:

  1. 开始:函数开始执行。
  2. 获取字体描述符:从字体字典中获取FontDescriptor字段。
  3. 字体描述符是否存在?:检查字体描述符是否存在。
    • 如果存在,加载字体描述符。
    • 如果不存在,继续执行。
  4. 加载字符宽度信息:从字体描述符中加载字符宽度信息。
  5. 是否有字体文件?:检查是否有字体文件。
    • 如果有,继续检查是否是子集化字体。
    • 如果没有,加载替代字体。
  6. 是否是子集化字体?:检查是否是子集化字体。
    • 如果是,截取字体名称。
    • 如果不是,继续执行。
  7. 加载替代字体:如果没有字体文件,加载替代字体。
  8. 是否是符号字体?:检查是否是符号字体。
    • 如果不是,设置为基础编码。
    • 如果是,继续执行。
  9. 加载PDF编码信息:加载PDF编码信息。
  10. 加载字形映射表:加载字形映射表。
  11. 清空字符名称数组:清空字符名称数组。
  12. 是否有字体面?:检查是否有字体面。
    ◦ 如果没有,直接返回true
    ◦ 如果有,继续检查是否是全大写字体。
  13. 是否是全大写字体?:检查是否是全大写字体。
    ◦ 如果是,遍历小写字母范围。
    ◦ 如果不是,直接检查字体度量信息并返回true
  14. 遍历小写字母范围:遍历小写字母范围。
  15. 字形索引是否有效且有字体文件?:检查字形索引是否有效且有字体文件。
    ◦ 如果有效且有字体文件,继续遍历。
    ◦ 如果无效或没有字体文件,计算对应的大写字母索引。
  16. 计算对应的大写字母索引:计算对应的大写字母索引。
  17. 设置字形索引:设置小写字母的字形索引。
  18. 大写字母宽度是否有效?:检查大写字母宽度是否有效。
    ◦ 如果有效,设置小写字母的宽度和边界框。
    ◦ 如果无效,继续遍历。
  19. 检查字体度量信息:检查字体度量信息。
  20. 返回true:函数执行结束,返回true

这个流程图清晰地展示了CPDF_SimpleFont::LoadCommon函数的执行逻辑,涵盖了所有关键分支和操作。

对于标准字体 没有 字体描述符 ,所以这里直接使用替代字体实现 (对于podofo libharu而言,使用的是 内置的字体度量信息)

简单字体 去 加载替代字体

加载替代字体是一个复杂的过程,我会给出流程图,但不会介绍细节

以下是使用Mermaid语法描述的CPDF_SimpleFont::LoadSubstFont函数的流程图:

开始
是否不使用字体宽度且不是固定间距字体?
初始化宽度为0
遍历字符宽度表
字符宽度是否有效?
继续遍历
宽度是否未设置?
设置宽度
宽度是否不一致?
结束遍历
是否所有字符宽度一致?
设置为固定间距字体
继续
获取字体权重
权重是否有效?
设置为正常权重
继续
加载替代字体
结束

流程说明:

  1. 开始:函数开始执行。
  2. 是否不使用字体宽度且不是固定间距字体?:检查是否不使用字体宽度且不是固定间距字体。
    • 如果是,继续处理。
    • 如果不是,跳过字符宽度检查。
  3. 初始化宽度为0:初始化宽度变量为0。
  4. 遍历字符宽度表:遍历字符宽度表。
  5. 字符宽度是否有效?:检查字符宽度是否有效。
    • 如果无效,继续遍历。
    • 如果有效,继续检查宽度是否未设置。
  6. 宽度是否未设置?:检查宽度是否未设置。
    • 如果未设置,设置宽度。
    • 如果已设置,检查宽度是否不一致。
  7. 宽度是否不一致?:检查宽度是否不一致。
    • 如果不一致,结束遍历。
    • 如果一致,继续遍历。
  8. 是否所有字符宽度一致?:检查是否所有字符宽度一致。
    • 如果一致,设置为固定间距字体。
    • 如果不一致,继续执行。
  9. 获取字体权重:获取字体权重。
  10. 权重是否有效?:检查权重是否有效。
    ◦ 如果无效,设置为正常权重。
    ◦ 如果有效,继续执行。
  11. 加载替代字体:加载替代字体。
  12. 结束:函数执行结束。

这个流程图清晰地展示了CPDF_SimpleFont::LoadSubstFont函数的执行逻辑,涵盖了所有关键分支和操作。

CPDF_SimpleFont::LoadSubstFont 函数的主要功能是 加载替代字体。当 PDF 文档中指定的字体无法直接加载时(例如字体文件缺失或无法解析),该函数会根据字体的属性(如字体名称、权重、倾斜角度等)选择一个替代字体,以确保文本能够正确渲染。


函数的具体功能:

  1. 检查字符宽度是否一致
    • 如果函数不使用字体宽度且字体不是固定间距字体,它会遍历字符宽度表,检查所有字符的宽度是否一致。
    • 如果所有字符宽度一致,函数会将字体标记为固定间距字体(FixedPitch)。

  2. 获取并验证字体权重
    • 从字体属性中获取字体权重(如 NormalBold 等)。
    • 如果权重无效(不在允许的范围内),函数会将权重设置为默认值(Normal)。

  3. 加载替代字体
    • 根据字体名称、是否为 TrueType 字体、字体标志、权重、倾斜角度等属性,调用 m_Font.LoadSubst 方法加载一个替代字体。
    • 替代字体的选择基于系统可用的字体资源,确保文本能够以相似的样式渲染。


函数的输入和依赖:

  1. 输入
    • 函数依赖以下成员变量:
    m_bUseFontWidth:是否使用字体宽度。
    m_Flags:字体的标志(如固定间距、符号字体等)。
    m_CharWidth:字符宽度表。
    m_BaseFontName:字体名称。
    m_Font:字体对象,用于加载替代字体。
    m_ItalicAngle:字体的倾斜角度。

  2. 依赖
    GetFontWeight():获取字体权重。
    m_Font.LoadSubst():加载替代字体的核心方法。


函数的输出:

• 函数没有返回值(void),但它会通过 m_Font.LoadSubst 方法加载一个替代字体,并更新字体的属性(如固定间距标志)。


应用场景:

在以下情况下,LoadSubstFont 函数会被调用:

  1. 字体文件缺失:PDF 文档中指定的字体文件不存在或无法加载。
  2. 字体不支持:PDF 文档中指定的字体格式不被当前系统支持。
  3. 字体属性不完整:字体描述符中缺少必要的信息(如字体文件路径)。

通过加载替代字体,函数确保 PDF 文档中的文本能够以相似的样式渲染,避免出现乱码或无法显示的情况。


示例:

假设 PDF 文档中指定了一个字体 Helvetica-Bold,但系统中没有该字体文件。LoadSubstFont 函数会:

  1. 检查字符宽度是否一致。
  2. 获取字体权重(Bold)。
  3. 根据字体名称 Helvetica-Bold、权重 Bold 等属性,加载一个替代字体(如 Arial-Bold)。

总结:

CPDF_SimpleFont::LoadSubstFont 函数的主要功能是 在无法加载指定字体时,根据字体属性选择一个替代字体。它通过以下步骤实现:

  1. 检查字符宽度是否一致,标记固定间距字体。
  2. 获取并验证字体权重。
  3. 加载替代字体,确保文本能够正确渲染。

这个函数是 PDF 字体处理中的重要环节,确保了文档的兼容性和可读性。

通过内置字体映射器查找并加载替代字体

加载替代字体是一个复杂的过程,我会给出流程图,但不会介绍细节

CFX_FontMapper::FindSubstFont 函数的主要功能是 ​根据字体名称、类型、标志、权重、倾斜角度等属性,查找并返回一个替代字体对象。该函数通过内置的字体映射器和外部字体信息管理器,选择最合适的字体来替代指定的字体,以确保文本能够正确渲染。

开始
weight 是否为 0?
设置 weight 为默认值
是否启用外部属性?
设置 weight 和 italic_angle 为默认值
继续
subst_name 是否为 Symbol?
设置 Symbol 字体属性并返回
subst_name 是否为 ZapfDingbats?
设置 Dingbats 字体属性并返回
解析字体名称
是否为 Base14 字体?
获取 Base14 字体样式和间距属性
解析字体样式和间距属性
O
是否强制粗体?
设置 weight 为 Bold
继续
是否解析样式成功?
更新 family 和 base_font
继续
是否启用外部字体信息管理器?
返回内置替代字体
获取字符集
是否为 CJK 字体?
设置 CJK 字体属性
继续
匹配已安装字体
是否找到匹配字体?
更新 family
是否为 CJK 字体?
设置 CJK 字体属性
继续
是否为 Base14 字体?
调整 Base14 字体样式
继续
是否为斜体字体?
设置 is_italic 为 true
继续
映射外部字体
是否成功映射字体?
返回外部替代字体
是否为 CJK 字体?
调整 weight 和 is_italic
是否找到匹配字体?
获取字体句柄
字符集是否为 Symbol?
是否成功获取字体句柄?
是否为 Symbol 字体?
字符集是否为 ANSI?
返回内置 Symbol 字体
重新调用 FindSubstFont
返回内置替代字体
查找字符集匹配的字体
是否找到匹配字体?
获取字体句柄
是否成功获取字体句柄?

以下是函数的详细流程:


1. ​初始化字体权重和属性

  • 如果传入的 weight 为 0,将其设置为默认值 pdfium::kFontWeightNormal
  • 如果未启用外部属性标志(FXFONT_USEEXTERNATTR),将 weight 设置为 pdfium::kFontWeightNormal,并将 italic_angle 设置为 0。

2. ​处理特殊字体(Symbol 和 ZapfDingbats)​

  • 如果字体名称为 Symbol 且不是 TrueType 字体,设置替代字体的属性为 Chrome Symbol,并返回内置的 Symbol 字体。
  • 如果字体名称为 ZapfDingbats,设置替代字体的属性为 Chrome Dingbats,并返回内置的 Dingbats 字体。

3. ​解析字体名称

  • 如果字体名称包含逗号(,),将其分为 family(字体家族)和 style(字体样式)。
  • 如果字体名称包含连字符(-),将其分为 familystyle
  • 根据 family 查找是否匹配内置的 14 种标准字体(Base14)。

4. ​获取字体样式和间距属性

  • 如果字体是 Base14 字体,根据字体索引获取其样式和间距属性。
  • 如果字体不是 Base14 字体,根据字体标志和名称解析其样式和间距属性。

5. ​调整字体权重和样式

  • 如果字体样式强制为粗体(ForceBold),将 weight 设置为 pdfium::kFontWeightBold
  • 解析 style 字符串,更新 weightnStyle

6. ​处理 CJK 字体

  • 如果字体是 CJK(中日韩)字体,设置替代字体的相关属性(如 m_bSubstCJKm_WeightCJKm_bItalicCJK)。

7. ​匹配已安装的字体

  • 根据 familysubst_name,在已安装的字体中查找匹配的字体。
  • 如果未找到匹配的字体,检查是否支持第三方字体。

8. ​调整字体家族和样式

  • 如果找到匹配的字体,更新 family
  • 如果是 Base14 字体,根据样式调整字体索引和 family

9. ​映射外部字体

  • 如果启用了外部字体信息管理器(m_pFontInfo),调用 MapFont 方法,根据 weightis_italicCharsetpitch_familyfamily 映射字体。
  • 如果成功映射到字体,返回外部替代字体。

10. ​处理未找到字体的场景

  • 如果未找到匹配的字体,返回内置的替代字体。
  • 如果是 Symbol 字体,重新调用 FindSubstFont,去除符号字体标志。
  • 如果是 ANSI 字体,返回内置的替代字体。

11. ​处理其他字符集

  • 如果字符集不是 Symbol 或 ANSI,在 m_FaceArray 中查找匹配的字体。
  • 如果找到匹配的字体,返回外部替代字体。
  • 如果未找到,返回内置的替代字体。

加载字体编码

以下是使用 Mermaid 语法描述的 CPDF_SimpleFont::LoadPDFEncoding 函数的流程图:

开始
获取编码对象 pEncoding = m_pFontDict->GetDirectObjectFor Encoding
是否存在 pEncoding?
m_BaseFontName 是否为 Symbol?
bTrueType 是否为真?
设置 m_BaseEncoding = FontEncoding::kMsSymbol
设置 m_BaseEncoding = FontEncoding::kAdobeSymbol
是否未嵌入且 m_BaseEncoding 为 kBuiltin?
设置 m_BaseEncoding = FontEncoding::kWinAnsi
结束
pEncoding 是否为名称对象?
m_BaseEncoding 是否为 AdobeSymbol 或 ZapfDingbats?
是否为符号字体且 m_BaseFontName 为 Symbol?
bTrueType 是否为真?
设置 m_BaseEncoding = FontEncoding::kAdobeSymbol
获取编码字符串 bsEncoding = pEncoding->GetString
bsEncoding 是否为 MacExpertEncoding?
替换 bsEncoding 为 WinAnsiEncoding
获取预定义编码 GetPredefinedEncoding bsEncoding, &m_BaseEncoding
获取字典对象 pDict = pEncoding->AsDictionary
是否存在 pDict?
m_BaseEncoding 是否为 AdobeSymbol 或 ZapfDingbats?
获取基础编码 bsEncoding = pDict->GetByteStringFor BaseEncoding
bTrueType 是否为真且 bsEncoding 为 MacExpertEncoding?
替换 bsEncoding 为 WinAnsiEncoding
获取预定义编码 GetPredefinedEncoding bsEncoding, &m_BaseEncoding
是否未嵌入或 bTrueType 为真且 m_BaseEncoding 为 kBuiltin?
设置 m_BaseEncoding = FontEncoding::kStandard
继续
加载差异编码表 LoadDifferences pDict

流程说明:

  1. 获取编码对象
    • 从字体字典中获取 Encoding 对象 pEncoding

  2. 检查是否存在编码对象
    • 如果不存在 pEncoding,根据 m_BaseFontName 是否为 Symbol 设置默认编码。
    • 如果未嵌入且 m_BaseEncodingkBuiltin,设置为 WinAnsi 编码。

  3. 处理名称对象
    • 如果 pEncoding 是名称对象,检查是否为 AdobeSymbolZapfDingbats,如果是则直接返回。
    • 如果是符号字体且 m_BaseFontNameSymbol,设置为 AdobeSymbol 编码。
    • 获取编码字符串 bsEncoding,如果为 MacExpertEncoding,替换为 WinAnsiEncoding
    • 调用 GetPredefinedEncoding 获取预定义编码。

  4. 处理字典对象
    • 如果 pEncoding 是字典对象,获取 BaseEncoding 字段。
    • 如果 bTrueType 为真且 BaseEncodingMacExpertEncoding,替换为 WinAnsiEncoding
    • 调用 GetPredefinedEncoding 获取预定义编码。
    • 如果未嵌入或 bTrueType 为真且 m_BaseEncodingkBuiltin,设置为 Standard 编码。
    • 调用 LoadDifferences 加载差异编码表。


总结:

该流程图详细描述了 CPDF_SimpleFont::LoadPDFEncoding 函数的执行逻辑,涵盖了所有关键分支和操作。通过该函数,系统能够根据字体字典中的编码信息设置字体的编码属性,确保文本能够正确渲染。

CPDF_SimpleFont::LoadPDFEncoding 函数的主要目的是 加载并设置字体的编码信息,以确保 PDF 文档中的文本能够正确解码和渲染。加载编码信息不仅仅是为了加载差异数组(Differences),还包括处理字体的基础编码、预定义编码以及特殊字体的编码需求。


为什么需要加载这些编码?

  1. 正确解码文本
    • PDF 文档中的文本通常使用特定的编码(如 WinAnsiMacRomanAdobeSymbol 等)进行存储。
    • 如果字体没有正确的编码信息,文本可能无法正确解码,导致乱码或显示错误。

  2. 处理字体嵌入
    • 如果字体是嵌入的,PDF 文档中可能包含自定义的编码信息(如差异数组 Differences)。
    • 加载编码信息可以确保嵌入字体的文本能够正确渲染。

  3. 支持特殊字体
    • 某些字体(如 SymbolZapfDingbats)有特殊的编码需求。
    • 加载编码信息可以确保这些特殊字体的文本能够正确显示。

  4. 兼容性
    • PDF 规范支持多种编码方式,加载编码信息可以确保 PDF 文档在不同平台和阅读器上的一致性。


编码信息的组成部分

  1. 基础编码(BaseEncoding)
    • 基础编码是字体的默认编码方式,如 WinAnsiMacRomanAdobeSymbol 等。
    • 如果字体没有指定编码,系统会根据字体名称和类型设置默认的基础编码。

  2. 差异数组(Differences)
    • 差异数组是 PDF 文档中自定义的编码表,用于覆盖或扩展基础编码。
    • 如果字体是嵌入的,PDF 文档可能包含差异数组,以支持自定义字符映射。

  3. 预定义编码(Predefined Encoding)
    • 预定义编码是 PDF 规范中定义的标准化编码方式,如 WinAnsiMacExpert 等。
    • 系统会根据字体名称和编码对象选择最合适的预定义编码。


为什么需要加载差异数组?

差异数组(Differences)是 PDF 文档中自定义的编码表,用于覆盖或扩展基础编码。加载差异数组的主要目的是:

  1. 支持自定义字符映射
    • 如果 PDF 文档中使用了自定义字符(如特殊符号或非标准字符),差异数组可以确保这些字符能够正确映射和显示。

  2. 处理嵌入字体
    • 如果字体是嵌入的,PDF 文档可能包含自定义的编码信息,差异数组可以确保嵌入字体的文本能够正确渲染。

  3. 覆盖基础编码
    • 差异数组可以覆盖基础编码中的某些字符映射,以满足特定的显示需求。


函数的具体逻辑

  1. 检查编码对象
    • 如果字体字典中没有编码对象,根据字体名称和类型设置默认的基础编码。

  2. 处理名称对象
    • 如果编码对象是名称对象(如 WinAnsiMacRoman),调用 GetPredefinedEncoding 获取预定义编码。

  3. 处理字典对象
    • 如果编码对象是字典对象,获取 BaseEncoding 字段,并调用 GetPredefinedEncoding 获取预定义编码。
    • 如果未嵌入或字体是 TrueType,设置默认的 Standard 编码。
    • 调用 LoadDifferences 加载差异数组。


总结

加载编码信息的主要目的是 确保 PDF 文档中的文本能够正确解码和渲染。差异数组是编码信息的一部分,用于支持自定义字符映射和嵌入字体的特殊需求。通过加载编码信息,系统能够正确处理字体的基础编码、预定义编码以及自定义的差异数组,确保文本的正确显示和兼容性。

加载字形映射表

CPDF_Type1Font::LoadGlyphMap 函数的主要功能是 加载字体的字形映射表,即将字符码(charcode)映射到字体的字形索引(glyph index)。这个映射表是字体渲染的核心,它决定了字符如何被转换为字形并显示在屏幕上。

以下是该函数的详细流程和解释:


1. 获取字体的底层 FreeType 面对象

• 调用 m_Font.GetFace() 获取字体的 FreeType 面对象 face
• 如果没有有效的面对象,函数直接返回。


2. 处理 Apple 平台的特殊逻辑

• 如果目标平台是 Apple 系统(BUILDFLAG(IS_APPLE)),函数会检查是否使用 Core Text 技术。
• 如果没有平台特定字体(m_Font.GetPlatformFont()),函数会尝试创建平台字体。
• 如果字体名称为 "DFHeiStd-W5",禁用 Core Text。


3. 处理非嵌入式、非符号字体且为 TrueType 字体的场景

• 如果字体是非嵌入的、非符号字体且为 TrueType 字体,函数会尝试使用 Microsoft Symbol 字符映射表(UseTTCharmapMSSymbol)。
• 遍历所有字符码(charcode),尝试通过 Unicode 值获取字形索引。
• 如果找到至少一个有效的字形索引,函数会返回。


4. 选择 Unicode 字符映射表

• 如果字体不是 TrueType 字体或未使用 Microsoft Symbol 映射表,函数会选择 Unicode 字符映射表(face->SelectCharMap(fxge::FontEncoding::kUnicode))。
• 如果基础编码为内置编码(kBuiltin),将其设置为标准编码(kStandard)。


5. 遍历所有字符码并加载字形索引

• 对于每个字符码(charcode),函数会获取 Adobe 字符名称(GetAdobeCharName)。
• 如果找到字符名称,函数会将其转换为 Unicode 值(UnicodeFromAdobeName),并设置到编码表中。
• 调用 face->GetCharIndex 获取字形索引。
• 如果字符名称为 ".notdef" 且字形索引无效,函数会将其映射到空格字符(Unicode 0x20)。


6. 处理符号字体

• 如果字体是符号字体(FontStyleIsSymbolic(m_Flags)),函数会遍历所有字符码,获取 Adobe 字符名称并转换为 Unicode 值。
• 如果没有找到字符名称,函数会直接使用字符码获取字形索引。
• 如果字形索引有效,函数会获取字形名称并转换为 Unicode 值。


7. 处理非符号字体

• 如果字体不是符号字体,函数会遍历所有字符码,获取 Adobe 字符名称并转换为 Unicode 值。
• 调用 m_Font.GetFace()->GetNameIndex 获取字形索引。
• 如果字符名称为 ".notdef""space",函数会将其映射到空格字符(Unicode 0x20)。


8. 处理 Apple 平台的扩展 GID

• 如果目标平台是 Apple 系统且未使用 Core Text,函数会将扩展 GID(m_ExtGID)设置为字形索引。


9. 返回

• 函数执行完毕,返回。


详细流程总结

  1. 获取 FreeType 面对象
    • 如果无效,直接返回。

  2. 处理 Apple 平台的特殊逻辑
    • 检查是否使用 Core Text,尝试创建平台字体。

  3. 处理 TrueType 字体
    • 如果字体是 TrueType 字体,尝试使用 Microsoft Symbol 映射表加载字形索引。

  4. 选择 Unicode 字符映射表
    • 如果字体不是 TrueType 字体,选择 Unicode 字符映射表。

  5. 遍历字符码并加载字形索引
    • 获取 Adobe 字符名称,转换为 Unicode 值,并获取字形索引。
    • 如果字符名称为 ".notdef",映射到空格字符。

  6. 处理符号字体
    • 如果字体是符号字体,直接使用字符码获取字形索引。

  7. 处理非符号字体
    • 如果字体不是符号字体,使用 Adobe 字符名称获取字形索引。

  8. 处理 Apple 平台的扩展 GID
    • 如果未使用 Core Text,设置扩展 GID 为字形索引。

  9. 返回
    • 函数执行完毕,返回。


加载字形映射表
获取字体面对象 face
字体面是否有效?
返回
目标平台是否是Apple系统?
假设使用Core Text bCoreText = true
是否有平台特定字体?
字体名称是否为 DFHeiStd-W5 ?
禁用Core Text bCoreText = false
获取平台接口 pPlatform
获取字体数据范围 span
创建平台字体
平台字体是否创建成功?
禁用Core Text bCoreText = false
继续后续流程
字体是否未嵌入且非符号字体且为TrueType字体?
是否使用TrueType字符映射表?
标记是否找到至少一个字形索引 bGotOne = false
遍历所有字符码 charcode
是否遍历完成?
是否找到至少一个字形索引?
是否未使用Core Text?
将扩展GID设置为字形索引
返回
遍历Unicode前缀数组 prefix
是否遍历完成?
计算Unicode值 unicode
获取字形索引
字形索引是否有效?
标记找到字形索引 bGotOne = true
跳出内层循环
选择Unicode字符映射表
基础编码是否为内置编码?
设置为基础标准编码
继续后续流程
遍历所有字符码 charcode
是否遍历完成?
是否未使用Core Text?
将扩展GID设置为字形索引
返回
获取Adobe字符名称 name
是否找到名称?
设置Unicode编码
获取字形索引
字形索引是否无效且名称为 .notdef ?
设置为空格字符
获取空格字形索引
继续后续流程
使用Type1字符映射表
目标平台是否是Apple系统?
是否使用Core Text?
字体是否为符号字体?
遍历所有字符码 charcode
是否遍历完成?
返回
获取Adobe字符名称 name
是否找到名称?
设置Unicode编码
获取字形索引
设置扩展GID
获取字形索引
获取字形名称 glyph_name
字形名称是否为空?
设置Unicode编码为0
转换为Unicode
设置Unicode编码
选择Unicode字符映射表 bUnicode
遍历所有字符码 charcode
是否遍历完成?
返回
获取Adobe字符名称 name
是否找到名称?
设置Unicode编码
映射字形名称 pStrUnicode
获取字形名称索引 name_index
映射成功但索引无效?
使用映射后的名称
设置字形索引
设置扩展GID
字形索引是否有效?
名称是否为 .notdef 或 space ?
设置为空格字符
获取字形索引
获取字形索引
计算扩展GID
字体是否为符号字体?
遍历所有字符码 charcode
是否遍历完成?
是否未使用Core Text?
将扩展GID设置为字形索引
返回
获取Adobe字符名称 name
是否找到名称?
设置Unicode编码
获取字形索引
获取字形索引
字形索引是否有效?
获取字形名称 glyph_name
字形名称是否为空?
设置Unicode编码为0
转换为Unicode
设置Unicode编码
选择Unicode字符映射表 bUnicode
遍历所有字符码 charcode
是否遍历完成?
是否未使用Core Text?
将扩展GID设置为字形索引
返回
获取Adobe字符名称 name
是否找到名称?
设置Unicode编码
获取字形索引
字形索引是否有效?
名称是否为 .notdef 或 space ?
设置为空格字符
设置无效字形索引
获取字形索引

在这个 LoadGlyphMap 函数里 Unicode 和 charcode 的区别是什么

CPDF_Type1Font::LoadGlyphMap 函数中,Unicodecharcode 是两个不同的概念,它们分别代表了字符的两种不同编码方式。以下是它们的区别和具体作用:


1. charcode(字符码)

定义charcodePDF 文档中使用的字符编码,也称为字符索引或字符编号。
范围:通常是 0 到 255(8 位),或者在某些情况下可能更大。
作用
charcode 是 PDF 文档中存储的原始字符编码,用于表示字符在文档中的位置。
◦ 它并不直接对应字符的实际内容,而是需要通过 编码表(如 WinAnsiMacRomanAdobeSymbol 等)映射到具体的字符。
示例
◦ 在 WinAnsi 编码中,charcode 65 对应字符 "A"
◦ 在 AdobeSymbol 编码中,charcode 65 可能对应一个符号字符(如 "Δ")。


2. Unicode(统一码)

定义Unicode 是一种 全球通用的字符编码标准,用于表示几乎所有语言的字符。
范围Unicode 的范围非常大,通常使用 16 位或 32 位编码(如 UTF-16、UTF-32)。
作用
Unicode 用于表示字符的实际内容,确保字符在不同系统和平台上的一致性。
◦ 在字体渲染中,Unicode 是字符的最终表示形式,用于查找字体的字形索引(glyph index)。
示例
UnicodeU+0041 对应字符 "A"
UnicodeU+0394 对应字符 "Δ"


3. charcodeUnicode 的关系

映射关系
charcode 需要通过 编码表(如 WinAnsiAdobeSymbol 等)映射到 Unicode
◦ 在 LoadGlyphMap 函数中,charcode 首先被映射到 Unicode,然后通过 Unicode 查找字体的字形索引(glyph index)。
具体流程
1. 获取 charcode 对应的 Adobe 字符名称(GetAdobeCharName)。
2. 将 Adobe 字符名称转换为 Unicode 值(UnicodeFromAdobeName)。
3. 使用 Unicode 值查找字体的字形索引(face->GetCharIndex)。


4. 为什么需要同时使用 charcodeUnicode

charcode 的作用
charcode 是 PDF 文档中存储的原始编码,直接反映了文档的字符布局。
◦ 它依赖于特定的编码表(如 WinAnsiAdobeSymbol),这些编码表是 PDF 文档的一部分。
Unicode 的作用
Unicode 是一种通用的字符编码标准,确保字符在不同系统和平台上的一致性。
◦ 字体渲染引擎(如 FreeType)通常使用 Unicode 来查找字形索引,因此需要将 charcode 转换为 Unicode


5. 具体代码示例

LoadGlyphMap 函数中,charcodeUnicode 的使用如下:

// 获取 charcode 对应的 Adobe 字符名称
const char* name = GetAdobeCharName(m_BaseEncoding, m_CharNames, charcode);

// 将 Adobe 字符名称转换为 Unicode 值
wchar_t unicode = UnicodeFromAdobeName(name);

// 设置 Unicode 编码
m_Encoding.SetUnicode(charcode, unicode);

// 使用 Unicode 值查找字体的字形索引
m_GlyphIndex[charcode] = face->GetCharIndex(unicode);

6. 总结

charcode 是 PDF 文档中使用的字符编码,依赖于特定的编码表(如 WinAnsiAdobeSymbol)。
Unicode 是一种全球通用的字符编码标准,用于表示字符的实际内容。
• 在 LoadGlyphMap 函数中,charcode 首先被映射到 Unicode,然后通过 Unicode 查找字体的字形索引。
• 这种设计确保了 PDF 文档中的字符能够正确解码并渲染,同时支持多种编码方式和平台兼容性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丁金金_chihiro_修行

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值