The same PDF can look different when opened on different machines: if the font a document references is not installed locally, the viewer substitutes another one. Conversely, when brand standards or legal requirements say an outgoing file may only use specified fonts, you first need to know which fonts the original uses. Common browser-side PDF libraries mostly draw pages; they do not expose font resources.

This article uses Spire.PDF for JavaScript to read and replace the fonts in a PDF: first list the document's fonts with PdfDocument.UsedFonts, then replace the standard and non-embedded TrueType fonts with a new TrueType font via PdfUsedFont.Replace(). Everything runs in the browser; files are read and written through a virtual file system (VFS) and never uploaded to a server.

This article covers two core features:

For installation and project configuration, see Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.


Get the Fonts Used in a PDF

Spire.PDF for JavaScript provides the PdfDocument.UsedFonts property for reading every font used in a document. Each item in the collection is a PdfUsedFont, whose Name, Size, Style, and Type give the font name, size, style, and font type.

The steps are:

  1. Get the Spire.PDF WASM module and confirm it is ready.
  2. Load the sample PDF into the VFS, create a PdfDocument object, and load the file.
  3. Read PdfDocument.UsedFonts and take each PdfUsedFont from the collection.
  4. Put together each font's name, size, style, and type, and write them to a text file.
  5. Read the result file from the VFS and trigger a browser download.

The code for getting the fonts in a PDF is as follows:

function App() {
  const getFonts = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the sample PDF into the VFS
    const inputFileName = 'Font_Sample.pdf';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the document
    const doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // UsedFonts lists the fonts the document currently uses
    const fonts = doc.UsedFonts;

    const styleNames = new Map([
      [pdfModule.PdfFontStyle.Regular.value, 'Regular'],
      [pdfModule.PdfFontStyle.Bold.value, 'Bold'],
      [pdfModule.PdfFontStyle.Italic.value, 'Italic'],
      [pdfModule.PdfFontStyle.Underline.value, 'Underline'],
      [pdfModule.PdfFontStyle.Strikeout.value, 'Strikeout'],
    ]);
    const typeNames = new Map([
      [pdfModule.PdfFontType.Standard.value, 'Standard'],
      [pdfModule.PdfFontType.TrueType.value, 'TrueType'],
      [pdfModule.PdfFontType.TrueTypeEmbedded.value, 'Embedded TrueType'],
    ]);

    // Collect each font's name, size, style, and type
    let report = `Document: ${inputFileName}\r\nFont count: ${fonts.length}\r\n\r\n`;
    for (let i = 0; i < fonts.length; i++) {
      const font = fonts.get(i);
      report += `${i + 1}. ${font.Name}\r\n`;
      report += `   Size: ${font.Size}\r\n`;
      report += `   Style: ${styleNames.get(font.Style.value)}\r\n`;
      report += `   Type: ${typeNames.get(font.Type.value)}\r\n\r\n`;
    }

    // Write the font list into the VFS
    const outputFileName = 'FontList.txt';
    window.dotnetRuntime.Module.FS.writeFile(outputFileName, report);
    doc.Close();

    // Read the result file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'text/plain;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Get the Fonts Used in a PDF</h1>
      <button onClick={getFonts}>
        Get fonts
      </button>
    </div>
  );
}

export default App;

The font list is written to a text file, listing each font's name, size, style, and type:

The font list is written to a text file, listing each font's name, size, style, and type


Replace the Fonts Used in a PDF

Spire.PDF for JavaScript also provides the PdfUsedFont.Replace() method, which swaps one font in a document for another font object. It works only with standard Type1 fonts and non-embedded TrueType fonts; embedded fonts (such as subset-embedded CJK fonts) cannot be replaced and must be skipped.

The steps are:

  1. Get the Spire.PDF WASM module and confirm it is ready.
  2. Load the sample PDF and the replacement font file into the VFS, with the font file placed under /Library/Fonts/.
  3. Create a PdfDocument object, load the PDF, and read PdfDocument.UsedFonts.
  4. Create a PdfTrueTypeFont object, iterate over UsedFonts, skip embedded fonts, and call PdfUsedFont.Replace() on standard and non-embedded TrueType fonts.
  5. Save the result with PdfDocument.SaveToFile() and trigger a browser download.

The code for replacing the fonts in a PDF is as follows:

function App() {
  const replaceFonts = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check whether the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the replacement font and the sample PDF
    await window.spire.FetchFileToVFS('ARIALUNI.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'Font_Sample.pdf';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the document
    const doc = new pdfModule.PdfDocument();
    doc.LoadFromFile(inputFileName);

    // UsedFonts returns the fonts used in the document
    const fonts = doc.UsedFonts;
    const newFont = new pdfModule.PdfTrueTypeFont({
      fontFamily: 'Arial Unicode MS',
      size: 12,
      style: pdfModule.PdfFontStyle.Regular,
      unicode: true
    });

    // Replace one by one: Replace() supports only standard Type1 and non-embedded TrueType fonts
    for (let i = 0; i < fonts.length; i++) {
      const font = fonts.get(i);
      // Embedded fonts cannot be replaced; skip them
      if (font.Type.value === pdfModule.PdfFontType.TrueTypeEmbedded.value) {
        continue;
      }
      font.Replace(newFont);
    }

    // Save the result file
    const outputFileName = 'ReplacedFonts.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Replace the Fonts Used in a PDF</h1>
      <button onClick={replaceFonts}>
        Replace fonts
      </button>
    </div>
  );
}

export default App;

After replacement, the text in the standard font uses the new TrueType font while embedded fonts stay unchanged:

After replacement, the text in the standard font uses the new TrueType font while embedded fonts stay unchanged


FAQ

Why do the text size and style change after replacement

Reason: Replace() takes a complete font object, and replacement applies the new object's own size and style; the original sizes of individual text runs are not preserved.

Solution: Set the target font's size and style to the values you want in the result. For example, to make everything 12 pt regular, pass size: 12 and style: PdfFontStyle.Regular to PdfTrueTypeFont.

Why do Chinese or Japanese characters turn into boxes or question marks after replacement

Reason: The target font has no glyphs for those characters, or PdfTrueTypeFont was created without Unicode enabled.

Solution: Use a font that covers the target language (such as Arial Unicode MS) and pass unicode: true when creating it. The font file must first be loaded into /Library/Fonts/ in the VFS so Spire.PDF can embed it.

Can I replace only one of the fonts

Reason: UsedFonts lists each font in the document, and Replace() acts on a single PdfUsedFont.

Solution: Check Name while iterating and replace only the matching font:

for (let i = 0; i < fonts.length; i++) {
  const font = fonts.get(i);
  if (font.Name === 'ArialMT') {
    font.Replace(newFont);
  }
}

Font names of subset-embedded fonts may carry prefixes such as BCDEEE+, so write the comparison against the Name you actually read from the list.

Why does Replace() report that the font is not a standard Type 1 or non-embedded TrueType font

Reason: PdfUsedFont.Replace() only works on standard Type1 fonts and non-embedded TrueType fonts. CJK fonts in PDFs are often subset-embedded TrueType fonts (UsedFonts reports them as Embedded TrueType), and Spire.PDF cannot replace them directly.

Solution: Check Type while iterating UsedFonts and call Replace() only on standard and non-embedded TrueType fonts; skip the rest, or prepare a document that uses standard or non-embedded fonts first.


Get a Free License

To remove the evaluation message from result documents or lift the feature limits, contact sales for a 30-day temporary license.

When a product manual or conference handout is printed as a booklet, the pages of a multi-page document have to be rearranged onto facing sheets in saddle-stitch order. In desktop software that means dragging pages by hand, which goes wrong easily once the page count grows, and every new document starts over. Hand it to a server, and the file has to leave the user's device first.

This article uses Spire.PDF for JavaScript to create PDF booklets. It loads, imposes, and saves PDFs in the browser via WebAssembly, reading and writing every file through a virtual file system (VFS) with no backend involved.

This article covers two core features:

For installation and project configuration, see Integrate Spire.PDF for JavaScript in a React Project. The examples below assume Spire.PDF is installed and the WebAssembly module is initialized.


Impose a PDF as a Two-Sided Booklet Automatically

Spire.PDF for JavaScript provides the PdfDocument.CreateBooklet() method, which imposes the pages of an existing PDF onto facing pages in saddle-stitch order. The width and height parameters set the sheet size of the finished product; when bothSides is true, the layout is arranged for double-sided printing.

The steps are:

  1. Get the Spire.PDF WASM module and confirm it is ready.
  2. Load the source PDF into the VFS.
  3. Create a PdfDocument object and call CreateBooklet() with a width equal to two A4 pages side by side and the height of one A4 page.
  4. Save the result with SaveToFile().
  5. Read the result file from the VFS and trigger a browser download.

The code for creating a booklet by automatic imposition is as follows:

function App() {
  const createBooklet = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check that the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the source PDF into the VFS
    const inputFileName = 'CMS-Technical-Manual.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Spread size: width is two A4 pages side by side, height is one A4 page
    const width = pdfModule.PdfPageSize.A4().Width * 2;
    const height = pdfModule.PdfPageSize.A4().Height;

    // Create a document and impose the pages in saddle-stitch order
    const doc = new pdfModule.PdfDocument();
    doc.CreateBooklet({ fileName: inputFileName, width: width, height: height, bothSides: true });

    // Save the result file
    const outputFileName = 'Booklet_Auto.pdf';
    doc.SaveToFile(outputFileName);
    doc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Create a Two-Sided Booklet</h1>
      <button onClick={createBooklet}>
        Create Booklet
      </button>
    </div>
  );
}

export default App;

The source pages are imposed onto spreads two at a time in saddle-stitch order:

Source pages imposed onto spreads two at a time in saddle-stitch order


Build Facing Pages in a Custom Page Order

Spire.PDF for JavaScript also provides the PdfPageBase.CreateTemplate() method, which extracts the content of a page into a template that PdfTemplate.Draw() can draw at a given position on a new page. A page-order array decides which two pages land on each sheet; the example puts consecutive pages side by side in reading order.

The steps are:

  1. Get the Spire.PDF WASM module and confirm it is ready.
  2. Load the source PDF into the VFS.
  3. Create a PdfDocument object and load the source document; then create an output document whose width is two A4 pages side by side and whose height is one A4 page.
  4. For each pair in the page order, take a page template with CreateTemplate() and draw it onto the left or right half of the spread with Draw().
  5. Save the result with SaveToFile(), then read it from the VFS and trigger a download.

The code for building facing pages manually is as follows:

function App() {
  const createCustomBooklet = async () => {
    // Get the Spire.PDF WASM module
    const pdfModule = window.wasmModule?.spirepdf;

    // Check that the module is ready
    if (!pdfModule) {
      alert('Spire.PDF is not ready yet');
      return;
    }

    // Load the source PDF into the VFS
    const inputFileName = 'CMS-Technical-Manual.pdf';
    await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}/data/`);

    // Create a PdfDocument object and load the source document
    const srcDoc = new pdfModule.PdfDocument();
    srcDoc.LoadFromFile(inputFileName);
    const pageCount = srcDoc.Pages.Count;
    const a4 = pdfModule.PdfPageSize.A4();

    // Output document: width is two A4 pages side by side, height is one A4 page
    const bookletDoc = new pdfModule.PdfDocument();
    bookletDoc.PageSettings.Margins.All = 0;
    bookletDoc.PageSettings.Width = a4.Width * 2;
    bookletDoc.PageSettings.Height = a4.Height;

    // Two consecutive pages per sheet: 1|2, 3|4, 5|6, ...
    for (let i = 0; i < pageCount; i += 2) {
      const sheet = bookletDoc.Pages.Add();

      // Left half
      const leftTemplate = srcDoc.Pages.get_Item(i).CreateTemplate();
      leftTemplate.Draw({ page: sheet, location: new pdfModule.PointF(0, 0) });

      // Right half; with an odd page count the last sheet leaves the right side blank
      if (i + 1 < pageCount) {
        const rightTemplate = srcDoc.Pages.get_Item(i + 1).CreateTemplate();
        rightTemplate.Draw({ page: sheet, location: new pdfModule.PointF(a4.Width, 0) });
      }
    }

    // Save the result file
    const outputFileName = 'Booklet_CustomOrder.pdf';
    bookletDoc.SaveToFile(outputFileName);
    bookletDoc.Close();
    srcDoc.Close();

    // Read the generated file from the VFS and trigger a download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/pdf' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Custom Page Order Imposition</h1>
      <button onClick={createCustomBooklet}>
        Build Facing Pages
      </button>
    </div>
  );
}

export default App;

Two consecutive pages sit side by side on one sheet; a 6-page document produces 3 spreads:

Two consecutive pages side by side on one sheet; a 6-page document produces 3 spreads


FAQ

What determines the paper size of the booklet

Reason: The width and height passed to CreateBooklet() describe the sheet size of the finished spread, not the size of a single page in the source document. This article uses "A4 width × 2" and "A4 height", which gives an A3-sized spread with one A4 page on each half.

Solution: To print on A4 paper, set the sheet to A4 landscape — width equal to the A4 height, height equal to the A4 width; each half then holds an A5 page, and the source pages are scaled down to half a sheet.

// A4 landscape: one A5 page on each half
const width = pdfModule.PdfPageSize.A4().Height;
const height = pdfModule.PdfPageSize.A4().Width;

What does the bothSides parameter do

Reason: bothSides tells CreateBooklet() whether the result will be printed double-sided. true groups pages into front and back pairs in saddle-stitch order and pads the page count to a multiple of 4; false lays pages out for single-sided printing and adds no blank pages.

Solution: Pass true when you print on a duplex printer and fold and staple the result; pass false for single-sided printing only. The print mode must match the imposed layout, or the page order will not line up.

How is the booklet arranged when the page count is not a multiple of 4

Reason: A saddle-stitch sheet holds four pages (two on each side); when the pages run out, the last sheet has blank positions.

Solution: With automatic imposition, CreateBooklet() pads the required spreads. With manual imposition, the loop checks i + 1 < pageCount before drawing the right half, so an unpaired page simply leaves the right side blank.

In manual imposition the content drifts to one side or runs off the sheet

Reason: PdfTemplate.Draw() draws at the template's original size by default. If a source page is wider than half a sheet, the right-hand page crosses the center line or extends past the edge.

Solution: Make the source page size match half the sheet. In this article the source pages are A4 and the spread is two A4 pages wide, so placing them at x = 0 and x = A4.Width aligns exactly. If the source size differs, use PdfCanvas.DrawTemplate() with size set to the half-sheet size; the template is scaled to fit.

sheet.Canvas.DrawTemplate({
  template: rightTemplate,
  location: new pdfModule.PointF(a4.Width, 0),
  size: new pdfModule.SizeF(a4.Width, a4.Height)
});

Get a Free License

If you want to remove the evaluation message from the result document or lift the feature limits, contact sales for a temporary license valid for 30 days.

When you parse a PDF in Python, you are not merely opening the file and printing its contents to the console. You are extracting information—text, tables, form fields, and metadata—and organizing it into Python data structures such as dictionaries and lists. These structures can then be serialized to JSON for use by downstream programs.

This tutorial shows how to parse PDF in Python using Spire.PDF for Python, a standalone Python PDF parser library that does not require Adobe Acrobat. You will learn to extract text page by page, parse tables into rows and columns, read existing form field values, and retrieve document metadata. You will also combine page-level text and table results into a structured JSON object that downstream programs can process.

Parse PDF documents - read text, tables, metadata, and form fields

Quick Navigation

  1. What Does Parsing a PDF in Python Mean?
  2. Load a PDF and Inspect Its Contents
  3. Parse PDF Text into Structured Python Data
  4. Parse Tables from PDF into Rows and Columns
  5. Extract Form Fields and Document Metadata
  6. Handle Scanned PDFs and Special Cases
  7. Use Parsed PDF Data in Python Workflows
  8. Summary

What Does Parsing a PDF in Python Mean?

Parsing a PDF in Python involves extracting the information you need from a document and organizing it into a form that a program can use. Depending on the PDF and the task, this may mean collecting text by page, extracting table cells into rows and columns, reading form field values, or retrieving document metadata. Depending on the document, you can also combine parsing with other techniques to target specific data fields.

In this tutorial, "reading" refers broadly to accessing PDF contents, while "parsing" focuses on extracting those contents and arranging them into structures for downstream processing. The terms can overlap in PDF development. The practical distinction is in the output:

  • Reading a PDF gives you access to its contents—pages, text, images—so you can view or print what is inside.
  • Parsing a PDF organizes the extracted content into data structures keyed by page number, row and column indices, or field names, so that another program can process the results without human inspection.

Different parsing goals call for different approaches:

What you want to parse Method Section
Text PdfTextExtractor Parse PDF Text into Structured Python Data
Tables PdfTableExtractor Parse Tables from PDF into Rows and Columns
Form fields PdfFormWidget Extract Form Fields and Document Metadata
Scanned pages OCR Handle Scanned PDFs and Special Cases

This guide focuses on parsing text, tables, and form data. If you need to extract images from a PDF, see the existing Python Read PDF tutorial.


Load a PDF and Inspect Its Contents

Before parsing, you need to load the PDF document and understand its basic structure—how many pages it contains and what dimensions each page has.

Install Spire.PDF for Python

pip install spire.pdf

Load and inspect a PDF

from spire.pdf import PdfDocument

doc = PdfDocument()
doc.LoadFromFile("sample.pdf")
print(f"Pages: {doc.Pages.Count}")
for i in range(doc.Pages.Count):
    page = doc.Pages.get_Item(i)
    print(f"Page {i+1}: {page.Size.Width} x {page.Size.Height}")
doc.Close()

This snippet loads a PDF file, prints the total page count, and iterates through every page to report its width and height. The LoadFromFile method accepts a file path string; Pages.Count returns the number of pages; and Pages.get_Item(i) gives you access to an individual page object.

Once the document is loaded, you can pass page objects to the extractors in the following sections.


Parse PDF Text into Structured Python Data

The most common task when you parse a PDF in Python is extracting text. To extract text from PDF in Python, instead of concatenating all text into a single string, the approach below stores each page's text in a dictionary keyed by page number. This structure lets downstream code look up specific pages without splitting strings or tracking offsets.

import json
from spire.pdf import PdfDocument, PdfTextExtractor, PdfTextExtractOptions

doc = PdfDocument()
doc.LoadFromFile("sample.pdf")

# Parse text into a structured dictionary: {page_number: text}
parsed_text = {}
for i in range(doc.Pages.Count):
    page = doc.Pages.get_Item(i)
    extractor = PdfTextExtractor(page)
    options = PdfTextExtractOptions()
    options.IsExtractAllText = True
    parsed_text[i + 1] = extractor.ExtractText(options)

doc.Close()

# Serialize for downstream processing
with open("parsed_text.json", "w", encoding="utf-8") as f:
    json.dump(parsed_text, f, ensure_ascii=False, indent=2)

Here, PdfTextExtractor is created for each page, and PdfTextExtractOptions with IsExtractAllText = True instructs the extractor to retrieve all text on that page. The result is stored in parsed_text as a dictionary mapping page numbers (starting from 1) to their text content. Finally, the dictionary is serialized to JSON.

The PdfTextExtractOptions class also provides an IsSimpleExtraction property, which offers a different line-break handling style. The examples in this tutorial use IsExtractAllText = True for comprehensive text retrieval. Depending on the document, you can also try IsSimpleExtraction and keep the option whose output best matches your content.

PDF text content on the left and the extracted JSON on the right

Note that JSON keys are always strings ("1", "2"), but this does not affect the dictionary's usefulness as input for downstream programs—you can convert keys back to integers with int(k) when needed.

If you need finer control over text extraction—such as extracting text from a specific region, searching with regular expressions, or replacing text—see the text extraction tutorial for those techniques.


Parse Tables from PDF into Rows and Columns

Many PDF documents contain tabular data—invoices, reports, financial statements. The PdfTableExtractor class can identify and extract tables from PDF pages. It returns table objects whose rows, columns, and cell text can be accessed programmatically, following the table's layout including its merged cells. The code below iterates through all pages, extracts every table found, and stores the results as a list of dictionaries.

import json
from spire.pdf import PdfDocument, PdfTableExtractor

doc = PdfDocument()
doc.LoadFromFile("sample.pdf")

extractor = PdfTableExtractor(doc)
all_tables = []

for page_idx in range(doc.Pages.Count):
    tables = extractor.ExtractTable(page_idx) or []
    for table in tables:
        rows = table.GetRowCount()
        cols = table.GetColumnCount()
        table_data = []
        for r in range(rows):
            row = [table.GetText(r, c) for c in range(cols)]
            table_data.append(row)
        all_tables.append({"page": page_idx + 1, "data": table_data})

doc.Close()

# Display results with empty-table safety
for t in all_tables:
    row_count = len(t["data"])
    col_count = len(t["data"][0]) if row_count else 0
    print(f"Page {t['page']}: {row_count} rows x {col_count} cols")

# Serialize for downstream processing
with open("parsed_tables.json", "w", encoding="utf-8") as f:
    json.dump(all_tables, f, ensure_ascii=False, indent=2)

The ExtractTable method returns a list of table objects found on the given page index. The or [] safeguard handles both empty lists and potential None returns. For each table, GetRowCount and GetColumnCount provide the dimensions, and GetText(row, col) retrieves the cell content at the specified position. The code handles the case where a page has no tables gracefully—the loop simply does not execute.

The display loop uses an explicit empty-table safety check (len(t["data"][0]) if row_count else 0) to avoid an IndexError when a table has zero rows.

PDF table on the left and the extracted JSON on the right

If you need to export tables to Excel format rather than JSON, see the PDF to Excel conversion tutorial.


Extract Form Fields and Document Metadata

PDF forms contain interactive fields—text boxes, check boxes, list boxes—that users fill in. Parsing these fields programmatically lets you automate data collection from filled forms. You can also extract PDF document metadata (title, author, subject, keywords) for cataloging and search.

Parse form field values

from spire.pdf import (
    PdfDocument,
    PdfFormWidget,
    PdfTextBoxFieldWidget,
)

doc = PdfDocument()
doc.LoadFromFile("form.pdf")
form = PdfFormWidget(doc.Form)

fields = {}
for i in range(form.FieldsWidget.Count):
    field = form.FieldsWidget.get_Item(i)
    if isinstance(field, PdfTextBoxFieldWidget):
        fields[field.Name] = field.Text

print(fields)
doc.Close()

This code wraps the document's form with PdfFormWidget, then iterates through all form fields. The isinstance check filters for text box fields specifically—PdfTextBoxFieldWidget—and stores each field's name and value in a dictionary.

PDF form fields on the left and the extracted field values on the right

This example reads the values from the text box fields already defined in the form. For a detailed guide on the other field types (check boxes, list boxes, signature fields), see the form field extraction tutorial.

Parse document metadata

from spire.pdf import PdfDocument

doc = PdfDocument()
doc.LoadFromFile("sample.pdf")
info = doc.DocumentInformation
metadata = {
    "title": info.Title, "author": info.Author,
    "subject": info.Subject, "keywords": info.Keywords
}
print(metadata)
doc.Close()

The DocumentInformation property provides access to the PDF's metadata fields. Each field is read as a string and stored in a dictionary for easy access.


Handle Scanned PDFs and Special Cases

Most PDFs parse directly. A few benefit from an extra step or a companion technique, and the table below outlines these situations and points to the relevant guidance:

Situation Context Guidance
Scanned, image-only pages Content is stored as images rather than a text layer Use OCR to read the text → OCR tutorial
Multi-column or richly laid-out pages Reading order follows the PDF's internal text stream Use region extraction to target a specific area → text extraction tutorial
Tables with merged or borderless cells The grid is not drawn with visible lines Use PDF-to-Excel with layout options → PDF to Excel tutorial
Uncommon font encodings Some PDFs embed fonts in less common encodings Choose the extraction options that match the document

For image-only scanned PDFs without a text layer, the content you see is stored as page images rather than extractable text. OCR recognizes the text in those images. The full OCR implementation is covered in the OCR tutorial and is not reproduced here.

When a page uses multiple columns or an irregular layout, region-based extraction lets you target a specific area by coordinates, which helps keep the reading order you expect.


Use Parsed PDF Data in Python Workflows

The previous sections produced separate data structures: parsed_text (a dictionary of page text) and all_tables (a list of table data). In practice, you often need a single unified representation of the document that combines text and tables page by page.

The code below merges the two results into one structure. This example assumes that you have already run the text and table extraction examples above in the same Python session.

import json

# Reuse parsed_text and all_tables from earlier sections
unified = {"pages": []}
for page_num in sorted(parsed_text.keys()):
    page_tables = [t["data"] for t in all_tables if t["page"] == page_num]
    unified["pages"].append({
        "page": page_num,
        "text": parsed_text[page_num],
        "tables": page_tables
    })

with open("parsed_document.json", "w", encoding="utf-8") as f:
    json.dump(unified, f, ensure_ascii=False, indent=2)

PDF document on the left and the unified JSON output on the right

This unified structure is composed by the developer from the text dictionary and the table list produced earlier, joined on page number. Once assembled, the extracted data can be transformed into a pandas DataFrame for analysis, mapped to database columns, or sent to another API endpoint. Note that the unified JSON is a nested structure; converting it to a flat DataFrame requires selecting specific fields or expanding table data.

For this workflow, the practical benefit of parsing is that extracted text and table data are organized into a unified representation that downstream programs can consume directly.


Summary

Parsing a PDF in Python involves more than extracting its text. With Spire.PDF for Python, you can organize page-level text into dictionaries, extract tables into rows and columns, and retrieve existing form field values and document metadata.

These results can be combined into a unified JSON structure for downstream processing, such as data analysis, database storage, or integration with other applications. The approach depends on the PDF's contents: image-only scanned pages are handled with OCR, while multi-column or richly formatted pages can be parsed with the extraction options that suit their layout.

By choosing the appropriate extraction method and organizing the results for your application, you can build a PDF parsing workflow that fits your data-processing needs.

If you need to test Spire.PDF for Python in a real business environment, you can apply for a temporary license.

Page 4 of 371
page 4