Skip to content

fix(print): pages with Type 3 fonts go to Windows printers as pictures (#2684) - #3021

Open
guillehr2 wants to merge 1 commit into
storytold:mainfrom
guillehr2:fix/2684-print-type3-pages-as-images
Open

guillehr2 wants to merge 1 commit into
storytold:mainfrom
guillehr2:fix/2684-print-type3-pages-as-images

Conversation

@guillehr2

Copy link
Copy Markdown
Contributor

What this changes

Printing to a printer on Windows no longer hands the Windows print component pages that draw with Type 3 fonts. Those pages are rendered by PdfCraft at the Print as image resolution (300 dpi unless changed in Advanced) and sent as pictures; every other page of the job stays vector, as before. Fixes #2684.

Why. @mtlcl410 reproduced the crash on Windows 11 with a virtual printer (FinePrint) and narrowed it to the Type 3 font Chrome wrote for the recipe's text: a glyph followed by code 0x0F was enough. Windows.Data.Pdf renders the same page fine on its own, so the crash is in the print path (Windows.Data.Pdf drawing into Direct2D's print control, through craft-print). It is native (0xC0000094 or 0xC0000005), so nothing in PdfCraft can catch it and the whole app closes. Print as image (#1965) avoided it. This takes direction 2 of that comment: only the pages that need it become pictures, and only on Windows.

How.

  • pdfcraft_print::type3_pages(doc, pages, content) finds the pages whose print draws with a Type 3 font. It reads resources, not content streams: the page's fonts (inherited ones too), and through form XObjects (forms in forms), tiling patterns, soft-mask groups (a shading pattern's too) and the appearances of the annotations that print under the chosen Comments & Forms. It never panics and is bounded: a seen-set per page, a depth limit (64) and a budget of 20,000 reads per page. A page whose resources go past the limit or the budget counts as using a Type 3 font, because the picture is the safe side. Broken entries (a /Resources that is a number, missing objects, a /SMask that is /None, cyclic forms) are not Type 3 fonts.
  • print::Settings::type3_raster: Option<Raster> asks for this. Session::print_pdf renders the pages found the same way Print as image does (print_raster::pictures, sharing its render step with rasterize) and pdfcraft_print::impose_with_pictures draws each picture in its page's place, over the displayed page and turned back into user space, so imposition, rotation, crops and labels are unchanged. The picture already shows what prints of the page, so its annotations are not drawn again. The other pages go through page_form as before. impose is impose_with_pictures with no pictures.
  • spool::TYPE3_AS_IMAGE (cfg!(windows)) says where this applies. The Print dialog sets type3_raster for a printer on Windows, not for Save as PDF, not in a browser and not when Print as image is already on. CUPS gets the PDF as it was.
  • Visible: when a page being printed uses Type 3 fonts, the dialog says "Pages that use Type 3 fonts print as images, because Windows can close PdfCraft while printing them. The other pages print as usual." under the printer settings, like the two-sided note. The log says which pages went as images (log::info!).
  • Headless: doc_print takes type3_as_image (default on when printing to a printer on Windows, off otherwise) and lists the pages it sent as pictures (1-based) as type3_as_image in its result.

How you verified it

  • cargo fmt --check
  • clippy with -D warnings on the crates I changed: cargo clippy -p pdfcraft-print -p pdfcraft-engine -p pdfcraft-automation -p pdfcraft-ui-egui --all-targets -- -D warnings is clean
  • tests pass on the crates I changed: cargo test -p pdfcraft-print (28 passed), cargo test -p pdfcraft-engine --lib (276 passed, 1 ignored), cargo test -p pdfcraft-automation --test automation (176 passed), cargo test -p pdfcraft-ui-egui --lib --test print (428 passed, 1 ignored; 14 passed). I ran the test files that cover this change, not every ui-egui test file.
  • cargo xtask layers, cargo xtask assets and cargo xtask parity: layers OK (37 crates), assets OK after re-pinning the 22 catalog hashes in ATTRIBUTION.toml, parity OK

What I reproduced, and what I didn't. I could not reproduce the crash itself.

  • I made two Type 3 test files locally (neither is committed): a real Chrome PDF, printed by headless Chrome from an HTML page set in Windows' variable fonts Bahnschrift and Segoe UI Variable (Chrome writes variable fonts as Type 3: six Type 3 fonts, y-flipped /FontMatrix), and a hand-written Skia-style Type 3 font with the issue's sequence, a glyph followed by code 0x0F whose d1 box is all zeros.
  • I printed them through pdfcraft-cli run (doc_print, printer "Microsoft Print to PDF" named explicitly, type3_as_image: false, so the old vector path). That printer's PORTPROMPT: port asks where to save the file. The prompt couldn't be answered from a headless run, so those jobs stopped when the job was created, with 0x8007003F (the print was cancelled), or waited for an answer. Job creation comes before any page is drawn, so Direct2D's print control never drew a Type 3 page here: the crash is neither reproduced nor ruled out with Microsoft Print to PDF. One run with this change, where the Type 3 page went as a picture, completed (spooled 1 sheet(s)).
  • So the fix rests on @mtlcl410's FinePrint reproduction and on the fact that Print as image avoided the crash there. I did not print to paper.

Tests. I saw each new test fail with the fix reverted, then pass with it:

  • pdfcraft-print: type3_fonts_are_found_through_forms_patterns_soft_masks_and_printed_annotations (detection off: left: [] right: [1, 2, 3, 4, 5, 6, 8, 9]), a_picture_replaces_its_page_on_the_sheet_and_the_other_pages_stay_vector (impose_with_pictures ignoring the pictures: one picture each, left: 0 right: 2), type3_detection_survives_malformed_cyclic_deep_and_huge_resources (detection off: left: []; without the seen-set the cyclic forms page came back as Type 3 through the depth limit: left: [3, 7, 8, 9]; without the depth limit and the budget: left: [9]).
  • pdfcraft-engine: pages_with_type3_fonts_print_as_pictures_and_the_others_stay_vector (detection off: left: [] right: [0, 2]; detection on but print_pdf not routing the pages: two pictures, and no Type 3 font left to send, left: (1, 0) right: (0, 2), so the Type 3 font went to the printer), printing_type3_pages_as_pictures_survives_broken_resources (without the seen-set: the cyclic page was reported, so the is_empty assertion failed).
  • pdfcraft-automation: doc_print_sends_type3_pages_as_images (detection off: no type3_as_image in the result).
  • pdfcraft-ui-egui (tests/print.rs): type3_pages_go_to_windows_printers_as_images_and_the_dialog_says_so (detection off: left: [] right: [0]; the dialog not setting type3_raster: left: None right: Some(Raster { dpi: 300.0, grayscale: false })). On Windows this test checks the dialog's note and settings; elsewhere it checks that nothing changes.

Never-crash

  • No new unwrap/expect/panic!/unreachable!/todo! outside tests
  • No indexing or slicing with input-derived positions; checked/saturating arithmetic; the walk over resources is bounded by a seen-set, a depth limit and a read budget; a rendered page is checked against its width × height before it is used
  • No unsafe
  • The crash is native and not reproducible in CI; the synthetic tests pin the decision (a Chrome-style Type 3 page goes as a picture, the others stay vector, and Windows is never handed a Type 3 font) and the hostile inputs of the new walk

Provenance

  • No asset from an Adobe product
  • No font files committed. The test fonts are a few bytes of Type 3 glyph procedures written in the tests.
  • No GPL/AGPL code
  • Behaviour from ISO 32000-2 (Type 3 fonts, resources, patterns, soft masks), from the issue and from black-box observation of Chrome's output
  • No new assets. The new interface string is in all 22 catalogs as machine translations (cargo xtask assets --write re-pinned their hashes); native speakers are welcome to improve them.

Security

  • --root confinement unchanged: doc_print writes through the same resolve path
  • MCP and the control channel untouched
  • No network access

Parity and reach

  • parity/acrobat-features.toml: print.print-as-image notes say pages with Type 3 fonts go this way on their own on Windows printers, with the new tests as evidence
  • Headless: doc_print type3_as_image, with an end-to-end test
  • crates/print/README.md (it still said print as image was not done) and a ROADMAP progress-log line

Anything a reviewer should look at first

  • Resources, not content. A page whose resources name a Type 3 font it never draws with is printed as a picture too. Chrome writes a resource dictionary per page with only the fonts the page uses, so I kept the cheap, bounded check. Reading content streams would be more precise but slower on big documents, and the dialog reads every page when it opens on Windows.
  • Form fields only. With Form fields only, the page content isn't printed, so only the appearances of the fields count. If a field's appearance uses a Type 3 font, the page goes as a picture, and like Print as image today that picture also shows the page content: the renderer can't draw the fields alone. I don't expect any real file to hit this, but it's a judgement call.
  • Windows only. TYPE3_AS_IMAGE is cfg!(windows), so macOS and Linux and Save as PDF keep vector output. Direction 1 (Type 3 text to paths, still vector) or direction 3 (a helper process) could replace this later. The picture pages are larger in the spool, as with Print as image ([Bug Report] [PdfCraft] Print Error #2514).
  • Comments on picture pages. A picture page shows its comments the way Print as image does: left out for Document and Form fields only, drawn otherwise, so with Document and stamps those pages also show their other comments.
  • Pictures and impose. impose_with_pictures is a new public function next to impose, instead of building a mixed source document. That keeps page numbers, crops (Label) and Comments & Forms exactly as they were for the vector pages.

storytold#2684)

Printing a page that draws with a Type 3 font, as Chrome writes them,
could close the whole app on Windows: the crash is native, inside
Windows.Data.Pdf drawing into Direct2D's print control, so nothing in
PdfCraft can catch it. Print as image avoided it.

On Windows printers the pages that draw with Type 3 fonts are now
rendered at the Print as image resolution and sent as pictures; the
other pages stay vector. Save as PDF, browsers, macOS and Linux keep
the pages as they are.

- pdfcraft-print: type3_pages() reads each page's fonts, form
  XObjects, tiling patterns, soft masks and the appearances of the
  annotations that print, bounded by a seen-set, a depth limit and a
  read budget (past them, the page counts as using a Type 3 font).
  Settings::type3_raster asks for it; impose_with_pictures() draws a
  page's picture in its place; spool::TYPE3_AS_IMAGE is cfg!(windows).
- engine: print_pdf renders the pages found (print_raster::pictures,
  sharing its render step with rasterize); print_type3_pages().
- ui: the Print dialog sets it for Windows printers, says so when a
  page being printed uses Type 3 fonts, and logs the pages. New string
  in all 22 catalogs (machine translations).
- automation: doc_print type3_as_image, on by default for Windows
  printers, lists the pages sent as pictures.

Fixes storytold#2684

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PDFcraft Crashes when I try to print a hard copy a document

1 participant