Юникод в PDF - PullRequest
       35

Юникод в PDF

33 голосов
/ 24 сентября 2008

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

(something)

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

(\527)

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


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

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


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


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

Ответы [ 7 ]

33 голосов
/ 02 октября 2008

В PDF-справке в главе 3 говорится об Unicode:

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

12 голосов
/ 27 сентября 2008

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

8 голосов
/ 24 апреля 2016

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

Пример кода для разбора cmap раздел здесь: https://support.microsoft.com/en-us/kb/241020

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

4 голосов
/ 18 августа 2016

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

3 голосов
/ 02 октября 2008

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

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

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

2 голосов
/ 05 августа 2015

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

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

Таким образом, чтобы использовать Unicode в PDF напрямую

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

ИМХО, эти пункты делают абсолютно невозможным использование Юникода напрямую .



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

std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;

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

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 и установки его Differences

Для "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<std::string>::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<wchar_t>::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. Ссылка на тот же объект font-register указана в / Resources Entry всех страниц. Если вы делаете это по-другому (например, вы используете один шрифт-регистр на страницу) - вам, возможно, придется адаптировать код к вашей ситуации ...

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

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";
        ...
    }
}

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

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

0 голосов
/ 24 сентября 2008

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

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

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

...