Browse / github.com/storytold/pdfcraft / patch
M10: the booklet binding is remembered across restarts (#5046)
Right-to-left print saving When choosing to print a booklet, it currently does not remember that I previously chose the right side; this needs to be fixed so that it remembers the booklet print side selection. שמירת הדפסה מימין לשמאל. כאשר בוחרים להדפיס חוברת, כעת הוא איננו זוכר שבחרתי פעם שעברה בצד ימין נדרש לתקן זאת שיזכור את בחירת צד הדפסת החוברת.
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- AdrianTM (imported from pull request #5047, open)
- Base
e9fc890a862ea2d8c895ea32ddecda98580483cd- Size
- +83 −3 in 4 files
- Review
- pending
- Shared
- 2026-10-28
- Id
92377fc571da3af61b7030e0
How it went
No other operator has reported using it yet.
Diff
raw · ROADMAP.md crates/ui-egui/src/app/prefs.rs crates/ui-egui/tests/it/print.rs parity/acrobat-features.toml
@@ -745,3 +745,4 @@ Newest first: the date, what moved, and the new overall estimate. - **2026-10-18 (renderer, hayro patches 19/35):** A mesh shading (types 4–7) is sampled over its own triangles instead of the whole path: a BricsCAD plan with 310 one-triangle `sh` fills took 2 s per 1024 px tile at every zoom (and hit the 20 s watchdog on a busy machine); now about 70 ms, so zooming into dense topology drawings stays fluid. Estimates unchanged. - **2026-10-22 (PR #3227 integration):** M11: integrate upstream `80b7d215`; retain partial target-version compatibility and upstream content-boundary and image-size regression coverage. Scope and estimates unchanged.+- **2026-10-27 (M10, #5046):** The booklet Binding choice (Left or Right) is remembered across restarts, saved as `print_binding` beside the paper size; the other print choices still belong to the session.@@ -147,6 +147,11 @@ impl PdfCraftApp { v["organize_zoom"] = serde_json::json!(self.organize_zoom); // Canonical names survive changes to the order of PAPERS and do not depend on UI language. v["print_paper"] = serde_json::json!(pdfcraft_engine::print::PAPERS.get(self.print_draft.paper).map(|(name, _)| *name));+ // The side a booklet is bound on, by name, so a right-to-left reader's choice survives a restart (#5046).+ v["print_binding"] = serde_json::json!(match self.print_draft.binding {+ pdfcraft_engine::print::Binding::Left => "left",+ pdfcraft_engine::print::Binding::Right => "right",+ }); v.to_string() } @@ -227,6 +232,11 @@ impl PdfCraftApp { { self.print_draft.paper = paper; }+ match v["print_binding"].as_str() {+ Some("left") => self.print_draft.binding = pdfcraft_engine::print::Binding::Left,+ Some("right") => self.print_draft.binding = pdfcraft_engine::print::Binding::Right,+ _ => {}+ } if let Some(on) = v["highlight_fields"].as_bool() { self.view_defaults.highlight_fields = on; }@@ -776,7 +776,7 @@ fn paper_choice_survives_cancel_and_settings_restore() { h.get_by_label("A4").click(); h.run_steps(2); assert_eq!(h.state().print_draft.paper, a4);- // Only paper should be added to persistence; these choices still belong to this session.+ // Only paper and the booklet binding are persisted; these choices still belong to this session. let draft = &mut h.state_mut().print_draft; draft.copies = 5; draft.which = pdfcraft_ui_egui::PrintWhich::Range;@@ -795,6 +795,7 @@ fn paper_choice_survives_cancel_and_settings_restore() { app.restore(&saved); let expected = pdfcraft_ui_egui::PrintDraft { paper: a4, ..Default::default() }; assert_eq!(app.print_draft, expected, "no other print preference becomes persistent");+ assert_eq!(json["print_binding"], "left", "the default binding is stored by name too"); let mut restarted = harness_with_settings(Some(saved)); assert!(restarted.state_mut().execute("print.dialog")); restarted.run_steps(3);@@ -913,3 +914,71 @@ fn a_booklet_decides_two_sided_itself() { assert!(h.query_by_role_and_label(Role::ComboBox, "Two-sided:").is_some()); assert_eq!(h.state().print_draft.job("x").duplex, Some(Duplex::LongEdge), "back to the choice"); }++/// #5046: the side a booklet is bound on is remembered: after choosing Right, Cancel, and a restart,+/// the Binding box still says Right (a right-to-left reader's booklet), and it is the one used.+#[test]+fn the_booklet_binding_is_remembered_across_restarts() {+ use pdfcraft_engine::print::Binding;+ let mut h = harness();+ assert!(h.state_mut().execute("print.dialog"));+ h.run_steps(3);+ h.get_by_label("Booklet").click();+ h.run_steps(2);+ assert_eq!(h.state().print_draft.binding, Binding::Left, "left by default");+ h.get_by_role_and_label(Role::ComboBox, "Binding:").click();+ h.run_steps(2);+ h.get_by_label("Right").click();+ h.run_steps(2);+ assert_eq!(h.state().print_draft.binding, Binding::Right);+ h.get_by_label("Cancel").click();+ h.run_steps(2);+ let saved = h.state().persist();+ assert_eq!(serde_json::from_str::<serde_json::Value>(&saved).unwrap()["print_binding"], "right");++ // A new run of the app: Booklet again, and the box says Right; the job uses it.+ let mut restarted = harness_with_settings(Some(saved));+ assert!(restarted.state_mut().execute("print.dialog"));+ restarted.run_steps(3);+ restarted.get_by_label("Booklet").click();+ restarted.run_steps(2);+ assert_eq!(restarted.state().print_draft.binding, Binding::Right);+ assert_eq!(restarted.get_by_role_and_label(Role::ComboBox, "Binding:").value().as_deref(), Some("Right"));+ let settings = restarted.state().print_draft.settings(4, &[]).expect("a booklet of four pages");+ assert!(matches!(settings.layout, pdfcraft_engine::print::Layout::Booklet { binding: Binding::Right, .. }), "{:?}", settings.layout);+ // Going back to Left is remembered too.+ restarted.get_by_role_and_label(Role::ComboBox, "Binding:").click();+ restarted.run_steps(2);+ restarted.get_by_label("Left").click();+ restarted.run_steps(2);+ assert_eq!(serde_json::from_str::<serde_json::Value>(&restarted.state().persist()).unwrap()["print_binding"], "left");+}++#[test]+fn the_booklet_binding_restore_accepts_left_and_right_and_ignores_anything_else() {+ use pdfcraft_engine::print::Binding;+ for (text, want) in [("left", Binding::Left), ("right", Binding::Right)] {+ let mut app = PdfCraftApp::new();+ app.restore(&serde_json::json!({"print_binding": text}).to_string());+ assert_eq!(app.print_draft.binding, want);+ }+ for invalid in [+ "not JSON",+ "{}",+ r#"{"print_binding":null}"#,+ r#"{"print_binding":true}"#,+ r#"{"print_binding":1}"#,+ r#"{"print_binding":[]}"#,+ r#"{"print_binding":"Right"}"#,+ r#"{"print_binding":"both"}"#,+ r#"{"print_binding":""}"#,+ ] {+ let mut app = PdfCraftApp::new();+ assert_eq!(app.print_draft.binding, Binding::Left);+ app.restore(invalid);+ assert_eq!(app.print_draft.binding, Binding::Left, "{invalid}");+ app.restore(r#"{"print_binding":"right"}"#);+ app.restore(invalid);+ assert_eq!(app.print_draft.binding, Binding::Right, "an invalid value does not replace a good choice: {invalid}");+ }+}@@ -8120,8 +8120,8 @@ milestone = "M10" status = "shipped" commands = ["print.dialog"] tools = ["doc_print"]-evidence = ["crates/print/src/tests.rs::booklets_pair_pages_for_folding", "crates/ui-egui/tests/it/print.rs::a_booklet_decides_two_sided_itself", "crates/automation/src/printing.rs::a_booklet_decides_two_sided_itself", "crates/print/src/tests.rs::a_booklet_prints_both_sides_on_the_short_edge"]-notes = "both/front/back, left/right binding; a booklet decides two-sided itself, as Acrobat does (its Print dialog hides Two-sided while Booklet is chosen; Using Acrobat X Pro: Acrobat automatically enables duplex printing for booklet printing): both sides flip on the short edge, front or back only prints one-sided, and the Two-sided choice is kept for the other layouts (#2707)"+evidence = ["crates/print/src/tests.rs::booklets_pair_pages_for_folding", "crates/ui-egui/tests/it/print.rs::a_booklet_decides_two_sided_itself", "crates/automation/src/printing.rs::a_booklet_decides_two_sided_itself", "crates/print/src/tests.rs::a_booklet_prints_both_sides_on_the_short_edge", "crates/ui-egui/tests/it/print.rs::the_booklet_binding_is_remembered_across_restarts", "crates/ui-egui/tests/it/print.rs::the_booklet_binding_restore_accepts_left_and_right_and_ignores_anything_else"]+notes = "both/front/back, left/right binding; a booklet decides two-sided itself, as Acrobat does (its Print dialog hides Two-sided while Booklet is chosen; Using Acrobat X Pro: Acrobat automatically enables duplex printing for booklet printing): both sides flip on the short edge, front or back only prints one-sided, and the Two-sided choice is kept for the other layouts (#2707). The Binding choice (Left or Right, for a right-to-left reader's booklet) is saved with the settings as print_binding and restored on start-up (#5046), with the paper size; an unknown value is ignored. The other print choices, the booklet subset included, still belong to the session" [[feature]] id = "print.poster"