Artículo técnico

TextOut de HotPDF en Delphi: tamaño, estilo, rotación y espaciado

Cada cadena visible en un documento de HotPDF llega a través de una sola llamada: TextOut(X, Y, angle, Text). El ejemplo del Hola Mundo la usa en su forma más simple, la fuente configurada una vez y cuatro argumentos con valores predeterminados sensatos. Pasada esa primera página, los mismos cuatro argumentos cargan todo el peso del diseño. El tercer argumento rota el bloque. La fuente establecida justo antes decide el tamaño y el estilo. Y el par X, Y, medido desde la esquina de la página en puntos, es lo único que se interpone entre un reporte limpio y un texto que se superpone, se recorta o se desvía una línea más abajo en la impresora de otra persona. Aquí es donde TextOut demuestra su valor, y donde los valores predeterminados dejan de ser suficientes

Vale la pena fijar la firma en la mente antes que nada: X e Y son Single en puntos, angle es un Extended en grados, y Text es un WideString, por lo que Unicode pasa de forma directa sin una llamada por separado. Una segunda sobrecarga toma un PWORD más una longitud para cuando usted ya posee los códigos de los glifos, pero para las cadenas ordinarias la forma WideString es la que va a utilizar

El tamaño y el estilo provienen de SetFont, no de TextOut

TextOut no tiene parámetro de tamaño. El tamaño, el peso, la inclinación, todo esto reside en la llamada SetFont que precede al bloque, y se mantiene vigente hasta que el siguiente SetFont lo reemplace. Ese es el único dato que explica la mayor parte de la confusión del primer día: una línea sale en negrita porque tres llamadas antes algo estableció [fsBold] y nada lo limpió

Pdf.CurrentPage.SetFont('Times New Roman', [], 24);
Pdf.CurrentPage.TextOut(72, 740, 0, 'Quarterly Report');        // 24pt regular

Pdf.CurrentPage.SetFont('Times New Roman', [fsBold], 12);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Revenue');                 // 12pt bold

Pdf.CurrentPage.SetFont('Times New Roman', [fsItalic], 11);
Pdf.CurrentPage.TextOut(72, 694, 0, 'figures in thousands');    // 11pt italic

Pdf.CurrentPage.SetFont('Courier New', [fsBold, fsItalic], 10);
Pdf.CurrentPage.TextOut(72, 676, 0, '  +18.4% YoY');            // styles combine

El segundo argumento es un conjunto TFontStyles, de modo que [fsBold, fsItalic] es negrita cursiva y [] es normal. El tamaño está en puntos, la misma unidad que las coordenadas, lo que facilita el razonamiento sobre el espaciado vertical: una línea de 12 puntos necesita aproximadamente entre 14 y 16 puntos de paso vertical para respirar, por lo que reducir Y en 14 por línea es un interlineado inicial razonable. No hay un avance de línea automático. Usted calcula cada línea base por sí mismo, lo cual es tedioso para un párrafo pero exacto para un formulario, donde cada campo se encuentra en una coordenada fija

Dos notas prácticas sobre el nombre de la fuente. Se resuelve de acuerdo con las fuentes instaladas en el equipo de compilación, y lo que sea que el sistema operativo devuelva es lo que se incrusta, por lo que no se garantiza que un nombre que se resuelva en su escritorio y un nombre que se resuelva en un servidor de compilación sean la misma tipografía. Y la fuente tiene que cubrir las escrituras en la cadena. Un bloque de texto cirílico o CJK bajo un tipo de letra exclusivamente latino se procesa como cuadros de glifos faltantes sin ningún error, lo cual es la razón por la que la página de Hola Mundo recurre a un tipo de letra Unicode amplio cuando mezcla idiomas

Página TextOut de HotPDF mostrando Arial, Times New Roman y Courier New renderizados con estilos regular, negrita y cursiva a través de varios conjuntos de caracteres

El argumento de ángulo rota alrededor del ancla

El tercer argumento es el que la mayoría del código deja en cero por siempre. Si pasa un valor distinto de cero, el bloque rota en sentido antihorario sobre su propia ancla (X, Y), la parte inferior izquierda del texto, por esa cantidad de grados. El ancla en sí no se mueve, de modo que la misma coordenada que colocó una etiqueta horizontal ubica a su gemela rotada; solo cambia la dirección en la que marchan los glifos

Diagrama de la geometría de rotación de TextOut de HotPDF: pasadas a 0, 45 y 90 grados pivotando en sentido antihorario alrededor de un anclaje fijo abajo a la izquierda, con ejemplos de llamadas TextOut de Delphi para lomos de libros, marcas de agua y encabezados de columna inclinados
Un ángulo distinto de cero gira la serie en sentido antihorario alrededor de su anclaje inmóvil en la esquina inferior izquierda, y las baselines compartidas avanzan X exactamente como las líneas horizontales avanzan Y
Pdf.CurrentPage.SetFont('Arial', [fsBold], 11);

// Una etiqueta de eje vertical sobre el margen izquierdo: 90 grados se lee de abajo hacia arriba
Pdf.CurrentPage.TextOut(40, 300, 90, 'Units sold');

// Una marca de agua DRAFT en diagonal sobre el cuerpo de la página
Pdf.CurrentPage.SetFont('Arial', [fsBold], 60);
Pdf.CurrentPage.TextOut(150, 250, 45, 'DRAFT');

// Encabezados de columna inclinados 60 grados para que las etiquetas largas entren en una tabla angosta
Pdf.CurrentPage.SetFont('Arial', [], 9);
Pdf.CurrentPage.TextOut(120, 600, 60, 'Q1 actual');
Pdf.CurrentPage.TextOut(160, 600, 60, 'Q2 actual');

Noventa grados es el caso común, una etiqueta que sube por el lado de un gráfico o un título de lomo. Cuarenta y cinco grados maneja encabezados de columnas inclinados, el truco que permite que una etiqueta ancha se ubique sobre una columna estrecha sin derramarse hacia sus vecinos. La rotación no cambia cómo se interpreta el ancla, lo que hace tropezar a las personas: un bloque de 90 grados aún comienza en (X, Y) y crece hacia arriba desde ahí, de modo que para centrar una etiqueta rotada, usted ajusta el ancla, no el ángulo. Cuando varios bloques rotados comparten una línea base, deles la misma Y y dé saltos en X, exactamente como saltaría en Y para líneas horizontales apiladas

Ubicación de coordenadas sin adivinar

Las coordenadas son la parte que sobrevive a la revisión o falla silenciosamente. HotPDF mide desde la esquina inferior izquierda de la página, con Y creciendo hacia arriba, en puntos a 72 por pulgada. Una página tamaño carta (US Letter) mide 612 por 792 puntos; A4 mide 595 por 842. Un margen superior de una pulgada en tamaño carta coloca su primera línea base cerca de Y = 792 menos 72 menos el tamaño de la fuente, no en un número pequeño cerca de la parte superior. Cualquiera que venga de coordenadas de pantalla, donde Y crece hacia abajo desde cero, escribe la primera línea fuera del borde inferior y pasa diez minutos preguntándose a dónde fue a parar

HotPDF: diagrama de página US Letter que muestra la aritmética de línea base con LeftMargin 72, TopBaseline 720, y un Leading fijo que avanza hacia el piso del margen inferior en Y 72, junto al bucle de diseño con constantes nombradas y su guarda manual de AddPage y reinicio de SetFont
Los anclajes con nombre convierten una columna de números mágicos en aritmética: decremente la baseline en curso en Leading por línea y custodie usted mismo el piso con AddPage

Trate el diseño como aritmética contra anclas con nombre en lugar de una columna de números mágicos. Un margen izquierdo, una línea base móvil que usted decrementa por línea y un interlineado fijo convierten un bloque de etiquetas en un bucle corto en lugar de un muro de literales:

const
  LeftMargin = 72;        // 1 inch in
  TopBaseline = 720;       // primera línea, ~1 pulgada abajo en Letter
  Leading = 16;            // paso vertical entre líneas
var
  Y: Single;
  Line: string;
begin
  Pdf.CurrentPage.SetFont('Arial', [], 11);
  Y := TopBaseline;
  for Line in ReportLines do
  begin
    Pdf.CurrentPage.TextOut(LeftMargin, Y, 0, Line);
    Y := Y - Leading;
    if Y < 72 then            // se llegó al margen inferior
    begin
      Pdf.AddPage;
      Pdf.CurrentPage.SetFont('Arial', [], 11);  // la fuente se resetea en cada página nueva
      Y := TopBaseline;
    end;
  end;
end;

El protector contra saltos de página es la línea que todo el mundo olvida primero y el campo que golpea más duro. No hay diseño de flujo debajo de TextOut. Si decrementa más allá del margen inferior, el texto sigue dibujándose en el margen exterior, fuera de la página, hacia la nada, sin ninguna advertencia. Por lo tanto, usted vigila la Y por sí mismo, llama a AddPage cuando cruza el piso y restablece la línea base. El SetFont después de AddPage no es un relleno opcional: la fuente actual no sobrevive a un salto de página, y el primer bloque en la nueva página sale en el tipo de letra predeterminado del visor si lo omite

Espaciado de caracteres y palabras para ajuste y alineación

En ocasiones, una cadena es correcta pero del ancho incorrecto: un encabezado que tiene que abarcar una regla fija, un código que debe leerse con dígitos más holgados, una columna que necesita que sus valores se ajusten para alinearse. PDF cuenta con dos operadores de estado de texto para esto, el espaciado de caracteres (Tc, espacio adicional agregado después de cada glifo) y el espaciado de palabras (Tw, espacio adicional agregado en cada carácter de espacio), y ambos se expresan en unidades de espacio de texto sin escalar, de manera efectiva en puntos al tamaño de fuente actual. Son estados, no argumentos para TextOut, así que los configura, dibuja y los restablece

HotPDF: el espaciado de caracteres Tc reparte espacio extra uniformemente tras cada glifo de SUMMARY, frente al espaciado de palabras Tw que lo acumula solo en los caracteres de espacio, cableado al ciclo de asignar, dibujar y reiniciar que evita que el estado de espaciado se filtre a párrafos posteriores
Tc distribuye su ajuste por cada glifo mientras que Tw actúa solo en el carácter de espacio, y restablecer en la línea siguiente mantiene limpio el estado de dibujo
// Espacia las letras de un título corto para que se estire a lo ancho de una línea
Pdf.CurrentPage.SetCharacterSpacing(4);
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(72, 740, 0, 'S U M M A R Y');
Pdf.CurrentPage.SetCharacterSpacing(0);   // resetea antes del texto de cuerpo normal

// Abre los espacios entre palabras en una sola línea ancha
Pdf.CurrentPage.SetWordSpacing(6);
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Name        Department        Extension');
Pdf.CurrentPage.SetWordSpacing(0);

El espaciado de palabras solo actúa sobre el carácter de espacio (código 32), lo cual tiene una consecuencia que vale la pena conocer: no hace nada dentro de un bloque CJK que no tiene espacios ASCII, e interactúa de forma extraña con el texto codificado como índices de glifos en lugar de bytes. Para la salida tabular latina es la manera barata de ampliar los espacios sin volver a escribir la cadena. El espaciado de caracteres es la mejor herramienta para un encabezado que debe alcanzar un ancho objetivo, ya que distribuye el ajuste de manera uniforme a través de cada glifo en lugar de acumularlo en los espacios

El restablecimiento es toda la disciplina. El espaciado, al igual que la fuente, es parte del estado de dibujo de la página y el estado persiste hasta que usted lo cambia. Si le da espaciado de letras a un encabezado y olvida ponerlo en cero, cada párrafo a continuación hereda el estiramiento, lo que se lee como una incorrección sutil y difícil de ubicar que sobrevive a una lectura rápida y falla en una revisión cuidadosa. El hábito confiable es establecer un valor de espaciado, dibujar el bloque que lo necesita y restablecerlo a cero en la siguiente línea, para que ningún código posterior tenga que saber lo que hizo una sección anterior

Página TextOut de HotPDF comparando el escalado de texto horizontal, espaciado de caracteres, espaciado de palabras y modos de renderizado de relleno frente a trazo

Comprobación de la salida donde realmente se rompe

El diseño de texto falla en la segunda máquina, no en la primera, por lo que las comprobaciones que importan suceden lejos de su escritorio. Abra el archivo generado en un sistema sin la fuente de desarrollador instalada y confirme que los tipos de letra incrustados todavía se renderizan, incluidos el latín acentuado, cualquier escritura no latina y la puntuación, en un solo paso en lugar de hacer comprobaciones aleatorias de los caracteres fáciles. Seleccione y copie unas pocas líneas para confirmar que el texto es texto real y no contornos, lo cual importa en el momento en que la búsqueda o la extracción están en el alcance. Alimente el diseño con datos representativos, la etiqueta alemana más larga y el número más ancho, no con un marcador de posición ordenado, porque el bloque que desborda un campo siempre es el que usted no ingresó a mano. Y si la página tiene que aterrizar en un formulario preimpreso, imprima o rasterice una muestra y colóquela contra el original; un desplazamiento de línea base de un cuarto de milímetro es invisible en la pantalla y obvio en papel

Si aún no ha escrito una sola página, comience con el ejemplo Hola Mundo de HotPDF, que configura el documento, la fuente y el sistema de coordenadas inferior izquierdo del cual depende todo lo anterior. Las llamadas TextOut, SetFont y de espaciado que se muestran aquí son parte del Componente HotPDF para Delphi y C++Builder