Artículo técnico

Texto vertical CJK en PDF con Delphi y HotPDF

Coloque una novela japonesa sobre la página y lo primero que notará es que el texto corre hacia abajo por la columna, no a través de la línea, y que las columnas avanzan desde el borde derecho de la hoja hacia el izquierdo. Un lector criado con esto considera que el texto horizontal es tenuemente clínico. El problema de ingeniería es que el PDF, al igual que casi todos los sistemas de texto digital, se construyó alrededor de una línea base horizontal que crece de izquierda a derecha, y un flujo de contenido no tiene noción de "escribir este párrafo hacia abajo en su lugar". Entonces, cuando una aplicación Delphi tiene que producir un certificado, un poema, un letrero o un documento legal en formato tradicional para un lector taiwanés, japonés o coreano, el diseño debe ensamblarse a mano: un carácter debajo del siguiente, una columna a la izquierda de la anterior

HotPDF le proporciona un interruptor que hace la contabilidad por carácter por usted. La fuente que configure porta una bandera IsVertical, y una vez que está activada, una sola llamada a TextOut apila una cadena entera en una columna vertical en lugar de correrla a lo largo de una línea base. La ubicación de las columnas, el orden de derecha a izquierda y una sustitución de glifos discretamente importante son los temas que aborda el resto de esta página

Página A4 en PDF producida por HotPDF que muestra texto en chino, japonés y coreano apilado en columnas verticales que se leen de derecha a izquierda
Una sola página A4 con texto en chino, japonés y coreano en columnas verticales, generada desde un procedimiento Delphi.

El interruptor se encuentra en SetFont

El diseño vertical no es una propiedad de la página o del documento. Es una propiedad de la fuente con la que se dibuja, y usted la activa con el quinto argumento de SetFont:

// SetFont(FontName, FontStyle, Size, FontCharset, IsVertical)
// The 5th argument flips the current font into vertical mode.
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, False); // horizontal
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, DEFAULT_CHARSET, True);  // vertical

Debido a que la bandera viaja con la fuente, usted cambia entre escritura horizontal y vertical simplemente llamando a SetFont nuevamente con un último argumento diferente. Una página puede contener un título horizontal en la parte superior y texto del cuerpo vertical debajo, y HotPDF mantiene los dos modos separados por objeto de fuente en lugar de por página. Eso es lo que hace posible los diseños mixtos sin ningún manejo de modo especial de su lado: cada TextOut después de un SetFont vertical se apila, cada TextOut después de uno horizontal corre a lo largo de la línea base, y el último SetFont es el que prevalece

El cuarto argumento es el conjunto de caracteres de Windows, el mismo que toma una llamada horizontal. Pasar DEFAULT_CHARSET le permite al sistema resolver glifos por cadena, lo que importa aquí porque un documento vertical suele mezclar escrituras. Todo lo demás acerca de la fuente sigue aplicándose: debe estar instalada en la máquina de compilación y, casi siempre, querrá FontEmbedding := True de modo que el archivo renderice los mismos glifos CJK en un lector que nunca tuvo Arial Unicode MS

Una llamada a TextOut es una columna

Con una fuente vertical activa, una llamada a TextOut ya no despliega su cadena hacia los lados a partir del punto dado. Planta el primer carácter en la parte superior y desciende por el resto directamente, avanzando por la altura de la línea de la fuente para cada glifo. La X que usted pasa fija la columna; la Y que usted pasa fija dónde comienza la parte superior de la columna. Para diagramar un pasaje real, por lo tanto, debe emitir un TextOut por columna e ir moviendo la X hacia la izquierda entre llamadas, porque las columnas CJK se leen de derecha a izquierda

var
  Pdf: THotPDF;
const
  ColTop = 760;   // y of the first glyph in every column (points)
  ColGap = 28;    // horizontal distance between columns
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'VerticalText.pdf';
    Pdf.FontEmbedding := True;       // embed the CJK face for portable rendering
    Pdf.BeginDoc;
    Pdf.CurrentPage.Size := psA4;

    // A horizontal heading first, in the ordinary writing mode.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 16, DEFAULT_CHARSET, False);
    Pdf.CurrentPage.TextOut(60, 800, 0, 'Tang poem, vertical layout');

    // Switch the font into vertical mode; every TextOut below now stacks.
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 18, DEFAULT_CHARSET, True);

    // Columns advance right to left, so X decreases with each call.
    Pdf.CurrentPage.TextOut(520, ColTop, 0, '床前明月光');
    Pdf.CurrentPage.TextOut(520 - ColGap,     ColTop, 0, '疑是地上霜');
    Pdf.CurrentPage.TextOut(520 - ColGap * 2, ColTop, 0, '舉頭望明月');
    Pdf.CurrentPage.TextOut(520 - ColGap * 3, ColTop, 0, '低頭思故鄉');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

El poema se lee como debería: la columna de más a la derecha primero, de arriba abajo, luego el ojo salta a la izquierda a la siguiente columna. No hay nada en la cadena en sí que codifique ese orden. Usted lo codifica en las coordenadas X, disminuyéndolas llamada a llamada. Si invierte la dirección, el verso sale al revés, lo cual es el error más común al portar un diseño horizontal al vertical

Coordenadas: hacia abajo en la página, de derecha a izquierda

Hay dos ejes en juego y tiran en direcciones opuestas, así que vale la pena ser precisos. HotPDF mide desde la esquina inferior izquierda de la página, con Y incrementando hacia arriba, en puntos. Por lo tanto, una columna vertical comienza en una Y alta (cerca de la parte superior de la hoja) y los caracteres descienden desde allí, a medida que HotPDF resta la altura de una línea para cada uno. Usted establece esa Y inicial una vez por columna y el componente se encarga del descenso

El eje horizontal es el que usted maneja manualmente. Cada columna se asienta en su propia X, y las columnas sucesivas descienden a valores de X más pequeños porque el orden de lectura es de derecha a izquierda. Un ritmo sensato es tomar la X de la columna de más a la derecha, y luego restar un espacio de columna fijo para cada llamada posterior, como hace el ejemplo con ColGap. El espacio lo elige usted; demasiado apretado y las columnas adyacentes se tocan, demasiado suelto y el bloque se ve escaso. Para texto del cuerpo a 12 a 18 puntos, un espacio un poco más grande que el tamaño de la fuente se lee cómodamente

Glifos que tienen que cambiar de forma

Unos pocos caracteres no son simples copias rotadas de sus formas horizontales; tienen que ser sustituidos cuando el texto se vuelve vertical. El que HotPDF maneja por usted es la marca de sonido prolongado japonés (U+30FC), la barra de vocal larga que aparece en palabras katakana como コーヒー. Dibujado horizontalmente, es un guión corto sobre la línea base. Si apilara ese mismo glifo en una columna, se acostaría cruzado en la columna, lo cual es incorrecto: en japonés vertical, la marca se convierte en un trazo vertical que conecta los dos caracteres entre los que se sitúa. HotPDF detecta la U+30FC en la ruta vertical y la renderiza como una barra vertical (U+007C) para que la marca de vocal larga apunte en la dirección correcta sin ningún trabajo de su parte

Esa única sustitución cubre el caso que rompe a la mayoría de las implementaciones ingenuas, pero vale la pena saber dónde el problema general se vuelve más profundo. La tipografía vertical completa también rota letras latinas y puntuación occidental noventa grados, cambia pequeños kanas, y reposiciona corchetes y comas a sus formas verticales, y una implementación completa de eso vive en las características verticales OpenType de una fuente más que en reglas fijas. HotPDF puede aprovechar esas características cuando la fuente las contiene: alternativas verticales (las características GSUB vert y vrt2) y el interletraje vertical (búsquedas GPOS vkrn y vpal) son opcionales, aplicadas a lo largo de la ruta vertical solo cuando la fuente activa realmente las define. Para texto CJK mixto en una sola tipografía de amplia cobertura como Arial Unicode MS, el manejo integrado de U+30FC sumado a un paso uniforme de altura de línea es suficiente para producir columnas correctas y legibles; las características OpenType importan cuando pasa a una fuente diseñada para una configuración vertical fina y desea su posicionamiento kana nativo y el espaciado entre glifos

Mezcla de escrituras y orientaciones en una sola página

Los documentos reales rara vez son puros. Una página vertical en japonés puede incluir una leyenda horizontal en inglés, un número de página a lo largo del fondo, o un bloque de texto en coreano puesto verticalmente al lado del japonés. Debido a que la bandera vertical es un interruptor a nivel de fuente, usted compone estos aspectos alternando llamadas SetFont en lugar de administrar algún estado a nivel de toda la página. Defina una fuente horizontal, escriba el encabezado y el número de página, defina una fuente vertical, coloque las columnas, defina una fuente horizontal de nuevo para un pie de página. Cada región asume el modo del SetFont más reciente, por lo que la única disciplina que se requiere es llamarla cada vez que cambie de dirección

Un detalle que planear cuando las escrituras se mezclan: los ideogramas chinos, japoneses y coreanos son casi cuadrados y se apilan con un paso uniforme, pero las secuencias latinas incrustadas en una columna vertical no tienen ese avance uniforme. Si necesita unas pocas palabras latinas dentro de un texto que, por lo demás, es vertical, decida deliberadamente si deberían estar rotadas para correr a lo largo de la columna o estar de pie como un corto recuadro horizontal, y posicione ese fragmento con su propio TextOut en lugar de dejar que viaje en el paso vertical destinado a ideogramas. Tratar la secuencia mixta como su propio problema de ubicación mantiene el ritmo de la columna intacto

Del ejemplo a la producción

Las piezas son pequeñas y se componen de forma predecible. Active la bandera vertical en SetFont, emita un TextOut por columna desde una Y superior fija y disminuya X en las distintas llamadas para que las columnas se lean de derecha a izquierda. Incruste la fuente para que los glifos CJK sobrevivan el viaje a la máquina del lector, y permita que el componente maneje la marca de vocal larga U+30FC y, donde la fuente las soporte, las características verticales OpenType. A partir de ahí, hacer productivo el diseño es en su mayor parte aritmética: derivar las posiciones X de la columna a partir de un ancho de bloque medido, dividir los pasajes largos en columnas que quepan en el alto de la página, y reservar espacio para encabezados y folios horizontales

Para conocer la superficie más amplia de texto y fuentes sobre la que esto se construye, incluyendo las convenciones horizontales de TextOut y el registro de fuentes Unicode, vea el ejemplo multilingüe Hola Mundo y el ejemplo de TextOut. El interruptor vertical SetFont y las llamadas TextOut mostradas aquí son parte del Componente HotPDF para Delphi y C++Builder