Enviad la frase árabe يوضح ملف PDF هذا al TextOut ordinario y la página que se obtiene es incorrecta en dos sentidos a la vez. Las palabras van de izquierda a derecha en lugar de derecha a izquierda, y las letras aparecen separadas en sus formas aisladas en lugar de unirse en palabras conectadas. No hay ningún error. El código Delphi compila, el fichero se abre, y un revisor que lea árabe os dice que la salida es inutilizable. La solución es una llamada, no un cambio de biblioteca: HotPDF enruta el texto de derecha a izquierda a través de un método separado, RtLTextOut, que gestiona la reordenación que el TextOut ordinario no hace. Cuatro cosas de ese método determinan si la salida es utilizable: qué hace con la cadena, cómo su argumento charset selecciona el sistema de escritura, el cambio a nivel de documento que realiza como efecto secundario y el trabajo con fuentes que debe hacerse primero.
Por qué el texto de derecha a izquierda necesita su propia llamada
Un flujo de contenido PDF no almacena texto editable. Almacena glifos en posiciones fijas, lo que significa que quien emite el flujo es responsable de decidir en qué orden van esos glifos. En pantalla el sistema operativo lo hizo por vosotros: al introducir árabe en un TEdit, la pila de texto del SO lo reordena y une antes de que se vea un solo píxel. Exactamente por eso la cadena se ve perfecta en vuestro formulario y se rompe en el PDF. El escritorio hizo el trabajo silenciosamente, y en cuanto escribís vuestro propio flujo de contenido, ese trabajo vuelve a vuestro lado.
TextOut os toma la palabra. Dibuja los puntos de código en el orden en que se pasan, de izquierda a derecha, lo cual es correcto para latín, cirílico y CJK, e incorrecto para árabe y hebreo. RtLTextOut es la llamada que primero reordena la línea en orden visual de derecha a izquierda y luego dibuja. HotPDF mantiene los dos métodos deliberadamente separados en lugar de adivinar la dirección a partir de los caracteres, de modo que la elección de cuál llamar es la elección del comportamiento de escritura que se obtiene. La mecánica más profunda de la reordenación bidireccional y la unión contextual árabe es un tema aparte, tratado en el artículo sobre modelado de texto árabe y RTL con HotPDF; aquí el punto práctico es más estrecho. Usad RtLTextOut para las secuencias de derecha a izquierda, usad TextOut para todo lo demás, y nunca enrutéis una a través de la otra.

El argumento charset decide el sistema de escritura
Lo que le dice a RtLTextOut si está componiendo árabe o hebreo no es el método, sino la fuente. SetFont toma un juego de caracteres de Windows como cuarto argumento, y ese valor lleva las reglas del sistema de escritura a la llamada de derecha a izquierda: 178 selecciona árabe, 177 selecciona hebreo. Estableced el charset, luego dibujad, y las dos líneas siguientes salen en el orden de lectura correcto sin ninguna configuración adicional.
// Arabic: charset 178 tells RtLTextOut to apply Arabic rules
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
// Hebrew: charset 177 switches the rules to Hebrew
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');
Dos detalles sobre esas coordenadas son fáciles de pasar por alto. La posición que se pasa sigue siendo el inicio de la secuencia en el sistema de coordenadas propio de la página, medido desde la esquina inferior izquierda con Y creciendo hacia arriba, el mismo origen que usa todo TextOut; RtLTextOut cambia el orden de los glifos, no desde dónde mide la página. Y como con cualquier llamada de dibujo, SetFont debe ir primero y debe repetirse después de cada AddPage, porque la fuente activa no sobrevive a un salto de página. Si se olvida la repetición, la segunda página regresa a la fuente que estaba activa, que para el árabe normalmente significa cuadros vacíos.
No invierte el texto que ya habéis invertido
El error único que más tiempo de depuración consume aquí es pasar a RtLTextOut una cadena que ya habéis invertido manualmente. La gente llega a este método después de que un primer intento con el TextOut ordinario resultara al revés, y un parche habitual es invertir los caracteres en el código antes de dibujar. RtLTextOut invierte internamente por sí solo, por lo que una cadena preinvertida se invierte una segunda vez y regresa exactamente donde estaba. Pasad el texto en orden lógico, el orden en que lo escribiríais y lo leeríais en voz alta, y dejad que la llamada haga la reordenación.
La trampa es más pérfida que una inversión simple porque una cadena doblemente invertida puede parecer correcta para una frase de prueba totalmente árabe y luego romperse en cuanto una línea lleva una palabra latina o un número. Dentro de una línea de derecha a izquierda, esas secuencias incrustadas deberían leerse de izquierda a derecha, y la inversión manual destruye ese anidamiento mientras que el caso de árabe puro casualmente lo supera. Así que el error pasa vuestro primer test superficial y aparece más tarde en una factura real con un número de cuenta. Eliminad cualquier inversión manual en el momento en que cambiéis a RtLTextOut.
El efecto secundario Direction que conviene conocer
Llamar a RtLTextOut cambia más que la línea que estáis dibujando. También cambia la preferencia de dirección de lectura del documento a derecha a izquierda, lo mismo que estableceríais manualmente mediante la propiedad Direction. Ese setter añade vpDirection a las ViewerPreferences del documento, lo que le dice a un visor cómo organizar las dobles páginas y desde qué lado comienza un diseño de páginas enfrentadas. Cuando todo el documento es árabe o hebreo, esto es exactamente lo que queréis, y lo obtenéis gratis.
Vale la pena conocerlo precisamente porque es invisible en una sola página. Si el documento es principalmente de izquierda a derecha con un bloque de derecha a izquierda, la primera llamada a RtLTextOut inclinará igualmente la preferencia de todo el fichero, y nada en la prueba de una página lo mostrará. El síntoma aparece semanas después cuando alguien imprime un folleto a doble cara y los pliegos salen en espejo. Si eso no es lo que queréis, restored Direction explícitamente después de la secuencia de derecha a izquierda:
// RtLTextOut already set the document direction to RightToLeft;
// restore left-to-right if the document is predominantly LTR
Pdf.Direction := LeftToRight;
Para un documento que genuinamente se lee de derecha a izquierda, dejadlo como está. El punto es saber que la llamada tiene un efecto a nivel de documento para que la sorpresa del folleto nunca ocurra.
Registrad la fuente que distribuís, no la que esperáis que esté instalada
Ninguna de las reordenaciones importa si la fuente no tiene glifos que dibujar. El fallo clásico es un informe que se renderiza perfectamente en la máquina del desarrollador, donde Arial Unicode MS resulta estar presente, y aparece como filas de cuadros vacíos en el servidor de un cliente donde Windows sustituyó silenciosamente por una fuente sin cobertura árabe alguna. El remedio es dejar de confiar en las fuentes del sistema instaladas y registrar una que distribuís con la aplicación.
// Ship a known Arabic font and register it before drawing
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');
Con el registro vienen dos límites. Una fuente incorporada mediante RegisterUnicodeTTF se incrusta, y la gestión Unicode incrustada de HotPDF requiere el documento en PDF 1.5 o posterior; eso solo es un problema si algo aguas abajo exige PDF 1.4, pero cuando ocurre el fallo es silencioso. El otro es legal más que técnico: los ficheros TrueType llevan bits de permiso de incrustación, y una tipografía que se ve bien en pantalla puede estar licenciada de forma que prohíba distribuirla dentro de documentos de clientes. Confirmad la licencia antes de incrustar, no después de una reclamación.
Un ejemplo de consola completo
Reuniendo las piezas, aquí hay un programa autónomo que escribe una página con una línea árabe, una línea hebrea y una línea mixta que lleva un nombre de producto latino. Cada bloque establece su charset y luego dibuja en orden lógico.
program RtLTextOutDemo;
{$APPTYPE CONSOLE}
uses
HPDFDoc; // HotPDF main unit
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'RtLTextOut.pdf';
Pdf.BeginDoc;
// A Latin heading goes through the ordinary TextOut path
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');
// Arabic: charset 178, logical order, RtLTextOut does the reordering
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 720, 0,
'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');
// Hebrew: charset 177
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 680, 0,
'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');
// Mixed line: the embedded Latin word still reads left to right
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 640, 0,
'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');
Pdf.EndDoc;
Writeln('Wrote RtLTextOut.pdf');
finally
Pdf.Free;
end;
end.
Ejecutadlo y abrid el resultado. Las líneas árabe y hebrea se leen de derecha a izquierda, las letras se unen donde el sistema de escritura las une, y en la última línea el token HotPDF se sitúa de izquierda a derecha dentro de la secuencia árabe, que es el resultado correcto según la especificación aunque sorprende a cualquiera que vea el diseño bidireccional por primera vez. Ese último punto vale la pena incluirlo en los criterios de aceptación antes de que un lector nativo revise la salida, porque la secuencia incrustada que se lee en la dirección «incorrecta» respecto al sistema de escritura circundante es lo que con más frecuencia se reporta como error cuando no lo es.
Verificar la salida
Una página que tiene buen aspecto no es lo mismo que una página correcta, así que comprobadla como lo hará un sistema aguas abajo. Copiad el texto del visor y comparad los puntos de código con vuestra cadena fuente; el orden visual correcto con el orden lógico desordenado es un modo de fallo real. Ejecutad la búsqueda dentro del documento del visor para una palabra que podéis ver en la página. Luego abrid el fichero en una máquina que no tenga vuestras fuentes de desarrollo, la más propicia para exponer una sustitución silenciosa. Nada de eso reemplaza a un hablante nativo leyendo un documento genuino, que detecta problemas que ninguna cadena de prueba sintética detectará, así que programad esa revisión antes de que el formato se publique.
RtLTextOut gestiona la reordenación bidireccional y la unión contextual árabe, lo que cubre la gran mayoría del trabajo de informes y documentos de derecha a izquierda. Donde se detiene, los sistemas de escritura que necesitan más que reordenación y unión como las familias índicas, y las características opcionales de OpenType que pasan por la sustitución de un solo glifo, se describe junto con los detalles de cobertura de glifos y modelado en el artículo complementario sobre modelado de texto árabe y RTL con HotPDF.
Las llamadas RtLTextOut, SetFont y RegisterUnicodeTTF mostradas aquí forman parte del HotPDF Component para Delphi y C++Builder.