আজ যদি “Puppeteer PDF alternative” খুঁজে এখানে এসে থাকেন, আপনার আসল প্রশ্ন সম্ভবত:
“একটা invoice print করতে আমার serverless function cold-start 2 seconds আর 900 MB RAM কেন নিচ্ছে?”
Puppeteer অসাধারণ tool। কিন্তু অনেক team যে কাজের জন্য এটি ব্যবহার করে, অর্থাৎ structured data থেকে predictable PDF, তার জন্য এটি অতিরিক্ত বড় সমাধান। এই লেখাটি সেই team-এর জন্য, যারা Puppeteer production-এ পাঠাতে যাচ্ছে কিন্তু মনে মনে আরও যুক্তিসঙ্গত পথ খুঁজছে।
Puppeteer দিয়ে আপনি আসলে কী ship করছেন
npm install puppeteer করলে dependencies বাদেই প্রায় 170 MB Chromium build আসে। Runtime-এ headless Chromium এক page render করতে 600-900 MB resident memory এবং browser spin-up-এ 1-2 seconds cold start নিতে পারে। প্রতিটি render:
- Browser process boot করে, অথবা pool reuse করে।
- নতুন tab খোলে।
- আপনার HTML / URL-এ navigate করে।
domcontentloadedএবং সাধারণত fonts, images, web components-এর জন্য অপেক্ষা করে।page.pdf()painted page-কে Chromium PDF engine দিয়ে serialize করে।- Tab বন্ধ করে।
এটাই whole-web-platform tax। 90-page legal contract হোক বা পাঁচ লাইনের shipping label, একই tax দিতে হয়।
যদি input সত্যিই HTML হয় এবং CSS layout, JavaScript, web fonts দরকার হয়, এই tax ন্যায্য। Invoices, labels, receipts, tickets, statements, certificates-এর জন্য সাধারণত অযথা খরচ।
Puppeteer কোথায় জেতে
Migration ভাবার আগে এই অংশ সৎভাবে দেখুন:
- Faithful HTML/CSS rendering. Design system HTML emit করে এবং pixel-identical PDF দরকার হলে Puppeteer সেরা। এটি Chrome printing।
- Web-platform features. SVG filters, CSS Grid edge cases, web components, JavaScript content, third-party iframes কাজ করে।
- Visual debugging. Render চলাকালে screenshot নেওয়া এবং headless DevTools দেখা যায়।
- Zero translation step. Content যদি আগে থেকেই webpage হয়, schema mapping নেই।
page.goto(url); await page.pdf()-ই pipeline।
এইগুলোর মধ্যে দুটো আপনার workload-এ সত্য হলে, migrate করবেন না। Puppeteer ঠিক tool।
Puppeteer কোথায় হারায়
অন্য সব ক্ষেত্রে cost দ্রুত বাড়ে।
Serverless memory এবং cold start
Puppeteer চালানো typical Node 20 Lambda বা Cloudflare Container:
| Metric | Typical value |
|---|---|
| Container image size | 250-400 MB (Chromium + Node + your code) |
| Cold-start time | 1.8 - 2.5 seconds |
| Warm RAM per render | 600 - 900 MB |
| Concurrent renders per 1 GB instance | 1 (sometimes 2 if pages are tiny) |
Invoice service যদি মাসে ১,০০,০০০ রেন্ডার করে, প্রতিটি cold container-এ browser boot-এর energy দিচ্ছেন, যদিও কোনো render-ই JavaScript execution চাইছিল না।
Containers-এ font trap
Chromium default font set নিয়ে আসে, কিন্তু CJK, Cyrillic, Devanagari, Arabic এবং অনেক script-specific glyph missing থাকে। Production-এ দেখা যায়:
Tokyo office-এর Q3 2025 invoice print করে
▢▢▢▢ 2025年第3四半期. Customer escalates. Team Dockerfile font installs এবং CSS fallback debug করতে sprint কাটায়।
শুধু NotoSans CJK image-এ ~50 MB যোগ করে। Global Noto fallback set ~250 MB যোগ করতে পারে। একটি Japanese invoice print করতে Chromium এবং বিশাল font bundle দুটোই বহন করছেন।
Determinism
Puppeteer renders Chromium versions-এর মধ্যে byte-identical নয়। Patch upgrade kerning, font baseline বা page breaks বদলাতে পারে। PDF diff tests থাকলে প্রতিটি Chromium update investigation হয়ে যায়।
Render-time JavaScript
“Static” HTML-ও parse, layout, paint, serialize হয়। Warm process-এও এটি প্রায় 80-400 ms per page। একই one-page invoice JSON থেকে binary renderer-এ দিলে 3-8 ms লাগে।
gPdf কোথায় fit করে
gPdf মডেল উল্টে দেয়: document-কে HTML হিসেবে browser দিয়ে paint করায় না। আপনি document বর্ণনা করেন structured JSON (DocumentRequest) হিসেবে, আর Rust renderer WebAssembly হয়ে সরাসরি PDF emit করে। Browser নেই, DOM নেই, JavaScript layout pass নেই।
HTML-shaped সমস্যায় এটি সীমিত। কিন্তু invoice / label / receipt / statement / certificate ধরনের documents-এ JSON-first model বেশি মানায়:
- Data আগে থেকেই structured. Invoice সাধারণত
{ customer, lines, totals, taxes, notes }object। এটিকে HTML বানিয়ে browser দিয়ে আবার layout করানো অপ্রয়োজনীয়। - Layout contract হয়।
font_size: 11সবসময় 11 points,gap: 8সবসময় 8 points। - Output byte-identical. Same input → same bytes. PDF diff নির্ভরযোগ্য।
- Cold start runtime startup, browser boot নয়। Cloudflare Workers-এ V8 isolate 5-20 ms-এ শুরু হয়; WASM module একই isolate-এ hot থাকে।
gPdf-তে typical one-page invoice render edge-এ 3-5 ms p50 wall-clock। Puppeteer warm path-এর চেয়ে প্রায় দুই orders এবং cold path-এর চেয়ে তিন orders দ্রুত।
Decision matrix
| Workload | Use Puppeteer | Use gPdf |
|---|---|---|
| Existing HTML report → PDF | ✅ first choice | ⚠️ requires rewrite |
| Invoices, statements, receipts | ⚠️ heavy hammer | ✅ first choice |
| Shipping labels with barcodes | ❌ avoid (font issues) | ✅ first choice |
| E-invoice (Factur-X / ZUGFeRD / EN 16931) | ❌ no built-in support | ✅ built-in |
| PDF/A long-term archival | ⚠️ needs Ghostscript pass | ✅ built-in profiles |
| Pixel-faithful design system mockups | ✅ first choice | ❌ wrong tool |
| Charts that need real D3 / Recharts | ✅ first choice | ❌ wrong tool |
| Tickets, certificates, name-tags | ⚠️ overkill | ✅ first choice |
| Anything that needs JavaScript at render time | ✅ only choice | ❌ wrong tool |
ডান কলাম তিনটির বেশি row-তে জিতলে savings ছোট নয়।
Real comparison: one-page invoice render
Same content, paper size, NotoSans fonts এবং PDF/A-3b profile:
| Puppeteer (warm Lambda, 1 GB) | gPdf (warm Cloudflare Worker) | |
|---|---|---|
| p50 latency | 180 ms | 3.4 ms |
| p99 latency | 420 ms | 8 ms |
| Cold-start penalty | +1800 ms first render | +12 ms first render |
| Memory at peak | 720 MB | 18 MB |
| Image / module size | 280 MB | 4.5 MB |
| CJK glyphs | ❌ unless explicit install | ✅ embedded NotoSans CJK |
| খরচ / ১,০০,০০০ রেন্ডার | ~$240 (Lambda compute) | ~$5 (gPdf Basic plan) |
শেষ row অনেককে অবাক করে। কিন্তু এটি teaser price নয়, structural gap। Chromium boot, browser memory বা container cold starts amortize করতে হয় না।
“$5/১,০০,০০০ পৃষ্ঠা খুব সস্তা শোনাচ্ছে। catch কী?”
Catch হলো আমরা browser ship করি না। Warm V8 isolate-এ binary renderer চালানোর cost milliseconds CPU এবং kilobytes memory। Puppeteer-shaped price নেওয়া মানে এমন infrastructure-এর জন্য charge করা যা আমরা চালাই না।
কখন Puppeteer-ই বেছে নেবেন
সৎ উত্তর সবসময় “gPdf use করুন” নয়:
- Puppeteer production-এ চলছে এবং সমস্যা করছে না। শুধু বদলানোর জন্য migrate করবেন না। Compute bill 400 USD/মাস ছাড়ালে বা cold-start SLA ভাঙলে gPdf evaluate করুন।
- Documents সত্যিই existing webpages. User-generated 60-page report with charts এবং dynamic content JSON migration নয়, redesign।
- Web preview-এর সঙ্গে pixel-perfect parity দরকার। কিছু workflow-তে editor এবং print দুই পাশেই Chromium দরকার।
এসব না হলে math সরল: smaller deploy, lower latency, lower bill, byte-identical output এবং font-install drama কম।
Real workload migrate কীভাবে করবেন
সাধারণত প্রতি document type-এ 1-2 দিনের spike:
- একটি document বেছে নিন, highest-volume দিয়ে শুরু করুন, most complex নয়।
- HTML template-এর logical sections গুলো gPdf JSON elements (
text,box,table,barcode,image) এ map করুন। - Playground-এ real
DocumentRequestনিয়ে iterate করুন। - Existing data-shape থেকে JSON emit করার ছোট mapper লিখুন।
- Puppeteer endpoint-এর সঙ্গে এক সপ্তাহ A/B চালান। PDFs diff করুন। তারপর সিদ্ধান্ত নিন।
বেশিরভাগ team এক দিনে JSON model ধরতে পারে। কঠিন অংশ new tool নয়; পুরনো template-এর HTML/CSS gymnastics খুলে আনা।
TL;DR
Puppeteer web pages-এর জন্য সঠিক উত্তর। Documents-এর জন্য আপনি document-কে data হিসেবে describe করার ছোট one-time cost এড়াতে প্রতি render-এ 100-200× tax দিচ্ছেন। যদি system invoices, labels, receipts, statements বা tickets তৈরি করে যেখানে shape একই, values বদলায়, gPdf-এর মতো edge-native renderer দ্রুত, ছোট, সস্তা এবং deterministic হবে।
Playground-এ চেষ্টা করুন। এটি real edge worker, signup নেই, browser-এ 5 ms-এর নিচে response।