XFAフォーム — Python、Rust、Node.js、Go、C#でXMLフォームデータを検出・読み取る
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とは何か
XFAフォームは、標準のAcroFormフィールドではなくXMLベースのテンプレートをPDF内に埋め込む形式です。Adobe社が考案したものであり、以下のような場面で広く使われています。
- 政府フォーム — IRS、移民局、州機関の書類
- 金融フォーム — ローン申請書、保険請求書
- 企業フォーム — 入社手続き、調達、コンプライアンス
XFAはPDF 2.0(ISO 32000-2:2020)で廃止予定となりましたが、既存のXFA文書は今もなお大量に流通しています。
XFAとAcroFormの比較
| 機能 | AcroForm | XFA |
|---|---|---|
| 形式 | PDFオブジェクト | XMLテンプレート |
| 対応ライブラリ | すべてのPDFライブラリ | 一部のみ |
| 動的レイアウト | なし | あり |
| PDF 2.0ステータス | サポート | 廃止予定 |
| 代表的な作成元 | ほとんどのフォーム作成ツール | Adobe LiveCycle、Adobe Designer |
PyMuPDFとpypdfがXFAフォームを処理できない理由
一般的なPython PDFライブラリでXFAフォームを読もうとすると、エラーや警告もなく空の結果が返ってくることがよくあります。これは、PyMuPDF、pypdf、pdfplumber、pdfminerがXFAをサポートしていないためです。
PyMuPDF(fitz)— 何も言わずに空を返す
PyMuPDFのdoc.get_form_fields()とページの.widgets()はAcroFormフィールドしか読みません。PDFがXFA専用フォームの場合(IRS・移民局・州機関の文書によく見られます)、PyMuPDFは何の警告も出さずに空の結果を返します。
# 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
XFAフォームにAcroFormのフォールバックレイヤーが含まれている場合、PyMuPDFはフィールドの一部を返すことがありますが、実際のXFAデータ(動的レイアウト・計算値・ネストされたサブフォーム)は見えません。
pypdf — XFAフォームでも空を返す
pypdfのフォームフィールド読み取りも同じ制限があります。AcroFormフィールドしかアクセスできず、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とpdfminer — XFAサポートなし
pdfplumberとpdfminerはXFAフォームからフォームフィールドを読もうとしません。XFAの検出・抽出用のAPIが存在しないのです。
PDF Oxide — XFAをネイティブに読む
PDF OxideはXFA XMLテンプレートを直接解析し、すべてのフィールド・値・フォーム構造を抽出します。
# 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
AcroFormフォールバックレイヤーのない政府フォーム・IRS文書・保険申請書など、あらゆるXFAベースのPDFに対応しています。
インストール
pip install pdf_oxide
XFAフォームの検出
PDFに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
WASMではXFAフォームを検出し、AcroFormフィールドの読み取りにフォールバックできます。
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
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を提供します。シンプルなルーティングロジックには、検出は1回の呼び出しで済みます。
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文書にフラグを立て、それらのPDFを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)はすべての5つのバインディングで利用可能です。XFA フィールドの列挙と値の抽出(名前・型・値・データセットXML)は現在PythonとRustのみで公開されています。Node.js・Go・C#バインディングは検出とAcroFormフォールバックの読み取りに対応しています。GoやC#からXFAフィールド値を読み取る必要があるワークフローでは、PythonまたはRustのステップを経由してください。
XFAが重要な理由
大半のPython PDFライブラリはXFAコンテンツを静かに無視します。extract_text()とフォームフィールドAPIはAcroFormフォールバックレイヤー(存在する場合)しか見えません。XFA専用のフォームの多くにはAcroFormフォールバックがなく、他のツールからは完全に見えない状態になります。
- PyMuPDF(pymupdf)のXFAフォーム —
get_form_fields()と.widgets()はXFA専用PDFで空を返します。PyMuPDFにはXFAサポートがなく、追加する予定もありません。 - pypdfのXFAサポート — pypdfの
get_form_text_fields()はXFAコンテンツを読めません。AcroFormフォールバックフィールドが存在する場合のみそちらが見えます。 - pdfplumber — XFAサポートなし。フォーム抽出はAcroFormフィールドに限定されます。
- pdfminer — XFAサポートなし。XFAフォームデータの検出・抽出ができません。
PDF OxideはXFA XMLテンプレートを直接読み取る唯一のPython PDFライブラリであり、PyMuPDF・pypdf・pdfplumber・pdfminerでは見えないフォーム構造とデータにアクセスできます。
関連ページ
- フォームデータ抽出 — AcroForm抽出API
- PDFフォームへの入力 — フォーム入力ガイド
- フォームフィールドの編集 — 高度なフォーム操作