1<!doctype html><html lang="en" class="h-full"><head><!-- TO THE GOD OF ABRAHAM, ISAAC AND JACOB. I DEDICATE THIS WORK TO YOU MAY YOU BLESS IT 2 AND MAY IT BLESS THOSE YOU USE IT, MORESO MAY THEY KNOW YOU BY NAME, REPENT AND BE LED TO YOUR WILL AND KINGDOM. 3 Our Father who is in the heavens, let Your Name be set-apart, let Your reign come, 4 let Your desire be done on earth as it is in heaven. Give us today our daily bread. 5 And forgive us our debts, as we forgive our debtors. And do not lead us into trial, 6 but deliver us from the wicked one because Yours is the reign and the power and the esteem, forever. Amen. --><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Blog - PDF Generation Tutorials & Guides | LightningPDF</title><meta name="description" content="Tutorials, guides, and news about PDF generation, APIs, and document automation."><meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><meta name="googlebot" content="index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1"><!-- Open Graph --><meta property="og:title" content="Blog - PDF Generation Tutorials & Guides | LightningPDF"><meta property="og:description" content="Tutorials, guides, and news about PDF generation, APIs, and document automation."><meta property="og:type" content="website"><meta property="og:site_name" content="LightningPDF"><meta property="og:url" content="https://lightningpdf.dev/blog"><link rel="canonical" href="https://lightningpdf.dev/blog"><meta property="og:image" content="https://lightningpdf.dev/static/images/og-default.png"><meta property="og:image:alt" content="LightningPDF - PDF Generation API"><meta property="og:image:width" content="1200"><meta property="og:image:height" content="630"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@lightningpdf"><meta name="twitter:title" content="Blog - PDF Generation Tutorials & Guides | LightningPDF"><meta name="twitter:description" content="Tutorials, guides, and news about PDF generation, APIs, and document automation."><meta name="twitter:image" content="https://lightningpdf.dev/static/images/og-default.png"><!-- Google Analytics -->
vendor: 63 bytes, line 6
6<script async src="https://www.googletagmanager.com/gtag/js?id=
6G-X4WHTYX9DF
vendor: 11 bytes, line 6
6"></script>
6<script> 7
vendor: 139 bytes, lines 7-10
7window.dataLayer = window.dataLayer || []; 8 function gtag(){dataLayer.push(arguments);} 9 gtag('js', new Date()); 10 gtag('config', '
10G-X4WHTYX9DF
vendor: 7 bytes, lines 10-11
10'); 11
11</script>
11<!-- Favicon --><link rel="icon" href="/favicon.svg" type="image/svg+xml"><link rel="icon" href="/favicon.ico" sizes="48x48"><!-- Compiled Tailwind CSS (includes custom theme + input.css) --><link rel="preload" href="/static/css/output.css" as="style"><style>[x-cloak]{display:none!important}</style><link rel="stylesheet" href="/static/css/output.css"><!-- Fonts served locally from /static/fonts/ (defined in input.css @font-face) --><!-- Alpine.js (local) - needed on all pages for nav/interactions -->
11<script defer src="/static/js/alpine-persist.min.js"></script>
11<script defer src="/static/js/alpine.min.js"></script>
11<!-- Monaco Code Editor - only on designer page (served locally for speed) --></head><body class="flex flex-col bg-document-50 min-h-full" hx-ext="response-targets,loading-states,preload"><nav class="bg-white/95 backdrop-blur-sm border-b border-document-200 sticky top-0 z-40" x-data="{ mobileOpen: false, userOpen: false, solutionsOpen: false }"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"><div class="flex items-center justify-between h-14 sm:h-16"><!-- Left: Logo + Nav links --><div class="flex items-center min-w-0"><a href="/" class="flex items-center gap-2 flex-shrink-0" aria-label="LightningPDF home"><div class="w-8 h-8 sm:w-9 sm:h-9 bg-gradient-to-br from-primary-500 to-primary-700 rounded-xl flex items-center justify-center shadow-sm"><svg class="w-4 h-4 sm:w-5 sm:h-5 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path></svg></div><span class="hidden sm:inline text-lg font-bold text-document-800">Lightning<span class="text-primary-600">PDF</span></span></a><!-- Desktop nav --><div class="hidden lg:flex items-center ml-8 space-x-1"><!-- Solutions dropdown (logged-out only) --> <div class="relative" @click.away="solutionsOpen = false"><button @click="solutionsOpen = !solutionsOpen" class="inline-flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100 inline-flex items-center gap-1" aria-haspopup="true" :aria-expanded="solutionsOpen ? 'true' : 'false'">Solutions <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path></svg></button><div x-show="solutionsOpen" x-transition:enter="transition ease-out duration-100" x-transition:enter-start="transform opacity-0 scale-95" x-transition:enter-end="transform opacity-100 scale-100" x-transition:leave="transition ease-in duration-75" x-transition:leave-start="transform opacity-100 scale-100" x-transition:leave-end="transform opacity-0 scale-95" x-cloak class="absolute left-0 mt-1 w-72 rounded-lg border border-document-200 bg-white shadow-lg z-50 py-1"><a href="/saas-invoicing-pdf" class="block px-4 py-2.5 text-sm text-document-700 hover:bg-document-50 transition-colors"><div class="font-medium text-document-900">Shopify & WooCommerce invoicing</div><div class="text-xs text-document-500 mt-0.5">Branded order PDFs for storefronts</div></a> <a href="/logistics-pdf-api" class="block px-4 py-2.5 text-sm text-document-700 hover:bg-document-50 transition-colors"><div class="font-medium text-document-900">Logistics & fulfillment</div><div class="text-xs text-document-500 mt-0.5">Pick lists, BOLs, packing slips at warehouse speed</div></a> <a href="/healthcare-reporting-pdf" class="block px-4 py-2.5 text-sm text-document-700 hover:bg-document-50 transition-colors"><div class="font-medium text-document-900">Multilingual clinics</div><div class="text-xs text-document-500 mt-0.5">Patient invoices in every language they read</div></a></div></div><a href="/playground" class="inline-flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100">Playground</a> <a href="/docs" class="inline-flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100">Docs</a> <a href="/pricing" class="inline-flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100">Pricing</a></div></div><!-- Right: Auth / User menu (desktop) --><div class="hidden lg:flex items-center gap-3 flex-shrink-0"><a href="/auth/login" class="text-sm font-medium text-document-600 hover:text-document-900 transition-colors">Sign in</a> <a href="/auth/register" class="pbolt-cta inline-flex items-center px-4 py-2 text-sm font-semibold text-white bg-primary-600 rounded-lg hover:bg-primary-700 transition-colors shadow-sm">Get Started Free</a></div><!-- Hamburger: visible below lg (phones + tablets) --><button @click="mobileOpen = !mobileOpen" class="lg:hidden p-2 rounded-md text-document-500 hover:text-document-900 hover:bg-document-100 transition-colors" aria-label="Toggle menu"><svg x-show="!mobileOpen" class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
11<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg> <svg x-show="mobileOpen" x-cloak class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg></button></div></div><!-- Mobile/tablet menu: visible below lg --><div x-show="mobileOpen" x-transition x-cloak class="lg:hidden border-t border-document-200 bg-white max-h-[calc(100vh-3.5rem)] sm:max-h-[calc(100vh-4rem)] overflow-y-auto"><div class="px-4 py-3 space-y-1"><!-- Public links --><p class="px-3 pt-1 pb-2 text-xs font-semibold text-document-500 uppercase tracking-wider">Explore</p><a href="/playground" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> Playground</a> <a href="/docs" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"></path></svg> Docs</a> <a href="/pricing" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"></path></svg> Pricing</a> <div class="border-t border-document-200 my-2"></div><p class="px-3 pt-1 pb-2 text-xs font-semibold text-document-500 uppercase tracking-wider">Solutions</p><a href="/saas-invoicing-pdf" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path></svg> Shopify & Woo invoicing</a> <a href="/logistics-pdf-api" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 104 0m-4 0a2 2 0 114 0m6 0a2 2 0 104 0m-4 0a2 2 0 114 0"></path></svg> Logistics & fulfillment</a> <a href="/healthcare-reporting-pdf" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium transition-colors text-document-600 hover:text-document-900 hover:bg-document-100"><svg class="w-4 h-4 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
11<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"></path></svg> Multilingual clinics</a> <!-- Account section --><div class="border-t border-document-200 my-2"></div><a href="/auth/login" class="flex items-center gap-3 px-3 py-2.5 rounded-md text-sm font-medium text-document-600 hover:text-document-900 hover:bg-document-100 transition-colors">Sign in</a> <a href="/auth/register" class="pbolt-cta block rounded-lg bg-primary-600 px-4 py-2.5 text-sm font-semibold text-white hover:bg-primary-700 transition-colors text-center mt-2">Get Started Free</a></div></div></nav><div x-data="{ dismissed: localStorage.getItem('shopify_launch_dismissed') === '1' }" x-show="!dismissed" x-cloak class="bg-nature-50 border-b border-nature-200"><div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-2"><div class="flex items-center justify-between gap-4"><div class="flex items-center gap-2 min-w-0 text-sm"><span class="hidden sm:inline-flex items-center text-[10px] font-mono uppercase tracking-widest bg-nature-100 text-nature-800 px-2 py-0.5 rounded">NEW</span> <span class="text-nature-800 truncate">Paperbolt is now live on the Shopify App Store</span> <a href="https://apps.shopify.com/paperbolt-pdf-invoices" target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-1 text-nature-700 hover:text-nature-900 font-semibold underline underline-offset-2 decoration-nature-300 hover:decoration-nature-600 transition-colors flex-shrink-0">Browse the listing <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg></a></div><button @click="dismissed = true; localStorage.setItem('shopify_launch_dismissed', '1')" class="text-nature-500 hover:text-nature-700 p-1 flex-shrink-0" aria-label="Dismiss announcement"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg></button></div></div></div><main class="flex-grow">
11<script type="application/ld+json">{"@context":"https://schema.org","@type":"Blog","name":"LightningPDF Blog","description":"Tutorials, guides, and insights about PDF generation","url":"https://lightningpdf.dev/blog","publisher":{"@type":"Organization","name":"LightningPDF"}}</script>
vendor: 1 bytes, line 11
11
12<script type="application/ld+json">{"@context":"https://schema.org","@type":"ItemList","numberOfItems":9,"itemListElement":[{"@type":"ListItem","position":1,"url":"https://lightningpdf.dev/blog/lightningpdf-vs-lightpdf","name":"LightningPDF vs LightPDF: Two Different Products, One Confusing Letter"},{"@type":"ListItem","position":2,"url":"https://lightningpdf.dev/blog/indic-script-shaping-in-pdf","name":"Indic script shaping in PDF: why most PDF tools render Hindi as boxes"},{"@type":"ListItem","position":3,"url":"https://lightningpdf.dev/blog/why-two-engines","name":"Why our PDF API uses two engines"},{"@type":"ListItem","position":4,"url":"https://lightningpdf.dev/blog/validator-passing-peppol-bis-3","name":"Validator-passing Peppol BIS 3.0 without an enterprise SDK"},{"@type":"ListItem","position":5,"url":"https://lightningpdf.dev/blog/wordpress-7-pdf-plugin-compatibility","name":"WordPress 7.0 Is Here. Will Your PDF Invoice Plugin Survive It?"},{"@type":"ListItem","position":6,"url":"https://lightningpdf.dev/blog/extract-tables-from-pdf","name":"Extract Tables from PDFs: 5 Methods That Actually Work"},{"@type":"ListItem","position":7,"url":"https://lightningpdf.dev/blog/kreuzberg-vs-pymupdf-vs-pdfplumber","name":"Kreuzberg vs PyMuPDF vs pdfplumber: Which PDF Parser Should You Use?"},{"@type":"ListItem","position":8,"url":"https://lightningpdf.dev/blog/document-pipeline-python","name":"Build a Document Pipeline in Python: From Database to PDF"},{"@type":"ListItem","position":9,"url":"https://lightningpdf.dev/blog/pdf-to-json-extract-structured-data","name":"PDF to JSON: How to Extract Structured Data from PDFs"}]}</script>
12 <div class="min-h-screen bg-gradient-to-b from-document-50 to-white"><div class="max-w-4xl mx-auto px-4 py-16 sm:px-6 lg:px-8"><div class="text-center mb-12"><h1 class="text-4xl font-display text-primary-600 mb-4">Blog</h1><p class="text-xl text-document-600">Tutorials, guides, and insights about PDF generation</p></div><div class="space-y-8"><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/lightningpdf-vs-lightpdf" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">LightningPDF vs LightPDF: Two Different Products, One Confusing Letter</h2><p class="text-document-600 text-sm mb-3">LightningPDF is a developer API for generating PDFs from HTML. LightPDF is an end-user PDF editor. Here is how to tell which one you are looking for.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>Aug 13, 2026</span> <span>2 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/"pdf"" aria-label="View posts tagged "pdf"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pdf"</a><a href="/blog/tag/"api"" aria-label="View posts tagged "api"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"api"</a><a href="/blog/tag/"comparison"" aria-label="View posts tagged "comparison"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"comparison"</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/indic-script-shaping-in-pdf" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Indic script shaping in PDF: why most PDF tools render Hindi as boxes</h2><p class="text-document-600 text-sm mb-3">The reason Hindi, Bengali, Tamil, and other Indic scripts come out as tofu boxes in DOMPDF, mPDF, TCPDF, and most other PDF libraries is not a font problem. It is a text-shaping problem. Here is what the shaping does, why HarfBuzz exists, and what running Chromium gets you for free.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF</span> <span>Jun 4, 2026</span> <span>5 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/internationalization" aria-label="View posts tagged internationalization" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">internationalization</a><a href="/blog/tag/indic" aria-label="View posts tagged indic" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">indic</a><a href="/blog/tag/harfbuzz" aria-label="View posts tagged harfbuzz" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">harfbuzz</a><a href="/blog/tag/pdf" aria-label="View posts tagged pdf" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">pdf</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/why-two-engines" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Why our PDF API uses two engines</h2><p class="text-document-600 text-sm mb-3">A single-engine PDF API forces a trade-off between speed and capability. Splitting the engine into a Go-native fast path and a Chromium fallback removes the trade-off for the most common shapes. Here is how we route, what we measured, and what we got wrong.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF</span> <span>Jun 4, 2026</span> <span>5 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/architecture" aria-label="View posts tagged architecture" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">architecture</a><a href="/blog/tag/performance" aria-label="View posts tagged performance" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">performance</a><a href="/blog/tag/pdf" aria-label="View posts tagged pdf" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">pdf</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/validator-passing-peppol-bis-3" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Validator-passing Peppol BIS 3.0 without an enterprise SDK</h2><p class="text-document-600 text-sm mb-3">How we built a Peppol BIS Billing 3.0 emitter that passes the official Schematron in around 200 lines of Go. The schema is shorter than the integration guide. The integration guide is short.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF</span> <span>Jun 3, 2026</span> <span>4 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/e-invoicing" aria-label="View posts tagged e-invoicing" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">e-invoicing</a><a href="/blog/tag/peppol" aria-label="View posts tagged peppol" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">peppol</a><a href="/blog/tag/belgium" aria-label="View posts tagged belgium" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">belgium</a><a href="/blog/tag/ubl" aria-label="View posts tagged ubl" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">ubl</a><a href="/blog/tag/go" aria-label="View posts tagged go" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">go</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/wordpress-7-pdf-plugin-compatibility" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">WordPress 7.0 Is Here. Will Your PDF Invoice Plugin Survive It?</h2><p class="text-document-600 text-sm mb-3">WordPress 7.0 dropped PHP 7.2/7.3, moved the editor into an iframe, and shipped a new admin theme. Here is what actually breaks invoice plugins, and why cloud-rendered PDFs do n
12ot care.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>May 21, 2026</span> <span>4 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/wordpress" aria-label="View posts tagged wordpress" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">wordpress</a><a href="/blog/tag/woocommerce" aria-label="View posts tagged woocommerce" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">woocommerce</a><a href="/blog/tag/compatibility" aria-label="View posts tagged compatibility" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">compatibility</a><a href="/blog/tag/invoice" aria-label="View posts tagged invoice" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">invoice</a><a href="/blog/tag/plugin" aria-label="View posts tagged plugin" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">plugin</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/extract-tables-from-pdf" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Extract Tables from PDFs: 5 Methods That Actually Work</h2><p class="text-document-600 text-sm mb-3">A hands-on comparison of five ways to extract tables from PDFs in Python: pdfplumber, Camelot, Tabula, AWS Textract, and manual regex. With code, benchmarks, and honest pros and cons.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>Apr 1, 2026</span> <span>5 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/"pdf"" aria-label="View posts tagged "pdf"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pdf"</a><a href="/blog/tag/"python"" aria-label="View posts tagged "python"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"python"</a><a href="/blog/tag/"tables"" aria-label="View posts tagged "tables"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"tables"</a><a href="/blog/tag/"extraction"" aria-label="View posts tagged "extraction"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"extraction"</a><a href="/blog/tag/"data"" aria-label="View posts tagged "data"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"data"</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/kreuzberg-vs-pymupdf-vs-pdfplumber" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Kreuzberg vs PyMuPDF vs pdfplumber: Which PDF Parser Should You Use?</h2><p class="text-document-600 text-sm mb-3">A head-to-head comparison of Kreuzberg, PyMuPDF, and pdfplumber for Python PDF parsing. Benchmarks, architecture differences, and code examples to help you pick the right tool.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>Apr 1, 2026</span> <span>6 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/"python"" aria-label="View posts tagged "python"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"python"</a><a href="/blog/tag/"pdf"" aria-label="View posts tagged "pdf"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pdf"</a><a href="/blog/tag/"extraction"" aria-label="View posts tagged "extraction"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"extraction"</a><a href="/blog/tag/"comparison"" aria-label="View posts tagged "comparison"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"comparison"</a><a href="/blog/tag/"kreuzberg"" aria-label="View posts tagged "kreuzberg"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"kreuzberg"</a><a href="/blog/tag/"pymupdf"" aria-label="View posts tagged "pymupdf"
12" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pymupdf"</a><a href="/blog/tag/"pdfplumber"" aria-label="View posts tagged "pdfplumber"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pdfplumber"</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/document-pipeline-python" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">Build a Document Pipeline in Python: From Database to PDF</h2><p class="text-document-600 text-sm mb-3">A complete tutorial for building a Python document pipeline that queries a database, formats data with Jinja2, generates PDFs via API, and delivers them via email or S3.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>Apr 1, 2026</span> <span>3 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/"python"" aria-label="View posts tagged "python"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"python"</a><a href="/blog/tag/"tutorial"" aria-label="View posts tagged "tutorial"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"tutorial"</a><a href="/blog/tag/"automation"" aria-label="View posts tagged "automation"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"automation"</a><a href="/blog/tag/"pipeline"" aria-label="View posts tagged "pipeline"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pipeline"</a><a href="/blog/tag/"database"" aria-label="View posts tagged "database"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"database"</a></div></article><article class="bg-white rounded-2xl p-6 shadow-soft hover:shadow-float transition-all duration-300"><a href="/blog/pdf-to-json-extract-structured-data" class="block"><h2 class="text-xl font-semibold text-document-800 mb-2 hover:text-primary-600 transition-colors">PDF to JSON: How to Extract Structured Data from PDFs</h2><p class="text-document-600 text-sm mb-3">Three practical approaches to extracting structured data from PDFs into JSON: regex on raw text, template-based extraction, and AI-powered extraction with code for each.</p><div class="flex items-center gap-4 text-xs text-document-500"><span>By LightningPDF Team</span> <span>Apr 1, 2026</span> <span>4 min read</span></div></a> <div class="flex flex-wrap gap-1 mt-3"><a href="/blog/tag/"pdf"" aria-label="View posts tagged "pdf"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"pdf"</a><a href="/blog/tag/"json"" aria-label="View posts tagged "json"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"json"</a><a href="/blog/tag/"python"" aria-label="View posts tagged "python"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"python"</a><a href="/blog/tag/"extraction"" aria-label="View posts tagged "extraction"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"extraction"</a><a href="/blog/tag/"api"" aria-label="View posts tagged "api"" class="bg-primary-50 text-primary-600 px-2 py-0.5 rounded-full text-xs hover:bg-primary-100 transition-colors">"api"</a></div></article></div><nav aria-label="Blog pagination" class="mt-12 flex items-center justify-center gap-2"><span class="px-3 py-2 text-sm font-medium bg-primary-600 text-white rounded-lg">1</span> <a href="/blog?page=2" class="px-3 py-2 text-sm text-document-500 hover:text-primary-600 transition-colors">2</a> <a href="/blog?page=3" class="px-3 py-2 text-sm text-document-500 hover:text-primary-600 transition-colors">3</a> <a href="/blog?page=4" class="px-3 py-2 text-sm text-document-500 hover:text-primary-600 transition-colors">4</a> <a href="/blog?page=5" class="px-3 py-2 text-sm text-document-500 hover:text-primary-600 transition-colors">5</a> <a href="/blog?page=2" class="px-3 py-2 text-sm text-document-600 hover:text-primary-600 transition-colors">Next →</a></nav></div></div></main><footer class="border-t border-document-200 mt-auto bg-white"><div class="max-w-7xl mx-auto py-10 px-4 sm:px-6 lg:px-8">
12<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-5 gap-8 mb-8"><!-- Product --><div><h4 class="text-sm font-semibold text-document-800 mb-3">Product</h4><ul class="space-y-2"><li><a href="/docs" class="text-sm text-document-500 hover:text-document-900 transition-colors">Documentation</a></li><li><a href="/pricing" class="text-sm text-document-500 hover:text-document-900 transition-colors">Pricing</a></li><li><a href="/marketplace" class="text-sm text-document-500 hover:text-document-900 transition-colors">Templates</a></li><li><a href="/blog" class="text-sm text-document-500 hover:text-document-900 transition-colors">Blog</a></li></ul></div><!-- Developers --><div><h4 class="text-sm font-semibold text-document-800 mb-3">Developers</h4><ul class="space-y-2"><li><a href="/docs/generate-pdf" class="text-sm text-document-500 hover:text-document-900 transition-colors">API Reference</a></li><li><a href="/docs/getting-started" class="text-sm text-document-500 hover:text-document-900 transition-colors">Quickstart</a></li><li><a href="/auth/register" class="text-sm text-document-500 hover:text-document-900 transition-colors">Get API Key</a></li><li><a href="https://status.lightningpdf.dev" target="_blank" rel="noopener noreferrer" class="text-sm text-document-500 hover:text-document-900 transition-colors">Status</a></li></ul></div><!-- Company --><div><h4 class="text-sm font-semibold text-document-800 mb-3">Company</h4><ul class="space-y-2"><li><a href="/privacy" class="text-sm text-document-500 hover:text-document-900 transition-colors">Privacy</a></li><li><a href="/terms" class="text-sm text-document-500 hover:text-document-900 transition-colors">Terms</a></li></ul></div><!-- Connect --><div><h4 class="text-sm font-semibold text-document-800 mb-3">Connect</h4><ul class="space-y-2"><li><a href="/support" class="text-sm text-document-500 hover:text-document-900 transition-colors">Contact Support</a></li><li><a href="https://t.me/+ITaWB8ePvT00N2E0" target="_blank" rel="noopener noreferrer" class="inline-flex items-center gap-1.5 text-sm text-document-500 hover:text-document-900 transition-colors"><svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z"></path></svg> Telegram community</a></li></ul></div><!-- Family --><div><h4 class="text-sm font-semibold text-document-800 mb-3">Family</h4><p class="text-xs text-document-500 mb-3">Sibling tools from the same maker.</p><ul class="space-y-2"><li><a href="https://thunderhooks.com" target="_blank" rel="noopener noreferrer" class="text-sm text-document-500 hover:text-document-900 transition-colors inline-flex items-center gap-1">Thunderhooks <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg></a></li><li><a href="https://devformat.tools" target="_blank" rel="noopener noreferrer" class="text-sm text-document-500 hover:text-document-900 transition-colors inline-flex items-center gap-1">DevFormat <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path></svg></a></li></ul></div></div><div class="border-t border-document-200 pt-6 flex flex-col sm:flex-row items-center justify-between gap-4"><div class="flex items-center gap-2"><div class="w-6 h-6 bg-gradient-to-br from-primary-500 to-primary-700 rounded-lg flex items-center justify-center"><svg class="w-3.5 h-3.5 text-white" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
12<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"></path></svg></div><span class="text-sm font-semibold text-document-700">LightningPDF</span></div><p class="text-xs text-document-500">© 2026 LightningPDF. All rights reserved.</p></div></div></footer><!-- Toast Container --><div id="toast-container" class="fixed bottom-4 right-4 space-y-2 z-50"></div><!-- Enhanced JavaScript -->
12<script> 13 // Configure HTMX to send CSRF token with all requests 14 document.addEventListener('htmx:configRequest', function(evt) { 15 const csrfCookie = document.cookie.split('; ').find(row => row.startsWith('_csrf=')); 16 if (csrfCookie) { 17 const csrfToken = decodeURIComponent(csrfCookie.split('=')[1]); 18 evt.detail.headers['X-CSRF-Token'] = csrfToken; 19 } else { 20 console.warn('No CSRF cookie found'); 21 } 22 }); 23 </script>
23<script> 24 // Toast system (vanilla JS, no jQuery needed) 25 function showToast(message, type) { 26 type = type || 'info'; 27 var icons = { success: '\u{1F338}', error: '\u{1F344}', warning: '\u26A0\uFE0F', info: '\u2728' }; 28 var bgColors = { 29 success: 'bg-nature-50 border-nature-200', 30 error: 'bg-red-50 border-red-200', 31 warning: 'bg-warning-50 border-warning-200', 32 info: 'bg-primary-50 border-primary-200' 33 }; 34 var container = document.getElementById('toast-container'); 35 if (!container) return; 36 var toast = document.createElement('div'); 37 toast.className = 'toast ' + type + ' animate-fade-in border rounded-xl p-4 shadow-lg ' + (bgColors[type] || bgColors.info); 38 toast.innerHTML = '<div class="flex items-center justify-between"><div class="flex items-center"><div class="text-lg mr-3">' + (icons[type] || icons.info) + '</div><div class="text-sm font-medium text-document-800">' + message + '</div></div><button class="ml-4 text-document-400 hover:text-document-600 p-1 rounded-lg hover:bg-document-100 transition-colors" onclick="this.closest(\'.toast\').remove()"><svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg></button></div>'; 39 container.appendChild(toast); 40 var dismissTime = type === 'error' ? 8000 : type === 'warning' ? 6000 : 4000; 41 setTimeout(function() { 42 toast.style.opacity = '0'; 43 toast.style.transform = 'translateY(-10px)'; 44 setTimeout(function() { toast.remove(); }, 300); 45 }, dismissTime); 46 } 47 window.showToast = showToast; 48 49 // App initialization (only runs when HTMX is loaded) 50 document.addEventListener('DOMContentLoaded', function() { 51 if (typeof htmx === 'undefined') return; 52 53 // Handle HX-Trigger showToast events from server responses 54 document.body.addEventListener('showToast', function(evt) { 55 var d = evt.detail || {}; 56 showToast(d.message || 'Done', d.type || 'info'); 57 }); 58 59 document.body.addEventListener('htmx:afterRequest', function(evt) { 60 var detail = evt.detail; 61 var xhr = detail.xhr; 62 var path = detail.pathInfo ? detail.pathInfo.requestPath : ''; 63 if (xhr && xhr.status === 200 && path.includes('/save')) { 64 showToast('Template saved successfully!', 'success'); 65 } 66 if (xhr && xhr.status >= 400) { 67 var errorMsg = 'Request failed'; 68 try { 69 if (xhr.responseText) { 70 try { 71 var json = JSON.parse(xhr.responseText); 72 errorMsg = (json.error && json.error.message) || json.message || errorMsg; 73 } catch (_) { 74 var s = xhr.status; 75 if (s === 400) errorMsg = 'Invalid request. Please check your input.'; 76 else if (s === 401) errorMsg = 'Session expired. Please log in again.'; 77 else if (s === 403) errorMsg = 'Permission denied.'; 78 else if (s === 404) errorMsg = 'Resource not found.'; 79 else if (s === 429) errorMsg = 'Too many requests. Please slow down.'; 80 else if (s >= 500) errorMsg = 'Server error. Please try again later.'; 81 } 82 } 83 } catch (e) { console.error('Error parsing response:', e); } 84 showToast(errorMsg, 'error'); 85 } 86 if (path.includes('/preview')) { 87 var pc = document.getElementById('preview-content'); 88 if (pc) { pc.style.opacity = '1'; pc.style.transform = 'scale(1)'; } 89 } 90 }); 91 92 document.body.addEventListener('htmx:beforeRequest', function(evt) { 93 var path = evt.detail.pathInfo ? evt.detail.pathInfo.requestPath : ''; 94 if (path.includes('/preview')) { 95 var pc = document.getElementById('preview-content'); 96 if (pc) { pc.style.opacity = '0.6'; pc.style.transform = 'scale(0.98)'; pc.style.transition = 'all 0.3s ease'; } 97 } 98 }); 99 100 document.body.addEventListener('htmx:responseError', function(evt) { 101 console.error('HTMX Error:', evt.detail); 102 showToast('Request failed', 'error'); 103 }); 104 105 document.addEventListener('keydown', function(e) { 106 if (e.ctrlKey && e.key === 's') { 107 e.preventDefault(); 108 var saveBtn = document.querySelector('[hx-post="/designer/save"]'); 109 if (saveBtn) { htmx.trigger(saveBtn, 'click'); showToast('Saving...', 'info'); } 110 } 111 if (e.ctrlKey && e.key === 'g') { 112 e.preventDefault(); 113 if (typeof generatePDF === 'function') generatePDF(); 114 } 115 }); 116 117 // Auto-resize textareas 118 function autoResize(ta) { ta.style.height = 'auto'; ta.style.height = Math.min(ta.scrollHeight, 400) + 'px'; } 119 document.querySelectorAll('textarea:not([name="content"])').forEach(function(ta) { 120 autoResize(ta); 121 ta.addEventListener('input', function() { autoResize(ta); }); 122 }); 123 }); 124 </script>
124</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.