Edit PDFs in the browser. Convert to 12+ formats. Zero external dependencies for the core engine.
Quick Start Β· Architecture Β· Engine Deep Dive Β· Contributing
Bloom is a full-featured PDF editor built with Next.js and React, powered by a custom-written, dependency-free TypeScript PDF engine. Unlike other PDF tools that wrap native C/C++ libraries or call external APIs, Bloom parses, renders, edits, and serializes PDFs using pure TypeScript β from the binary lexer all the way to the Canvas renderer.
The project has two major subsystems:
| Subsystem | Location | Purpose |
|---|---|---|
| Client Engine | src/engine/ |
PDF parsing, rendering, editing, signatures, security, forms, OCR layout, and more β runs in the browser |
| Document Engine | server/src/engines/ |
Document intelligence pipeline β structure-preserving conversion to DOCX, XLSX, PPTX, HTML, Markdown, EPUB, and 6 more formats. Runs in the editor's Web Worker, or as an optional standalone server |
π§ Export Engine Status: The export/conversion pipeline is currently in progress. The full pipeline (Parse β Layout β IDM β Semantic β Tables β Graphics β Structure β UDM β Export) is architecturally complete, and all 12 exporter plugins are wired up. Active work continues on improving conversion fidelity and edge-case handling.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser (Client) β
β β
β ββββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββββββ
β β React UI β β Zustand β β Client PDF Engine ββ
β β (Radix + ββββ Store ββββ (src/engine/) ββ
β β Fabric.js) β β β β ββ
β ββββββββββββββββ ββββββββββββββββ β Parser β Renderer ββ
β β Editor β Writer ββ
β β Signatures β Securityββ
β β Forms β Flow β Bloom ββ
β βββββββββββββββββββββββββββ
β β β
β Web Worker (in the tab) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Bloom engine (server/dist) β no network hop β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β Bloom Document Intelligence Engine ββ
β β ββ
β β Parse β OCR β Layout β IDM β Typography β Semantic ββ
β β β Tables β Graphics β Structure β UDM β Export ββ
β β ββ
β β Priority Queue (high/normal/low) β Retry + Dead Letter ββ
β β Telemetry β Storage β Job Manager β DI Container ββ
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The editor is a fully static site and the document engine runs in a Web Worker inside the browser tab β the PDF never leaves the device, and conversion needs no server, no API key and no second terminal. The same engine package can also run as an optional standalone HTTP server (npm run server:dev on :8787) for Docker/ops deployments.
Bloom's core differentiator is that the entire PDF engine is hand-written in TypeScript. Here's what that means:
The client engine handles everything from raw binary parsing to pixel-perfect Canvas rendering:
| Module | What It Does | Key Data Structures |
|---|---|---|
Parser (parser/) |
Binary lexer tokenizes raw PDF bytes β cross-reference table resolution β object graph construction | PDFDict, PDFArray, PDFStream, PDFRef, XRefTable, PDFDocumentData (all custom classes, no wrappers) |
Content Interpreter (content/) |
Walks PDF content streams, executing operators (Tj, TJ, cm, re, m, lβ¦) to produce positioned text runs, path items, and images | TextRun, GlyphPosition, PathItem, GraphicsState, Matrix (6-element affine transform array) |
Font System (fonts/) |
Parses TrueType/OpenType binary tables, CMap encodings, GSUB ligature substitution, and GPOS pair kerning | TTFFont, FontData, CMapData, LigatureRule, GPOSPairAdjustment |
Renderer (render/) |
Full Canvas 2D rendering pipeline β color spaces (DeviceRGB, DeviceCMYK, ICCBased), transparency, soft masks, tiling patterns, shading, clipping paths, Type 3 fonts | GraphicsStateStack, ColorSpace, TilingPattern, SoftMaskInfo, Shading |
Flow Engine (flow/) |
Line reconstruction β paragraph detection β Knuth-Plass line breaking β BiDi text reordering β justification β grapheme cluster segmentation | TextLine, Paragraph, StyledSegment, DocumentFlow, ShapedGlyph, DetectedTable |
Bloom Model (bloom/) |
Word-processor-like document model β ingests parsed PDF pages into editable blocks with runs, layout, and hit-testing | BloomBlock, BloomRun, BloomPage, BloomDocument, BloomCaret |
Editor (editor/) |
Text editing, image insertion, object transforms, redaction, highlights, annotations (9 types), links, invisible text layers | TextEdit, Annotation (union of 10 subtypes), EditableObject, Affine |
Writer (writer/) |
Incremental PDF save (append-only updates), full serialization, page operations (insert, delete, reorder, rotate, extract) | IncrementalUpdateSession, RevisionChain, OffsetManager |
Signatures (signatures/) |
Complete digital signature engine β visual signatures (draw/type/upload), signature fields, DER/CMS parsing, PKCS#12 import, timestamp authority, LTV, DSS, ByteRange finalization | VisualSignature, CMSSignedData, CertificateInfo, SignatureField, SignatureLibrary |
Security (security/) |
Full PDF encryption/decryption engine β RC4, AES, password auth, permissions, public-key encryption, metadata stripping, JavaScript scanning, redaction verification, integrity scanning | EncryptionContext, PdfPermissions, SecurityPolicy, FullSecurityReport |
Forms (forms/) |
AcroForm parsing, widget detection, field value setting, appearance stream generation, checkbox/radio/choice support, calculation order | AcroFormField, AcroFormWidget, FlattenFieldResult |
Editing Infrastructure (editing/) |
QuadTree spatial index, undo/redo transaction stack, scene graph, affine transforms, snap-to-guide alignment | QuadTree, TransactionStack, EditorHistory, SceneGraph, SnapGuide |
Color (color/) |
ICC profile parsing, LUT-based color transforms, DeviceCMYKβRGB conversion | ICCProfile, ICCLutInfo, Mft2Table |
Watermark (watermark/) |
Text/image/pattern watermark creation, detection, and removal | Watermark, DetectedWatermark, RemovalResult |
AI (ai/) |
Document chunking, semantic search index (TF-IDF), text diff/compare | DocumentChunk, SemanticSearchIndex, DocumentCompareResult |
Optimize (optimize/) |
Garbage collection of unreferenced objects, stream deduplication, image recompression | GarbageCollectResult, DeduplicateResult, ReachabilityResult |
OCR (ocr/) |
Page layout analysis β projection profiles, region detection, deskew angle computation | LayoutRegion, PageLayout, ProjectionProfile |
Accessibility (accessibility/) |
Scaffolded for tagged PDF structure | β |
The conversion pipeline transforms PDF binary data into editable document formats through a 12-stage pipeline:
PDF bytes
β
βΌ
ββββββββββββββββββββ
β 1. ParserEngine β PDF β RawDocument + ObjectGraph + PageSpatialIndex
β β Binary lexer, object resolver, content extractor
β β Data: Map<string, GraphNode> (directed object graph)
β β PageSpatialIndex (grid-based spatial lookup)
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 2. OCR/Fusion β Pluggable OCR provider + recognition fusion
β β Merges OCR text with parser-extracted text
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 3. LayoutEngine β RawDocument β LayoutDocument
β β Normalize β SpatialIndex β Cluster β Whitespace
β β β XY-Cut Segment β Classify β Reading Order
β β Data: LayoutRegion[], ReadingOrderGraph
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 4. IDM Engine β Layout β IntermediateDocument
β β Canonical, format-independent document tree
β β Section β Page β Block β Run β Word β Character
β β Data: IntermediateDocument (immutable tree + nodeIndex)
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 5. Typography β Style profiling and clustering
β β Font/size/weight clustering β style graph
β β Data: TypographyAnalysis { profiles[], graph }
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 6. Semantic β Heading detection, list detection, quote detection
β β Produces semantic node graph + reading order
β β Data: SemanticDocument { nodes{}, readingOrder[] }
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 7. Table Engine β Bordered + borderless table detection
β β Grid reconstruction β cell merging β header detection
β β Data: LogicalTable { rows[], columns[], cells[] }
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 8. Graphics β Image/vector/chart reconstruction
β β Caption association, text wrapping analysis
β β Data: GraphicsModel { objects[], resources }
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 9. Structure β Headers/footers, TOC, bookmarks, footnotes,
β β page numbers, sections, hyperlinks
β β Data: DocumentStructureModel
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 10. UDM Assembly β Merges IDM + Semantic + Tables + Graphics +
β β Structure + Recognition + Typography
β β into UnifiedDocumentModel
β β β Sole input to all exporters
ββββββββββ¬ββββββββββ
βΌ
ββββββββββββββββββββ
β 11. Export β Plugin SDK β each format is an IExportPlugin
β (In Progress) β DOCX β XLSX β PPTX β HTML β Markdown β EPUB
β β RTF β ODT β TXT β JSON β XML β SVG
ββββββββββββββββββββ
Architecture rule: Exporters depend only on the UDM β they never touch the PDF parser, raw model, or layout objects. This clean separation means adding a new export format requires zero knowledge of PDF internals.
| Structure | Module | Implementation |
|---|---|---|
| ObjectGraph | Server Parser | Directed graph (Map<string, GraphNode>) linking every extracted PDF object with parent/child/sibling relationships and spatial metadata |
| PageSpatialIndex | Server Parser | Grid-based spatial index (configurable cell size, default 72pt) for O(1) nearest-neighbor and rectangle queries |
| QuadTree | Client Editing | Recursive quadrant subdivision for spatial hit-testing of display items on the canvas |
| XRefTable | Client Parser | Cross-reference table (Map<string, XRefEntry>) mapping object numbers to byte offsets, supporting both cross-reference tables and cross-reference streams |
| TransactionStack | Client Editing | Undo/redo stack storing immutable snapshots of document state for each editing operation |
| ReadingOrderGraph | Server Layout | Directed acyclic graph of region reading order with weighted edges and topological sort |
| IntermediateDocument | Server IDM | Immutable tree (Section β Page β Block β Run β Word β Character) with flat nodeIndex for O(1) lookup by ID |
| UnifiedDocumentModel | Server UDM | Composite model merging IDM + semantic + tables + graphics + structure + recognition + typography β the single source of truth for all exporters |
| InMemoryJobQueue | Server Queue | Priority queue with three lanes (high/normal/low), automatic retry with exponential backoff, and dead-letter queue for failed jobs |
- Node.js β₯ 18
- npm β₯ 9
# Clone the repository
git clone https://github.com/stemlen/bloom-pdf-editor.git
cd bloom-pdf-editor
# Install dependencies
npm install
npm --prefix server install # Server engine dependencies (one-time)
# Start development server
npm run dev # Builds engine β starts Next.jsOpen http://localhost:3000 β that's it. No API keys, no second terminal, no external services.
Export panel β src/lib/bloom-api.ts β Web Worker (src/lib/bloom-engine/bloom.worker.ts) β server/dist (compiled engine)
The compiled engine is bundled into a Web Worker, so document conversion runs entirely in the browser. It has no static Node.js imports: zlib is handed in at runtime (provideZlib β pako in the browser, node:zlib on the server) and the disk storage backend looks up node:fs only when used.
- Upload a PDF in the editor
- Toolbar β Export β Document Convert
- Pick a target format β Convert β Download
Client-side exports (PNG, JPEG, SVG render, plain text) are rendered directly by the editor engine. OCR downloads the tesseract.js worker and English language data from the jsDelivr CDN on first use; the optional RFC 3161 timestamp when digitally signing calls public timestamp servers, which browsers may block (signing then continues without a timestamp).
npm test # Client engine tests (Vitest)
npm run server:test # Server engine tests
# End-to-end tests (Playwright) against a production build
npx playwright install chromium # once
npm run fixtures # regenerate tests/fixtures (sample.pdf, form.pdf, small.png)
npm run build # static export β out/
npm run test:e2e # serves out/ on :4174 and runs tests/*.spec.tsThe end-to-end suite covers every editor feature β text editing, pages, annotations, images, forms, watermarks, signatures (visual and cryptographic), security, find & replace, OCR, exports and all 12 document conversions β plus light / dark / mobile smoke tests that fail on console errors or horizontal overflow. OCR needs network access to the jsDelivr CDN.
npm run build produces a static site in out/ (Next.js output: 'export'). wrangler.json serves that folder as Workers static assets, so no Worker script is needed:
npm run deploy # build + wrangler deploy
npm run preview # build + serve out/ locally through wrangler devFor Git-connected Workers Builds use build command npm run build and deploy command npx wrangler deploy. public/_headers (copied to out/_headers) sets the security headers and long-lived caching for /_next/static/*.
cd server
docker build -t bloom-engine .
docker run --rm -p 8787:8787 bloom-enginebloom-pdf-editor/
βββ src/
β βββ app/ # Next.js app router (pages, layouts)
β βββ engine/ # β
Client-side PDF engine (pure TypeScript)
β β βββ parser/ # Binary PDF lexer + parser
β β βββ content/ # Content stream interpreter
β β βββ fonts/ # TrueType/CMap/GSUB/GPOS
β β βββ render/ # Canvas 2D renderer
β β βββ flow/ # Line/paragraph/Knuth-Plass
β β βββ bloom/ # Word-processor document model
β β βββ editor/ # Text/image/object editing
β β βββ writer/ # PDF serialization + incremental save
β β βββ signatures/ # Digital signature engine
β β βββ security/ # Encryption/decryption/permissions
β β βββ forms/ # AcroForm support
β β βββ editing/ # QuadTree, undo/redo, scene graph
β β βββ color/ # ICC profiles, color transforms
β β βββ image/ # Image decoding
β β βββ watermark/ # Watermark create/detect/remove
β β βββ ai/ # Semantic search, text compare
β β βββ optimize/ # GC, dedup, image compression
β β βββ ocr/ # Layout analysis
β β βββ accessibility/ # Tagged PDF (scaffolded)
β β βββ qa/ # Quality assurance tests
β β βββ index.ts # Public API (730+ exports)
β β βββ types.ts # Core PDF object types
β βββ components/ # Shared art (logo, doodles, illustrations) + layout bits from bloompdf.app
β βββ lib/ # Shared utilities
β βββ bloom-engine/ # Web Worker that runs the document engine in the browser
βββ tests/ # Playwright end-to-end tests + fixtures
βββ server/
β βββ src/
β βββ engines/ # β
Document intelligence engines (browser worker or server)
β β βββ parser/ # PDF β RawDocument + ObjectGraph
β β βββ layout/ # Layout analysis (XY-Cut, clustering)
β β βββ ocr/ # Recognition fusion
β β βββ idm/ # Intermediate Document Model
β β βββ typography/ # Style profiling
β β βββ semantic/ # Semantic structure detection
β β βββ table/ # Table detection + reconstruction
β β βββ graphics/ # Graphics reconstruction
β β βββ structure/ # Document structure (TOC, headersβ¦)
β β βββ udm/ # Unified Document Model assembly
β β βββ exporter/ # 12 format exporters + plugin SDK
β β βββ common/ # Shared geometry + interfaces
β βββ api/ # HTTP API (Hono)
β βββ workers/ # Conversion pipeline worker
β βββ queues/ # Priority queue + retry + DLQ
β βββ jobs/ # Job lifecycle management
β βββ storage/ # Local filesystem / in-memory storage
β βββ cache/ # Cache layer
β βββ telemetry/ # Observability + metrics
β βββ container.ts # Dependency injection container
βββ docs/
β βββ ENGINE.md # Deep dive into every engine
βββ CONTRIBUTING.md # How to contribute
βββ CODE_OF_CONDUCT.md # Community guidelines
βββ LICENSE # Apache License 2.0
When running the standalone server (npm run server:dev):
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness + queue depth + supported formats |
GET |
/metrics |
Queue stats + telemetry averages |
POST |
/convert |
Upload PDF (multipart file + target, or raw body + x-target header) |
POST |
/batch |
JSON { items: [{ filename, target, contentBase64 }] } β batch job IDs |
GET |
/jobs/:id |
Job status + progress |
GET |
/download/:id |
Download converted file |
DELETE |
/jobs/:id |
Cancel a pending job |
Optional headers: x-api-key (when BLOOM_API_KEY is set), x-correlation-id, x-priority (high | normal | low).
# Convert a PDF to HTML
curl -F file=@document.pdf -F target=html http://localhost:8787/convert
# Check job status
curl http://localhost:8787/jobs/<id>
# Health check
curl http://localhost:8787/health- Pure TypeScript PDF parser (binary lexer, xref, object graph)
- Full Canvas 2D renderer (color spaces, transparency, patterns, shading)
- Text editing with Knuth-Plass line breaking and BiDi support
- Complete digital signature engine (visual, cryptographic, LTV)
- PDF encryption/decryption (RC4, AES, public-key)
- AcroForm support (text, checkbox, radio, choice, calculation order)
- Incremental save + full serialization
- Server conversion pipeline (12-stage)
- 12 export format plugins (DOCX, XLSX, PPTX, HTML, MD, EPUB, RTF, ODT, TXT, JSON, XML, SVG)
- Priority job queue with retry + dead-letter
- Docker support
- π§ Improve export fidelity across all formats
- Accessibility β tagged PDF structure
- Plugin marketplace for third-party exporters
- Collaborative editing
- Mobile-optimized UI
We'd love your help! Whether it's fixing a bug, improving docs, or building a new exporter plugin β all contributions are welcome.
See CONTRIBUTING.md for the full guide, including:
- Setting up the development environment
- Code style and conventions
- How to write an exporter plugin
- How to submit a pull request
This project is licensed under the Apache License 2.0 β see the LICENSE file for details.
Built with β€οΈ by the Bloom community