Formulários XFA — Detecte e Leia Dados XML em Python, Rust, Node.js, Go e C#
Detecção e análise de formulários 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) é um formato legado de formulários utilizado por muitos órgãos governamentais, instituições financeiras e sistemas corporativos. A grande maioria das bibliotecas Python para PDF não consegue lidar com formulários XFA. O PDF Oxide detecta, analisa e extrai dados deles com facilidade.
O Que É XFA?
Formulários XFA usam templates baseados em XML incorporados dentro de um PDF, em vez dos campos AcroForm padrão. Foram criados pela Adobe e são comuns em:
- Formulários governamentais — documentos do IRS, imigração, agências estaduais
- Formulários financeiros — solicitações de crédito, sinistros de seguros
- Formulários corporativos — onboarding de RH, compras, conformidade
O XFA foi descontinuado no PDF 2.0 (ISO 32000-2:2020), mas milhões de documentos XFA existentes continuam em circulação.
XFA vs AcroForm
| Característica | AcroForm | XFA |
|---|---|---|
| Formato | Objetos PDF | Templates XML |
| Suportado por | Todas as bibliotecas PDF | Poucas bibliotecas PDF |
| Layouts dinâmicos | Não | Sim |
| Status no PDF 2.0 | Suportado | Descontinuado |
| Fonte típica | A maioria dos criadores de formulários | Adobe LiveCycle, Adobe Designer |
Por Que PyMuPDF e pypdf Não Conseguem Lidar com Formulários XFA
Se você já tentou ler formulários XFA com bibliotecas Python populares para PDF, provavelmente viu resultados vazios sem nenhum erro ou aviso. Isso acontece porque PyMuPDF, pypdf, pdfplumber e pdfminer não têm suporte a XFA.
PyMuPDF (fitz) — retorna vazio silenciosamente
O doc.get_form_fields() e o .widgets() por página do PyMuPDF só leem campos AcroForm. Quando um PDF usa formulários apenas XFA (comum em documentos do IRS, imigração e agências estaduais), o PyMuPDF retorna resultados vazios sem nenhum aviso:
# 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
Se o formulário XFA inclui uma camada de fallback AcroForm, o PyMuPDF pode retornar um subconjunto parcial de campos — mas os dados XFA reais (layouts dinâmicos, valores calculados, subformulários aninhados) permanecem invisíveis.
pypdf — também retorna vazio em formulários XFA
A leitura de campos de formulário no pypdf esbarra na mesma limitação. Ele só consegue acessar campos AcroForm e não tem suporte a 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 e pdfminer — sem nenhum suporte a XFA
pdfplumber e pdfminer não tentam ler campos de formulários XFA. Eles não têm API para detecção ou extração de XFA.
PDF Oxide — lê XFA nativamente
O PDF Oxide parseia os templates XML do XFA diretamente, extraindo todos os campos, valores e a estrutura do formulário:
# 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 com formulários governamentais, documentos do IRS, solicitações de seguro e qualquer outro PDF baseado em XFA — incluindo formulários sem camada de fallback AcroForm.
Instalação
pip install pdf_oxide
Detectando Formulários XFA
Verificar se um PDF contém conteúdo XFA:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("form.pdf")
xfa = doc.analyze_xfa()
if xfa:
print("This PDF uses XFA forms")
print(f" Fields: {len(xfa.fields)}")
print(f" Has template: {xfa.has_template}")
print(f" Has datasets: {xfa.has_datasets}")
else:
print("Standard AcroForm (or no forms)")
WASM
No WASM, você pode detectar formulários XFA e recorrer à leitura de campos AcroForm como fallback:
import { WasmPdfDocument } from "pdf-oxide-wasm";
const doc = new WasmPdfDocument(bytes);
if (doc.hasXfa()) {
console.log("This PDF uses XFA forms");
// Read any AcroForm fallback fields
const fields = doc.getFormFields();
console.log(`AcroForm fallback fields: ${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
Analisando Campos XFA
Obter detalhes de cada campo no formulário 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()
Lendo Dados XFA
Extrair os valores atuais dos campos dos 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)
Processamento em Lote de Formulários XFA
Escanear um diretório para identificar quais PDFs usam 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 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
O binding para Node.js expõe a detecção de XFA e um XfaManager de nível mais alto para operações em nível de campo quando o gerenciador opcional do lado do Node está instalado. Para lógica simples de roteamento, a detecção é uma única chamada:
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
O binding para Go expõe a detecção de XFA. Use-o para sinalizar documentos XFA em pipelines e encaminhar esses PDFs para uma etapa em Python ou Rust para extração completa dos 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 cobertura dos bindings. A detecção de XFA (
hasXFA/HasXfa) está disponível nos cinco bindings. A enumeração completa de campos e extração de valores XFA (nomes, tipos, valores, XML de datasets) está atualmente exposta apenas em Python e Rust; os bindings de Node.js, Go e C# oferecem detecção e leitura do fallback AcroForm. Para fluxos de trabalho que precisam ler valores de campos XFA em Go ou C#, processe via uma etapa Python ou Rust.
Por Que XFA É Importante
A maioria das bibliotecas Python para PDF ignora silenciosamente o conteúdo XFA — extract_text() e as APIs de campos de formulário só enxergam a camada de fallback AcroForm (se ela existir). Muitos formulários exclusivamente XFA não têm fallback AcroForm, tornando-os invisíveis para outras ferramentas:
- Formulários XFA do PyMuPDF (pymupdf) —
get_form_fields()e.widgets()retornam vazio em PDFs com apenas XFA. O PyMuPDF não tem suporte a XFA e não tem planos de adicioná-lo. - Suporte a XFA no pypdf —
get_form_text_fields()do pypdf não consegue ler conteúdo XFA. Apenas os campos de fallback AcroForm ficam visíveis, se existirem. - pdfplumber — sem suporte a XFA. A extração de formulários se limita a campos AcroForm.
- pdfminer — sem suporte a XFA. Não consegue detectar nem extrair dados de formulários XFA.
O PDF Oxide é a única biblioteca Python para PDF que lê templates XML do XFA diretamente, dando acesso à estrutura do formulário e aos dados que PyMuPDF, pypdf, pdfplumber e pdfminer não conseguem ver.
Páginas Relacionadas
- Extração de Dados de Formulários — API de extração AcroForm
- Preencher Formulários PDF — guia de preenchimento de formulários
- Edição de Campos de Formulário — operações avançadas com formulários