A .NET 10 console application that compresses PDFs, auto-corrects page orientation, and makes them searchable using OCR. All dependencies use fully permissive open-source licenses (MIT / Apache 2.0).
Input PDF ─► Render ─► Orient ─► Deskew ─► OCR ─► Searchable PDF
(PDFium) (Tesseract (Leptonica (Tesseract (PDFsharp)
OSD) + SkiaSharp) HOCR)
- Renders each page to a high-resolution image via PDFium (Docnet.Core)
- Detects orientation using Tesseract OSD (Orientation and Script Detection)
- Validates rotation by comparing OCR confidence on both the original and rotated image — only rotates when it genuinely improves results (≥ 5% confidence gain)
- Deskews the page by detecting slight scan skew via Leptonica and applying a precise sub-degree rotation with SkiaSharp to straighten text lines
- Runs OCR on the corrected image, extracting word text and bounding boxes via HOCR
- Builds a new PDF with JPEG-compressed page images and an invisible text overlay for full-text search and selection
- .NET 10 SDK or later
cd PDF.OCR
dotnet buildDownload both files from tessdata_fast:
| File | Purpose |
|---|---|
eng.traineddata |
English OCR (or your language of choice) |
osd.traineddata |
Orientation and Script Detection (required for auto-rotation) |
Place them in a tessdata folder next to the executable or the project directory:
PDF.OCR/
tessdata/
eng.traineddata
osd.traineddata
PDF.OCR.csproj
Program.cs
The app searches for the tessdata folder in this order:
TESSDATA_PREFIXenvironment variable- Next to the executable (
bin/Debug/net10.0/) - Current working directory
dotnet run -- <input.pdf> [output.pdf] [options]Or, after publishing:
PDF.OCR <input.pdf> [output.pdf] [options]If no output path is given, the output is saved as <input>_searchable.pdf in the same directory.
| Option | Description | Default |
|---|---|---|
--dpi <value> |
Render resolution for OCR | 300 |
--quality <value> |
JPEG compression quality (1–100) | 75 |
--tessdata <path> |
Parent directory containing tessdata folder |
auto-detected |
--lang <code> |
Tesseract language code | eng |
# Basic — outputs scan_searchable.pdf
dotnet run -- scan.pdf
# Custom output path with lower DPI and compression
dotnet run -- scan.pdf output.pdf --dpi 200 --quality 60
# German language OCR with custom tessdata location
dotnet run -- scan.pdf --lang deu --tessdata /opt/tessdataProcessing 10 page(s) at 300 DPI, JPEG quality 75...
Page 1/10... (kept original, orig 90% vs rot 90%) (deskew -0.5°) (21 words, 88% conf) done.
Page 2/10... (rotated 90°, 50%→95%) (deskew +0.5°) (8 words, 90% conf) done.
Page 3/10... (rotated 90°, 34%→90%) (deskew +0.4°) (103 words, 89% conf) done.
...
Input: 36.9 MB - scan.pdf
Output: 5.0 MB - scan_searchable.pdf
Ratio: 13.6%
Pages are automatically checked for incorrect orientation using Tesseract's Orientation and Script Detection (OSD). To avoid false positives, rotation is only applied when it measurably improves OCR accuracy:
- OSD proposes a rotation (90°, 180°, or 270°)
- Quick OCR is run on both the original and rotated image
- Rotation is applied only if confidence improves by ≥ 5 percentage points
- Otherwise the original orientation is kept
This ensures pages that are already correctly oriented are never accidentally rotated.
After orientation correction, each page is checked for slight scan skew — the small tilt that occurs when a document isn't perfectly aligned on the scanner glass.
- Leptonica (bundled with Tesseract) analyzes the binarized page to detect the skew angle
- If the angle exceeds 0.1°, the color image is rotated by that precise amount using SkiaSharp
- Exposed corners from the rotation are filled with white
- The straightened image is then used for OCR and PDF output
Typical corrections are in the range of ±0.3° to ±1.0° — small enough to be invisible in the original, but enough to noticeably improve text alignment in the output.
| Platform | Status | Notes |
|---|---|---|
| Windows (x64, x86) | ✅ Full support | Works out of the box |
| Linux (x64, ARM, ARM64) | See below | |
| macOS (x64, Apple Silicon) | See below |
No extra setup needed. All native libraries are included via NuGet.
Docnet.Core and SkiaSharp include Linux native binaries. However:
-
Tesseract native library must be installed via system package manager:
# Debian/Ubuntu sudo apt install libtesseract-dev libleptonica-dev # Fedora/RHEL sudo dnf install tesseract-devel leptonica-devel
-
Fonts — PDFsharp needs fonts for the invisible text layer. Install a basic font package:
sudo apt install fonts-liberation # provides Arial-compatible fontsOn Linux, you may also need to set a custom
IFontResolverfor PDFsharp if system fonts aren't found. The current code enablesUseWindowsFontsUnderWindowson Windows only.
Same as Linux: install Tesseract via Homebrew (brew install tesseract) and ensure system fonts are available.
All dependencies use fully permissive open-source licenses — no commercial license required for any use case:
| Library | Version | License | Purpose |
|---|---|---|---|
| Docnet.Core | 2.6.0 | MIT | PDF page rendering via PDFium |
| Tesseract | 5.2.0 | Apache 2.0 | OCR engine (.NET wrapper) |
| PDFsharp | 6.2.4 | MIT | PDF document creation |
| SkiaSharp | 3.119.2 | MIT | Image processing (BGRA → JPEG/PNG) |
License verification: each license was confirmed from the <license type="expression"> field in the package .nuspec metadata.
Program.cs
├── Main() — CLI entry point, argument validation
├── MakeSearchablePdf() — Pipeline orchestrator (open PDF → process pages → save)
├── ProcessPage() — Per-page: render → orient → deskew → OCR → build PDF page
├── DetectRotation() — Tesseract OSD orientation detection
├── QuickOcrConfidence() — Fast OCR pass for rotation validation
├── RotateBitmap() — SkiaSharp 90°/180°/270° rotation
├── DetectSkewAngle() — Leptonica skew angle detection
├── DeskewBitmap() — SkiaSharp sub-degree rotation for straightening
├── DrawTextOverlay() — Invisible text layer (behind image) for searchability
├── ParseHocr() — Extract words + bounding boxes from Tesseract HOCR
├── ParseArguments() — CLI argument parsing
├── ResolveTessDataPath() — Auto-detect tessdata folder location
├── DefaultOutputPath() — Generate output filename (_searchable.pdf)
├── FormatSize() — Human-readable file size formatting
└── PrintUsage() — Help text
Records:
├── AppConfig — Immutable CLI configuration
└── OcrWord — Single word with bounding box from HOCR
- Pages are re-rendered as JPEG images at the specified quality level (default 75%)
- Lower
--qualityvalues produce smaller files but with more compression artifacts - Lower
--dpivalues reduce file size and speed up processing but may reduce OCR accuracy - For best OCR accuracy, keep DPI at 300; for smallest file size, try
--dpi 150 --quality 50 - Typical compression: 80–90% size reduction on scanned PDFs