XFA, la arquitectura de formularios XML (XML Forms Architecture), está obsoleta. La norma ISO 32000-1 la incluye en el §12.7 con la nota de que se elimina de PDF 2.0, y los visores modernos están descartando sus motores XFA uno por uno. Sin embargo, nada de eso ha vaciado los archivos. Formularios de admisión gubernamentales, solicitudes de seguros y extractos bancarios fueron creados como XFA durante la mayor parte de dos décadas, y esos archivos todavía llegan a las bandejas de entrada y a los procesos de documentos en la actualidad. Cuando el visor que solía renderizarlos deja de hacerlo, el formulario se convierte en una página en blanco con un mensaje genérico de "por favor, abra en un lector diferente". La solución duradera es acoplar (flatten) el XFA en contenido PDF estático que cualquier lector pueda pintar
La parte difícil de ese acoplado no son los campos. Los cuadros de texto y las casillas de verificación se asignan a widgets AcroForm con bastante limpieza. La parte difícil es el texto enriquecido que XFA almacena dentro de un elemento draw, en un bloque <exData contentType="text/html">. Ese bloque es un subconjunto de HTML con estilos en línea y, a menudo, enlaces (anchors). Llevarlo a la página significa reproducir tanto el texto con estilo como los hipervínculos activos, y los hipervínculos son el punto donde la mayoría de las implementaciones se rinden silenciosamente
Cómo se ve realmente el texto enriquecido XFA
El cuerpo de un exData es una pequeña porción de XHTML. Un párrafo es una etiqueta <p>; un fragmento de caracteres con estilo es un <span> con su propio CSS en línea para peso, postura, color y tamaño; y un hipervínculo es un <a href="..."> que envuelve su texto visible. Una sola línea puede contener varios spans seguidos, cada uno con diferentes estilos, y uno de ellos puede ser un enlace. El estilo no es una simple decoración que se pueda descartar. Una cláusula renderizada en rojo y en negrita porque es una advertencia legal tiene que permanecer en rojo y negrita después de ser acoplada, o de lo contrario el documento acoplado tergiversaría el original
Por lo tanto, el motor de acoplado no puede tratar el bloque como una sola cadena de texto. Tiene que recorrer la estructura en línea, resolver el estilo efectivo de cada secuencia (run) superponiendo el CSS en línea del span sobre la fuente base del elemento draw, y diseñar las secuencias una tras otra a lo largo de la línea. HotPDF modela cada uno de estos fragmentos diseñados como un registro interno TXFARichRun. El registro transporta el texto de la secuencia, su estilo resuelto, su cuadro medido y, en el caso de un enlace, el Href al que apunta
Diseñando las secuencias de izquierda a derecha
El posicionamiento es donde el texto enriquecido deja de ser un problema de análisis sintáctico (parsing) y se convierte en un problema de composición tipográfica (typesetting). Las secuencias comparten una línea, por lo que cada secuencia comienza donde terminó la anterior. No hay marcas que registren esas posiciones; deben ser medidas. La rutina interna LayoutRichText del motor mide cada secuencia con las mismas métricas de fuente que luego la pintarán, luego establece el desplazamiento horizontal de la secuencia a la suma acumulada de todos los anchos de las secuencias anteriores. La secuencia uno comienza en el origen del cuadro draw, la secuencia dos comienza en el ancho de la secuencia uno, la secuencia tres en el ancho combinado de las dos primeras, y así sucesivamente a través de la línea
Es por esto que la alineación de la fuente de medición importa tanto. La pasada de diseño (layout) mide avances; una pasada de renderizado separada dibuja glifos. Si esas dos pasadas no concuerdan con respecto a la fuente, los cuadros que el diseño computó no se sentarán debajo de los glifos que pinta el renderizador. HotPDF los mantiene en sincronía asignando el estilo resuelto de cada secuencia a una especificación de fuente, a través del ayudante interno RunStyleToFontSpec, que coincide con los propios valores predeterminados del renderizador de Arial a 10 puntos. El avance medido y el texto dibujado entonces coinciden, y el cuadro calculado de una secuencia realmente cubre los caracteres que un lector ve
// Forma conceptual de una secuencia diseñada. El motor construye una matriz de estas
// internamente; usted nunca las construye por sí mismo, pero los campos explican cómo el
// área activa de un enlace se deriva de la geometría medida en lugar del texto.
type
TRichRunInfo = record
Dx, Dy : Double; // superior-izquierdo, relativo al origen del cuadro draw
W, H : Double; // cuadro de secuencia medido (ancho de la pasada de diseño)
Text : AnsiString; // los caracteres visibles de la secuencia
Href : AnsiString; // URI destino para una secuencia <a>, '' en caso contrario
end;
De una secuencia de enlace a una anotación Link de PDF
Un hipervínculo en un PDF terminado no es parte del contenido de la página. Es un objeto separado, una anotación Link, descrita en la norma ISO 32000-1 §12.5.6.5. La anotación tiene un /Rect que define el rectángulo en el que se puede hacer clic en la página y una acción que se dispara cuando se hace clic en el rectángulo. Para un enlace externo, la acción es una acción URI: /S /URI con la dirección de destino como su cadena /URI. El texto visible debajo es contenido ordinario de la página; la anotación es la zona activa invisible superpuesta sobre él
La ruta de acoplado sigue exactamente este modelo. Cuando una secuencia transporta un Href, HotPDF primero dibuja el texto con estilo, y luego construye una anotación Link sobre el cuadro de la secuencia. El punto de entrada público para esa anotación es el método de página AddURILink, que crea el objeto /Type /Annot /Subtype /Link con una acción /URI y devuelve el diccionario de la anotación. Su rectángulo es el cuadro medido de la secuencia, traducido de las coordenadas locales del elemento draw a las coordenadas de la página. El resultado es un enlace que aterriza precisamente en el texto del enlace y en ningún otro lugar
// La misma API pública que usa la ruta de acoplado para cada secuencia de enlace. Produce
// una anotación Link ISO 32000-1 12.5.6.5: /Subtype /Link con una acción /URI
// sobre el rectángulo dado. La descripción opcional llena /Contents para que un
// lector de pantalla pueda anunciar el destino.
var
LinkRect: TRect;
Annot: THPDFDictionaryObject;
begin
LinkRect := Rect(72, 690, 268, 706); // área activa en el espacio de la página para la secuencia
Annot := Pdf.CurrentPage.AddURILink(LinkRect,
'https://www.example.gov/appeal', 'File an appeal online');
end;
Por qué el área activa debe provenir de anchos medidos
Es tentador imaginar ubicar el enlace buscando en la página su texto visible y dibujando el rectángulo alrededor de lo que se encuentre. Eso no funciona, y la razón es fundamental en la forma en que se almacena el texto acoplado. Las secuencias con estilo se pintan con fuentes de subconjunto (subset fonts) incrustadas. Una fuente de subconjunto reenumera los glifos que conserva, por lo que el flujo de contenido de la página contiene códigos CID hexadecimales, no los códigos de caracteres originales. Los bytes en la página no son las letras que lee un ser humano, y no se pueden buscar como texto. Una búsqueda de la leyenda del enlace no encuentra nada, porque esa leyenda no existe como texto literal en ninguna parte del flujo
El único ancla confiable para el rectángulo es la geometría que ya produjo la pasada de diseño. El desplazamiento de cada secuencia y el ancho medido se calcularon al fluir la línea, antes de que se reenumerara cualquier glifo, y describen dónde aparecerá físicamente el texto. HotPDF, por lo tanto, toma el rectángulo del enlace directamente del cuadro diseñado de la secuencia en lugar de cualquier búsqueda de texto. Debido a que la medición usó la fuente de renderizado, el cuadro es correcto independientemente de los subconjuntos. La geometría sobrevive a la codificación; el texto no. Ese es el argumento completo para el posicionamiento de ancho medido, y es la razón por la que un aplanador (flattener) que intenta reacondicionar enlaces mediante búsqueda de texto produce zonas activas que se desplazan o desaparecen
Controlando el acoplado desde su código
Para un PDF que ya contiene un paquete XFA, el punto de entrada es FlattenLoadedXFA. Cargue el documento, llame al método y guarde el resultado. El parámetro Editable decide qué sucede con los campos del formulario: pase True para mantenerlos como widgets AcroForm rellenables, o False para marcar cada widget como de solo lectura de modo que la salida sea un registro congelado. Los bloques draw de texto enriquecido, con sus secuencias con estilo y anotaciones de enlace, se producen de cualquier manera. La función devuelve el recuento de widgets que emitió
var
Pdf: THotPDF;
Emitted, i: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('xfa_appeal_form.pdf');
// True mantiene los campos rellenables; False los congela como solo lectura.
Emitted := Pdf.FlattenLoadedXFA(True);
// Cualquier cosa que el motor no pudo asignar se reporta, no genera una excepción (raise).
for i := 0 to Pdf.XFAFlattenWarnings.Count - 1 do
Writeln('XFA warning: ', Pdf.XFAFlattenWarnings[i]);
Pdf.SaveLoadedDocument('appeal_form_flat.pdf');
Writeln('Widgets emitted: ', Emitted);
finally
Pdf.Free;
end;
end;
Lea siempre XFAFlattenWarnings después de la llamada. La lista se borra al comienzo de cada acoplado y acumula una línea para cada elemento que el motor se negó a renderizar: un tipo de campo no admitido, una imagen de dibujo que no se decodificó, un bloque exData sin spans utilizables. Ninguno de estos genera una excepción, por lo que una lista de advertencias vacía es su evidencia de que todo se asignó, y una que no está vacía le indica exactamente qué originales inspeccionar. Cuando usted tiene el XFA en crudo como bytes XDP en lugar de un PDF cargado, el método hermano ApplyXFAAsAcroForm toma esos bytes directamente y comparte la misma ruta de código y el mismo comportamiento de advertencias. El método complementario AddXFAPacket va en la dirección opuesta, incrustando un paquete XFA en un documento que está construyendo
Confirmando el resultado en un lector
Abra el archivo acoplado en Acrobat, o en cualquier visor actual, y verifique dos cosas. Primero, que el texto enriquecido se renderizó con su estilo intacto: las secuencias en negrita están en negrita, las secuencias en color conservan su color, y los spans se ubican en el orden correcto en la línea en lugar de superponerse o salirse del cuadro. Segundo, que los hipervínculos están activos. Pase el cursor sobre un enlace y la barra de estado debería mostrar la dirección de destino; haga clic en él y la acción URI debería abrirlo. Use el inspector de anotaciones del visor para confirmar que cada uno es una verdadera anotación /Link cuyo /Rect abraza el texto del enlace, situándose sobre un contenido que ahora son simples glifos pintados en lugar de XFA renderizado de formulario. Esa combinación, texto estático con estilo más anotaciones Link reales en los rectángulos correctos, es lo que hace que el documento acoplado sobreviva a los motores XFA que ya no necesita
El acoplado de los propios campos, los cuadros de texto, las casillas de verificación y las listas de opciones que rodean este texto enriquecido, se cubre en nuestro tutorial sobre el acoplado de formularios XFA en widgets AcroForm. Para una visión más amplia sobre la creación y colocación manual de anotaciones Link, más allá de las que genera la ruta de acoplado, consulte trabajar con anotaciones PDF en HotPDF. Ambos se basan en la misma anotación y modelo de formularios que se envían con el Componente HotPDF para Delphi y C++Builder