2008-09-24 10 views
29

Mi programa genera documentos PDF relativamente simples bajo pedido, pero estoy teniendo problemas con caracteres Unicode, como kanji o símbolos matemáticos impares. Para escribir una cadena normal, en formato PDF, lo coloca entre paréntesis:Unicode en PDF

(something) 

También existe la opción de escapar de un personaje con códigos octales:

(\527) 

pero esto sólo llega hasta 512 caracteres. ¿Cómo codificas o escapas personajes más altos? He visto referencias a secuencias de bytes y cadenas codificadas con hexadecimal, pero ninguna de las referencias que he leído parece estar dispuesta a decirme cómo hacerlo realmente.


Editar: alternativa, que me señale una buena biblioteca PDF Java que va a hacer el trabajo para mí. El que estoy usando actualmente es una versión de gnujpdf (que he corregido varios errores, ya que el autor original parece haber desaparecido sin permiso), que le permite programar contra una interfaz AWT Graphics, e idealmente cualquier reemplazo debería hacer lo mismo.

Las alternativas parecen ser HTML -> PDF, o un modelo programático basado en párrafos y cuadros que se parece mucho al HTML. iText es un ejemplo de esto último. Esto significaría volver a escribir mi código existente, y no estoy convencido de que me den la misma flexibilidad para diseñarlo.


Edición 2: no me di cuenta antes, pero la biblioteca iText tiene una API Graphics2D y parece manejar Unicode a la perfección, así que eso es lo que va a utilizar. Aunque no es una respuesta a la pregunta como se le preguntó, me resuelve el problema.


Datos 3: iText está trabajando muy bien para mí. Supongo que la lección es que cuando te encuentres con algo que parece inútilmente difícil, busca a alguien que sepa más sobre ti que tú.

+5

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. :/ –

Respuesta

10

La respuesta simple es que no hay una respuesta sencilla. Si echas un vistazo a la especificación de PDF, verás un capítulo entero, y uno largo dedicado a los mecanismos de visualización de texto. Implementé todo el soporte de PDF para mi empresa, y el manejo de texto fue, con mucho, la parte más compleja del ejercicio. La solución que descubrió, usar una biblioteca de terceros para hacer el trabajo por usted, es realmente la mejor opción, a menos que tenga requisitos muy específicos y de propósito especial para sus archivos PDF.

-1

No soy un experto en PDF, y (como dijo Ferruccio) las especificaciones de PDF de Adobe que debe reportar a todo, pero un pensamiento surgido en mi mente:

¿Seguro que está utilizando una fuente que admite todos los personajes que necesita?

En nuestra aplicación, crear PDF a partir de páginas HTML (con una biblioteca de terceros), y tuvimos este problema con caracteres cirílicos ...

+0

Nos apegamos a las fuentes básicas que están en todas las computadoras y no incrustan ninguna fuente. –

+1

"Las especificaciones de PDF en Adobe deben decirle todo". Desafortunadamente, en mi experiencia, no deberían. – Renan

+2

@Renan: "Las especificaciones de PDF en Adobe deben decirle todo". Desafortunadamente, según mi experiencia, no los encuentras fácilmente y a menudo son innecesariamente complicados. – Algoman

3

Consulte el Apéndice D (página 995) de la especificación PDF. Existe una cantidad limitada de fuentes y juegos de caracteres predefinidos en una aplicación de consumidor de PDF. Para mostrar otros caracteres, debe incrustar una fuente que los contenga. También es preferible incrustar solo un subconjunto de la fuente, incluidos solo los caracteres necesarios, para reducir el tamaño del archivo. También estoy trabajando en mostrar caracteres Unicode en PDF y es una gran molestia.

Eche un vistazo a PDFBox o iText.

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

28

En la referencia PDF en el capítulo 3, esto es lo que dicen acerca de Unicode:

Las cadenas de texto se codifican en ya sea PDFDocEncoding o codificación de caracteres Unicode. PDFDocEncoding es un superconjunto de la codificación ISO Latin 1 y está documentado en el Apéndice D. Unicode está descrito en el Estándar Unicode por el Consorcio Unicode (ver la Bibliografía). Para cadenas de texto codificadas en Unicode, los primeros dos bytes deben ser 254 seguidos por 255. Estos dos bytes representan el marcador de orden de bytes Unicode, U + FEFF, que indica que la cadena está codificada en el UTF-16BE (big-endian) esquema de codificación especificado en el estándar Unicode. (Este mecanismo no permite comenzar una cadena usando PDFDocEncoding con los dos caracteres thorn ydieresis, que es poco probable que sea un comienzo significativo de una palabra o frase).

+0

Este es un extracto extremadamente útil. ¡Gracias! –

+10

Sabía que esto sonaba demasiado bueno para ser verdad. Las "cadenas de texto" se usan para metadatos de documentos (anotaciones, nombres de marcadores), ** no ** para texto renderizado. –

+0

@BrechtMachiels Al menos en la referencia de PDF 1.7, el operador de visualización de texto del objeto Texto ('BT') (' Tj') dice explícitamente "Mostrar una cadena de texto." Lo que significa que pueden estar codificados en UTF-16BE como se describe. – jdmichal

3

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

  1. 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.
  2. no puedes usar CID-Fonts como fuentes normales; no puedes cargarlas o escalarlas de la forma en que cargas y escalas las fuentes normales
  3. 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. ..

+0

por cierto, la lista de glifos que mencioné contiene más de 3600 entradas. El archivo de código generado es 175 KiB y el archivo de objeto compilado es 600 KiB grande (1.1 MiB en la versión de depuración) – Algoman

+0

Tan pronto como comience a usar otras fuentes que las fuentes estándar 14, las fuentes CID pueden llegar a ser bastante naturales. – mkl

+1

* 1. tendrías que convertir las fuentes normales en CID-Fonts, que probablemente sea extremadamente difícil * - esto es bastante sencillo para las fuentes OpenType (con CFF o contornos TrueType).Estos se pueden incluir como 'CIDFontType0' (CFF) o' CIDFontType2' (TrueType) usando la codificación 'Identity-H'. Esto es lo que hago en [rinohtype] (https://github.com/brechtm/rinohtype/blob/6e6b024e757eff57a8cef143710e667e0d2f365f/rinoh/backend/pdf/__init__.py#L75). –

4

La respuesta de Algoman es mal en muchas cosas. Usted puede hacer un documento PDF con unicode 'y no es una ciencia de cohetes, aunque necesita algo de trabajo. Sí, tiene razón, para usar más de 255 caracteres en una fuente, debe crear un objeto pdf de fuente compuesta (CIDFont). Luego, solo mencione la fuente TrueType real que desea usar como entrada DescendatFont de CIDFont. El truco es que después de eso tienes que usar índices de glifo de una fuente en lugar de códigos de caracteres. Para obtener este mapa de índices, debe analizar la sección cmap de una fuente: obtenga el contenido de la fuente con la función GetFontData y tome las manos en la especificación TTF. ¡Y eso es todo! ¡Acabo de hacerlo y ahora tengo un pdf Unicode!

Código de ejemplo para analizar cmap sección está aquí: https://support.microsoft.com/en-us/kb/241020

Y sí, no se olvide/ToUnicode entrada como @ user2373071 señaló o usuario no será capaz de buscar su PDF o copiar texto de la misma.

2

Como señaló Dredkin, debe usar los índices de glifos en lugar del valor del carácter Unicode en la secuencia de contenido de la página. Esto es suficiente para mostrar texto Unicode en PDF, pero el texto Unicode no se puede buscar. Para hacer que el texto pueda buscarse o copiar/pegar, también deberá incluir una secuencia/ToUnicode. Esta secuencia debe traducir cada glifo en el documento al carácter Unicode real.