XFA Flatten

Flatten XFA Forms

Drop an XFA form (the dynamic, LiveCycle-style PDF that shows “Please wait…” in viewers that don’t support it) and get back a plain, static PDF — the form layout rendered into ordinary page content, with no XFA and no AcroForm left behind. Opens anywhere.

Machine
Foxit PDF SDK version

How it works

XFA (XML Forms Architecture) forms store their layout as XML templates rather than fixed PDF content. Dynamic XFA forms are essentially a mini-app the viewer renders on the fly — which is why they break in most readers, mobile, and print pipelines, showing only a grey “please wait” placeholder.

The Foxit PDF SDK loads the XFA document, lays out the template with its data, and calls XFADoc::FlattenTo() to bake that layout into normal PDF page content. A second PDFPage::Flatten() pass removes any leftover form widgets, so the result is a clean, static PDF — no XFA, no AcroForm — that renders identically everywhere. Text stays text; nothing is rasterized.

Drop a regular PDF and the demo will tell you it isn’t an XFA form rather than touching it.

Pick the machine and the SDK version. The identical flattener source is compiled four ways — Kramer (Windows) and Jerry (Linux), each on Foxit PDF SDK 11.0 and 11.1 — so you can hold one axis fixed and vary the other. It’s a controlled 2×2: same code, same input, only the OS or the SDK version changes. Handy for telling whether a difference in the output comes from the SDK release or from the operating environment (fonts, libraries) it runs in.

Sample implementation code

The full pipeline this demo runs — detect the XFA layer, bake it into static page content, then flatten residual widgets — in each SDK language binding. Condensed for clarity.

#include "common/fs_common.h"
#include "pdf/fs_pdfdoc.h"
#include "pdf/fs_pdfpage.h"
#include "addon/xfa/fs_xfa.h"

using namespace foxit;
using namespace foxit::common;
using namespace foxit::pdf;
using namespace foxit::addon::xfa;

// Minimal callback stubs — every method returns a benign default
// (crib them from examples/simple_demo/xfa_form in the SDK package).
class MyAppProvider : public AppProviderCallback { /* ... */ };
class MyDocProvider : public DocProviderCallback { /* ... */ };

int main() {
  Library::Initialize(sn, key);            // key must include the XFA module
  PDFDoc doc(L"input.pdf");
  if (doc.Load() != e_ErrSuccess) return 1;

  // --- 1. Detect XFA: the AcroForm dictionary carries an "XFA" entry ---
  objects::PDFDictionary* catalog = doc.GetCatalog();
  objects::PDFObject* af = catalog ? catalog->GetElement("AcroForm") : NULL;
  objects::PDFDictionary* afd = af ? af->GetDict() : NULL;
  if (!afd || !afd->GetElement("XFA")) return 5;     // not an XFA form

  // --- 2. XFA needs a global app provider + a per-document provider ---
  Library::RegisterXFAAppProviderCallback(new MyAppProvider());
  XFADoc xfa_doc(doc, new MyDocProvider());  // throws e_ErrNoXFAModuleRight if unlicensed
  xfa_doc.StartLoad(NULL);                   // NULL pause -> load to completion

  // --- 3. Stage 1: bake the XFA layout into ordinary static page content ---
  xfa_doc.FlattenTo(L"stage1.pdf");

  // --- 4. Stage 2: flatten residual widgets, then compact save ---
  PDFDoc out(L"stage1.pdf");
  if (out.Load() == e_ErrSuccess) {
    for (int i = 0; i < out.GetPageCount(); ++i) {
      PDFPage page = out.GetPage(i);
      page.StartParse(PDFPage::e_ParsePageNormal, NULL, false);
      page.Flatten(true, PDFPage::e_FlattenAll);     // for_display = true
    }
    out.SaveAs(L"flattened.pdf",
               PDFDoc::e_SaveFlagNoOriginal
             | PDFDoc::e_SaveFlagXRefStream
             | PDFDoc::e_SaveFlagRemoveRedundantObjects);
  }
  Library::Release();
  return 0;
}
using foxit;
using foxit.common;
using foxit.pdf;
using foxit.addon.xfa;

Library.Initialize(sn, key);                 // key must include the XFA module
using var doc = new PDFDoc("input.pdf");
doc.Load(null);

// --- 1. Detect XFA: the AcroForm dictionary carries an "XFA" entry ---
var acroForm = doc.GetCatalog()?.GetElement("AcroForm")?.GetDict();
if (acroForm?.GetElement("XFA") == null) return;     // not an XFA form

// --- 2. Register the global app provider + per-document provider ---
Library.RegisterXFAAppProviderCallback(new MyAppProvider());
using var xfaDoc = new XFADoc(doc, new MyDocProvider());
xfaDoc.StartLoad(null);                      // null pause -> load to completion

// --- 3. Stage 1: bake the XFA layout into static page content ---
xfaDoc.FlattenTo("stage1.pdf");

// --- 4. Stage 2: flatten residual widgets, then compact save ---
using var outDoc = new PDFDoc("stage1.pdf");
outDoc.Load(null);
for (int i = 0; i < outDoc.GetPageCount(); i++) {
    var page = outDoc.GetPage(i);
    page.StartParse(PDFPage.ParseFlags.e_ParsePageNormal, null, false);
    page.Flatten(true, PDFPage.FlattenOptions.e_FlattenAll);
}
outDoc.SaveAs("flattened.pdf",
    (int)(PDFDoc.SaveFlags.e_SaveFlagNoOriginal
        | PDFDoc.SaveFlags.e_SaveFlagXRefStream
        | PDFDoc.SaveFlags.e_SaveFlagRemoveRedundantObjects));
Library.Release();
import com.foxit.sdk.common.Library;
import com.foxit.sdk.pdf.PDFDoc;
import com.foxit.sdk.pdf.PDFPage;
import com.foxit.sdk.pdf.objects.PDFDictionary;
import com.foxit.sdk.pdf.objects.PDFObject;
import com.foxit.sdk.addon.xfa.XFADoc;

Library.initialize(sn, key);                 // key must include the XFA module
PDFDoc doc = new PDFDoc("input.pdf");
doc.load(null);

// --- 1. Detect XFA: the AcroForm dictionary carries an "XFA" entry ---
PDFObject af = doc.getCatalog().getElement("AcroForm");
PDFDictionary acroForm = af != null ? af.getDict() : null;
if (acroForm == null || acroForm.getElement("XFA") == null) return;  // not XFA

// --- 2. Register the global app provider + per-document provider ---
Library.registerXFAAppProviderCallback(new MyAppProvider());
XFADoc xfaDoc = new XFADoc(doc, new MyDocProvider());
xfaDoc.startLoad(null);                      // null pause -> load to completion

// --- 3. Stage 1: bake the XFA layout into static page content ---
xfaDoc.flattenTo("stage1.pdf");

// --- 4. Stage 2: flatten residual widgets, then compact save ---
PDFDoc outDoc = new PDFDoc("stage1.pdf");
outDoc.load(null);
for (int i = 0; i < outDoc.getPageCount(); i++) {
    PDFPage page = outDoc.getPage(i);
    page.startParse(PDFPage.e_ParsePageNormal, null, false);
    page.flatten(true, PDFPage.e_FlattenAll);
}
outDoc.saveAs("flattened.pdf",
    PDFDoc.e_SaveFlagNoOriginal | PDFDoc.e_SaveFlagXRefStream
  | PDFDoc.e_SaveFlagRemoveRedundantObjects);
Library.release();
from FoxitPDFSDKPython3 import *

Library.Initialize(sn, key)                  # key must include the XFA module
doc = PDFDoc("input.pdf")
assert doc.Load("") == e_ErrSuccess

# --- 1. Detect XFA: the AcroForm dictionary carries an "XFA" entry ---
catalog = doc.GetCatalog()
af = catalog.GetElement("AcroForm")
acro_form = af.GetDict() if af else None
if not acro_form or not acro_form.GetElement("XFA"):
    raise SystemExit("not an XFA form")

# --- 2. Register the global app provider + per-document provider ---
Library.RegisterXFAAppProviderCallback(MyAppProvider())
xfa_doc = XFADoc(doc, MyDocProvider())       # raises if the license lacks XFA
xfa_doc.StartLoad(None)                      # None pause -> load to completion

# --- 3. Stage 1: bake the XFA layout into static page content ---
xfa_doc.FlattenTo("stage1.pdf")

# --- 4. Stage 2: flatten residual widgets, then compact save ---
out_doc = PDFDoc("stage1.pdf")
out_doc.Load("")
for i in range(out_doc.GetPageCount()):
    page = out_doc.GetPage(i)
    page.StartParse(PDFPage.e_ParsePageNormal, None, False)
    page.Flatten(True, PDFPage.e_FlattenAll)
out_doc.SaveAs("flattened.pdf",
               PDFDoc.e_SaveFlagNoOriginal | PDFDoc.e_SaveFlagXRefStream
             | PDFDoc.e_SaveFlagRemoveRedundantObjects)
Library.Release()
// Foxit PDF SDK for Node.js (naming follows the Node binding conventions)
const {
  Library, PDFDoc, PDFPage, XFADoc,
} = require('@foxitsoftware/foxit-pdf-sdk-node');

Library.initialize(sn, key);                 // key must include the XFA module
const doc = new PDFDoc('input.pdf');
doc.load(null);

// --- 1. Detect XFA: the AcroForm dictionary carries an "XFA" entry ---
const acroForm = doc.getCatalog()?.getElement('AcroForm')?.getDict();
if (!acroForm || !acroForm.getElement('XFA')) throw new Error('not an XFA form');

// --- 2. Register the global app provider + per-document provider ---
Library.registerXFAAppProviderCallback(new MyAppProvider());
const xfaDoc = new XFADoc(doc, new MyDocProvider());
xfaDoc.startLoad(null);                      // null pause -> load to completion

// --- 3. Stage 1: bake the XFA layout into static page content ---
xfaDoc.flattenTo('stage1.pdf');

// --- 4. Stage 2: flatten residual widgets, then compact save ---
const outDoc = new PDFDoc('stage1.pdf');
outDoc.load(null);
for (let i = 0; i < outDoc.getPageCount(); i++) {
  const page = outDoc.getPage(i);
  page.startParse(PDFPage.e_ParsePageNormal, null, false);
  page.flatten(true, PDFPage.e_FlattenAll);
}
outDoc.saveAs('flattened.pdf',
    PDFDoc.e_SaveFlagNoOriginal
  | PDFDoc.e_SaveFlagXRefStream
  | PDFDoc.e_SaveFlagRemoveRedundantObjects);
Library.release();

Samples are condensed for clarity — production code should check every return value and wrap SDK calls in the binding’s exception handling. Exact class/module names can differ slightly between SDK releases; the C++ tab matches the real source this demo runs (kept alongside the binary as foxit_xfa_flatten.cpp).