Насколько я понимаю, PDF на самом деле содержит не строку символов, которую вы видите при визуализации документа, а скорее последовательности глифов шрифтов и вспомогательные таблицы поиска, которые отображают эти символы обратно в коды символов. В тестовом примере OP шрифт, используемый для символа cjk в macOS, равен STSongti-SC-Regular
, а его идентификатор глифа - hex 0436
.
Я могу воспроизвести поведение OP только на macOS. И в Linux, и в Windows я вижу глиф, сопоставленный с символом, который изначально был в html-файле: U+5B50
. Пример сравнения показан ниже в выводе утилиты peepdf
:
Операции перехода от символа к символу и от символа к символу выполняются в onCharsToGlyphs()
и populate_glyph_to_unicode()
методах Skia SkFontHost_mac.cpp
соответственно. В macOS оба они полагаются на вызовы CTFontGetGlyphsForCharacters()
из библиотеки Core Text, перебирая все возможные символы для построения таблиц сопоставления.
Я сводил этот подход к следующему тестовому коду, распечатывая каждый идентификатор глифа и соответствующий код символа для данного шрифта:
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
), он не получает перезаписаны
if (CTFontGetGlyphsForCharacters(ctFont, utf16, glyphs, count)) {
// ...
if (glyphToUnicode[glyphs[0]] < 0x20) {
glyphToUnicode[glyphs[0]] = codepoint;
}
}
Итак, в конечном итоге я считаю, что:
- Chrome правильно определяет
STSongti-SC-Regular
идентификатор глифа для 5B50
, равный 0436
. Он использует этот глиф для символа cjk в pdf.
- Затем он строит таблицу поиска глиф-код-код для
STSongti-SC-Regular
, перебирая все возможные символы. Поскольку 0436
сопоставляется с двумя кодами, и сначала встречается 2f26
, это то, что записывается, и это значение, которое возвращается при копировании и вставке из документа.