Tome la frase árabe يوضح ملف PDF, pásela a TextOut y abra la salida: las letras avanzan en la dirección equivocada y cada una aparece en su forma aislada, desconectada de sus vecinas. Un lector de árabe ve algo parecido a inglés escrito al revés con un espacio tras cada letra. No ha fallado nada, no hay excepción ni aviso, simplemente no se ejecutaron dos transformaciones de texto distintas. Entender cuáles son esas transformaciones y qué API las aplica es toda la partida en la salida PDF de escrituras complejas
Este artículo recorre el texto de derecha a izquierda y de escrituras complejas con HotPDF, un componente PDF VCL nativo para Delphi y C++Builder, incluyendo dónde termina realmente su soporte de moldeado, algo igual de importante cuando se decide si cubre sus locales
Dos transformaciones se interponen entre una cadena y una línea impresa
Unicode almacena el texto en orden lógico: el orden en que se escribe, se guarda y se lee en voz alta. Un renderizador dibuja en orden visual. En escrituras de derecha a izquierda los dos difieren, y en contenido mixto, una frase árabe con el token latino "PDF" o un precio en dígitos, el Unicode Bidirectional Algorithm (UAX #9) define cómo se incrustan los tramos de izquierda a derecha dentro de una línea de derecha a izquierda. Esa es la primera transformación: reordenación
La segunda transformación es el moldeado contextual. Una letra árabe toma un glifo distinto según esté al inicio, en medio o al final de una palabra, o aislada; el punto de código no cambia nunca, solo la forma renderizada. Una tubería de texto que asigna puntos de código directamente a glifos por defecto produce la salida desconectada descrita arriba. El hebreo no necesita unión, pero sí reordenación; el árabe necesita ambas, por eso es el caso de prueba canónico
El desarrollo de escritorio oculta esta maquinaria. Cuando una aplicación VCL dibuja árabe en pantalla, la pila de texto del sistema operativo lo reordena y moldea de forma invisible, por eso la misma cadena que se renderiza perfectamente en un TEdit sale mal en un PDF ingenuo. Un flujo de contenido PDF almacena glifos posicionados, no tramos de texto editables; quien escribe el flujo es responsable del moldeado, y ese es el hueco que RtLTextOut existe para cerrar
RtLTextOut: reordenar y unir en una sola llamada
HotPDF separa a nivel de API la ruta latina de la ruta de escrituras complejas. TextOut dibuja lo que recibe, en el orden en que lo recibe; RtLTextOut ejecuta antes la reordenación y el análisis contextual. El parámetro charset de SetFont indica al motor qué reglas de escritura aplicar: 178 selecciona procesamiento árabe, 177 selecciona hebreo
// Arabic: pass logical order; RtLTextOut reorders and joins
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF');
// Hebrew: reordering only, no contextual joining
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
La trampa que más tiempo de depuración consume: RtLTextOut realiza la inversión por sí mismo. Alimentarlo con texto ya invertido, normalmente un resto de una "corrección" anterior con TextOut simple, invierte la línea dos veces. Incluso puede parecer correcto con una cadena de prueba puramente árabe y romperse en la primera línea que contenga letras latinas o dígitos, porque los tramos mixtos ya no siguen el orden UAX #9. Pase siempre orden lógico y deje que la API haga el trabajo
El contenido de dirección mixta también es donde las expectativas manuales fallan en las revisiones: dentro de una línea de derecha a izquierda, los números y palabras latinas incrustadas siguen leyéndose de izquierda a derecha. Revisores no familiarizados con la maquetación bidireccional lo notifican a menudo como fallo; es el comportamiento correcto según la especificación, y merece una nota en la documentación de aceptación antes de la primera revisión por un hablante nativo
La cobertura de glifos decide el resultado antes de que actúe el moldeado
El moldeado selecciona glifos; la fuente tiene que contenerlos. El fallo clásico de despliegue es un informe que se renderiza perfectamente en el puesto del desarrollador, donde Arial Unicode MS está instalado, y produce cuadrados vacíos en el servidor del cliente, donde Windows sustituyó silenciosamente por una fuente sin cobertura árabe. La solución es dejar de depender de fuentes instaladas en el sistema y registrar un fichero de fuente que usted distribuye
// Ship a known font instead of relying on installed system fonts
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12);
// Audit coverage for the codepoints your data actually uses
GID := Pdf.GetUnicodeGlyphForCodepoint($0628); // U+0628 ARABIC LETTER BEH
LogGlyphAudit($0628, GID);
Se aplican dos fronteras de versión. Las fuentes registradas así deben incrustarse, y el manejo de fuentes Unicode incrustadas de HotPDF requiere que la versión PDF del documento sea 1.5 o posterior, relevante solo si algún sistema posterior fija la salida en PDF 1.4. Además, la licencia de la fuente debe permitir la incrustación: los ficheros TrueType llevan bits de permiso de incrustación, y una fuente que se ve bien en pantalla puede no ser legalmente adecuada para distribuir dentro de documentos de clientes
GetUnicodeGlyphForCodepoint es el gancho de auditoría: recorra al arrancar el servicio los rangos de puntos de código que usan sus datos y registre los ID de glifo resueltos, para que una brecha de cobertura aparezca en una línea de log durante el despliegue en vez de como caracteres ausentes en una factura de cliente
Para texto Unicode que no sea de derecha a izquierda, cadenas CJK, diacríticos vietnamitas, escrituras europeas mixtas, se aplica la tubería simple: TextOut acepta un WideString y lo dibuja mediante la fuente registrada sin análisis bidireccional. Mantener separadas las dos rutas de llamada en el código de informes, una rutina para tramos RTL y otra para todo lo demás, hace explícito el comportamiento por locale en vez de enterrarlo en un flag que nadie recuerda establecer
El orden de lectura también es una propiedad del documento
La corrección a nivel de glifo no termina el trabajo. ISO 32000-1 §12.2 define una preferencia de visor, /Direction, que declara el orden de lectura predominante del documento. No cambia ningún glifo; indica a los visores cómo ordenar pliegos a dos páginas, dónde anclar la progresión en maquetas de páginas enfrentadas y qué dirección debe asumir la interfaz, detalles que importan en folletos y en cualquier documento que el usuario hojee
// Declare right-to-left reading order at the document level
Pdf.Direction := RightToLeft; // adds vpDirection to ViewerPreferences
Asignar Direction basta por sí solo: el setter de la propiedad añade vpDirection a ViewerPreferences automáticamente, por lo que la preferencia llega al fichero con una sola línea. La omisión que debe vigilarse es saltarse por completo la declaración, fácil precisamente porque nada cambia visualmente en una página aislada; solo aflora cuando alguien imprime un folleto dúplex y los pliegos salen reflejados
Dónde se detiene el moldeado de HotPDF
Un mapa honesto de capacidades ahorra una semana de evaluación. RtLTextOut maneja automáticamente la reordenación bidireccional y la unión contextual árabe. Las ligaduras tipográficas opcionales y la aplicación más amplia de características OpenType no son automáticas: GetSingleSubstituteGlyph(GID, 'liga') resuelve una sustitución cada vez, ID de glifo de entrada y etiqueta de característica al lado, lo que funciona para una lista de ligaduras conocida y finita que aplica usted mismo, pero no es un motor GSUB general. Para escrituras cuyas demandas de moldeado van más allá, las escrituras índicas con signos vocálicos reordenados son el ejemplo habitual, ejecute un piloto con cadenas reales de cliente antes de comprometer el locale, no extrapole desde resultados árabes
La verificación tiene que ser de extremo a extremo, porque una página puede verse bien y aun así fallar todos los usos posteriores. Tres comprobaciones detectan la mayor parte: copie el texto de vuelta desde Acrobat y compare los puntos de código con la cadena origen; busque dentro del documento una palabra que aparece en la página; y revise la salida en una máquina que no tenga instaladas sus fuentes de desarrollo. Un colega que lea nativamente el idioma mirando un documento real vale más que cualquier cantidad de datos de prueba sintéticos; programe esa revisión antes de publicar el formato, no después de la primera queja
Elija las cadenas de prueba deliberadamente en lugar de reutilizar lo que envió un traductor el año pasado. Un conjunto mínimo útil por locale: una frase de escritura pura, una frase con marcas latinas incrustadas, una línea con dígitos y moneda, y nombres con diacríticos o marcas combinantes. Los nombres reales de clientes rompen supuestos de moldeado que el texto de relleno nunca toca; el corpus de regresión debería crecer una entrada cada vez que un caso de soporte exponga un patrón nuevo
El registro de fuentes, el subsetting y la API general de dibujo de texto se cubren en el artículo sobre salida de informes, fuentes e imágenes con HotPDF; si los mismos documentos también deben cumplir perfiles de accesibilidad, los requisitos de etiquetado de idioma y estructura de el artículo de validación PDF/A y PDF/UA se apilan sobre el trabajo de moldeado descrito aquí
Las APIs de derecha a izquierda y de fuentes Unicode de este artículo se entregan con HotPDF Component para Delphi y C++Builder; la página del producto enlaza la referencia completa de salida de texto