quick_html_pdf 3.1.1
quick_html_pdf: ^3.1.1 copied to clipboard
A fast, high-performance Flutter Web package for converting HTML templates with dynamic data into vector PDFs using a custom measure-and-flow paginator + jsPDF.
Changelog #
3.1.1 #
Fixed #
CustomPaginatorleftover-space packing. An element that fits on a full page but not in the remaining space of the current page was pushed whole to the next page, leaving a large empty band. It is now split so the first slice fills the leftover space (when that space is at least120 px) and the remainder spills onto the next page. Below that threshold the element is still flushed to the next page, since thead + frame overhead would eat most of a smaller slice.
3.1.0 #
New #
border-radiussupport in the vector pipeline (PdfOutput.download/PdfOutput.bytes). Previously dropped — flatpdf.rect+ four straightpdf.linecalls ignored CSS rounding. Backgrounds and borders are now emitted via jsPDF'sroundedRectwhen the element has a non-zero radius. Print mode was unaffected and remains unchanged.JsPDF.roundedRect(x, y, w, h, rx, ry, {style})bridged injs_interop.dart.
Scope and limits #
- Only uniform 4-corner radii are honored. If
border-top-left-radius,border-top-right-radius,border-bottom-right-radius, andborder-bottom-left-radiusare not all identical (e.g.border-radius: 6px 0 6px 0), the element renders with sharp corners. A one-time debug log is emitted per distinct non-uniform combination so the consumer can spot it. - Elliptical radii (different rx and ry) are supported when applied uniformly across all four corners.
- Border rounding requires all four sides to share the same width, style, and color. Mixed-side borders fall back to the existing per-side straight-line path (radius is dropped in that case).
- Radii are clamped to
min(w/2, h/2)since jsPDF does not auto-clamp like CSS does.
3.0.0 #
Production-grade vector PDF pipeline. Replaces the v2 raster (html2pdf/html2canvas) bytes path. Three output modes with consistent layout. No CDN at runtime.
Breaking changes #
PdfOutputenum reshape:{ bytes, download }→{ print, download, bytes }.print— opens the browser print dialog (wasdownloadin v2).download— silent file save, no dialog. Uses the vector pipeline.bytes— vector emission, returnsUint8List. v2's html2pdf raster path is gone.
- Default output mode changed from
downloadtoprint(semantics preserved — both default behaviours open the browser print dialog). - Vector modes (
download/bytes) require a registered font. PassPdfOptions.fonts: [PdfFont(...)]. Without registration, generation throwsPdfGenerationException(code: 'no-fonts-registered')rather than silently substituting wrong glyphs (e.g. ₹ → ¹).printmode is unaffected. - No CDN at runtime. jsPDF (~360 KB) is vendored as a Flutter package asset and lazy-loaded on the first vector-mode call. Consumer apps no longer add
<script>tags toweb/index.html. - Deprecated fields:
PdfOptions.scale,PdfOptions.imageQuality,PdfOptions.pageBreakModes(andPageBreakModeenum). Vector pipeline uses CSSpage-break-*directly. Will be removed in v3.1.
New #
CustomPaginator— measure-and-flow paginator written in Dart. ReadsgetBoundingClientRect()deltas off the rendered iframe DOM, slices oversized tables row-by-row (preserving<thead>), handles single-table containers AND multi-table flex-row containers (Form 26AS-style "Sections" panels) with a two-phase pass: half-width slices while both children have content, then full-width tail slices once the shorter sibling exhausts. Replaces the previously-considered Paged.js polyfill — 100–200× faster on table-heavy docs (~1 s vs ~4–5 min on a 200-page tax form).PdfFont— describes a TTF for jsPDF registration. Provide font data via eithersrc(URL —fetch'd at register time) orbytes(rawUint8List, e.g. fromrootBundle.load). Plusfamily,weight,style.- Per-page chrome built by the paginator — header / footer / watermark slots assembled inside each
<div class="qhp-page">wrapper fromPdfOptions.headerHtml,footerHtml,watermarkUrl(andwatermarkSize/watermarkPosition).{{page}},{{pages}},{{date}},{{time}},{{datetime}}substitution is applied per-page. - Walker-level content clip —
DomWalkerhonors theqhp-page-contentslot'soverflow: hiddenso descendants of the content area don't bleed into the footer band even if pagination measurements drift. - Pagination safety buffer — paginator targets
contentHeight − 2 mmso sub-pixel layout drift between source measurement and per-page re-layout doesn't push the last row past the content rectangle. - Fail-loud glyph fallback in non-debug builds.
- Background-image emission in
_emitElementBox(data: URL only in v3.0; network URLs deferred to v3.1). - Width-sanity per-line check in DOM walker — drops down to per-word emission when jsPDF's metrics drift >5% from the browser's.
- Per-word + per-character fallback for high-fidelity text positioning when CSS letter-spacing / font-cascade differences cause drift.
letter-spacingandword-spacingCSS propagated to jsPDF (charSpaceparameter / per-word gap measurement).- New
PdfGenerationPhasevalues:domWalking,vectorEmission. - New
PdfGenerationException.codefield — stable machine-readable codes for telemetry / consumer branching (no-fonts-registered,glyph-fallback,jspdf-bootstrap-failed, etc.).
Migration from v2 #
// v2
await QuickHtmlPdf.generate(
htmlTemplate: tpl,
data: data,
options: PdfOptions(output: PdfOutput.download), // ← was: print dialog
);
// v3 — keep the dialog UX
await QuickHtmlPdf.generate(
htmlTemplate: tpl,
data: data,
options: PdfOptions(output: PdfOutput.print), // explicit
);
// v3 — silent download (new!)
await QuickHtmlPdf.generate(
htmlTemplate: tpl,
data: data,
options: PdfOptions(
output: PdfOutput.download,
filename: 'report.pdf',
fonts: [
PdfFont(family: 'Liberation Sans', src: 'assets/fonts/LiberationSans-Regular.ttf'),
PdfFont(family: 'Liberation Sans', src: 'assets/fonts/LiberationSans-Bold.ttf', weight: 'bold'),
],
),
);
Bundled JS versions #
- jsPDF:
2.5.1
2.0.1 #
Bug Fixes #
-
Fixed NativeByteBuffer to Uint8List conversion error
jsPDF.output('arraybuffer')returns JavaScriptArrayBuffer, notUint8Array- Fixed conversion:
JSArrayBuffer→ByteBuffer→Uint8Listusing.toDart.asUint8List() - Resolved
TypeError: type 'NativeByteBuffer' is not a subtype of type 'NativeUint8List'
-
Fixed Unicode/Hindi text not rendering in headers and footers
- Previous implementation used
pdf.text()which relies on jsPDF's built-in fonts (Helvetica, Times, Courier) without Unicode support - Now renders headers/footers using html2canvas (same as main content)
- Browser's font rendering engine properly handles Hindi, Chinese, Arabic, and all Unicode characters
- Added 'Noto Sans Devanagari' to default font stack for Hindi support
- Previous implementation used
Improvements #
- Headers and footers now use the same rendering pipeline as main content for consistent output
- Temporary DOM elements used for header/footer rendering are properly cleaned up
2.0.0 #
New Features #
-
Intelligent Page Breaks for Bytes Mode
- Integrated html2pdf.js for smart content splitting
- Respects CSS
page-break-inside: avoidon elements - Respects CSS
page-break-before: alwaysandpage-break-after: always - Respects
orphansandwidowsCSS properties - Finds natural break points between block elements
- Falls back to legacy html2canvas + jsPDF if html2pdf.js is not available
- Configurable
pageBreakModesoption:css,avoidAll,legacy
-
Headers/Footers with Dynamic Page Numbers
{{page}}placeholder replaced with current page number (1, 2, 3...){{pages}}placeholder replaced with total page count{{date}}placeholder for current date{{time}}placeholder for current time{{datetime}}placeholder for date and time- Headers/footers render on every page at consistent positions
- Configurable
headerHeightMmandfooterHeightMm - Configurable
headerFontSizeandfooterFontSize - Optional separator lines with
showHeaderLineandshowFooterLine - Content area automatically adjusted for header/footer space
Breaking Changes #
- Bytes mode now requires html2pdf.js for intelligent page breaks (recommended)
- Update your
web/index.htmlto use the new script:<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
Improvements #
- Added utility CSS classes:
.page-break-before,.keep-with-next - Cards, sections, and invoice items now have
page-break-inside: avoidby default - Better orphan/widow handling for paragraphs and list items
1.0.2 #
- Add repository metadata
1.0.1 #
- Documentation improvements
1.0.0 #
Initial release of QuickHtmlPdf - a fast HTML to PDF conversion package for Flutter Web.
Features #
-
Hybrid PDF Generation Strategy
PdfOutput.download: Instant PDF via native browser print (~50ms)PdfOutput.bytes: Returns PDF asUint8Listusing html2canvas + jsPDF
-
Template Engine
{{key}}- HTML-escaped interpolation{{nested.path}}- Dot notation for nested objects{{{rawHtml}}}- Unescaped HTML insertion{{#each items}}...{{/each}}- Loop blocks{{@index}},{{@index1}}- Loop index variables
-
PDF Options
- Page formats: A4, Letter, Legal
- Orientations: Portrait, Landscape
- Custom margins
- Custom header and footer HTML
- Configurable scale and image quality
-
Print CSS
- Automatic
@pagerules for correct sizing - Table header repetition across pages
- Page break utilities (
.page-break,.no-break)
- Automatic
-
Large Document Support
- Chunked rendering for 200+ page documents
- Memory-efficient sequential page processing
-
Developer Experience
- Debug mode with timing logs
- Clear error messages with phase information
UnsupportedErroron non-web platforms