🐛 Fix mask group drop shadow in PDF export (#12362)

This commit is contained in:
Elena Torró 2026-10-19 12:07:51 +02:00 committed by GitHub
parent f4f581ab74
commit c401151541
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 30 deletions

View File

@ -42,6 +42,7 @@ pub fn render_to_pdf(
let metadata = skia::pdf::Metadata {
creator: "Penpot".to_string(),
producer: "Penpot (Skia PDF)".to_string(),
raster_dpi: Some(300.0),
..Default::default()
};

View File

@ -822,34 +822,7 @@ fn render_group(
let child_opts = opts.for_children_of(element);
if masked {
// Mirror the GPU mask: render all children (including the mask shape)
// as content, then re-draw the mask silhouette (the group's first child)
// with DstIn to clip everything to it.
let paint = Paint::default();
let subtree_bounds = element.extrect(tree, scale);
canvas.save_layer(
&skia::canvas::SaveLayerRec::default()
.bounds(&subtree_bounds)
.paint(&paint),
);
for child_id in &children {
render_tree_inner(shared, canvas, child_id, tree, scale, &child_opts)?;
}
if let Some(mask_id) = element.mask_id() {
let mut mask_paint = Paint::default();
mask_paint.set_blend_mode(skia::BlendMode::DstIn);
canvas.save_layer(
&skia::canvas::SaveLayerRec::default()
.bounds(&subtree_bounds)
.paint(&mask_paint),
);
render_tree_inner(shared, canvas, mask_id, tree, scale, &child_opts)?;
canvas.restore(); // mask layer
}
canvas.restore(); // composition layer
render_masked_children(shared, canvas, element, &children, tree, scale, &child_opts)?;
} else {
for child_id in &children {
render_tree_inner(shared, canvas, child_id, tree, scale, &child_opts)?;
@ -863,6 +836,42 @@ fn render_group(
Ok(())
}
fn render_masked_children(
shared: &mut RenderResources,
canvas: &Canvas,
element: &Shape,
children: &[Uuid],
tree: ShapesPoolRef,
scale: f32,
child_opts: &TreeOpts,
) -> Result<()> {
let subtree_bounds = element.extrect(tree, scale);
canvas.save_layer(
&skia::canvas::SaveLayerRec::default()
.bounds(&subtree_bounds)
.paint(&Paint::default()),
);
for child_id in children {
render_tree_inner(shared, canvas, child_id, tree, scale, child_opts)?;
}
if let Some(mask_id) = element.mask_id() {
let mut mask_paint = Paint::default();
mask_paint.set_blend_mode(skia::BlendMode::DstIn);
canvas.save_layer(
&skia::canvas::SaveLayerRec::default()
.bounds(&subtree_bounds)
.paint(&mask_paint),
);
render_tree_inner(shared, canvas, mask_id, tree, scale, child_opts)?;
canvas.restore(); // mask layer
}
canvas.restore(); // composition layer
Ok(())
}
// ---------------------------------------------------------------------------
// Frames
// ---------------------------------------------------------------------------
@ -991,8 +1000,12 @@ fn render_container_drop_shadows(
let children = children_paint_order(tree, element);
let child_opts = opts.for_children_of(element);
for child_id in &children {
render_tree_inner(shared, canvas, child_id, tree, scale, &child_opts)?;
if element.is_masked_group() {
render_masked_children(shared, canvas, element, &children, tree, scale, &child_opts)?;
} else {
for child_id in &children {
render_tree_inner(shared, canvas, child_id, tree, scale, &child_opts)?;
}
}
canvas.restore();