buildPdf method
@@@ 2026-08-21 How PDFs are generated: native WebView + PrintDocumentAdapter.
Dead ends tried along the way, kept here so nobody circles back to them later:
- Printing.layoutPdf() -- brings up Android's native print preview, which depends on the system's Print Spooler service. This device doesn't have that service, so it hangs forever on "preparing preview" -- our own code never even gets called.
- Printing.convertHtml() -- a known bug in the printing package (github.com/DavBfr/dart_pdf/issues/1517): on some devices, the call neither returns nor throws -- it just hangs. Tested shrinking the HTML from 9.48 million characters down to 9,853 characters and it still hung, so it isn't a content-size issue.
- htmltopdfwidgets's HTMLToPdf() -- pure-Dart parsing, doesn't
hang, but it doesn't handle : a 6,097-character detail
table's HTML only parsed into 2 blocks -- the table effectively
disappeared, and it also blew past MultiPage's page-count limit.
The current approach: hand the HTML to the system's native WebView to lay out, then write a vector PDF (selectable text, scales without quality loss) via createPrintDocumentAdapter(). An independent implementation from (2), so it doesn't share the code path that hangs; unlike (3), layout is handled by a real browser engine, so
s and CSS render exactly as written. Chinese fonts don't need to be embedded -- the WebView can see the system's built-in Noto Sans CJK.
Implementation
Future<Uint8List> buildPdf({
String orient = 'P',
String paper = 'A4',
String fontAsset = 'assets/fonts/NotoSansTC-Regular.ttf',
}) async {
// The Web platform doesn't support this path; uses _webPrint instead
if (kIsWeb) {
await _webPrintBody(buildHtml(), paper, orient);
return Uint8List(0);
}
debugPrint('[buildPdf] starting (native WebView + PrintDocumentAdapter)');
// @@@ 2026-08-21 PDF now reuses exactly the same CSS as the on-screen
// preview.
//
// PDF used to go through reportCssPrint(), a separate stylesheet
// from the on-screen preview's reportCssScreen(), which resulted in
// fields wrapping heavily. Tried shrinking the font size and
// locking a 760px canvas with proportional scaling (printFitCss) to
// fix it -- both were guesses at "what actually fits."
//
// Testing then found: a phone in portrait's WebView preview width
// is about 720px, nearly identical to A4 portrait's printable width
// (718px), and it laid out with zero wrapping, ruler line intact.
// Which means the width was never the problem -- the two sides just
// had different CSS. No more guessing needed: just reuse the
// stylesheet already proven to lay out correctly.
//
// reportCssScreen carries screen-only decoration (gray background,
// shadow, inter-page gaps); @media print strips that out for
// printing, with the layout/font size/column widths left untouched.
final html = _fullHtmlLikeScreen(buildHtml(), paper, orient);
debugPrint('[buildPdf] step 1/2 HTML assembled (${html.length} chars '
'total, reusing the on-screen preview CSS, print font size derived '
'from the $kRulerChars-character ruler line), handing off to the '
'native WebView for PDF conversion...');
final result = await HtmlToPdfConverter().convertHtmlToPdfBytes(html: html);
debugPrint(
'[buildPdf] step 2/2 PDF generation complete (${result.length} bytes)');
return result;
}