Typing at a run boundary (e.g. near "Async" in a bullet) often lands the new text in a browser-created wrapper element whose only child is that text. resolveStyleEl's sibling-only search then found no data-fid run and fell back to dominantFid — which for a bullet is the bold LABEL run (its first run) — so the inserted text rendered bold. Make resolveStyleEl CLIMB toward the editor root and scan siblings at each level (nearest preceding run first, then following), so it reaches the real run spans alongside the wrapper and the inserted text continues the adjacent run's font. data-advances is still read only from the exact wrapping span, so inherited fonts correctly re-measure. Text typed inside an existing run is unaffected (step 1 returns its span immediately). 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.