Chrome另存为PDF,更改CJK字符

Abh*_*arg 8 html pdf unicode google-chrome

当我们尝试使用Chrome的“打印”选项将包含CJK字符的网页另存为PDF时,我们看到了一个问题。

chrome中的chrome呈现的字符在外观上看起来相同,但Unicode不同。

以下是基本的HTML。

<HTML>

<HEAD>
  Test Character
</HEAD>

<BODY>
  ?
</BODY>

</HTML>
Run Code Online (Sandbox Code Playgroud)

如果以chrome打开HTML,则该字符为
https://graphemica.com/%E5%AD%90

但是PDF中对应的字符是
https://graphemica.com/%E2%BC%A6

HTML和PDF的链接
https://1drv.ms/f/s!Aq5YnvMOo4V8iVzdRyjmX3X5L0TD

首先,我想了解为什么会这样,然后又可以解决该问题。是否有任何实用程序可以将我的角色转换为Chrome将要在PDF中呈现的角色。

作业系统版本:MacOS 10.13.6(17G65)

Chrome版本:75.0.3770.100(官方内部版本)(64位)

cod*_*ody 4

我的理解是,PDF 实际上并不包含您在呈现文档时看到的字符串,而是包含字体字形序列以及将这些字形映射回字符代码的支持查找表。在 OP 的测试用例中,macOS 上的 cjk 字符使用的字体是STSongti-SC-Regular,其字形 id 是 hex 0436

我只能在 macOS 上重现 OP 的行为。在 Linux 和 Windows 上,我看到映射到最初位于 html 文件中的字符的字形:U+5B50。下面显示了实用程序输出中的示例比较peepdf

在此输入图像描述

从字符到字形和字形到字符的操作分别在skia的onCharsToGlyphs()和方法中完成。在 macOS 上,这两者都依赖于对 Core Text 库的调用,迭代每个可能的字符来构建映射表。populate_glyph_to_unicode()SkFontHost_mac.cppCTFontGetGlyphsForCharacters()

我将这种方法归结为以下测试代码,打印出给定字体的每个字形 ID 和相应的字符代码:

NSString *fontName = @"STSongti-SC-Regular";
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)fontName, 10.0, NULL);

CFDataRef bitmap = CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault, CTFontCopyCharacterSet(fontRef));
CFIndex length = CFDataGetLength(bitmap);

const UInt8* bits = CFDataGetBytePtr(bitmap);

for (int i = 0; i < length; i++) {
    int mask = bits[i];
    if (!mask)
        continue;
    for (int j = 0; j < 8; j++) {
        CGGlyph glyph;
        UniChar unichar = (UniChar)((i << 3) + j);
        if (mask & (1 << j) && CTFontGetGlyphsForCharacters(fontRef, &unichar, &glyph, 1)) {
            NSLog(@"%04x %04x", glyph, unichar);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

查看输出,我们的字形代码有两个字符代码:

0436 2f26
0436 5b50

它首先遇到2f26,这很重要,因为在构建查找表时,如果已经为字形确定了字符代码(并且其值为 >= 0x20),则它不会被覆盖

if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
    // ...
    if (glyphToUnicode[glyphs[0]] < 0x20) {
        glyphToUnicode[glyphs[0]] = codepoint;
    }
}
Run Code Online (Sandbox Code Playgroud)

所以,最终我相信正在发生的是:

  1. Chrome 正确确定了to be 的STSongti-SC-Regular字形 ID 。它使用此字形表示 pdf 中的 cjk 字符。5B500436
  2. STSongti-SC-Regular然后,它通过迭代所有可能的字符来构建字形到字符代码的查找表。由于0436映射到两个代码并且它2f26首先遇到,因此这就是记录的内容,并且是从文档复制和粘贴时返回的值。