2008-09-24 6 views
29

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

(something) 

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

(\527) 

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


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

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


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


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

+5

В дополнение к обертке строк с помощью `()` вы также можете использовать `<>`. В пределах gt/lt вы используете шестнадцатеричные числа, а не буквы. Гораздо менее эффективно, но вам не нужно беспокоиться о побегах. ``: "Hello World!" как строка Unicode-16. Сообщение Плинта также важно ... вы ДОЛЖНЫ использовать FE FF. FFFE плохо. По какой-то причине. :/ – 2011-02-08 18:50:14

ответ

10

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

-1

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

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

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

+0

Мы придерживаемся основных шрифтов, которые находятся на каждом компьютере, а не встраивания каких-либо шрифтов. – 2008-09-24 17:04:28

+1

«Спецификации PDF в Adobe должны сообщать вам все». К сожалению, по моему опыту, они этого не делают. – Renan 2011-09-06 01:39:08

+2

@Renan: «Спецификации PDF в Adobe должны рассказать вам все». К сожалению, по моему опыту, вы не найдете их легко, и они часто излишне сложны. – Algoman 2015-07-07 10:31:39

3

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

Отъезд PDFBox или iText.

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

28

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

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

3

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

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

Так использовать Юникод в формате PDF непосредственно

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

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



Что я делаю вместо этого в настоящее время использую символы косвенно следующим образом: Для каждого шрифта, сгенерировать кодовую (и подстановки-таблицу для быстрого поиска) - в 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); 
    } 
} 

затем сгенерировать новую строку, в которой символы из исходной строки заменяются их позиции в кодовую страницу, как это: «! H € LLO World»

static std::string hex = "ABCDEF"; 
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 += ">"; 

, например, может стать < +01020303040506040703080905> и теперь вы можете просто поместить эту строку в формате PDF и он напечатан, используя оператор Tj, как обычно ...

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

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

Итак, как вы находите имена глифов (/ 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-файл, но я, во-первых, могу жить с этим. ..

4

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

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

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

2

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

Смежные вопросы