He trabajado varios días en este tema ahora y lo que he aprendido es que el Unicode es (tan bueno como) imposible en pdf. Usando caracteres de 2 bytes, la forma en que se describe el plinto solo funciona con CID-Fonts.
aparentemente, CID-Fonts es una construcción interna en pdf y en realidad no son fuentes en ese sentido; parecen ser más como subrutinas de gráficos, que pueden invocarse al abordarlas (con direcciones de 16 bits).
Así que para utilizar Unicode en PDF directamente
- que tendría que convertir las fuentes normales de CID-Fuentes, que es probablemente muy difícil - que tendría que generar las rutinas gráficas de la fuente original (?), extrae métricas de caracteres, etc.
- no puedes usar CID-Fonts como fuentes normales; no puedes cargarlas o escalarlas de la forma en que cargas y escalas las fuentes normales
- también, los caracteres de 2 bytes ni siquiera cubren el espacio Unicode completo
En mi humilde opinión, estos puntos hacen que sea absolutamente inviable utilizar unicode directamente.
Lo que estoy haciendo en su lugar está en usar los caracteres indirectamente de la siguiente manera: Para cada fuente, genero una página de códigos (y una tabla de consulta para búsquedas rápidas) - en C++ esto sería algo así como
std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;
entonces, cada vez que quiero poner un poco de unicode cuerdas en una página, que iterar sus personajes, búsquelo en la tabla de consulta y - si son nuevos, los agrego a la página de códigos de esta manera:
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);
}
}
entonces, generar una nueva cadena, donde los caracteres de la cadena original se sustituyen por sus posiciones en la página de códigos de esta manera: "! H € llo Mundial"
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 += ">";
por ejemplo, podría convertirse en < 01020303040506040703080905> y ahora sólo se puede poner esa cadena en el PDF y tenerlo impreso, utilizando el operador de Tj como de costumbre ...
pero ahora tiene un problema: el pdf no sabe que se significa "H" por 01. Para resolver este problema, también debe incluir la página de códigos en el archivo pdf. Esto se hace mediante la adición de un /Codificación al objeto Font y establecer sus diferencias
Para el "H € llo mundo!" ejemplo, este tipo de letra-Objeto funcionaría:
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
genero con este código:
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";
en cuenta que utilizo un mundial font-Registro - utilizo el mismo nombres de fuente/F1,/F2, ... en todo el documento pdf. Se hace referencia al mismo objeto de registro de fuente en /Resources Entrada de todas las páginas. Si lo hace de forma diferente (por ejemplo, usa un registro de fuente por página), es posible que tenga que adaptar el código a su situación ...
Entonces, ¿cómo se encuentran los nombres de los glifos (/ Euro para " € ",/exclam por"! ", Etc.)? En el código anterior, esto se hace simplemente llamando a "GlyphName (* j)". He generado este método con una fiesta-Script de la lista que se encuentra en
http://www.jdawiseman.com/papers/trivia/character-entities.html
y parece que este
const std::string GlyphName(wchar_t UnicodeCodepoint)
{
switch(UnicodeCodepoint)
{
case 0x00A0: return "nonbreakingspace";
case 0x00A1: return "exclamdown";
case 0x00A2: return "cent";
...
}
}
Un problema importante me queda abierta es que sólo esta funciona siempre que use como máximo 254 caracteres diferentes de la misma fuente. Para usar más de 254 caracteres diferentes, debería crear múltiples páginas de códigos para la misma fuente.
Dentro del pdf, las diferentes páginas de códigos están representadas por fuentes diferentes, por lo que para cambiar entre páginas de códigos, debe cambiar las fuentes, lo que teóricamente podría explotar mucho el pdf, pero yo puedo aceptarlo. ..
Además de envolver cadenas con '()', también puede usar '<>'. Dentro de gt/lt, usas números hexadecimales en lugar de letras. Mucho menos eficiente, pero no necesita preocuparse por los escapes. '': "¡Hola, mundo!" como una cadena Unicode-16. La publicación de Plinth también es importante ... DEBES usar FE FF. FFFE es malo Por alguna razón. :/ –