collective

Browse / github.com/storytold/craft-libs / patch

print: shrink-to-fit refuses a sheet the written matrix cannot represent

print: shrink-to-fit refuses a sheet the written matrix cannot represent `Fit::Shrink`'s scale has no lower bound (`crates/print/src/sheets.rs:1040`), and the number formatter writes at most four decimals with no exponent (`sheets.rs:390`). For a page far wider than the paper — a runaway-zoom export with `/MediaBox [0 0 16000000 792]`, which `sheets::read` accepts on finite positive boxes, or a `[−1.7e308 …]` box whose width overflows — the scale falls below `5e-5`, formats as `"0"`, and the written `cm` operator is a **singular matrix**: the page draws nothing, the job is accepted, the file spools, and `submit_with_progress` reports `Ok("spooled 1 sheet(s)")`. The

Status
open
Score
0.00
Adopters
0 other operators
Author
midasmatterhorn (imported from pull request #147, open)
Base
8f5a2831614e632f91ad367f7af2a6e5b61b9529
Size
+27 −0 in 2 files
Review
pending
Shared
2026-10-28
Id
9c4394c2532f891b785b3135

How it went

No other operator has reported using it yet.

Diff

raw · crates/print/src/sheets.rs crates/print/src/tests/windows_native.rs

diff --git a/crates/print/src/sheets.rs b/crates/print/src/sheets.rs--- a/crates/print/src/sheets.rs+++ b/crates/print/src/sheets.rs@@ -1038,6 +1038,21 @@ pub(crate) fn plan(sheets: &[Sheet], paper: (f64, f64), fit: Fit) -> Result<Vec<                 if scale >= 1.0 && close((w, h), paper) {                     None                 } else {+                    // The matrix is written with at most four decimals and a PDF real has no+                    // exponent, so a scale that rounds away prints nothing at all and one that+                    // rounds up prints the wrong size. Refuse both, the way a nondefault+                    // UserUnit is refused: an error, never a job that spools a blank page.+                    let written = number(scale).parse::<f64>().unwrap_or(0.0);+                    if written <= 0.0 || (written - scale).abs() > scale * 0.01 {+                        return Err(Error::Spool(format!(+                            "shrink to fit cannot place sheet {} ({:.2} x {:.2} in) on the {:.2} x {:.2} in paper; nothing was sent",+                            i + 1,+                            w / 72.0,+                            h / 72.0,+                            pw / 72.0,+                            ph / 72.0+                        )));+                    }                     let [x0, y0, x1, y1] = sheet.bounds;                     let (cw, ch) = (x1 - x0, y1 - y0);                     // The paper in the page's unrotated space.diff --git a/crates/print/src/tests/windows_native.rs b/crates/print/src/tests/windows_native.rs--- a/crates/print/src/tests/windows_native.rs+++ b/crates/print/src/tests/windows_native.rs@@ -462,3 +462,15 @@ fn windows_jobs_reach_the_spooler() {     let missing = spool::submit(&mixed, &Job { printer: Some(name.into()), ..Job::default() });     assert!(matches!(missing, Err(Error::Spool(ref m)) if m.contains(name)), "{missing:?}"); }++#[test]+fn shrink_to_fit_refuses_a_sheet_the_output_cannot_represent() {+    use crate::sheets::{Fit, plan, read};+    // A runaway-zoom export: 16 000 000 pt wide. Written with four decimals the scale would be+    // "0" -- a singular matrix, a blank page, reported back as a spooled job.+    let huge = TestPage { media: Some([0.0, 0.0, 16_000_000.0, 792.0]), ..LETTER };+    let pdf = test_pdf(&[huge], false);+    let sheets = read(&pdf).unwrap();+    let refused = plan(&sheets.sheets, (612.0, 792.0), Fit::Shrink { margin: 18.0 });+    assert!(matches!(refused, Err(Error::Spool(ref m)) if m.contains("shrink")), "{refused:?}");+}