Skip to content

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 pypdfget_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