Reflow (primary editor): - Geometric backstop in reflow_paragraph: adopt any text object fully inside the paragraph bbox that the model's objectIndices omitted (PDFium glyph->object map can return -1), so it's deleted + its original font is resolved. Fixes both the leftover-glyph "bulge/merge" and the font-substitution-on-edit (same root cause). No-op on correctly-indexed paragraphs (gate-proven byte-identical: overlay diff unchanged at 1.69%/1.70%). - Preserve data-fid on edited contentEditable nodes (extractFlatRuns climbs to nearest styled ancestor / inherits from adjacent run) so edits keep their real font instead of the dominant. - Converge preview & commit through one buildReflowData (no preview-OK/commit-wrong drift). - Center/right alignment: additive emission branch (greedy path only) + heading-align inference. - IME composition handling (suppress render mid-composition; don't commit on composing Enter). - Fix WASM document-handle leak (free superseded versions on documentId change/unmount). - Quiet the backstop instrumentation (warn -> debug) now the root cause is confirmed. Raw Text / StreamEditor (beta companion): - Permission-gate both text_objects endpoints behind canModify; label tool "(beta)". - P1a: preserve TJ kerning numbers for same-length edits (redistribute into original slots), fall back to single-string collapse otherwise (never worse than before). - P1b: reject edits with characters unencodable in the run's font (clear 400 instead of corruption). - P2a: commit Raw Text edits as a new document version (add_document) and adopt the id via pushHistory, so they join undo/redo instead of mutating in place. - P2b: exact hit-boxes from real font metrics (re-measured once @font-face loads). WASM preview rebuilt with the reflow changes; cache version bumped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PDF Engine
Cross-platform, high-performance, license-safe PDF rendering and editing SDK. A single C++ core (PDFium + Skia + FreeType + HarfBuzz) drives native desktop, server-side, and browser (WASM) rendering, with a FastAPI gateway and a React + TypeScript viewer on top.
Status: Phase 0 — Infrastructure. The stack must compile clean on Linux, macOS, and Windows before any feature work begins. See
docs/phase0.md.
Repository layout
| Path | Purpose |
|---|---|
engine/ |
C++23 PDF SDK core. The only code that links PDFium/Skia/FreeType. |
engine/src/parser/ |
Only place raw FPDF_* PDFium APIs may be used (Rule R2). |
bindings/ |
Language bindings — C ABI (.NET/ctypes) and pybind11 (Python). |
gateway/ |
FastAPI service: auth, metadata, storage, job queue. (placeholder) |
frontend/ |
React + TypeScript layered PDF viewer. (placeholder) |
wasm/ |
Emscripten build of the engine for in-browser rendering. (placeholder) |
third_party/ |
Vendored dependencies built from source (PDFium via depot_tools/GN). |
cmake/ |
Reusable CMake modules (pdfium.cmake, warnings, sanitizers). |
corpus/ |
Test PDF corpus for rendering/regression gates. |
scripts/ |
Bootstrap, lint, and developer tooling. |
docs/ |
Engineering docs and phase plans. |
Prerequisites
- CMake >= 3.25 and Ninja
- A C++23 compiler: MSVC 19.36+ (VS 2022 17.6+ or VS 2026 / Build Tools 2026 — IDE not required), GCC 13+, or Clang 16+
- vcpkg (set
VCPKG_ROOT;scripts/bootstrapcan install it). On Windows,setx VCPKG_ROOT ...does not affect already-open shells — set$env:VCPKG_ROOTin that shell or open a new terminal. - clang-format 22.1.5 (CI is pinned to this exact version) —
pip install clang-format==22.1.5on Windows - Python 3.11+ and Node 20+ (for gateway/frontend, later phases)
- For building PDFium from source: depot_tools (handled by
third_party/pdfium/build_pdfium.*— do not add it to your persistentPATH; itsninja.batshadows real Ninja and breaks the engine build, seedocs/phase0.md)
Quick start
# 1. One-time setup: checks tools, installs vcpkg, pins the dependency baseline.
# Windows: pwsh scripts/bootstrap.ps1
# Unix: ./scripts/bootstrap.sh
# 2. Build PDFium from source (slow, one-time — see third_party/pdfium/README.md).
# Windows: pwsh third_party/pdfium/build_pdfium.ps1
# Unix: ./third_party/pdfium/build_pdfium.sh
# 3. Configure + build + test via CMake presets.
cmake --preset windows-debug # or linux-debug / macos-debug
cmake --build --preset windows-debug
ctest --preset windows-debug
Windows + PDFium: the engine-linked-against-PDFium build is RelWithDebInfo + static CRT (not the Debug preset). Drop the
win-local-pdfiumpreset into a localCMakeUserPresets.jsonand use that — seedocs/phase0.mdfor the exact JSON and the reason (CRT alignment).
OneDrive note: this checkout lives in a OneDrive-synced folder whose path also contains a space. On Windows this is not optional — put your build directory outside OneDrive on a space-free path (e.g.
C:\Users\<you>\pdfeng-build\...), or vcpkg/meson and depot_tools will fail. On Linux/macOS it is just a sync nuisance — excludeout/andvcpkg/from sync, or pause sync while building. Seedocs/phase0.mdfor details.
Engineering rules (non-negotiable)
- R1 PDFium stays the parser core — no custom parser.
- R2 Only
engine/src/parser/may call rawFPDF_*APIs. CI enforces this. - R3 No custom graphics interpreter in Phase 1/2 — use PDFium's renderer.
- R4 Saving is incremental (append-only xref) by default.
- R5 WASM never blocks shipping — server-side render is the fallback.
Dependency versions are pinned (vcpkg baseline + a pinned PDFium ref).
Never track rolling HEAD.