Formularios XFA — Detectar y Leer Datos XML en Python, Rust, Node.js, Go y C#
Detección y análisis de formularios XFA:
from pdf_oxide import PdfDocument
doc = PdfDocument("government-form.pdf")
xfa = doc.analyze_xfa()
if xfa:
print(f"XFA form with {len(xfa.fields)} fields")
for field in xfa.fields:
print(f" {field.name}: {field.field_type}")
XFA (XML Forms Architecture) es un formato legado de formularios utilizado por muchas agencias gubernamentales, instituciones financieras y sistemas empresariales. La mayoría de las bibliotecas PDF para Python no pueden manejar formularios XFA en absoluto. PDF Oxide puede detectarlos, analizarlos y extraer datos de ellos.
¿Qué es XFA?
Los formularios XFA usan plantillas basadas en XML incrustadas dentro de un PDF, en lugar de los campos AcroForm estándar. Fueron creados por Adobe y son comunes en:
- Formularios gubernamentales — IRS, inmigración, documentos de agencias estatales
- Formularios financieros — solicitudes de préstamo, reclamaciones de seguros
- Formularios empresariales — incorporación de RRHH, adquisiciones, cumplimiento normativo
XFA quedó obsoleto en PDF 2.0 (ISO 32000-2:2020), pero millones de documentos XFA existentes siguen en circulación.
XFA vs. AcroForm
| Característica | AcroForm | XFA |
|---|---|---|
| Formato | Objetos PDF | Plantillas XML |
| Compatible con | Todas las bibliotecas PDF | Pocas bibliotecas PDF |
| Diseños dinámicos | No | Sí |
| Estado en PDF 2.0 | Compatible | Obsoleto |
| Fuente típica | La mayoría de las herramientas de creación de formularios | Adobe LiveCycle, Adobe Designer |
Por Qué PyMuPDF y pypdf No Pueden Manejar Formularios XFA
Si has intentado leer formularios XFA con las bibliotecas Python más populares para PDF, probablemente hayas obtenido resultados vacíos sin ningún error ni aviso. Esto se debe a que PyMuPDF, pypdf, pdfplumber y pdfminer no tienen soporte para XFA.
PyMuPDF (fitz) — devuelve vacío en silencio
Los métodos doc.get_form_fields() y .widgets() de PyMuPDF solo leen campos AcroForm. Cuando un PDF usa formularios exclusivamente XFA (habitual en documentos del IRS, inmigración y agencias estatales), PyMuPDF devuelve resultados vacíos sin ninguna advertencia:
# PyMuPDF — silently misses XFA data
import fitz
doc = fitz.open("government-form.pdf")
fields = doc[0].widgets() # Returns [] on XFA-only forms
form_data = doc.get_form_fields() # Returns {} on XFA-only forms
Si el formulario XFA incluye una capa de reserva AcroForm, PyMuPDF puede devolver un subconjunto parcial de campos, pero los datos XFA reales (diseños dinámicos, valores calculados, subformularios anidados) permanecen invisibles.
pypdf — también devuelve vacío en formularios XFA
La lectura de campos de formulario en pypdf choca con la misma limitación. Solo puede acceder a campos AcroForm y no tiene soporte para XFA:
# pypdf — cannot read XFA content
from pypdf import PdfReader
reader = PdfReader("government-form.pdf")
fields = reader.get_form_text_fields() # Returns {} on XFA-only forms
pdfplumber y pdfminer — sin ningún soporte para XFA
pdfplumber y pdfminer no intentan leer campos de formularios XFA. No disponen de API para detectar ni extraer XFA.
PDF Oxide — lee XFA de forma nativa
PDF Oxide parsea las plantillas XML de XFA directamente, extrayendo todos los campos, valores y la estructura del formulario:
# PDF Oxide — reads XFA natively
from pdf_oxide import PdfDocument
doc = PdfDocument("government-form.pdf")
xfa = doc.analyze_xfa()
print(f"{len(xfa.fields)} fields found") # All XFA fields extracted
Funciona con formularios gubernamentales, documentos del IRS, solicitudes de seguros y cualquier PDF basado en XFA, incluyendo formularios sin capa de reserva AcroForm.
Instalación
pip install pdf_oxide
Detectar Formularios XFA
Comprobar si un PDF contiene contenido XFA:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("form.pdf")
xfa = doc.analyze_xfa()
if xfa:
print("Este PDF usa formularios XFA")
print(f" Campos: {len(xfa.fields)}")
print(f" Tiene plantilla: {xfa.has_template}")
print(f" Tiene datasets: {xfa.has_datasets}")
else:
print("AcroForm estándar (o sin formularios)")
WASM
En WASM, puedes detectar formularios XFA y usar como alternativa la lectura de campos AcroForm:
import { WasmPdfDocument } from "pdf-oxide-wasm";
const doc = new WasmPdfDocument(bytes);
if (doc.hasXfa()) {
console.log("Este PDF usa formularios XFA");
// Leer los campos de reserva AcroForm
const fields = doc.getFormFields();
console.log(`Campos de reserva AcroForm: ${fields.length}`);
}
doc.free();
C++
#include <pdf_oxide/pdf_oxide.hpp>
#include <iostream>
auto doc = pdf_oxide::Document::open("government-form.pdf");
if (doc.has_xfa()) {
std::cout << "This PDF uses XFA forms\n";
// Read any AcroForm fallback fields
auto fields = doc.get_form_fields();
std::cout << "AcroForm fallback fields: " << fields.size() << "\n";
}
Swift
import PdfOxide
let doc = try Document.open("government-form.pdf")
if try doc.hasXfa() {
print("This PDF uses XFA forms")
// Read any AcroForm fallback fields
let fields = try doc.formFields()
print("AcroForm fallback fields: \(fields.count)")
}
Dart
import 'package:pdf_oxide/pdf_oxide.dart';
final doc = PdfDocument.open('government-form.pdf');
if (doc.hasXfa()) {
print('This PDF uses XFA forms');
// Read any AcroForm fallback fields
final fields = doc.getFormFields();
print('AcroForm fallback fields: ${fields.length}');
}
doc.close();
R
library(pdfoxide)
doc <- pdf_open("government-form.pdf")
if (pdf_has_xfa(doc)) {
cat("This PDF uses XFA forms\n")
# Read any AcroForm fallback fields
fields <- pdf_get_form_fields(doc)
cat("AcroForm fallback fields:", length(fields), "\n")
}
Julia
using PdfOxide
doc = open_document("government-form.pdf")
if has_xfa(doc)
println("This PDF uses XFA forms")
# Read any AcroForm fallback fields
fields = get_form_fields(doc)
println("AcroForm fallback fields: ", length(fields))
end
Zig
const pdf_oxide = @import("pdf_oxide");
var doc = try pdf_oxide.Document.open("government-form.pdf");
defer doc.deinit();
if (doc.hasXfa()) {
std.debug.print("This PDF uses XFA forms\n", .{});
// Read any AcroForm fallback fields
var fields = try doc.formFields();
defer fields.deinit();
std.debug.print("AcroForm fallback fields: {d}\n", .{try fields.count()});
}
Objective-C
#import "POXPdfOxide.h"
NSError *err = nil;
POXDocument *doc = [POXDocument openPath:@"government-form.pdf" error:&err];
if ([doc hasXfa]) {
NSLog(@"This PDF uses XFA forms");
// Read any AcroForm fallback fields
NSArray<POXFormField*> *fields = [doc formFieldsWithError:&err];
NSLog(@"AcroForm fallback fields: %lu", (unsigned long)fields.count);
}
Elixir
{:ok, doc} = PdfOxide.open("government-form.pdf")
if PdfOxide.has_xfa?(doc) do
IO.puts("This PDF uses XFA forms")
# Read any AcroForm fallback fields
{:ok, fields} = PdfOxide.form_fields(doc)
IO.puts("AcroForm fallback fields: #{length(fields)}")
end
Analizar Campos XFA
Obtener detalles de cada campo en el formulario XFA:
from pdf_oxide import PdfDocument
doc = PdfDocument("tax-form.pdf")
xfa = doc.analyze_xfa()
if xfa:
for field in xfa.fields:
print(f"Name: {field.name}")
print(f" Type: {field.field_type}")
print(f" Value: {field.value}")
print()
Leer Datos XFA
Extraer los valores actuales de los campos desde los datasets XFA:
from pdf_oxide import PdfDocument
doc = PdfDocument("filled-xfa.pdf")
xfa = doc.analyze_xfa()
if xfa and xfa.has_datasets:
data = {}
for field in xfa.fields:
if field.value:
data[field.name] = field.value
print(data)
Procesamiento en Lote de Formularios XFA
Escanear un directorio para identificar qué PDFs usan XFA:
from pdf_oxide import PdfDocument, PdfError
from pathlib import Path
pdf_dir = Path("government-forms/")
xfa_files = []
acroform_files = []
for pdf_path in pdf_dir.glob("*.pdf"):
try:
doc = PdfDocument(str(pdf_path))
xfa = doc.analyze_xfa()
if xfa:
xfa_files.append(pdf_path.name)
else:
acroform_files.append(pdf_path.name)
except PdfError as e:
print(f"Error: {pdf_path.name}: {e}")
print(f"XFA forms: {len(xfa_files)}")
print(f"Standard forms: {len(acroform_files)}")
API de Rust
use pdf_oxide::PdfDocument;
use pdf_oxide::xfa::analyze_xfa_document;
let mut doc = PdfDocument::open("xfa-form.pdf")?;
let analysis = analyze_xfa_document(&mut doc)?;
println!("XFA form detected: {} fields", analysis.fields.len());
for field in &analysis.fields {
println!(" {} ({:?}): {:?}", field.name, field.field_type, field.value);
}
Node.js / TypeScript
El binding para Node.js expone la detección de XFA y un XfaManager de alto nivel para operaciones a nivel de campo cuando el gestor opcional del lado de Node está instalado. Para lógica simple de enrutamiento, la detección es una sola llamada:
const { PdfDocument } = require("pdf-oxide");
const doc = new PdfDocument("government-form.pdf");
if (doc.hasXFA()) {
console.log("XFA form — route to specialized handler");
// AcroForm fallback fields (if any) via doc.getFormFields()
const fallback = doc.getFormFields();
console.log(`AcroForm fallback fields: ${fallback.length}`);
} else {
console.log("Standard AcroForm or no forms");
}
doc.close();
import { PdfDocument } from "pdf-oxide";
const doc = new PdfDocument("government-form.pdf");
if (doc.hasXFA()) {
const fallback = doc.getFormFields();
console.log(`XFA detected; ${fallback.length} AcroForm fallback fields`);
}
doc.close();
Go
El binding para Go expone la detección de XFA. Úsalo para marcar documentos XFA en pipelines y redirigir esos PDFs a un paso en Python o Rust para la extracción completa de campos:
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("government-form.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
if doc.HasXfa() {
fmt.Println("XFA form detected — route to Python/Rust extractor")
} else {
fmt.Println("Standard AcroForm or no forms")
}
}
C#
using PdfOxide;
using var doc = PdfDocument.Open("government-form.pdf");
if (doc.HasXfa)
{
Console.WriteLine("XFA form detected — route to specialized extractor");
}
else
{
Console.WriteLine("Standard AcroForm or no forms");
}
Nota sobre la cobertura de bindings. La detección de XFA (
hasXFA/HasXfa) está disponible en los cinco bindings. La enumeración completa de campos y extracción de valores XFA (nombres, tipos, valores, XML de datasets) está actualmente disponible solo en Python y Rust; los bindings de Node.js, Go y C# ofrecen detección y lectura del fallback AcroForm. En flujos de trabajo que necesiten leer valores de campos XFA desde Go o C#, añade un paso intermedio en Python o Rust.
Por Qué Importa XFA
La mayoría de las bibliotecas PDF para Python ignoran silenciosamente el contenido XFA — extract_text() y las APIs de campos de formulario solo ven la capa de reserva AcroForm (si existe). Muchos formularios exclusivamente XFA no tienen reserva AcroForm, lo que los hace invisibles para otras herramientas:
- Formularios XFA de PyMuPDF (pymupdf) —
get_form_fields()y.widgets()devuelven vacío en PDFs con solo XFA. PyMuPDF no tiene soporte para XFA y no tiene planes de añadirlo. - Soporte XFA de pypdf —
get_form_text_fields()de pypdf no puede leer contenido XFA. Solo son visibles los campos de reserva AcroForm, si es que existen. - pdfplumber — sin soporte para XFA. La extracción de formularios se limita a campos AcroForm.
- pdfminer — sin soporte para XFA. No puede detectar ni extraer datos de formularios XFA.
PDF Oxide es la única biblioteca PDF para Python que lee plantillas XML de XFA directamente, dándote acceso a la estructura del formulario y a los datos que PyMuPDF, pypdf, pdfplumber y pdfminer no pueden ver.
Páginas Relacionadas
- Extracción de Datos de Formularios — API de extracción AcroForm
- Rellenar Formularios PDF — guía de cumplimentación de formularios
- Edición de Campos de Formulario — operaciones avanzadas con formularios