mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-31 22:16:59 -06:00
Pptx/Render: picture fills with rotWithShape=0 stay upright
A shape's picture fill marked not to rotate with the shape turned and flipped with it (rotWithShape was neither kept nor written: always 1). PowerPoint keeps such a picture upright, stretched (or tiled) over the box the rotated shape covers on the slide and clipped to the shape. Now read, written (PictureFill::upright) and drawn so. Wild synthetic set: picture fills on rotated/flipped shapes 12.89 -> 3.87. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3ec12412e0
commit
cdaeb2fbde
@@ -82,6 +82,19 @@ pub fn fill_path(
|
||||
ctx.fill_path(path);
|
||||
}
|
||||
Fill::Picture(pf) => {
|
||||
// `rotWithShape="0"`: upright on the slide, stretched over the box the turned shape
|
||||
// covers, clipped to it.
|
||||
let c = m.as_coeffs();
|
||||
let turned = c[1].abs() > 1e-9 || c[2].abs() > 1e-9 || c[0] < 0.0 || c[3] < 0.0;
|
||||
if pf.upright && turned && m.determinant().abs() > 1e-12 {
|
||||
let on_slide = m.transform_rect_bbox(bounds);
|
||||
let on_path = m * path.clone();
|
||||
ctx.set_transform(Affine::IDENTITY);
|
||||
ctx.push_clip_layer(&on_path);
|
||||
picture(ctx, pres, &resolved(rctx, pf, ph), on_slide, Affine::IDENTITY, None);
|
||||
ctx.pop_layer();
|
||||
return;
|
||||
}
|
||||
ctx.push_clip_layer(path);
|
||||
picture(ctx, pres, &resolved(rctx, pf, ph), bounds, m, None);
|
||||
ctx.pop_layer();
|
||||
|
||||
@@ -683,3 +683,30 @@ fn stars_and_right_arrows_have_sharp_corners_by_default() {
|
||||
assert!(close(img.pixel(95, 95), WHITE, 30), "{:?}", img.pixel(95, 95));
|
||||
assert!(deckcraft_geom::preset::mitred_by_default("rightArrow") && !deckcraft_geom::preset::mitred_by_default("leftArrow"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_upright_picture_fill_stays_upright_in_a_turned_shape() {
|
||||
use deckcraft_model::style::PictureFill;
|
||||
let blue = Rgba::rgb(0, 0, 255);
|
||||
for upright in [false, true] {
|
||||
let mut p = Presentation::default();
|
||||
let media = p.add_media("t.png", "image/png", half_red_half_blue());
|
||||
// Half red (left) half blue picture in a 200 × 200 square turned a quarter (and so
|
||||
// covering the same square).
|
||||
let sh = Shape {
|
||||
id: ShapeId(30),
|
||||
xfrm: Some(Xfrm { rot: 90.0, ..Xfrm::new(100.0, 100.0, 200.0, 200.0) }),
|
||||
fill: Some(Fill::Picture(PictureFill { media, upright, ..Default::default() })),
|
||||
line: no_line(),
|
||||
..Default::default()
|
||||
};
|
||||
std::sync::Arc::make_mut(&mut p.slides[0]).shapes = vec![sh];
|
||||
let img = render_slide(&p, 0, &RenderOpts { scale: 1.0, ..Default::default() });
|
||||
let (left, top) = (img.pixel(110, 200), img.pixel(200, 110));
|
||||
if upright {
|
||||
assert!(close(left, RED, 30) && close(img.pixel(290, 200), blue, 30), "upright: red left {left:?}");
|
||||
} else {
|
||||
assert!(close(top, RED, 30), "turned with the shape: red at the top {top:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user