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