From 72cf92e5403ca84df4ae9572464bd77cf40586e6 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 05:00:26 -0800 Subject: [PATCH] Document Info: Spot Color Objects lists colorized images and skips clear gradients A grayscale or 1-bit image colorized with a spot color now counts as a Spot Color Object. A gradient whose stops are all at an opacity of 0 draws nothing, so its spot stops no longer list the object. --- crates/engine/src/cmd/docinfo.rs | 62 +++++++++++++++++++++++++++++--- docs/mcp.md | 5 +-- docs/target-app-parity.md | 2 +- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/cmd/docinfo.rs b/crates/engine/src/cmd/docinfo.rs index 3d2f554dc8..4ed63e7ec1 100644 --- a/crates/engine/src/cmd/docinfo.rs +++ b/crates/engine/src/cmd/docinfo.rs @@ -18,7 +18,7 @@ pub fn specs() -> Vec { "Document Info", ["Window", "Document Info"], None, - "{selectionOnly?, category?: document|objects|graphicStyles|brushes|spotColors|patterns|gradients|symbols|fonts|fontDetails|linkedImages|embeddedImages|repeats (only that one in sections and the text), format?: \"text\" (→ {text}: the report Document Info › Save… and Package write)} → {document, objects: {paths, compoundPaths, groups, …}, pathLength: pt (the paths' total length, compound paths' included, guides left out), fonts, images, swatches, graphicStyleNames (with selectionOnly: the styles the selected objects are linked to), brushNames (the Brushes panel's brushes; with selectionOnly: the brushes the selected objects' strokes use), repeatObjects: [{id, name, type: radial|grid|mirror, instances, rows?, cols?}] (the Repeat objects, those in groups and other repeats included; with selectionOnly: those in the selection), spotObjects: [{id, name, spotColors}] (the objects filled or stroked with a spot color swatch, as a solid color or in a gradient's stops, at a tint above 0, each once, by the rules patternObjects follows, with the spot colors they use; Registration and process colors are not spot colors; objects.spots counts them), spotColorNames and spotColors (the spot colors those objects use), patternObjects: [{id, name, patterns}] (the objects filled or stroked with a pattern, each once, those in groups and type whose drawn characters use one included, hidden fills and strokes, strokes with no width, guides, compound path members' own fills and strokes, images' fills and strokes without a shape and characters that draw nothing left out, with the patterns they use, which objects.patterns counts; with selectionOnly: those in the selection), patternNames (the patterns those objects use), gradientObjects: [{id, name, gradients}] (the objects filled or stroked with a gradient, by the same rules, with the gradients they use: a linked gradient swatch's name, else Linear gradient, Radial gradient or Freeform gradient; objects.gradients counts them), gradientNames (the gradients those objects use), …, sections: [{id, title, rows: [[label, value]]}] (the panel's categories; fontDetails: each font's file and whether its license lets it be embedded)}. An image linked to a file outside the automation read roots has link null, and its Linked Images row names the file alone", + "{selectionOnly?, category?: document|objects|graphicStyles|brushes|spotColors|patterns|gradients|symbols|fonts|fontDetails|linkedImages|embeddedImages|repeats (only that one in sections and the text), format?: \"text\" (→ {text}: the report Document Info › Save… and Package write)} → {document, objects: {paths, compoundPaths, groups, …}, pathLength: pt (the paths' total length, compound paths' included, guides left out), fonts, images, swatches, graphicStyleNames (with selectionOnly: the styles the selected objects are linked to), brushNames (the Brushes panel's brushes; with selectionOnly: the brushes the selected objects' strokes use), repeatObjects: [{id, name, type: radial|grid|mirror, instances, rows?, cols?}] (the Repeat objects, those in groups and other repeats included; with selectionOnly: those in the selection), spotObjects: [{id, name, spotColors}] (the objects filled or stroked with a spot color swatch, as a solid color or in the stops of a gradient with a stop above opacity 0, at a tint above 0, and grayscale images colorized with one, each once, by the rules patternObjects follows, with the spot colors they use; Registration and process colors are not spot colors; objects.spots counts them), spotColorNames and spotColors (the spot colors those objects use), patternObjects: [{id, name, patterns}] (the objects filled or stroked with a pattern, each once, those in groups and type whose drawn characters use one included, hidden fills and strokes, strokes with no width, guides, compound path members' own fills and strokes, images' fills and strokes without a shape and characters that draw nothing left out, with the patterns they use, which objects.patterns counts; with selectionOnly: those in the selection), patternNames (the patterns those objects use), gradientObjects: [{id, name, gradients}] (the objects filled or stroked with a gradient, by the same rules, with the gradients they use: a linked gradient swatch's name, else Linear gradient, Radial gradient or Freeform gradient; objects.gradients counts them), gradientNames (the gradients those objects use), …, sections: [{id, title, rows: [[label, value]]}] (the panel's categories; fontDetails: each font's file and whether its license lets it be embedded)}. An image linked to a file outside the automation read roots has link null, and its Linked Images row names the file alone", has_doc, info ), @@ -289,8 +289,8 @@ fn spot_swatches(d: &Document) -> HashSet<&str> { /// The spot colors paint `p` prints (`spots`: [`spot_swatches`]): the spot swatch a solid color is /// linked to, or those a gradient's stops are linked to, each once, in the order of the stops. A -/// link at a tint of 0 prints no ink, and a color that only looks like a spot color is not linked -/// to one. +/// link at a tint of 0 prints no ink, a gradient whose stops are all at an opacity of 0 draws +/// nothing, and a color that only looks like a spot color is not linked to one. fn spot_labels(spots: &HashSet<&str>, p: &Paint) -> Vec { let mut out: Vec = vec![]; let mut add = |swatch: &Option, tint: f32| { @@ -304,7 +304,7 @@ fn spot_labels(spots: &HashSet<&str>, p: &Paint) -> Vec { }; match p { Paint::Solid { swatch, tint, .. } => add(swatch, *tint), - Paint::Gradient(g) => g.gradient.stops.iter().for_each(|s| add(&s.swatch, s.tint)), + Paint::Gradient(g) if g.gradient.stops.iter().any(|s| s.opacity > 0.0) => g.gradient.stops.iter().for_each(|s| add(&s.swatch, s.tint)), _ => {} } out @@ -523,6 +523,18 @@ fn info(s: &mut Session, p: &Value) -> Result { } } } + // A grayscale or 1-bit image colorized with a spot color prints it, while its pixels take + // the color, as the renderer draws it. + if let NodeKind::Image(im) = &n.kind + && let Some(colorize) = n.extras().colorize.as_deref() + && d.image_blob(im).is_some_and(|b| b.takes_colorize()) + { + for name in spot_label(colorize) { + if !used_spots.contains(&name) { + used_spots.insert(0, name); + } + } + } // Objects counts the objects that Spot Color Objects, Pattern Objects and Gradient Objects list. if !used_spots.is_empty() { let name = n.display_name(); @@ -1174,6 +1186,48 @@ mod tests { assert!(!text.contains("Spare") && !text.contains("Look"), "{text}"); } + /// Spot Color Objects lists a grayscale image colorized with a spot color, and leaves out a + /// gradient whose stops are all at an opacity of 0, which draws nothing. + #[test] + fn spot_color_objects_take_colorized_images_and_skip_clear_gradients() { + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 200, "height": 200})).unwrap(); + s.execute("swatch.new", &json!({"name": "Ink", "color": {"model": "cmyk", "c": 0, "m": 1, "y": 1, "k": 0}, "spot": true})).unwrap(); + let mut bytes = Vec::new(); + image::DynamicImage::ImageLuma8(image::GrayImage::from_raw(2, 1, vec![0, 255]).unwrap()) + .write_to(&mut std::io::Cursor::new(&mut bytes), image::ImageFormat::Png) + .unwrap(); + let image = s + .edit("Place", |d, sel| { + d.images.insert("gray".into(), vectorcraft_doc::ImageBlob::png(bytes)); + let id = d.alloc_id(); + let im = vectorcraft_doc::ImageObject { + key: "gray".into(), + width: 2, + height: 1, + xf: Default::default(), + link: None, + placement: Default::default(), + pixels: None, + }; + d.insert(d.default_layer(), 0, Node::new(id, NodeKind::Image(Box::new(im))))?; + sel.set([id]); + Ok(id) + }) + .unwrap(); + s.execute("paint.setFill", &json!({"swatch": "Ink"})).unwrap(); + assert!(s.doc().unwrap().doc.node(image).unwrap().extras().colorize.is_some()); + let clear = s.execute("shape.rectangle", &json!({"x": 0, "y": 50, "width": 10, "height": 10})).unwrap()["id"].clone(); + let stops = + json!([{"offset": 0, "color": "#000", "swatch": "Ink", "opacity": 0}, {"offset": 1, "color": "#fff", "swatch": "Ink", "opacity": 0}]); + s.execute("paint.setFill", &json!({"ids": [clear], "gradient": {"kind": "linear", "stops": stops}})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [clear], "none": true})).unwrap(); + let i = s.execute("document.info", &json!({})).unwrap(); + let name = s.doc().unwrap().doc.node(image).unwrap().display_name(); + assert_eq!(i["spotObjects"], json!([{"id": image.0, "name": name, "spotColors": ["Ink"]}]), "{i}"); + assert_eq!(i["objects"]["spots"].as_u64(), Some(1), "{}", i["objects"]); + } + /// Document Info › Objects counts an object with a gradient or a pattern on both its fill and /// its stroke once, and with Selection Only a selection that holds a group and an object inside /// it counts that object once. diff --git a/docs/mcp.md b/docs/mcp.md index 05d6951cd2..707435b07c 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -6095,8 +6095,9 @@ stroked with a gradient, by the same rules as `patternObjects`, with the gradien swatch a gradient is linked to, else `Linear gradient`, `Radial gradient` or `Freeform gradient`; the gradients category lists them as Gradient Objects and `objects.gradients` counts them; gradient swatches no art uses are not listed; with `selectionOnly`, those in the selection), `gradientNames` (the gradients those objects use), `spotObjects` -(`[{id, name, spotColors}]`: every object filled or stroked with a spot color swatch, as a solid color or in a -gradient's stops, at a tint above 0, by the same rules as `patternObjects`, with the spot colors it uses; a process +(`[{id, name, spotColors}]`: every object filled or stroked with a spot color swatch, as a solid color or in the +stops of a gradient with a stop above opacity 0, at a tint above 0, and every grayscale image colorized with one, by +the same rules as `patternObjects`, with the spot colors it uses; a process color, even one of the same color, and Registration are not spot colors; the spotColors category lists them as Spot Color Objects and `objects.spots` counts them; spot swatches no art uses are not listed; with `selectionOnly`, those in the selection), `spotColorNames` and `spotColors` (the spot colors those objects use) and `pathLength` (pt: the total length of the paths, compound paths' pieces included and guides left diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 900787b283..d60a1a6c0e 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1352,7 +1352,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Document Info has a Repeat Objects category after Embedded Images. It lists each radial, grid and mirror Repeat object by name with its kind and the number of instances it makes. For a grid it gives the rows and columns instead, followed by the number of instances it draws when that is fewer. With Selection Only it lists the Repeat objects in the selection. The Document Info report and Package's report list them too (`document.info {category: "repeats"}`, `repeatObjects`). Document Info's Pattern Objects category lists the objects whose fill or stroke is a pattern, the category the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes, each by name with the patterns it uses. With Selection Only it lists those in the selection, and the Document Info report and Package's report list them too (`document.info {category: "patterns"}`, `patternObjects`). Listing an object with a pattern fill and a pattern stroke once, listing objects inside groups and type objects whose characters use a pattern, and leaving out hidden fills and strokes, strokes with no width, guides, a compound path member's own fills and strokes, an image's fills and strokes without a shape, characters the type does not draw and patterns no art uses, and counting the listed objects in Objects, are VectorCraft's own rules. Test: `cmd::docinfo::tests::info_lists_the_pattern_objects` (vectorcraft-engine). Document Info's Gradient Objects category lists the objects whose fill or stroke is a gradient, the category the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes as the count of objects with gradient fills or strokes. Each object is listed by name with the gradients it uses. With Selection Only it lists those in the selection, and the Document Info report and Package's report list them too (`document.info {category: "gradients"}`, `gradientObjects`). Naming a linked gradient by its swatch and any other gradient by its kind, and leaving out paint that draws nothing by the rules Pattern Objects follows, gradient swatches no art uses, gradient meshes and gradients drawn by effects, are VectorCraft's own rules. Objects counts the listed objects as Gradient Objects. Test: `cmd::docinfo::tests::info_lists_the_gradient_objects` (vectorcraft-engine). - Document Info's Spot Color Objects category lists the objects that use spot colors, the category the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes as objects using spot color separations. Each object is listed by name with the spot colors it uses. With Selection Only it lists those in the selection, and the Document Info report and Package's report list them too (`document.info {category: "spotColors"}`, `spotObjects`). Taking a solid color or a gradient stop linked to a spot swatch at a tint above 0, leaving out process colors that look the same, Registration, paint that draws nothing by the rules Pattern Objects follows, spot swatches no art uses and spot colors inside patterns, symbols, opacity masks, effects and placed files, are VectorCraft's own rules. Objects counts the listed objects as Spot Color Objects. Test: `cmd::docinfo::tests::info_lists_the_spot_color_objects` (vectorcraft-engine). + Document Info's Spot Color Objects category lists the objects that use spot colors, the category the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes as objects using spot color separations. Each object is listed by name with the spot colors it uses. With Selection Only it lists those in the selection, and the Document Info report and Package's report list them too (`document.info {category: "spotColors"}`, `spotObjects`). Taking a solid color or a gradient stop linked to a spot swatch at a tint above 0 and a grayscale image colorized with one, leaving out process colors that look the same, Registration, gradients whose stops are all at an opacity of 0, paint that draws nothing by the rules Pattern Objects follows, spot swatches no art uses and spot colors inside patterns, symbols, opacity masks, effects and placed files, are VectorCraft's own rules. Objects counts the listed objects as Spot Color Objects. Tests: `cmd::docinfo::tests::info_lists_the_spot_color_objects` and `spot_color_objects_take_colorized_images_and_skip_clear_gradients` (vectorcraft-engine). Document Info's Save… writes the category the panel shows, with Selection Only applied, as the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes ("Exports the selected category's visible information to a text file"). With All Categories shown it writes every category, which is VectorCraft's own rule (`docInfo.save {category}`). Test: `panels::doc_info::tests::save_writes_the_category_the_panel_shows`. The Brushes panel menu's Select All Unused selects the brushes that are not used in the document, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. A brush counts as used when a stroke paints with it in the art (opacity masks included), in symbols, graph designs, pattern tiles or graphic styles, or in the art of a used brush (`brush.unused`). A rename in Brush Options and Delete Brush change every stroke that paints with the brush: in the layers and opacity masks, symbols, graph designs, pattern tiles, graphic styles and the art of other brushes. A rename gives those strokes the new name, and Delete Brush makes them plain strokes, in one undo step.