DocRaptor yetkin bir üründür. HTML-to-PDF’in gold-standard engine’i olan Prince’i hosted REST API içine sarar; retries, async jobs ve iyi docs sağlar. On yılı aşkın süredir var ve birçok team için “Prince’i kendim çalıştırmak istemiyorum” durumunda obvious choice’tur.
Biz farklı bir tool shape’iyiz. gPdf hiç HTML almaz; structured JSON alır ve Cloudflare edge’de doğrudan PDF render eder. 100K pages/month list-price gap: $5/mo (gPdf Basic) vs $89/mo (DocRaptor Basic), yaklaşık 18×. Bu opening promo değildir. Structural’dır. Bu post neden bu structure’ın o price’ı ürettiğini ve her tool’un gerçekte nereye fit ettiğini açıklar.
İki mimari yan yana
| Layer | DocRaptor (HTML → PDF) | gPdf (JSON → PDF) |
|---|---|---|
| Input | HTML + CSS, Prince extensions for paged media ile | JSON DocumentRequest |
| Renderer | Prince, compiled C++ engine | Custom Rust engine, WebAssembly’a compiled |
| Hosting | DocRaptor centralised servers, US datacentre region | Cloudflare Workers, her CF colo, 300+ cities |
| Cold start | Server-side worker pool | V8 isolate boot, single-digit ms |
| Per-render compute | HTML/CSS üzerinde layout pass, sonra Prince pagination | Direct typesetting, layout interpretation pass yok |
| Per-render p50 | ~250-800 ms wall-clock, network + render | ~3-8 ms, network + render |
| Output determinism | High, Prince mature | Byte-identical, same JSON → same bytes |
Bu iki column’u “general HTML printer” ve “purpose-built document renderer” diye okursanız architectural decision’ı anlamış olursunuz. Geri kalan her şey, latency, cost ve feature lists dahil, o choice’ın downstream sonucudur.
Prince vergisi
Prince iyidir. Ayrıca çoğu invoice/receipt/label workflow’unun ihtiyaç duymadığı işi yapar: user’ın gönderebileceği arbitrary HTML için CSS Paged Media implement etmek; page-break rules, running headers, footnotes, cross-references, generated content boxes.
Bu generality’nin runtime cost’u vardır. Arbitrary HTML document’ı paginate etmek için engine şunları yapmak zorunda:
- HTML’i parse ve validate etmek
- CSS cascade’i parse ve resolve etmek, muhtemelen Prince’in kendi extensions’larıyla
- Render tree build etmek
- Multi-pass layout çalıştırmak, özellikle pages across tables veya balanced columns için
- Pages arasında cross-references resolve etmek
- PDF objects emit etmek
Bu passes’ın çoğu HTML’i input olarak kabul etmenin maliyetidir. Input zaten structured data ise, ki genelde öyledir çünkü invoice HTML’e wrap edilmeden önce bir yerde JSON object olarak vardır, her render’da compute ve latency olarak bu passes’ın bedelini ödersiniz ve output’a value eklemezler.
gPdf layout-interpretation step’ini tamamen atlar. JSON DocumentRequest page layout’u zaten structurally specify eder: { pages: [{ size, elements: [...] }] }. Renderer elements typeset eder, tables/lists’i deterministic şekilde paginate eder ve PDF emit eder. Resolve edilecek CSS cascade yok, compute edilecek float layout yok, cross-reference resolution pass yok.
Sonuç: DocRaptor’da ~300 ms süren aynı single-page invoice gPdf’te ~3 ms sürer. Daha hızlıyız çünkü daha hızlı Prince yazdık değil; daha hızlıyız çünkü Prince’in yaptığı şeylerin çoğunu yapmıyoruz.
”Gerçek olamayacak kadar ucuz” gerçek bir procurement itirazıdır
Bunu doğrudan ele almak gerekir, çünkü her B2B sales call’da karşımıza çıkar.
“$5/mo for 100K renders. DocRaptor $89. Anvil $0.10/PDF, yani aynı volume için $10,000. Sizinle ilgili sorun ne?”
Bunu charge edebilmemizin üç dürüst nedeni var:
1. Browser çalıştırmıyoruz
DocRaptor Prince infrastructure’ını customers arasında amortise eder. gPdf ise bir Cloudflare Worker’ı amortise eder; Workers Bundled’da maliyeti yaklaşık $0.50/million requests. JSON-shaped input ile renderer’ımız yaklaşık 1.5 ms CPU per render alır. Üzerine %50 margin koysanız bile cents-per-thousand-renders range’inde kalırsınız. Aritmetik fiyatın kendisidir.
2. Control plane çalıştırmıyoruz
Async jobs yok, callbacks yok, retry queue yok, document storage yok, preview-link UI yok, multi-tenant database yok. Her render stateless function’a tek round-trip ve dönüşten ibarettir. Bu, çoğu “PDF API” şirketinin bütçelediği tüm ops surface’i kaldırır; aynı surface onların price’ını justify eder.
3. Model zarar edeceğimiz workloads’u kendisi dışarı iter
Document gerçekten HTML-to-PDF istiyorsa, örneğin 60-page legal contract veya complex CSS-Grid report, ilk hour içinde JSON model’dan bounce eder ve DocRaptor’a gidersiniz. Bu workloads için defensive pricing yapmamız gerekmez, çünkü self-route ederler. Yalnızca “structured-data-to-document” workloads’un long-but-narrow tail’i için price etmemiz gerekir; burada per-render cost gerçekten tiny’dir.
Toplamda: $5/100K loss leader değildir; actual cost-of-goods-sold plus margin’dir. Browser ship etmediğinizde underlying compute gerçekten bu kadar ucuz olduğu için bunu sürdürebiliriz.
DocRaptor nerede doğru seçimdir
Self-serving comparison yazmamaya çalışıyoruz. DocRaptor’ın gerçekten kazandığı cases:
- Input tamamen control etmediğiniz HTML ise. User-generated reports, third-party templates, CMS’ten Markdown-to-HTML output. Arbitrary input için JSON mapper yazmak istemezsiniz.
- Prince’in support ettiği CSS Paged Media features’a ihtiyacınız varsa. Running headers/footers per chapter, complex footnote reflow, named-page selectors, generated tables of contents, indexes. gPdf common subset için structured equivalents sunar, ama
@page :leftselectors içinde yaşıyorsanız Prince sizin dostunuzdur. - Content team’iniz HTML/CSS yazar, JSON değil. Non-engineering team’e JSON authoring workflow dayatmayın. Bundan nefret ederler.
- Async + callbacks + document storage as a service. DocRaptor generated PDFs’i store eder ve delivery için signed URLs verir. gPdf strictly stateless’tir; sonucu sizin code’unuz store eder.
Bu buckets’tan birindeyseniz, DocRaptor’da kalın. Doğru tool odur.
gPdf nerede doğru seçimdir
Ters görüntü:
- Inputs zaten structured data: database rows, JSON API payloads, queue messages.
- Latency önemlidir: interactive checkout flows, real-time label printing, on-demand statement generation.
- Tests, audit trails veya e-invoice retention için byte-identical reproducibility önemlidir.
- Birkaç thousand renders/month üstündeki her volume’da cost-sensitive’sinizdir.
- GS1-128, QR, Data Matrix, PDF417, Aztec, MaxiCode gibi barcodes’u sub-millimetre precision ile üretmeniz gerekir. Prince technically SVG barcodes support eder, ama HTML/CSS üzerinden 0.1 mm overall length precision non-trivial’dır.
- Compliance için PDF/A (1b/2b/3b/4) veya Factur-X / ZUGFeRD attachments gerekir.
- JSON-to-PDF pipeline çalıştırabilecekken JSON-to-HTML-to-PDF pipeline çalıştırmak istemezsiniz.
Migration mekaniktir, stratejik değil
Yaygın endişe: “Switch etmek tüm templates’i rewrite etmek demek.” Genelde değil. Çoğu HTML-to-PDF template %20 layout’tur, bu bir kez JSON structure olur, ve %80 data interpolation’dır, renderer ne alırsa alsın aynıdır.
Practical path:
- Migrate etmek için tek document type seçin. Highest-volume olanla başlayın; biggest savings, smallest blast radius.
- HTML template’in data interface’ini, yani interpolate ettiği variables’ı alın ve küçük bir
mapToDocumentRequest(data)function yazın. - Output match edene kadar Playground’da iterate edin.
- Production’da A/B yapın: traffic’in %5’ini iki hafta gPdf’e route edin. PDFs diff edin. Bills compare edin.
- Vibes’a değil data’ya göre roll forward veya roll back yapın.
Teams’in bunu tek sprint’te yapıp sonraki ay PDF bill’inin %90’ını cebine koyduğunu gördük. Workload’unun gerçekten HTML-to-PDF case olduğunu fark edip DocRaptor’da kalan teams de gördük; bu da doğru decision’dı.
TL;DR
| DocRaptor | gPdf | |
|---|---|---|
| Best at | Arbitrary content için HTML → PDF | Structured documents için JSON → PDF |
| Price (100K pages/mo) | $89 | $5 |
| p50 render | 250-800 ms | 3-8 ms |
| Edge-deployed | Hayır, centralised | Evet, 300+ Cloudflare colos |
| Async + storage | Evet, included | Hayır, stateless by design |
| PDF/A + Factur-X | Prince extensions üzerinden | built-in |
Documents’ınız renderer için HTML giydirilmiş structured data ise, var olması gerekmeyen translation step için ödeme yapıyorsunuz. Playground’ı deneyin: invoices’larınızdan birini JSON ile describe edin, browser’da 5 ms altında render edin ve gap’in gut feeling’inizle uyuşup uyuşmadığını görün.