Chrome另存为PDF,更改CJK字符

时间:2019-07-03 18:49:34

标签: html google-chrome pdf unicode

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

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

下面是基本的HTML。

<HTML>

<HEAD>
  Test Character
</HEAD>

<BODY>
  子
</BODY>

</HTML>

如果以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中呈现的字符。

OS版本:MacOS 10.13.6(17G65)

Chrome版本:75.0.3770.100(正式版本)(64位)

1 个答案:

答案 0 :(得分:2)

我的理解是,PDF实际上并不包含在呈现文档时看到的字符串,而是包含字体字形和支持的查找表的序列,这些字形和支持的查找表将这些字形映射回字符代码。在OP的测试案例中,用于macOS上的cjk字符的字体为STSongti-SC-Regular,其字形ID为十六进制0436

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

enter image description here

分别从斯基亚语SkFontHost_mac.cpp的{​​{3}}和onCharsToGlyphs()方法中完成从字符到字形和字形到字符的操作。在macOS上,这两种方法都依赖于从Core Text库对populate_glyph_to_unicode()的调用,迭代每个可能的字符来构建映射表。

我将这种方法简化为以下测试代码,打印出给定字体的每个字形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);
        }
    }
}

浏览输出,我们的字形代码有两个字符代码:

0436 2f26
0436 5b50

它首先遇到2f26,这很重要,因为在构建查找表时,如果已经确定了字形的字符代码(并且其值> = 0x20),它{{ 3}}:

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

因此,最终我相信正在发生的事情是:

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