Chrome Сохранить как PDF изменяя символы CJK - PullRequest
8 голосов
/ 03 июля 2019

Мы видим проблему, когда пытаемся сохранить веб-страницу, содержащую символы CJK, в формате PDF с помощью параметра «Печать» Chrome.

Символ, отображаемый Chrome в PDF, визуально выглядит так же, но Unicode отличается.

Ниже приведен базовый HTML.

<HTML>

<HEAD>
  Test Character
</HEAD>

<BODY>
  子
</BODY>

</HTML>

Символ, если HTML-код открыт в Chrome, является
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 (ОфициальныйBuild) (64-битная версия)

1 Ответ

2 голосов
/ 12 июля 2019

Насколько я понимаю, PDF на самом деле содержит не строку символов, которую вы видите при визуализации документа, а скорее последовательности глифов шрифтов и вспомогательные таблицы поиска, которые отображают эти символы обратно в коды символов. В тестовом примере OP шрифт, используемый для символа cjk в macOS, равен STSongti-SC-Regular, а его идентификатор глифа - hex 0436.

Я могу воспроизвести поведение OP только на macOS. И в Linux, и в Windows я вижу глиф, сопоставленный с символом, который изначально был в html-файле: U+5B50. Пример сравнения показан ниже в выводе утилиты peepdf:

enter image description here

Операции перехода от символа к символу и от символа к символу выполняются в 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;
    }
}

Итак, в конечном итоге я считаю, что:

  1. Chrome правильно определяет STSongti-SC-Regular идентификатор глифа для 5B50, равный 0436. Он использует этот глиф для символа cjk в pdf.
  2. Затем он строит таблицу поиска глиф-код-код для STSongti-SC-Regular, перебирая все возможные символы. Поскольку 0436 сопоставляется с двумя кодами, и сначала встречается 2f26, это то, что записывается, и это значение, которое возвращается при копировании и вставке из документа.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...