Блог

gPdf vs DocRaptor: чому рендеринг на edge перемагає HTML-to-PDF

DocRaptor використовує Prince для HTML-to-PDF на хостинговому бекенді. gPdf рендерить структурований JSON прямо на Cloudflare edge. Різниця ціни 18×, і це не приманка.

DocRaptor — компетентний продукт. Він загортає Prince, gold-standard engine для HTML-to-PDF, у hosted REST API з retries, async jobs і добрими docs. Існує понад десятиліття, і для багатьох команд це obvious choice, коли “не хочу запускати Prince сам”.

Ми інший тип інструмента. gPdf взагалі не приймає HTML; він приймає структурований JSON і render-ить PDF безпосередньо на Cloudflare edge. List-price gap для 100 000 сторінок/міс.: 5 USD/міс. (gPdf Basic) vs 89 USD/міс. (DocRaptor Basic), приблизно 18×. Це не opening promo. Це structural. Ця стаття пояснює, чому структура дає таку ціну, і де кожен інструмент реально fit.

Дві архітектури поруч

Layer DocRaptor (HTML → PDF) gPdf (JSON → PDF)
Input HTML + CSS, з Prince extensions for paged media JSON DocumentRequest
Renderer Prince, compiled C++ engine Custom Rust engine, compiled to WebAssembly
Hosting Centralised servers DocRaptor, US datacentre region Cloudflare Workers, кожен CF colo, 300+ cities
Cold start Server-side worker pool V8 isolate boot, single-digit ms
Per-render compute Layout pass over HTML/CSS, потім Prince paginates Direct typesetting, без проходу інтерпретації макета
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

Якщо читати ці дві колонки як “універсальний HTML-принтер” проти “спеціалізований рендерер документів”, ви вже зрозуміли архітектурне рішення. Усе інше, latency, cost і навіть переліки можливостей, є наслідком цього вибору.

Податок Prince

Prince добрий. Він також робить роботу, яка не потрібна більшості процесів рахунків, квитанцій і етикеток: implement CSS Paged Media для довільного HTML, який користувач може надіслати; page-break rules, running headers, footnotes, cross-references, generated content boxes.

Ця універсальність має витрати середовища виконання. Щоб paginate довільний HTML document, engine має:

  1. Parse і validate HTML
  2. Parse і resolve CSS cascade, потенційно з власними Prince extensions
  3. Build render tree
  4. Run multi-pass layout, особливо для tables across pages або balanced columns
  5. Resolve cross-references across pages
  6. Emit PDF objects

Більшість цих passes — це вартість прийняття HTML як input. Якщо input уже структуровані дані, що майже завжди так, бо invoice існує як JSON object до того, як ви wrap його в HTML, ви платите за ці passes у compute і latency на кожному render, а вони не додають value до output.

gPdf повністю пропускає крок інтерпретації макета. JSON DocumentRequest уже structurally specifies page layout: { pages: [{ size, elements: [...] }] }. Renderer typesets elements, deterministic paginate tables/lists і emit PDF. Немає CSS cascade для resolve, немає float layout для compute, немає cross-reference resolution pass.

Результат: той самий single-page invoice, який займає ~300 ms у DocRaptor, займає ~3 ms у gPdf. Ми швидші не тому, що написали швидший Prince; ми швидші тому, що не робимо більшість того, що робить Prince.

“Занадто дешево, щоб бути правдою” — реальне procurement objection

Це потрібно address directly, бо воно виникає на кожному B2B sales call.

“5 USD/міс. for 100 000 рендерів. DocRaptor — 89. Anvil — 0.10/PDF, тобто $10,000 для same volume. Що з вами не так?”

Є три чесні reasons, чому ми можемо charge таку price:

1. Ми не запускаємо browser

DocRaptor amortises Prince infrastructure across customers. gPdf amortises one Cloudflare Worker, який коштує приблизно $0.50/million requests на Workers Bundled. З JSON-shaped input наш renderer займає приблизно 1.5 ms CPU per render. Додайте 50% margin, і ви все ще в cents-per-thousand-renders range. Arithmetic і є price.

2. Ми не запускаємо control plane

Немає async jobs, callbacks, retry queue, document storage, preview-link UI, multi-tenant database. Кожен render — single round-trip до stateless function і назад. Це прибирає весь ops surface, який більшість “PDF API” companies закладає в budget, і саме цей surface виправдовує їхню price.

3. Model сам відсіює workloads, на яких ми втрачали б гроші

Якщо document справді потребує HTML-to-PDF, наприклад 60-page legal contract або complex CSS-Grid report, ви bounce від JSON model у першу hour і підете до DocRaptor. Нам не потрібно price defensively для цих workloads, бо вони self-route. Ми price лише long-but-narrow tail “structured-data-to-document” workloads, де per-render cost справді tiny.

Разом: $5/100 000 не loss leader; це actual cost-of-goods-sold plus margin. Ми можемо тримати це, бо underlying compute дійсно настільки дешевий, коли ви не ship browser.

Де DocRaptor правильний вибір

Ми намагаємося не писати self-serving comparison. Cases, де DocRaptor справді wins:

  • Ваш input — HTML, який ви не повністю control. User-generated reports, third-party templates, Markdown-from-CMS-rendered-to-HTML. Не варто писати JSON mapper для arbitrary input.
  • Вам потрібні CSS Paged Media features, які supports Prince. Running headers/footers per chapter, complex footnote reflow, named-page selectors, generated tables of contents, indexes. gPdf має structured equivalents для common subset, але якщо ви живете в @page :left selectors, Prince — ваш friend.
  • Ваша контент-команда пише HTML/CSS, а не JSON. Не нав’язуйте процес створення JSON неінженерній команді. Вони це зненавидять.
  • Async + callbacks + document storage as a service. DocRaptor stores generated PDFs і дає signed URLs для delivery. gPdf strictly stateless; result stores ваш code.

Якщо ви в будь-якому з цих buckets, залишайтесь на DocRaptor. Це правильний tool.

Де gPdf правильний вибір

Дзеркальна ситуація:

  • Ваші inputs уже є структурованими даними: database rows, JSON API payloads, queue messages.
  • Latency важлива: interactive checkout flows, real-time label printing, on-demand statement generation.
  • Вам потрібна byte-identical reproducibility для tests, audit trails або e-invoice retention.
  • Ви cost-sensitive на будь-якому volume понад кілька thousand renders/month.
  • Вам потрібні barcodes, як GS1-128, QR, Data Matrix, PDF417, Aztec, MaxiCode, з sub-millimetre precision. Prince technically supports SVG barcodes, але 0.1 mm overall length precision через HTML/CSS non-trivial.
  • Вам потрібні PDF/A (1b/2b/3b/4) або Factur-X / ZUGFeRD attachments for compliance.
  • Ви не хочете запускати JSON-to-HTML-to-PDF pipeline, коли можна запускати JSON-to-PDF pipeline.

Міграція механічна, не стратегічна

Поширене worry: “Switching означає rewrite усіх templates.” Зазвичай ні. Most HTML-to-PDF templates — це 20% layout, який один раз стає JSON structure, і 80% data interpolation, що однаковий незалежно від renderer.

Practical path:

  1. Виберіть один document type для migration. Почніть із highest-volume one: biggest savings, smallest blast radius.
  2. Візьміть data interface HTML template, тобто variables, які він interpolates, і напишіть невелику mapToDocumentRequest(data) function.
  3. Iterate у Playground, доки output match.
  4. A/B у production: route 5% traffic до gPdf на два weeks. Diff PDFs. Compare bills.
  5. Roll forward або roll back на основі data, не vibes.

Ми бачили teams, які робили це за один sprint і економили 90% PDF bill наступного місяця. Ми також бачили teams, які розуміли, що їхній workload насправді HTML-to-PDF case, і залишалися на DocRaptor; це теж правильне decision.

Коротко

DocRaptor gPdf
Best at HTML → PDF для arbitrary content JSON → PDF для structured documents
Ціна (100 000 сторінок/міс.) $89 $5
p50 render 250-800 ms 3-8 ms
Edge-deployed Ні, centralised Так, 300+ Cloudflare colos
Async + storage Так, included Ні, stateless by design
PDF/A + Factur-X Через Prince extensions built-in

Якщо ваші documents — це структуровані дані, одягнена в HTML для renderer, ви платите за translation step, якого не має бути. Спробуйте Playground: describe один invoice у JSON, render у browser менш ніж за 5 ms і подивіться, чи gap збігається з вашим gut feeling.