From 3e7cca3a0d2d07a8dad021fd4012878993a570fc Mon Sep 17 00:00:00 2001 From: aiongg Date: Mon, 12 Oct 2026 05:40:04 +0000 Subject: [PATCH] =?UTF-8?q?Integration:=20text=20wrap=20Same=20As=20Clippi?= =?UTF-8?q?ng=20follows=20the=20graphic's=20clipping=20path=20(#450=20?= =?UTF-8?q?=C3=97=20#384)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #384's contour wrap followed the frame's path for Same As Clipping, which was all a graphic could clip to; #450 gives graphics clipping paths. contour_path takes the clip region (Graphic::clip_region) when the graphic has one. Test: a triangle clip widening downwards, Same As Clipping, left side: lines beside the narrow top run further in than those at the base. --- crates/compose/src/lib.rs | 10 ++++++++-- crates/compose/src/tests.rs | 32 ++++++++++++++++++++++++++++++++ crates/doc/src/item.rs | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/crates/compose/src/lib.rs b/crates/compose/src/lib.rs index e02c46670..f3034b3fb 100644 --- a/crates/compose/src/lib.rs +++ b/crates/compose/src/lib.rs @@ -462,9 +462,15 @@ fn wrap_exclusion( } /// The outline a contour wrap of `kind` around frame `it` follows, in the frame's inner space. -/// Bounding Box follows the part of the placed graphic the frame shows; detected edges, alpha -/// channels and image paths need the image, so they follow the frame's path (as other frames do). +/// Bounding Box follows the part of the placed graphic the frame shows; Same As Clipping follows +/// the graphic's clipping path when it has one; detected edges, alpha channels and image paths need +/// the image, so they follow the frame's path (as other frames do). fn contour_path(it: &Item, kind: designcraft_doc::ContourType) -> designcraft_geom::BezPath { + if kind == designcraft_doc::ContourType::SameAsClipping + && let Some((path, _)) = it.graphic().and_then(|g| g.clip_region()) + { + return path; + } if kind == designcraft_doc::ContourType::BoundingBox && let Some(r) = it.graphic().and_then(|g| g.shown_bounds(it.inner_bounds())) { diff --git a/crates/compose/src/tests.rs b/crates/compose/src/tests.rs index 3ba37f944..50ac421a3 100644 --- a/crates/compose/src/tests.rs +++ b/crates/compose/src/tests.rs @@ -422,6 +422,38 @@ fn wrap_on_both_sides_sets_text_left_then_right_of_the_object() { assert!(!cs.is_overset()); } +/// Same As Clipping follows a placed graphic's clipping path: beside a triangle that widens +/// downwards, text reaches further in at the narrow top than at the wide bottom. +#[test] +fn same_as_clipping_wrap_follows_the_clipping_path() { + use designcraft_geom::{PathData, Point, SubPath}; + let (mut d, sid, _) = doc_with(&[LOREM; 4].join(" "), Rect::new(0.0, 0.0, 300.0, 800.0), ParaAttrs::default()); + let lid = d.default_layer(); + let id = ItemId(d.alloc()); + let mut it = + designcraft_doc::Item::new(id, lid, designcraft_doc::Shape::Rectangle, designcraft_geom::shapes::rectangle(Rect::new(150.0, 100.0, 300.0, 300.0))); + let triangle = PathData::single(SubPath::polyline(&[Point::new(75.0, 0.0), Point::new(150.0, 200.0), Point::new(0.0, 200.0)], true)); + it.content = designcraft_doc::Content::Graphic(designcraft_doc::Graphic { + asset: designcraft_doc::AssetId(1), + size: (150.0, 200.0), + xf: designcraft_geom::Affine::translate((150.0, 100.0)), + clip: Some(std::sync::Arc::new(designcraft_doc::ClippingPath::new(designcraft_doc::ClippingKind::DetectEdges, triangle))), + auto_fit: Default::default(), + fit_align: 0, + crop: [0.0; 4], + wrap: Default::default(), + }); + it.wrap.mode = WrapMode::Contour; + it.wrap.side = WrapSide::LeftSide; + it.wrap.contour = designcraft_doc::ContourType::SameAsClipping; + d.insert_item(SpreadRef::Doc(0), it, None).unwrap(); + let cs = compose_story(&d, sid, &ComposeOptions::default()); + let beside: Vec<&Line> = cs.frames[0].lines.iter().filter(|l| l.baseline - l.ascent > 100.0 && l.baseline + l.descent < 300.0).collect(); + let (top, bottom) = (beside.first().unwrap().x1, beside.last().unwrap().x1); + assert!(top > bottom + 40.0, "top {top}, bottom {bottom}"); + assert!(bottom < 160.0, "the bottom lines stop near the triangle's base: {bottom}"); +} + #[test] fn one_sided_wrap_leaves_the_other_side_empty() { for (side, right) in [(WrapSide::RightSide, true), (WrapSide::LeftSide, false), (WrapSide::LargestArea, false)] { diff --git a/crates/doc/src/item.rs b/crates/doc/src/item.rs index 5ff0e4956..531081e71 100644 --- a/crates/doc/src/item.rs +++ b/crates/doc/src/item.rs @@ -477,7 +477,7 @@ pub enum ContourType { PhotoshopPath, /// The frame's path. GraphicFrame, - /// The graphic's clipping path, which is the frame's path. + /// The graphic's clipping path, or the frame's path when it has none. #[default] SameAsClipping, }