PDF Generation dengan Claude Code: Invoice dan Report via Playwright
Implementasi PDF dengan Claude Code: Playwright, print CSS, font, invoice, report, screenshot, dan jebakan umum.
Tombol “Download PDF” terlihat kecil sampai dokumen dipakai pelanggan. Invoice harus punya angka benar, pajak, margin, tabel yang rapi, dan total yang tidak terpotong. Report bulanan biasanya berisi ringkasan, tabel, screenshot, grafik, dan rekomendasi. Sertifikat harus menampilkan nama, tanggal, ID, logo, dan layout yang tetap bagus saat dicetak.
Pendekatan paling praktis untuk tim web adalah HTML-to-PDF dengan renderer browser nyata. Dokumen dibuat sebagai HTML, ukuran kertas dan margin diatur dengan print CSS, lalu Chromium merender dan mengekspor PDF lewat Playwright atau Puppeteer. Claude Code bisa membantu cepat, tetapi prompt harus menyebut ukuran halaman, font, background, page break, screenshot verification, dan larangan membuat seluruh dokumen sebagai canvas image.
Gunakan dokumentasi resmi sebagai dasar: Playwright page.pdf, Puppeteer PDF generation dan PDFOptions, MDN @page dan printing CSS, serta Claude Code overview. Untuk konteks ClaudeCodeLab, baca juga spreadsheet automation, Playwright testing, dan testing strategies.
Kenapa mulai dari HTML-to-PDF
Ada tiga cara umum membuat PDF. Pertama, library seperti jsPDF menggambar teks dan garis di koordinat tertentu. Cocok untuk label kecil, tetapi invoice kompleks akan menjadi kumpulan angka x dan y yang sulit dirawat. Kedua, halaman atau canvas dijadikan gambar lalu dimasukkan ke PDF. Hasil awal terlihat mirip, tetapi teks sulit dicari, ukuran file besar, zoom buram, dan regression test sulit.
Ketiga, HTML-to-PDF. Tabel tetap table, heading tetap heading, dan @page mengatur A4 serta margin. Sebelum export, HTML print bisa dibuka di browser dan dibandingkan dengan screenshot. Ini membuat Claude Code lebih mudah direview karena ia mengubah HTML, CSS, script Node, dan Playwright test, bukan koordinat manual.
flowchart TD
A["Data bisnis"] --> B["Template HTML"]
B --> C["Print CSS dan @page"]
C --> D["Render Chromium"]
D --> E["File PDF"]
C --> F["Perbandingan screenshot"]
F --> G["Bukti review"]
Use case nyata
Use case pertama adalah invoice. Elemen pentingnya adalah seller, buyer, item, pajak, total, due date, dan nomor invoice. Bug serius biasanya berupa rounding salah, pajak hilang, background tabel tidak ikut tercetak, atau total terpotong page break.
Use case kedua adalah report bulanan. SEO, iklan, SaaS, dan sales report sering mencampur ringkasan, tabel, screenshot, dan grafik. Page flow penting: heading jangan tertinggal sendirian di akhir halaman, dan grafik jangan terpisah dari penjelasan.
Use case ketiga adalah sertifikat. Nama, kursus, tanggal, ID, logo, dan QR code sering cukup satu halaman. Simpan informasi penting sebagai HTML text, lalu gunakan image untuk logo, tanda tangan, atau QR.
Use case keempat adalah audit report internal. Permission change, deployment, approval, dan incident note harus mudah ditelusuri. Tambahkan generated date, environment, app version, dan source data ID di footer.
Prompt untuk Claude Code
Implementasikan PDF generation.
Arah:
- Render template HTML dengan Playwright Chromium lalu export PDF.
- Jangan ubah seluruh dokumen menjadi satu canvas image.
- Gunakan A4 portrait, margin 14mm, background visible, dan print CSS.
- Gunakan font stack yang aman untuk Indonesia, Inggris, dan Jepang.
- Struktur harus reusable untuk invoice, report, dan sertifikat.
Implementasi:
- Tambahkan scripts/create-invoice-pdf.mjs.
- Generate out/invoice-ID-2026-0602.pdf dari sample data.
- Format uang dengan Intl.NumberFormat.
- Escape user input sebelum masuk ke HTML.
- Pakai printBackground dan preferCSSPageSize di page.pdf.
Verifikasi:
- Dokumentasikan run command.
- Buat print HTML bisa dicek lewat screenshot comparison.
- Cek font, page break, background, dan total.
Kode yang bisa langsung dijalankan
npm init -y
npm pkg set type=module
npm i -D playwright
npx playwright install chromium
mkdir scripts out
node scripts/create-invoice-pdf.mjs
import { chromium } from "playwright";
import { mkdir } from "node:fs/promises";
import { dirname, resolve } from "node:path";
const outputPath = resolve("out/invoice-ID-2026-0602.pdf");
const money = new Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR" });
const invoice = {
number: "ID-2026-0602",
buyer: "Contoh Digital Indonesia",
seller: "Masa Design Lab",
issuedAt: "2026-06-02",
items: [
{ name: "Desain template PDF", quantity: 1, unitPrice: 8000000 },
{ name: "Script generation Playwright", quantity: 1, unitPrice: 12000000 },
{ name: "Print CSS dan verifikasi font", quantity: 1, unitPrice: 6000000 },
],
};
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (char) => ({
"&": "&", "<": "<", ">": ">", '"': """, "'": "'",
})[char]);
}
function renderHtml(data) {
const subtotal = data.items.reduce((sum, item) => sum + item.quantity * item.unitPrice, 0);
const tax = Math.round(subtotal * 0.11);
const rows = data.items.map((item) => `<tr>
<td>${escapeHtml(item.name)}</td>
<td class="num">${item.quantity}</td>
<td class="num">${money.format(item.unitPrice)}</td>
<td class="num">${money.format(item.quantity * item.unitPrice)}</td>
</tr>`).join("");
return `<!doctype html><html lang="id"><head><meta charset="utf-8">
<style>
@page { size: A4; margin: 14mm; }
body { font-family: "Noto Sans", Arial, sans-serif; color: #202124; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
header { display: flex; justify-content: space-between; border-bottom: 3px solid #1f5eff; padding-bottom: 14px; }
h1 { margin: 0; font-size: 28px; }
table { width: 100%; border-collapse: collapse; margin-top: 24px; }
th { background: #eef3ff; text-align: left; }
th, td { border-bottom: 1px solid #d7dce5; padding: 10px 8px; }
.num { text-align: right; white-space: nowrap; }
.total { margin-left: auto; width: 260px; margin-top: 20px; font-size: 18px; font-weight: 700; }
.avoid-break { break-inside: avoid; page-break-inside: avoid; }
</style></head><body>
<header><h1>Invoice</h1><div>Nomor: ${escapeHtml(data.number)}<br>Tanggal: ${escapeHtml(data.issuedAt)}</div></header>
<p><strong>Pembeli:</strong> ${escapeHtml(data.buyer)}</p>
<p><strong>Penjual:</strong> ${escapeHtml(data.seller)}</p>
<table><thead><tr><th>Item</th><th class="num">Qty</th><th class="num">Harga</th><th class="num">Jumlah</th></tr></thead><tbody>${rows}</tbody></table>
<div class="total avoid-break">Total: ${money.format(subtotal + tax)}</div>
</body></html>`;
}
await mkdir(dirname(outputPath), { recursive: true });
const browser = await chromium.launch();
try {
const page = await browser.newPage();
await page.setContent(renderHtml(invoice), { waitUntil: "networkidle" });
await page.evaluate(() => document.fonts.ready);
await page.emulateMedia({ media: "print" });
await page.pdf({ path: outputPath, printBackground: true, preferCSSPageSize: true, margin: { top: "0", right: "0", bottom: "0", left: "0" } });
console.log(`Created ${outputPath}`);
} finally {
await browser.close();
}
Jebakan dan cara cek
Jebakan umum: membuat seluruh PDF sebagai gambar, lupa printBackground, font yang ada di laptop tetapi tidak ada di CI Linux, logo eksternal belum selesai load, user text tidak di-escape, dan catatan panjang yang mendorong total ke halaman baru. Jangan hanya test invoice pendek.
Verifikasi harus mencakup unit test untuk total, file PDF benar-benar dibuat, dan visual check. Buka print route, jalankan page.emulateMedia({ media: "print" }), lalu simpan screenshot Playwright. Cek dokumen pendek, panjang, tanpa logo, dan dengan teks multilingual.
CTA monetisasi dan hasil verifikasi
PDF generation mudah dihubungkan ke monetisasi karena hasilnya nyata: invoice, report, certificate, checklist, dan deliverable klien. Mulai dari resource Claude Code gratis, gunakan produk ClaudeCodeLab untuk template berulang, dan lihat training atau konsultasi jika tim ingin memasang PDF generation, review, dan Playwright di repo nyata.
Workflow artikel ini diverifikasi dengan Node.js lokal, Playwright Chromium, dan sample invoice data. Kebiasaan paling berguna bukan sekadar memanggil page.pdf, tetapi menjadikan HTML print sebagai surface yang bisa dites. Dalam pekerjaan Masa, masalah font, background hilang, dan page-break drift lebih sering muncul daripada error syntax.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.