Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

layout-aware-pdf2json

A command-line tool that extracts structured content — text blocks and tables — from PDF files and exports it as JSON, built on top of MuPDF.

It goes beyond raw text extraction by reconstructing tables (merging split and overlapping cells using MuPDF's GRID layout info) and computing a sensible reading order for the page using a recursive XY-cut algorithm, so multi-column and mixed layouts come out in the right order.

Features

  • Table reconstruction — detects tables via MuPDF's structured text GRID, merges split/overlapping cells, and groups them into logical blocks (including multi-row/column spans).
  • Text block extraction — pulls free-text content alongside tables.
  • Reading order — orders all page items (text blocks + tables) using a recursive XY-cut, correctly handling single-column, multi-column, and asymmetric layouts.
  • JSON output — one JSON file per run, with one entry per page containing blocks and tables, each with bounding boxes and reading order.

Requirements

  • CMake 3.10+

  • A C++17 compiler

  • Prebuilt MuPDF (static and dynamic) libraries, placed under mupdf/lib/ for each supported platform:

    • Ubuntu and RHEL on Linux
    • Windows x64
    • macOS/Darwin

    Not included in this repository — see the note below.

  • MuPDF 1.29.0. This project uses MuPDF's structured-text "Table Hunt" GRID API (fz_stext_grid_info and related types), which isn't present in older releases. If you build against a different MuPDF version, confirm it still has this API first — check with:

    grep -A2 "define FZ_VERSION" mupdf/include/mupdf/fitz/version.h

This project is designed to link against prebuilt MuPDF binaries rather than building MuPDF from source. Those binaries (and the mupdf/include/ headers) are not part of this repo; you'll need to obtain them separately and place them in the mupdf/ tree described under Project layout before cmake --build will succeed. This is deliberate, not an oversight: MuPDF is dual-licensed under AGPL-3.0 and a commercial Artifex license, and this project avoids redistributing Artifex's binaries itself so that each user's use of MuPDF stays governed by whichever license they're actually entitled to use.

The prebuilt mupdfwrapper executables (dist/) aren't tracked in this repo either — they're published on the Releases page instead, one archive per platform, so most users never need to build anything at all.

scripts/fetch-mupdf.sh (Linux/macOS) and scripts/fetch-mupdf.ps1 (Windows) download and build the pinned MuPDF release into the mupdf/ layout above.

Building

Linux (Ubuntu / RHEL)

mkdir build && cd build
cmake .. -DSYSTEM_NAME=UBUNTU   # or -DSYSTEM_NAME=RHEL
cmake --build .

If -DSYSTEM_NAME is omitted, the build script tries to auto-detect your distro from /etc/os-release.

For a release build:

cmake .. -DSYSTEM_NAME=UBUNTU -DCMAKE_BUILD_TYPE=RELEASE

To build with AddressSanitizer:

cmake .. -DSYSTEM_NAME=UBUNTU -DUSE_ASAN=ON

Windows

mkdir build && cd build
cmake .. -DSYSTEM_NAME=WINDOWS
cmake --build . --config Release

Links against the static MuPDF archives under mupdf/lib/windows/ by default (LINK_MUPDF_STATIC defaults to ON); pass -DLINK_MUPDF_STATIC=OFF to link the dynamic mupdf.dll/mupdf.lib pair instead.

macOS (Darwin)

cmake -S . -B build \
  -DCMAKE_BUILD_TYPE=Release \
  -DSYSTEM_NAME=DARWIN

cmake --build build --verbose

Links against the static libmupdf.a under mupdf/lib/darwin/ by default (LINK_MUPDF_STATIC defaults to ON); pass -DLINK_MUPDF_STATIC=OFF to link the dynamic libmupdf.dylib instead.

Usage

./mupdfwrapper <input.pdf> [output.json]
  • input.pdf — path to the PDF to process (required).
  • output.json — path to write the output to (optional, defaults to output.json in the working directory).

While processing, the tool prints PAGE_DONE <n>/<total> to stdout after each page, so it can be used as a progress indicator by a calling process.

Output format

{
  "pages": [
    {
      "page": 1,
      "blocks": [
        {
          "readingOrder": 0,
          "text": "...",
          "bbox": { "x0": 0, "y0": 0, "x1": 0, "y1": 0 }
        }
      ],
      "tables": [
        {
          "readingOrder": 1,
          "bbox": { "x0": 0, "y0": 0, "x1": 0, "y1": 0 },
          "cells": [
            {
              "startRow": 0,
              "endRow": 0,
              "startCol": 0,
              "endCol": 1,
              "text": "..."
            }
          ]
        }
      ]
    }
  ]
}

Project layout

layout-aware-pdf2json/
├── .gitignore
├── CMakeLists.txt
├── COPYING.txt        # AGPL-3.0 license text
├── NOTICE              # third-party attribution (MuPDF/Artifex)
├── CITATION.cff
├── scripts/
│   ├── fetch-mupdf.sh    # fetches + builds MuPDF for Linux/macOS
│   └── fetch-mupdf.ps1   # fetches MuPDF source for Windows (manual build step)
├── src/
│   └── main.cpp          # extraction, table reconstruction, reading order, JSON export
├── dist/                 # gitignored — build output, published via Releases, not committed
│   ├── ubuntu/mupdfwrapper
│   ├── rhel/mupdfwrapper
│   ├── windows/mupdfwrapper.exe
│   └── darwin/mupdfwrapper
└── mupdf/                # gitignored — not redistributed, you must supply this
    ├── include/           # MuPDF headers (same headers for every platform)
    └── lib/
        ├── ubuntu/        # .so and .a you provide for Ubuntu/Debian
        ├── rhel/          # .so and .a you provide for RHEL/CentOS/Rocky/Alma/Fedora
        ├── windows/       # .dll and .lib you provide for Windows
        └── darwin/        # libmupdf.dylib and libmupdf.a you provide for macOS

Everything under mupdf/ above is what CMakeLists.txt expects to find, not something this repo ships — you'll need to drop in MuPDF's own headers and compiled libraries for whichever platform(s) you're building for before cmake --build will succeed. dist/ is local build output: it's where a build lands on your machine, but it isn't committed to git — published builds go out through Releases instead, packaged one archive per platform (e.g. layout-aware-pdf2json-v1.0.0-ubuntu.tar.gz).

References

This project implements (a variant of) the recursive XY-cut algorithm for document layout analysis, based on:

  • G. Nagy and S. Seth, "Hierarchical representation of optically scanned documents," in Proc. 7th International Conference on Pattern Recognition (ICPR), Montreal, 1984, pp. 347–349. DigitalCommons@University of Nebraska-Lincoln

  • J. Ha, R. M. Haralick, and I. T. Phillips, "Recursive X-Y cut using bounding boxes of connected components," in Proc. 3rd International Conference on Document Analysis and Recognition (ICDAR), Montreal, 1995, pp. 952–955. IEEE Xplore

License

Copyright (C) 2026 Isabella Mastroianni

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0) — see COPYING.txt.

It links against and bundles MuPDF, which Artifex licenses under AGPL-3.0 for open-source use (a commercial license is also available from Artifex for proprietary use). Consequently, use of this project, and any modification or network-distributed service built on it, is subject to the terms of the AGPL-3.0. If you need to use this code under different terms, you must obtain a commercial MuPDF license from Artifex.

About

Command-line tool that extracts structured text and tables from PDFs to JSON, with table reconstruction and layout-aware reading order via recursive XY-cut. Built on MuPDF.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages