MRC PDF compression — a self-contained CLI that shrinks any PDF while keeping it visually lossless and fully functional. Output preserves the original text (as a searchable invisible layer), bookmarks, comments, links, and embedded attachments.
Input: Any PDF
Output: MRC-compressed PDF (Mixed Raster Content) — typically 3–10× smaller on scanned documents
git clone https://github.com/msmarkgu/mrcpdf.git
cd mrcpdf
./bootstrap.sh # installs JDK 21, Gradle, jbig2enc, CJK font (~120 MB)
./run.sh input.pdf -o output.pdfA pure-MRC PDF compressor split out of the TrulyFreeOCR project (OCR removed — no Tesseract, no PaddleOCR, no GPU, no cloud):
- Visually lossless — MRC splits each page into a background JPEG (downsampled + smoothed) and a razor-sharp JBIG2/CCITT foreground mask, so text stays pixel-crisp while the background compresses heavily.
- Preserves content — existing text is re-embedded as an invisible searchable layer; bookmarks, annotations, and embedded attachments are deep-copied into the output.
- Business-friendly license — Apache 2.0, no disclosure obligations. All runtime dependencies are permissively licensed.
- Self-contained — single fat JAR +
bootstrap.sh/bootstrap.bat. JDK, Gradle, and the JBIG2 native binary are all project-local. No sudo, no Python, no system deps. Copy the folder and it runs anywhere. - No cloud / no GPU — CPU-only, fully offline, zero data leaves the machine.
Prerequisites: git, curl, tar, unzip, and bash (or a terminal emulator on Windows).
No admin rights needed — every dependency is downloaded into project subdirectories and stays there. The bootstrap script works on Linux, macOS, and Windows.
-
Clone the repository:
git clone https://github.com/msmarkgu/mrcpdf.git cd mrcpdf -
Run the bootstrap script for your platform:
# Linux / macOS ./bootstrap.sh # Windows (PowerShell or Command Prompt) bootstrap.bat
What gets installed (4 components, all under deps/)
- OpenJDK 21 LTS →
deps/jdk/ - Gradle 8.0.1 →
deps/gradle/ - jbig2enc for JBIG2 foreground compression →
deps/jbig2enc/$OS/ - Noto Sans SC CJK font (SIL OFL 1.1) →
deps/fonts/
./run.sh input.pdf -o output.pdfBuild the fat JAR manually (not needed if you use run.sh):
./gradlew build # produces build/mrcpdf.jar (skips tests for speed)
./deps/jdk/bin/java -jar build/mrcpdf.jar input.pdf -o output.pdf| Option | Description |
|---|---|
input.pdf |
Input PDF file (positional). |
-o, --output <file> |
Output PDF path (default output.pdf). |
--dpi <n> |
Rendering DPI for page images (default 300). |
--bg-scale <0.1-1.0> |
Background downscale factor (default 0.33). |
--jpeg-quality <0.1-1.0> |
Background JPEG quality when a mask is present (default 0.50). |
--pdfa |
Enable PDF/A-2b output (XMP metadata, sRGB OutputIntent). |
--threads <n> |
Worker threads for page prep (default: available processors). |
--settings <file> |
Path to settings.jsonc (default ./settings.jsonc). |
The jbig2enc binary and CJK font bundled in deps/ are used automatically; all defaults are configurable via settings.jsonc, and CLI flags take precedence.
Each page is re-encoded as three layers:
- Background — the cleaned page image: downsampled (3× by default), mildly blurred, and JPEG-compressed at 4:2:0 chroma subsampling. Text regions are inpainted away so the JPEG carries almost no text detail.
- Foreground mask — a binary stencil of the text pixels, compressed with JBIG2 (shared symbol dictionary across all pages) or CCITT G4 fallback. Drawn as a PDF ImageMask in black, it restores text at full resolution.
- Text layer — the source PDF's existing text, re-positioned word-by-word as invisible (
RenderingMode.NEITHER) searchable text.
Because the mask supplies the sharp text and the background supplies the smooth content, file size drops dramatically with no visible quality loss.
Note: mrcpdf re-embeds text extracted from the source PDF. If the source has no text layer (e.g. a raw scan), pages are still MRC-compressed but carry no invisible text — there is no OCR.
| Element | Status |
|---|---|
| Document info (title, author, subject, keywords, creator, producer) | ✅ Preserved |
| Bookmarks / outlines | ✅ Preserved (deep copy) |
| Annotations (comments, underlines, …) | ✅ Preserved (deep copy) |
| Embedded attachments | ✅ Preserved (deep copy) |
| XMP metadata | ✅ Preserved |
| Page size / orientation | ✅ Preserved (per crop box) |
- The invisible text layer is re-rendered with a single font (the Standard 14 font, or a TTF/OTF configured via
pdf.fontPathin settings.jsonc). Original font family, size styling, and color are not reproduced in the hidden layer — the visual layer (background + mask) is unaffected. - Annotation destinations and named destinations are kept but not remapped to the new document; page-number-based links (bookmarks pointing to pages in order) generally still work.
./gradlew build # compile + build build/mrcpdf.jar (skips tests)
./gradlew test # run tests only
./gradlew generateTestPdfs # regenerate the tests/*.pdf fixtures- Source:
src/main/java/com/mrcpdf/ - Tests:
src/test/java/com/mrcpdf/(JUnit 5; requires Java 21) - Performance/compression analysis:
docs/MRC-compression-effect.md,docs/report-mrc.md - Design notes:
docs/mrc-only-plan.md,docs/bookmarks-hyperlinks-attachments.md - Test runs (sample CLI output, compression results):
docs/test-runs.md
./scripts/benchmark_mrc.sh # all real-world PDFs
./scripts/benchmark_mrc.sh simple-text.pdf # a single fileLicensed under the Apache License, Version 2.0. See LICENSE and NOTICE.md for third-party attribution.
Runtime dependencies: Apache PDFBox (Apache 2.0), picocli (Apache 2.0), jbig2enc (Apache 2.0), jai-imageio-jpeg2000, OpenJDK 21 (GPLv2 + Classpath Exception), Noto Sans SC font (SIL OFL 1.1).