PDF import: pattern swatches read inside an alpha mask keep their images; a luminosity mask test

This commit is contained in:
bflatastic 2026-10-13 07:35:32 +07:00
parent fce90e3144
commit 44d170f059
2 changed files with 25 additions and 9 deletions

View File

@ -1340,10 +1340,15 @@ impl<'p> Builder<'p> {
{
return Some(Paint::Pattern { pattern: name.clone(), xf });
}
// A swatch is shared with uses outside an alpha mask, and pattern paints keep their
// colours there: its images are read as they are.
let white = std::mem::take(&mut self.white_images);
let art = self.nested(Rect::ZERO, |b| {
// A cell whose content can't be decoded is empty.
let _ = t.interpret(b, Affine::IDENTITY, stroke);
})?;
});
self.white_images = white;
let art = art?;
let sig = paints(&art);
let entry = self.pattern_keys.entry(key).or_default();
entry.0 += 1;

View File

@ -78,11 +78,9 @@ fn the_mask(d: &Document) -> (Node, OpacityMask) {
(n, m)
}
/// An alpha soft mask whose art is an image with its own alpha (cairo, and so Inkscape, rasterizes
/// masks so): the image is read white with that alpha, so the mask's luminance is the alpha, not
/// the image's colour.
#[test]
fn an_alpha_soft_mask_of_an_image_uses_the_images_alpha() {
/// The two pixels of the image a soft mask of kind `s` draws: black, opaque on the left and clear
/// on the right (its own `/SMask`).
fn mask_image_pixels(s: &str) -> [[u8; 4]; 2] {
let g = first_extra(1);
let group = stream(
&format!(
@ -91,7 +89,6 @@ fn an_alpha_soft_mask_of_an_image_uses_the_images_alpha() {
),
"q 100 0 0 100 0 0 cm /Im0 Do Q",
);
// Black pixels, opaque on the left and clear on the right.
let image = stream(
&format!(
"/Type /XObject /Subtype /Image /Width 2 /Height 1 /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /ASCIIHexDecode /SMask {} 0 R",
@ -103,7 +100,7 @@ fn an_alpha_soft_mask_of_an_image_uses_the_images_alpha() {
stream("/Type /XObject /Subtype /Image /Width 2 /Height 1 /ColorSpace /DeviceGray /BitsPerComponent 8 /Filter /ASCIIHexDecode", "FF00>");
let bytes = one_page(
"/GS0 gs 1 0 0 rg 0 0 100 100 re f",
&format!("/ExtGState << /GS0 << /SMask << /Type /Mask /S /Alpha /G {g} 0 R >> >> >>"),
&format!("/ExtGState << /GS0 << /SMask << /Type /Mask /S /{s} /G {g} 0 R >> >> >>"),
&[&group, &image, &smask],
);
let d = open(&bytes);
@ -116,7 +113,21 @@ fn an_alpha_soft_mask_of_an_image_uses_the_images_alpha() {
});
let [key] = keys.as_slice() else { panic!("one image in the mask art: {keys:?}") };
let px = image::load_from_memory(&d.images[key].bytes).unwrap().to_rgba8();
assert_eq!((px.get_pixel(0, 0).0, px.get_pixel(1, 0).0), ([255, 255, 255, 255], [255, 255, 255, 0]));
[px.get_pixel(0, 0).0, px.get_pixel(1, 0).0]
}
/// An alpha soft mask whose art is an image with its own alpha (some PDF writers rasterize masks
/// so): the image is read white with that alpha, so the mask's luminance is the alpha, not the
/// image's colour (ISO 32000-1 11.5.2: an alpha mask takes the group's alpha, not its colour).
#[test]
fn an_alpha_soft_mask_of_an_image_uses_the_images_alpha() {
assert_eq!(mask_image_pixels("Alpha"), [[255, 255, 255, 255], [255, 255, 255, 0]]);
}
/// A luminosity soft mask takes the group's colour (ISO 32000-1 11.5.3): its image stays black.
#[test]
fn a_luminosity_soft_mask_of_an_image_keeps_the_images_colour() {
assert_eq!(mask_image_pixels("Luminosity"), [[0, 0, 0, 255], [0, 0, 0, 0]]);
}
#[test]