Юникод в формате PDF

Моя программа генерирует относительно простые PDF-документы по запросу, но у меня возникают проблемы с символами юникода, такими как иероглифы кандзи или нечетные математические символы. Чтобы написать обычную строку в PDF, вы поместите ее в скобки:

(something) 

Существует также возможность избежать символа с восьмеричными кодами:

 (\527) 

но это только до 512 символов. Как вы кодируете или избегаете более высоких символов? Я видел ссылки на streamи байтов и строки с шестнадцатеричным кодированием, но ни одна из прочитанных мной ссылок, похоже, не готова рассказать мне, как на самом деле это делать.


Изменить: Альтернативно, укажите мне хорошую библиотеку Java PDF, которая будет выполнять эту работу для меня. Тот, который я сейчас использую, представляет собой версию gnujpdf (в которой я исправил несколько ошибок, поскольку исходный автор, похоже, прошел AWOL), что позволяет вам программировать интерфейс AWT Graphics, и в идеале любая замена должна делать тоже самое.

Альтернативы кажутся либо HTML -> PDF, либо программной моделью, основанной на абзацах и блоках, которые очень похожи на HTML. iText является примером последнего. Это означало бы переписывание моего существующего кода, и я не уверен, что они дадут мне такую ​​же гибкость в планировании.


Редактировать 2: я не понимал этого раньше, но библиотека iText имеет API Graphics2D и, похоже, отлично работает с юникодом, так что я буду использовать. Хотя это не ответ на заданный вопрос, он решает проблему для меня.


Редактирование 3: iText работает хорошо для меня. Думаю, урок, когда сталкивается с чем-то, что кажется бессмысленным, искать кого-то, кто знает об этом больше, чем вы.

Простой ответ заключается в том, что нет простого ответа. Если вы посмотрите на спецификацию PDF, вы увидите целую главу – и длинную в этом – посвященную механизмам отображения текста. Я реализовал всю поддержку PDF для своей компании, и обработка текста была, безусловно, самой сложной частью упражнений. Решение, которое вы обнаружили, – используйте стороннюю библиотеку для выполнения вашей работы – действительно лучший выбор, если у вас нет особых специфических специальных требований для ваших файлов PDF.

В ссылке в PDF в главе 3 это то, что они говорят о Unicode:

Текстовые строки кодируются либо в кодировке PDFDocEncoding, либо в кодировке Unicode. PDFDocEncoding является надмножеством кодировки ISO Latin 1 и документируется в Приложении D. Юникод описан в Unicode Standard консорциумом Unicode (см. Библиографию). Для текстовых строк, закодированных в Юникоде, первые два байта должны быть 254, за которыми следуют 255. Эти два байта представляют маркер порядка байтов Unicode U + FEFF, указывая, что строка кодируется в схеме кодирования UTF-16BE (big-endian) указанных в стандарте Unicode. (Этот механизм не позволяет начинать строку с использованием PDFDocEncoding с двумя символами thorn ydieresis, что вряд ли станет значимым началом слова или фразы).

Ответ Алгомана неверен во многих вещах. Вы можете сделать PDF-документы с unicode в нем ‘, и это не ракетостроение, хотя для этого нужна определенная работа. Да, он прав, чтобы использовать более 255 символов в одном шрифте, вам нужно создать сложный шрифт (CIDFont) pdf-объект. Затем вы просто упомянете фактический шрифт TrueType, который вы хотите использовать в качестве записи DescendatFont для CIDFont. Фокус в том, что после этого вам нужно использовать глифные индексы шрифта вместо кодов символов. Чтобы получить эту карту индексов, вам нужно разобрать раздел cmap шрифта – получить содержимое шрифта с функцией GetFontData и взять на себя спецификацию TTF. Вот и все! Я только что сделал это, и теперь у меня есть unicode pdf!

Пример кода для синтаксического parsingа cmap Здесь: https://support.microsoft.com/en-us/kb/241020

И да, не забывайте / запись в ToUnicode, указав @ user2373071, или пользователь не сможет искать ваш PDF-файл или скопировать текст с него.

См. Приложение D (стр. 995) спецификации PDF. Существует ограниченное количество шрифтов и наборов символов, предварительно определенных в потребительском приложении PDF. Чтобы отобразить другие символы, вам необходимо встроить шрифт, содержащий их. Также желательно вставлять только подмножество шрифта, включая только обязательные символы, чтобы уменьшить размер файла. Я также работаю над отображением символов Unicode в PDF, и это серьезная проблема.

Проверьте PDFBox или iText.

http://www.adobe.com/devnet/pdf/pdf_reference.html

Как отметил дредкин, вам нужно использовать индексы глифов вместо значения символа Юникода в streamе содержимого страницы. Этого достаточно для отображения текста в формате Unicode в формате PDF, но текст Юникода не будет доступен для поиска. Чтобы сделать текст доступным для поиска или скопировать / вставить его, вам также потребуется включить stream / ToUnicode. Этот stream должен перевести каждый глиф в документе на фактический символ Юникода.

Я уже несколько дней работал над этой темой, и то, что я узнал, заключается в том, что unicode (насколько это возможно) невозможно в формате pdf. Использование двухбайтовых символов, как описано в плинту, работает только с CID-Fonts.

По-видимому, CID-Fonts – это встроенная в pdf конструкция, и в этом смысле они не являются действительно шрифтами – они, похоже, больше похожи на графические подпрограммы, которые могут быть вызваны путем их адресации (с 16-разрядными адресами).

Так что использовать unicode в pdf напрямую

  1. вам нужно будет преобразовать обычные шрифты в CID-Fonts, что, вероятно, очень сложно – вам придется создавать графические подпрограммы из исходного шрифта (?), извлекать метрики символов и т. д.
  2. вы не можете использовать CID-шрифты, как обычные шрифты – вы не можете загружать или масштабировать их так, как вы загружаете и масштабируете обычные шрифты
  3. Кроме того, 2-байтовые символы даже не охватывают полное пространство Unicode

IMHO, эти пункты делают абсолютно невозможным непосредственное использование unicode.



Вместо этого я использую символы косвенно следующим образом: для каждого шрифта я генерирую кодовую страницу (и таблицу поиска для быстрого поиска) – в c ++ это было бы чем-то вроде

 std::map > Codepage; std::map > LookupTable; 

то всякий раз, когда я хочу поместить некоторую строку unicode на страницу, я повторяю ее символы, просматриваю их в таблице lookup и, если они новы, я добавляю их на кодовую страницу следующим образом:

 for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++) { if(LookupTable[fontname].find(*i) == LookupTable[fontname].end()) { LookupTable[fontname][*i] = Codepage[fontname].size(); Codepage[fontname].push_back(*i); } } 

то я сгенерирую новую строку, где символы из исходной строки заменяются их позициями в кодовой странице следующим образом:

 static std::string hex = "0123456789ABCDEF"; std::string result = "<"; for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++) { int id = LookupTable[fontname][*i] + 1; result += hex[(id & 0x00F0) >> 4]; result += hex[(id & 0x000F)]; } result += ">"; 

например, «H € llo World!» может стать <01020303040506040703080905>, и теперь вы можете просто вставить эту строку в pdf и напечатать ее, используя оператор Tj, как обычно …

но у вас теперь есть проблема: pdf не знает, что вы имеете в виду «H» на 01. Чтобы решить эту проблему, вы также должны включить кодовую страницу в файл pdf. Это делается путем добавления / Encoding к объекту Font и установки его разностей

Для «H € llo World!» Например, этот Font-Object будет работать:

 5 0 obj << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Times-Roman /Encoding << /Type /Encoding /Differences [ 1 /H /Euro /l /o /space /W /r /d /exclam ] >> >> >> endobj 

Я генерирую его с помощью этого кода:

 ObjectOffsets.push_back(stream->tellp()); // xrefs entry (*stream) << ObjectCounter++ << " 0 obj \n<<\n"; int fontid = 1; for(std::list::iterator i = Fonts.begin(); i != Fonts.end(); i++) { (*stream) << " /F" << fontid++ << " << /Type /Font /Subtype /Type1 /BaseFont /" << *i; (*stream) << " /Encoding << /Type /Encoding /Differences [ 1 \n"; for(std::vector::iterator j = Codepage[*i].begin(); j != Codepage[*i].end(); j++) (*stream) << " /" << GlyphName(*j) << "\n"; (*stream) << " ] >>"; (*stream) << " >> \n"; } (*stream) << ">>\n"; (*stream) << "endobj \n\n"; 

Обратите внимание, что я использую глобальный регистр шрифтов - я использую те же имена шрифтов / F1, / F2, ... во всем документе PDF. Тот же объект-регистр шрифтов ссылается на вкладку / Resources Entry всех страниц. Если вы сделаете это по-другому (например, вы используете один регистр шрифтов на странице) - вам, возможно, придется адаптировать код к вашей ситуации ...

Итак, как вы находите имена глифов (/ Euro для «€», / exclam для «!» И т. Д.)? В приведенном выше коде это делается простым вызовом «GlyphName (* j)». Я создал этот метод с помощью BASH-скрипта из списка, найденного в

http://www.jdawiseman.com/papers/trivia/character-entities.html

и это выглядит так

 const std::string GlyphName(wchar_t UnicodeCodepoint) { switch(UnicodeCodepoint) { case 0x00A0: return "nonbreakingspace"; case 0x00A1: return "exclamdown"; case 0x00A2: return "cent"; ... } } 

Основная проблема, которую я оставил открытой, заключается в том, что это работает только до тех пор, пока вы используете не более 254 разных символов из того же шрифта. Чтобы использовать более 254 разных символов, вам нужно будет создать несколько кодовых страниц для одного и того же шрифта.

Внутри pdf разные кодовые страницы представлены разными шрифтами, поэтому для переключения между кодовыми страницами вам нужно будет переключать шрифты, которые теоретически могут сильно ударить ваш pdf-файл, но я для одного, могу жить с этим ...

Я не эксперт в формате PDF, и (как сказал Ферруччио) спецификации PDF в Adobe должны рассказать вам все, но в голове появилась мысль:

Вы уверены, что используете шрифт, который поддерживает все персонажи, которые вам нужны?

В нашем приложении мы создаем PDF с HTML-страниц (с третьей стороной), и у нас была эта проблема с кириллическими символами …

  • Ручное преобразование кодов Unicode в UTF-8 и UTF-16
  • Почему современный Perl избегает UTF-8 по умолчанию?
  • Можно ли принудительно Excel распознавать файлы CSV UTF-8 автоматически?
  • Что такое внутреннее представление Java для String? Изменен UTF-8? UTF-16?
  • Преобразование Unicode в ASCII без ошибок в Python
  • Использование PDFBox для записи кодированных строк UTF-8 в PDF-файл
  • Форматирование командной строки MySQL с помощью UTF8
  • Использование 'use utf8;' дает мне «Широкий характер в печати»,
  • RStudio не выбирает кодировку, которую я говорю ей при чтении файла
  • Устаревший заголовок
  • Ruby on Rails 3, несовместимые кодировки символов: UTF-8 и ASCII-8BIT с i18n
  • Давайте будем гением компьютера.