当我们尝试使用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位)
答案 0 :(得分:2)
我的理解是,PDF实际上并不包含在呈现文档时看到的字符串,而是包含字体字形和支持的查找表的序列,这些字形和支持的查找表将这些字形映射回字符代码。在OP的测试案例中,用于macOS上的cjk字符的字体为STSongti-SC-Regular
,其字形ID为十六进制0436
。
我只能在macOS上重现OP的行为。在Linux和Windows上,我都看到该字形映射到html文件U+5B50
中原来的字符。下面是peepdf
实用程序的输出中的比较示例:
分别从斯基亚语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;
}
}
因此,最终我相信正在发生的事情是:
STSongti-SC-Regular
的{{1}}字形ID为5B50
。它将这个字形用于pdf中的cjk字符。0436
构建字形到字符的查找表。由于STSongti-SC-Regular
映射到两个代码,并且首先遇到0436
,因此记录的内容就是从文档复制和粘贴时返回的值。