XFA-форми — Виявлення та зчитування XML-даних у Python, Rust, Node.js, Go і C#
Виявлення та аналіз 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) — застарілий формат форм, що використовується багатьма державними установами, фінансовими організаціями та корпоративними системами. Більшість Python-бібліотек для PDF не можуть обробляти XFA-форми взагалі. PDF Oxide вміє їх виявляти, аналізувати та витягувати з них дані.
Що таке XFA?
XFA-форми використовують XML-шаблони, вбудовані безпосередньо у PDF, замість стандартних полів AcroForm. Формат розроблений компанією Adobe і поширений у таких сферах:
- Державні форми — документи IRS, імміграційні служби, державні відомства
- Фінансові форми — заявки на кредит, страхові вимоги
- Корпоративні форми — адаптація нових співробітників, закупівлі, відповідність вимогам
XFA був визнаний застарілим у PDF 2.0 (ISO 32000-2:2020), проте мільйони XFA-документів досі перебувають в обігу.
XFA проти AcroForm
| Характеристика | AcroForm | XFA |
|---|---|---|
| Формат | Об’єкти PDF | XML-шаблони |
| Підтримка | Всі PDF-бібліотеки | Одиниці PDF-бібліотек |
| Динамічні макети | Ні | Так |
| Статус у PDF 2.0 | Підтримується | Застарілий |
| Типове джерело | Більшість інструментів для форм | Adobe LiveCycle, Adobe Designer |
Чому PyMuPDF і pypdf не справляються з XFA-формами
Якщо ви пробували читати XFA-форми за допомогою популярних Python-бібліотек для PDF, ви напевне бачили порожні результати — без помилок і без попереджень. Причина проста: PyMuPDF, pypdf, pdfplumber і pdfminer не підтримують XFA.
PyMuPDF (fitz) — мовчки повертає порожній результат
Методи doc.get_form_fields() та .widgets() у PyMuPDF читають виключно поля AcroForm. Коли PDF містить форми лише у форматі XFA (типово для документів IRS, міграційних служб та державних відомств), PyMuPDF повертає порожній результат без жодного попередження:
# PyMuPDF — мовчки пропускає дані XFA
import fitz
doc = fitz.open("government-form.pdf")
fields = doc[0].widgets() # Повертає [] для форм лише з XFA
form_data = doc.get_form_fields() # Повертає {} для форм лише з XFA
Якщо XFA-форма містить резервний шар AcroForm, PyMuPDF може повернути частину полів — але справжні XFA-дані (динамічні макети, обчислені значення, вкладені підформи) залишаться невидимими.
pypdf — також повертає порожнє для XFA-форм
Читання полів форм у pypdf натикається на те саме обмеження. Бібліотека має доступ лише до полів AcroForm і не підтримує XFA:
# pypdf — не може читати вміст XFA
from pypdf import PdfReader
reader = PdfReader("government-form.pdf")
fields = reader.get_form_text_fields() # Повертає {} для форм лише з XFA
pdfplumber і pdfminer — підтримки XFA немає взагалі
pdfplumber і pdfminer не намагаються зчитувати поля з XFA-форм. API для виявлення чи витягування XFA у них відсутні.
PDF Oxide — читає XFA нативно
PDF Oxide безпосередньо парсить XML-шаблони XFA, витягуючи всі поля, значення та структуру форми:
# PDF Oxide — читає XFA нативно
from pdf_oxide import PdfDocument
doc = PdfDocument("government-form.pdf")
xfa = doc.analyze_xfa()
print(f"{len(xfa.fields)} fields found") # Всі поля XFA витягнуто
Це працює з державними формами, документами IRS, страховими заявками та будь-якими іншими PDF на основі XFA — включно з формами без резервного шару AcroForm.
Встановлення
pip install pdf_oxide
Виявлення XFA-форм
Перевірити, чи PDF містить вміст XFA:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("form.pdf")
xfa = doc.analyze_xfa()
if xfa:
print("Цей PDF використовує XFA-форми")
print(f" Поля: {len(xfa.fields)}")
print(f" Є шаблон: {xfa.has_template}")
print(f" Є набори даних: {xfa.has_datasets}")
else:
print("Стандартна AcroForm (або без форм)")
WASM
У WASM можна виявити XFA-форми і перейти до читання полів AcroForm як запасного варіанту:
import { WasmPdfDocument } from "pdf-oxide-wasm";
const doc = new WasmPdfDocument(bytes);
if (doc.hasXfa()) {
console.log("Цей PDF використовує XFA-форми");
// Читання резервних полів AcroForm
const fields = doc.getFormFields();
console.log(`Резервні поля 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
Аналіз полів XFA
Отримати детальну інформацію про кожне поле у 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()
Зчитування даних XFA
Витягти поточні значення полів із наборів даних 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)
Пакетна обробка XFA-форм
Сканування директорії для виявлення PDF, що використовують 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)}")
Rust API
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
Прив’язка до Node.js надає виявлення XFA та, за наявності встановленого опційного менеджера на стороні Node, XfaManager вищого рівня для операцій на рівні полів. Для простої логіки маршрутизації достатньо одного виклику:
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
Прив’язка до Go підтримує виявлення XFA. Використовуйте її, щоб позначати XFA-документи у конвеєрах і перенаправляти їх до кроку Python або Rust для повного витягування полів:
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");
}
Примітка щодо охоплення прив’язок. Виявлення XFA (
hasXFA/HasXfa) доступне у всіх п’яти прив’язках. Повна перелік полів і витягування значень XFA (імена, типи, значення, XML наборів даних) наразі реалізовані лише у Python і Rust; прив’язки Node.js, Go і C# підтримують виявлення та читання резервного AcroForm. Для робочих процесів, яким потрібні значення XFA-полів з Go або C#, використовуйте проміжний крок Python або Rust.
Чому XFA має значення
Більшість Python-бібліотек для PDF мовчки ігнорують вміст XFA — extract_text() та API полів форм бачать лише резервний шар AcroForm (якщо він існує). Багато форм лише з XFA не мають резервного шару AcroForm, що робить їх повністю невидимими для інших інструментів:
- XFA-форми у PyMuPDF (pymupdf) —
get_form_fields()та.widgets()повертають порожнє для PDF лише з XFA. PyMuPDF не підтримує XFA і не планує цього робити. - Підтримка XFA у pypdf —
get_form_text_fields()pypdf не може читати вміст XFA. Видимі лише резервні поля AcroForm — якщо вони взагалі є. - pdfplumber — без підтримки XFA. Витягування форм обмежене полями AcroForm.
- pdfminer — без підтримки XFA. Не може виявляти чи витягувати дані XFA-форм.
PDF Oxide — єдина Python-бібліотека для PDF, яка читає XML-шаблони XFA безпосередньо, надаючи доступ до структури та даних форм, невидимих для PyMuPDF, pypdf, pdfplumber і pdfminer.
Пов’язані сторінки
- Витягування даних форм — API витягування AcroForm
- Заповнення PDF-форм — посібник із заповнення форм
- Редагування полів форм — розширені операції з формами