diff --git a/ROADMAP.md b/ROADMAP.md index f26825ebf..be66e5e88 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -537,7 +537,7 @@ Newest first. - **2026-10-12** · On screen, blend modes blend with the objects below and not with the paper, as in InDesign and in PDF export: a Color Dodge or Screen object over blank paper shows as it is. No readiness percentage change. - **2026-10-12** · A gradient feather on a group fades the group as one, on screen and in PDF. Before, the screen ignored it, and PDF masked the group with a box of zero size because a group has no path of its own. No readiness percentage change. - **2026-10-12** · In a CMYK transparency blend space (print documents), blend modes blend in CMYK on screen as in InDesign. Color Dodge with 100% K text turns coloured areas under it white and leaves the paper alone, where RGB blending changed almost nothing. PDF export already did this. No readiness percentage change. -- **2026-10-12** · Double-clicking a frame handle fits the frame to its content as in InDesign (a corner fits both sides, an edge only its own). Object › Fitting › Fit Frame to Content now fits text frames to their text too, growing them for overset text. No readiness percentage change. +- **2026-10-12** · Double-clicking a frame handle fits the frame to its content as in InDesign (a corner fits both sides, an edge only its own). Object › Fitting › Fit Frame to Content now fits text frames to their text too, growing them for overset text; fitting leaves locked objects and objects on locked layers as they are. No readiness percentage change. - **2026-10-11** · Moving a sheared and rotated text frame no longer leaves trails of text behind: canvas repaints cover the frame's whole text box, not just its outline. No readiness percentage change. - **2026-10-12** · Words in capitals hyphenate like capitalized words (FERRUM COM-MODI), as in InDesign; Hyphenate Capitalized Words off keeps them whole. No readiness percentage change. - **2026-10-12** · Load Swatches refuses an `.ase` file with more than 100,000 colors, and `swatch.load` merges a file of 100,000 colors in under 0.1 s (it took 9.6 s of CPU in a test build). No readiness percentage change. diff --git a/crates/engine/src/cmd/object.rs b/crates/engine/src/cmd/object.rs index 352ad0e39..efd04d528 100644 --- a/crates/engine/src/cmd/object.rs +++ b/crates/engine/src/cmd/object.rs @@ -567,7 +567,7 @@ pub fn specs() -> Vec { "Fitting", ["Object", "Fitting"], None, - "{mode: fillProportionally (preserves aspect ratio, covers frame, clips overflow)|fitProportionally (preserves aspect ratio, shows whole image, may leave empty space)|fitContentToFrame (stretches content to frame)|centerContent (keeps current size, positions at fitting alignment)|fitFrameToContent (resizes frame to content), axis?: both|height|width (frame-local sides Fit Frame to Content moves; default both), ids?}", + "{mode: fillProportionally (preserves aspect ratio, covers frame, clips overflow)|fitProportionally (preserves aspect ratio, shows whole image, may leave empty space)|fitContentToFrame (stretches content to frame)|centerContent (keeps current size, positions at fitting alignment)|fitFrameToContent (resizes frame to content), axis?: both|height|width (frame-local sides Fit Frame to Content moves; default both), ids?} — locked objects and objects on locked layers are left as they are", has_selection, fit ), @@ -2781,7 +2781,7 @@ fn fitted_text_frame(d: &Document, id: ItemId, height: bool, width: bool) -> Res // First fit the height at the existing width, then measure the complete text's width. // Recheck height after fitting width, since a small width change can change a line break. if height { - out = fit_text_height(out, &mut compose)?; + out = fit_text_height(out, id, tf.options.inset[2], &mut compose)?; } if width && tf.options.columns <= 1 { let cs = compose(out); @@ -2792,13 +2792,18 @@ fn fitted_text_frame(d: &Document, id: ItemId, height: bool, width: bool) -> Res } } if height { - out = fit_text_height(out, &mut compose)?; + out = fit_text_height(out, id, tf.options.inset[2], &mut compose)?; } } Ok(Some(out)) } -fn fit_text_height(mut rect: Rect, compose: &mut impl FnMut(Rect) -> Arc) -> Result { +fn fit_text_height( + mut rect: Rect, + id: ItemId, + inset_bottom: f64, + compose: &mut impl FnMut(Rect) -> Arc, +) -> Result { let mut low = 1.0; let limit = 20_000.0; let mut high = rect.height().clamp(1.0, limit); @@ -2825,6 +2830,13 @@ fn fit_text_height(mut rect: Rect, compose: &mut impl FnMut(Rect) -> Arc rect.y1 && *b - rect.y1 <= 0.02) { + rect.y1 = b; + } Ok(rect) } @@ -4731,7 +4743,11 @@ mod fit_text_frame_tests { assert!(b.x0 == 100.0 && b.width() > 100.0 && b.width() < 250.0, "{b:?}"); let d = s.doc().unwrap().doc.clone(); let sid = d.item(id).unwrap().text_frame().unwrap().story; - assert!(!s.cache.get(&d, sid, None).is_overset(), "still fits"); + let cs = s.cache.get(&d, sid, None); + assert!(!cs.is_overset(), "still fits"); + // The bottom is on the baseline, so a frame scaled up with its text still fits. + let baseline = cs.frame(id).unwrap().lines[0].baseline; + assert!((b.y1 - baseline).abs() < 1e-9, "{b:?} {baseline}"); } #[test]