Merge pull request #2030 from midasmatterhorn/fix/issue-2004-ai-opacity-mask

eps: an opacity mask in the editing data is named, not drawn whole (#2004)
This commit is contained in:
BFlat
2026-10-16 03:58:38 +07:00
committed by GitHub
2 changed files with 18 additions and 1 deletions
+10 -1
View File
@@ -1356,8 +1356,17 @@ impl<'a> Reader<'a> {
self.frames.iter().all(|f| !matches!(&f.kind, Kind::Layer(a) if !a.visible))
}
/// An object's dictionary: its name (`AIArtName`, or the XML id `AI10_ArtUID`).
/// An object's dictionary: its name (`AIArtName`, or the XML id `AI10_ArtUID`), and the
/// opacity mask it may carry.
fn art_dictionary(&mut self, o: &Obj) {
// A mask (`/Mask`, keyed `AI9_Mask`): its art is on `X=` … `X+` lines, and drawing the
// object without it shows what the mask hides, so it must be named (#2004). A mask that
// is disabled changes nothing.
if let Some(mask) = o.obj("AI9_Mask").or_else(|| o.obj("Mask"))
&& mask.nums("Disabled").first().copied() != Some(1.0)
{
self.unreadable("opacity masks");
}
let name = match o.text("AIArtName") {
Some(name) => name,
None => match o.obj("AI10_ArtUID").and_then(Obj::content_text) {
+8
View File
@@ -667,6 +667,14 @@ fn what_it_doesnt_read_is_an_error_where_it_shows() {
assert!(e.contains("pattern fills"), "{e}");
let e = read(stream(&layer("/SymbolInstance :\n(Dot) /SymbolRef ,\n;\n")).as_bytes()).unwrap_err();
assert!(e.contains("symbols"), "{e}");
// An opacity mask (#2004): drawing the layer's art whole would show what the mask hides, so
// the mask is named and the file is read from its PDF part instead.
let mask = "%_/ArtDictionary :\n%_/XMLUID : (M) ; (AI10_ArtUID) ,\n%_/Mask :\n%_1 /Bool (Linked) ,\n%_0 /Bool (Disabled) ,\n%_X=\n\
%_0 0 0 1 k\n%_0 0 m\n%_5 0 L\n%_5 5 L\n%_0 5 L\n%_0 0 L\n%_f\n%_X+\n%_ (Art) ,\n%_1 /Bool (Clipping) ,\n%_0 /Bool (Inverted) ,\n%_; (AI9_Mask) ,\n%_;\n";
let e = read(stream(&layer(mask)).as_bytes()).unwrap_err();
assert!(e.contains("opacity masks"), "{e}");
// A mask that is disabled changes nothing: the art reads.
read_ok(&layer(&mask.replace("%_0 /Bool (Disabled)", "%_1 /Bool (Disabled)")));
// On a layer that doesn't show, it is left out of it.
let hidden = "%AI5_BeginLayer\n0 1 1 1 0 0 0 1 255 79 79 0 50 0 Lb\n(Off) Ln\n1 2 frobnicate\n(J) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p\nLB\n%AI5_EndLayer--\n";
let r = read_ok(hidden);