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 ha eliminado de PDF 2.0, y los visores modernos están retirando sus motores XFA uno por uno. Nada de eso ha vaciado los archivos. Los formularios de admisión gubernamentales, las solicitudes de seguros y los extractos bancarios se crearon como XFA durante la mayor parte de dos décadas, y esos archivos siguen llegando a las bandejas de entrada y a los flujos de trabajo de documentos hoy en día. Cuando el visor que solía representarlos deja de hacerlo, el formulario se convierte en una página en blanco con un marcador de posición que dice "abra en un lector diferente". La solución duradera es aplanar el XFA en contenido PDF estático que cualquier lector pueda pintar.
La parte difícil de ese aplanamiento no son los campos. Los cuadros de texto y las casillas de verificación se asignan a los widgets de AcroForm de manera bastante limpia. La parte difícil es el texto enriquecido que XFA almacena dentro de un elemento de dibujo, en un bloque <exData contentType="text/html">. Ese bloque es un subconjunto de HTML con estilo en línea y, a menudo, anclajes. 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
Un cuerpo exData es una pequeña porción de XHTML. Un párrafo es un <p>; un tramo 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 tramos seguidos, cada uno con un estilo diferente, y uno de ellos puede ser un anclaje. El estilo no es una decoración que se pueda omitir. Una cláusula representada en rojo negrita porque es una advertencia legal debe seguir en rojo negrita después del aplanamiento, o de lo contrario el documento aplanado tergiversará el original.
Así que el motor de aplanado no puede tratar el bloque como una sola cadena. Tiene que recorrer la estructura en línea, resolver el estilo efectivo de cada ejecución superponiendo el CSS en línea del span sobre la fuente base del elemento de dibujo, y disponer las ejecuciones una tras otra a lo largo de la línea. HotPDF modela cada uno de estos fragmentos dispuestos como un registro interno TXFARichRun. El registro lleva el texto de la ejecución, su estilo verificado, su caja medida y, para un anclaje, la Href a la que apunta.
Disponer las ejecuciones de izquierda a derecha
El posicionamiento es donde el texto enriquecido deja de ser un problema de análisis y se convierte en un problema de tipografía. Las ejecuciones comparten una línea, por lo que cada una comienza donde terminó la anterior. No hay marcas que registren esas posiciones; tienen que medirse. La rutina interna LayoutRichText del motor mide cada ejecución con las mismas métricas de fuente que la pintarán más tarde, luego establece el desplazamiento horizontal de la ejecución a la suma acumulada de todos los anchos de ejecución anteriores. La ejecución uno comienza en el origen de la caja de dibujo, la dos comienza en el ancho de la ejecución uno, la tres en el ancho combinado de las dos primeras, y así sucesivamente a lo largo de la línea.
Esta es la razón por la que la alineación de la fuente de medición importa tanto. El paso de diseño mide los avances; un paso de renderizado separado dibuja los glifos. Si esos dos pasos no coinciden en la fuente, las cajas que calculó el diseño no se situarán debajo de los glifos que pinta el renderizador. HotPDF los mantiene sincronizados mapeando el estilo verificado de cada ejecución a una especificación de fuente, a través del asistente interno RunStyleToFontSpec, que coincide con los valores predeterminados del propio renderizador de Arial a 10 puntos. El avance medido y el texto dibujado coinciden entonces, y la caja calculada de una ejecución cubre genuinamente los caracteres que ve el lector.
// Conceptual shape of one laid-out run. The engine builds an array of these
// internally; you never construct them yourself, but the fields explain how a
// link's hit box is derived from measured geometry rather than from text.
type
TRichRunInfo = record
Dx, Dy : Double; // top-left, relative to the draw-box origin
W, H : Double; // measured run box (width from the layout pass)
Text : AnsiString; // the run's visible characters
Href : AnsiString; // URI target for an <a> run, '' otherwise
end;
De una ejecución de anclaje a una anotación de enlace PDF
Un hipervínculo en un PDF terminado no es parte del contenido de la página. Es un objeto separado, una anotación de enlace (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 colocada sobre él.
La ruta de aplanado sigue exactamente este modelo. Cuando una ejecución lleva una Href, HotPDF primero dibuja el texto con estilo y luego construye una anotación de enlace sobre la caja de la ejecución. 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 anotación. Su rectángulo es la caja medida de la ejecución, traducida de las coordenadas locales del elemento de dibujo a coordenadas de la página. El resultado es un enlace que se sitúa precisamente en el texto de anclaje y en ningún otro lugar.
// The same public API the flatten path uses for each anchor run. It produces
// an ISO 32000-1 12.5.6.5 Link annotation: /Subtype /Link with a /URI action
// over the given rectangle. The optional description fills /Contents so a
// screen reader can announce the target.
var
LinkRect: TRect;
Annot: THPDFDictionaryObject;
begin
LinkRect := Rect(72, 690, 268, 706); // page-space hit box for the run
Annot := Pdf.CurrentPage.AddURILink(LinkRect,
'https://www.example.gov/appeal', 'File an appeal online');
end;
Por qué la zona activa debe provenir de los anchos medidos
Es el momento de evitar el error común: es tentador imaginar la localización del 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 para la forma en que se almacena el texto aplanado. Las ejecuciones con estilo se pintan con fuentes de subconjunto incrustadas. Una fuente de subconjunto vuelve a numerar 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 anclaje no encuentra nada, porque esa leyenda no existe como texto literal en ninguna parte del flujo.
El único anclaje confiable para el rectángulo es la geometría que el paso de diseño ya produjo. El desplazamiento y el ancho medido de cada ejecución se calcularon al hacer fluir la línea, antes de que se volviera a numerar cualquier glifo, y describen dónde aparecerá físicamente el texto. Por lo tanto, HotPDF toma el rectángulo de enlace directamente de la caja trazada de la ejecución en lugar de cualquier búsqueda de texto. Debido a que la medición utilizó la fuente de renderizado, la caja es correcta independientemente del subconjunto. La geometría sobrevive a la codificación; el texto no. Ese es todo el argumento para el posicionamiento por ancho medido, y es por eso que un aplanador que intenta reajustar los enlaces mediante búsqueda de texto produce zonas activas que se desplazan o desaparecen.
Dirigir el aplanamiento 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 de AcroForm rellenables, o False para marcar cada widget como de solo lectura de modo que la salida sea un registro congelado. Los bloques de dibujo de texto enriquecido, con sus ejecuciones 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 keeps fields fillable; False freezes them read-only.
Emitted := Pdf.FlattenLoadedXFA(True);
// Anything the engine could not map is reported, not raised.
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 limpia al comienzo de cada aplanamiento y acumula una línea por 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 tramos utilizables. Ninguno de ellos lanza una excepción, por lo que una lista de advertencias vacía es su evidencia de que todo se mapeó, y una que no esté vacía le dice exactamente qué originales inspeccionar. Cuando tenga el XFA sin procesar 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 sentido contrario, incrustando un paquete XFA en un documento que esté construyendo.
Confirmar el resultado en un lector
Abra el archivo aplanado en Acrobat, o en cualquier visor actual, y compruebe dos cosas. Primero, que el texto enriquecido se representó con su estilo intacto: las ejecuciones en negrita están en negrita, las de color llevan su color y los tramos se sitúan en el orden correcto en la línea en lugar de superponerse o salirse de la caja. Segundo, que los hipervínculos estén activos. Al pasar el cursor sobre un anclaje, la barra de estado debería mostrar la dirección de destino; haga clic en él y la acción URI debería abrirlo. Utilice el inspector de anotaciones del visor para confirmar que cada una es una anotación /Link genuina cuyo /Rect abraza el texto de anclaje, sentada sobre contenido que ahora son glifos pintados simples en lugar de XFA representado por el 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 aplanado sobreviva a los motores XFA que ya no necesita.
El aplanamiento de los campos mismos, los cuadros de texto, las casillas de verificación y las listas de opciones que rodean a este texto enriquecido, se cubre en nuestro tutorial sobre el aplanamiento de formularios XFA en widgets de AcroForm. Para obtener una visión más amplia de la creación y colocación manual de anotaciones Link, más allá de las que genera la ruta de aplanado, consulte el trabajo con anotaciones PDF en HotPDF. Ambos se basan en el mismo modelo de anotaciones y formularios que se incluye con el Componente HotPDF para Delphi y C++Builder.