PDF Flatten

Flatten PDFs

Bake signatures, annotations, and form widgets into the page content stream — so they survive an eSign or any other tool that strips form objects. Text and vectors stay as text and vectors; nothing gets rasterized. Powered by the Foxit PDF SDK (PDFPage::Flatten()).

Machine
Foxit PDF SDK version

How it works

The Foxit PDF SDK runs PDFPage::Flatten() on every page, which rewrites the page content stream to include the rendered appearance of any signature fields, form widgets, and annotations — and then removes those objects from the page. The result looks identical but no longer carries form/widget structure that downstream tools might strip.

Output is saved with a compressed xref stream and unreferenced objects pruned, so size typically stays the same or shrinks. Far better than a TIFF round-trip: text remains searchable, vector graphics stay vector, and signatures keep their original visual fidelity.

Pick the machine and the SDK version. The identical flattener source (foxit_flatten.cpp) is compiled four ways — Kramer (Windows) and Jerry (Linux), each on Foxit PDF SDK 11.0 and 11.1 — a controlled 2×2 for separating SDK-release differences from OS/environment differences. Same code, same input; only the OS or the SDK version changes.

Sample implementation code

The full pipeline this demo runs — parse each page, flatten annotations and form widgets into the content stream, then compact-save — in each SDK language binding. Condensed for clarity.

#include "common/fs_common.h"
#include "pdf/fs_pdfdoc.h"
#include "pdf/fs_pdfpage.h"

using namespace foxit;
using namespace foxit::common;
using namespace foxit::pdf;

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

  // --- Flatten every page ---
  for (int i = 0; i < doc.GetPageCount(); ++i) {
    PDFPage page = doc.GetPage(i);
    page.StartParse(PDFPage::e_ParsePageNormal, NULL, false);
    // for_display=true bakes the displayed appearance (what the user
    // sees, including signed-signature appearances) into the content
    // stream. e_FlattenAll = annotations + form widgets.
    page.Flatten(true, PDFPage::e_FlattenAll);
  }

  // e_SaveFlagNoOriginal: rewrite from current in-memory state
  // e_SaveFlagXRefStream: compressed xref stream
  // e_SaveFlagRemoveRedundantObjects: drop unreferenced objects
  //   (the now-orphaned annotation/widget objects -- that's the size win)
  doc.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;

Library.Initialize(sn, key);
using var doc = new PDFDoc("input.pdf");
doc.Load(null);

// --- Flatten every page ---
for (int i = 0; i < doc.GetPageCount(); i++) {
    var page = doc.GetPage(i);
    page.StartParse(PDFPage.ParseFlags.e_ParsePageNormal, null, false);
    // forDisplay=true bakes the displayed appearance (what the user
    // sees, including signed-signature appearances) into the content
    // stream. e_FlattenAll = annotations + form widgets.
    page.Flatten(true, PDFPage.FlattenOptions.e_FlattenAll);
}

// NoOriginal: rewrite from in-memory state; XRefStream: compressed xref;
// RemoveRedundantObjects prunes the now-orphaned annotation/widget objects.
doc.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;

Library.initialize(sn, key);
PDFDoc doc = new PDFDoc("input.pdf");
doc.load(null);

// --- Flatten every page ---
for (int i = 0; i < doc.getPageCount(); i++) {
    PDFPage page = doc.getPage(i);
    page.startParse(PDFPage.e_ParsePageNormal, null, false);
    // forDisplay=true bakes the displayed appearance (what the user
    // sees, including signed-signature appearances) into the content
    // stream. e_FlattenAll = annotations + form widgets.
    page.flatten(true, PDFPage.e_FlattenAll);
}

// NoOriginal: rewrite from in-memory state; XRefStream: compressed xref;
// RemoveRedundantObjects prunes the now-orphaned annotation/widget objects.
doc.saveAs("flattened.pdf",
    PDFDoc.e_SaveFlagNoOriginal | PDFDoc.e_SaveFlagXRefStream
  | PDFDoc.e_SaveFlagRemoveRedundantObjects);
Library.release();
from FoxitPDFSDKPython3 import *

Library.Initialize(sn, key)
doc = PDFDoc("input.pdf")
assert doc.Load("") == e_ErrSuccess

# --- Flatten every page ---
for i in range(doc.GetPageCount()):
    page = doc.GetPage(i)
    page.StartParse(PDFPage.e_ParsePageNormal, None, False)
    # for_display=True bakes the displayed appearance (what the user
    # sees, including signed-signature appearances) into the content
    # stream. e_FlattenAll = annotations + form widgets.
    page.Flatten(True, PDFPage.e_FlattenAll)

# NoOriginal: rewrite from in-memory state; XRefStream: compressed xref;
# RemoveRedundantObjects prunes the now-orphaned annotation/widget objects.
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 } = require('@foxitsoftware/foxit-pdf-sdk-node');

Library.initialize(sn, key);
const doc = new PDFDoc('input.pdf');
doc.load(null);

// --- Flatten every page ---
for (let i = 0; i < doc.getPageCount(); i++) {
  const page = doc.getPage(i);
  page.startParse(PDFPage.e_ParsePageNormal, null, false);
  // forDisplay=true bakes the displayed appearance (what the user
  // sees, including signed-signature appearances) into the content
  // stream. e_FlattenAll = annotations + form widgets.
  page.flatten(true, PDFPage.e_FlattenAll);
}

// NoOriginal: rewrite from in-memory state; XRefStream: compressed xref;
// RemoveRedundantObjects prunes the now-orphaned annotation/widget objects.
doc.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 each binary as foxit_flatten.cpp in server_11_0/ and server_11_1/).