Вопрос

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

(something)

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

(\527)

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


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

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


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


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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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



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

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.Это делается путем добавления /Кодировка к объекту Font и установив его Различия

Для "H € llo World!" Пример, этот шрифт будет работать:

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-документе.Тот же объект регистра шрифта упоминается в /Ресурсы Ввод всех страниц.Если вы сделаете это по-другому (например.вы используете один регистр шрифта на странице) - возможно, вам придется адаптировать код к вашей ситуации...

Так как же найти названия глифов (/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-страниц (при помощи сторонней библиотеки), и у нас возникла такая проблема с кириллицей...

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top