From 1d7690029f7d60bcf1586871428bff31d754bb1f Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Thu, 12 Nov 2026 00:59:50 -0800 Subject: [PATCH] Document Info: Spot Color Objects lists the objects that use spot colors The spotColors category is now titled Spot Color Objects and lists each object filled or stroked with a spot color, by name with the spot colors it uses, instead of the spot swatch names. A solid color or a gradient stop linked to a spot swatch at a tint above 0 counts; process colors that look the same and Registration do not. Paint that draws nothing is left out by the rules Pattern Objects and Gradient Objects follow, and type whose characters use a spot color is listed. Objects counts the listed objects as Spot Color Objects. document.info returns spotObjects, spotColorNames and objects.spots; spotColors now holds the spot colors those objects use. The report that Document Info > Save... and Package write lists the same rows. The Spot Color Objects, Pattern Objects and Gradient Objects titles have a row in each of the 27 complete catalogs, and a test checks that every complete catalog has the three. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/docinfo.rs | 277 +++++++++++++++++++++----- crates/engine/src/tests/package.rs | 2 +- crates/ui-egui/src/i18n/bg.tsv | 3 + crates/ui-egui/src/i18n/bs.tsv | 3 + crates/ui-egui/src/i18n/ca.tsv | 3 + crates/ui-egui/src/i18n/cs.tsv | 3 + crates/ui-egui/src/i18n/da.tsv | 3 + crates/ui-egui/src/i18n/de.tsv | 3 + crates/ui-egui/src/i18n/el.tsv | 3 + crates/ui-egui/src/i18n/es.tsv | 3 + crates/ui-egui/src/i18n/fi.tsv | 3 + crates/ui-egui/src/i18n/fr.tsv | 3 + crates/ui-egui/src/i18n/hi.tsv | 3 + crates/ui-egui/src/i18n/hu.tsv | 3 + crates/ui-egui/src/i18n/id.tsv | 3 + crates/ui-egui/src/i18n/it.tsv | 3 + crates/ui-egui/src/i18n/ja.tsv | 3 + crates/ui-egui/src/i18n/ka.tsv | 3 + crates/ui-egui/src/i18n/ko.tsv | 3 + crates/ui-egui/src/i18n/nl.tsv | 3 + crates/ui-egui/src/i18n/pl.tsv | 3 + crates/ui-egui/src/i18n/pt-br.tsv | 3 + crates/ui-egui/src/i18n/ru.tsv | 3 + crates/ui-egui/src/i18n/sv.tsv | 3 + crates/ui-egui/src/i18n/tests.rs | 22 ++ crates/ui-egui/src/i18n/tr.tsv | 3 + crates/ui-egui/src/i18n/uk.tsv | 3 + crates/ui-egui/src/i18n/vi.tsv | 3 + crates/ui-egui/src/i18n/zh-hans.tsv | 3 + crates/ui-egui/src/i18n/zh-hant.tsv | 3 + crates/ui-egui/src/panels/doc_info.rs | 15 +- docs/mcp.md | 7 +- docs/target-app-parity.md | 1 + 33 files changed, 352 insertions(+), 53 deletions(-) diff --git a/crates/engine/src/cmd/docinfo.rs b/crates/engine/src/cmd/docinfo.rs index ff1fbe3591..3d2f554dc8 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), 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 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", has_doc, info ), @@ -40,7 +40,7 @@ pub const CATEGORIES: [(&str, &str); 13] = [ ("objects", "Objects"), ("graphicStyles", "Graphic Styles"), ("brushes", "Brushes"), - ("spotColors", "Spot Colors"), + ("spotColors", "Spot Color Objects"), ("patterns", "Pattern Objects"), ("gradients", "Gradient Objects"), ("symbols", "Symbols"), @@ -52,7 +52,7 @@ pub const CATEGORIES: [(&str, &str); 13] = [ ]; /// The object counts of `document.info` and their labels. -const OBJECT_LABELS: [(&str, &str); 18] = [ +const OBJECT_LABELS: [(&str, &str); 19] = [ ("paths", "Paths"), ("compoundPaths", "Compound Paths"), ("groups", "Groups"), @@ -63,6 +63,7 @@ const OBJECT_LABELS: [(&str, &str); 18] = [ ("symbolInstances", "Symbol Instances"), ("gradients", "Gradient Objects"), ("patterns", "Pattern Objects"), + ("spots", "Spot Color Objects"), ("meshes", "Gradient Meshes"), ("blends", "Blends"), ("envelopes", "Envelopes"), @@ -131,7 +132,7 @@ fn repeat_entry(d: &Document, n: &Node, spec: &RepeatSpec) -> (Value, (String, S /// The Document Info categories of `d` (only `category` when given) from `info` (what /// `document.info` reports), its fonts, its image objects, the rows of its Repeat objects and the -/// rows of its objects filled or stroked with a pattern and with a gradient. +/// rows of its objects filled or stroked with a spot color, with a pattern and with a gradient. #[allow(clippy::too_many_arguments)] fn sections( d: &Document, @@ -139,6 +140,7 @@ fn sections( fonts: &BTreeSet, images: &[ImageRow], repeats: &[(String, String)], + spot_objects: &[(String, String)], pattern_objects: &[(String, String)], gradient_objects: &[(String, String)], category: Option<&str>, @@ -183,7 +185,7 @@ fn sections( } "graphicStyles" => names("graphicStyleNames"), "brushes" => names("brushNames"), - "spotColors" => names("spotColors"), + "spotColors" => spot_objects.to_vec(), "patterns" => pattern_objects.to_vec(), "gradients" => gradient_objects.to_vec(), "symbols" => names("symbols"), @@ -273,15 +275,50 @@ fn gradient_label(p: &Paint) -> Option { } } -/// The type object `n` holds and the runs its layout draws, when it uses a pattern or a gradient: -/// the runs with a glyph outline (list markers and ruby included) or an underline or strikethrough -/// bar. Spaces and line breaks draw no outline. A type object's characters carry their own fill and -/// stroke, and the type object's own fills and strokes paint on its characters, so only those runs -/// paint. The type is laid out with its inline graphics' bounds resolved from `d`'s symbols, as the -/// renderer lays it out. -fn painted_runs<'a>(d: &Document, n: &'a Node) -> Option<(&'a vectorcraft_doc::TextObject, BTreeSet)> { +/// The names of `d`'s spot color swatches: the swatches whose prints go to a plate of their own +/// ([`vectorcraft_doc::inks::inks`]). A name held by several swatches is the first one's, as +/// [`Document::swatch`] finds it, and the Registration swatch prints on every plate, so it is not +/// one. +fn spot_swatches(d: &Document) -> HashSet<&str> { + let mut first: BTreeMap<&str, bool> = BTreeMap::new(); + for s in d.swatches_iter() { + first.entry(s.name.as_str()).or_insert(s.spot && s.paint.color().is_some()); + } + first.into_iter().filter(|&(name, spot)| spot && name != vectorcraft_color::swatch::REGISTRATION).map(|(name, _)| name).collect() +} + +/// 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. +fn spot_labels(spots: &HashSet<&str>, p: &Paint) -> Vec { + let mut out: Vec = vec![]; + let mut add = |swatch: &Option, tint: f32| { + if let Some(name) = swatch + && tint > 0.0 + && spots.contains(name.as_str()) + && !out.contains(name) + { + out.push(name.clone()); + } + }; + match p { + Paint::Solid { swatch, tint, .. } => add(swatch, *tint), + Paint::Gradient(g) => g.gradient.stops.iter().for_each(|s| add(&s.swatch, s.tint)), + _ => {} + } + out +} + +/// The type object `n` holds and the runs its layout draws, when it uses a spot color, a pattern or +/// a gradient (`spots`: [`spot_swatches`]): the runs with a glyph outline (list markers and ruby +/// included) or an underline or strikethrough bar. Spaces and line breaks draw no outline. A type +/// object's characters carry their own fill and stroke, and the type object's own fills and strokes +/// paint on its characters, so only those runs paint. The type is laid out with its inline +/// graphics' bounds resolved from `d`'s symbols, as the renderer lays it out. +fn painted_runs<'a>(d: &Document, spots: &HashSet<&str>, n: &'a Node) -> Option<(&'a vectorcraft_doc::TextObject, BTreeSet)> { let NodeKind::Text(t) = &n.kind else { return None }; - let is_listed = |p: &Paint| matches!(p, Paint::Pattern { .. } | Paint::Gradient(_)); + let is_listed = |p: &Paint| matches!(p, Paint::Pattern { .. } | Paint::Gradient(_)) || !spot_labels(spots, p).is_empty(); let own = n.appearance.items.iter().any(|i| match i { vectorcraft_doc::AppearanceItem::Fill(f) => is_listed(&f.paint), vectorcraft_doc::AppearanceItem::Stroke(s) => is_listed(&s.paint), @@ -297,43 +334,53 @@ fn painted_runs<'a>(d: &Document, n: &'a Node) -> Option<(&'a vectorcraft_doc::T Some((t, painted)) } -/// The patterns or gradients (`label`: [`pattern_label`] or [`gradient_label`]) that `n`'s own -/// fills and strokes paint, each once. A hidden fill or stroke and a stroke with no width paint -/// nothing, and neither does any fill or stroke of a guide (drawn in the guide color), of a compound -/// path's member (`members`: the compound path's own fills and strokes paint it) or of type whose -/// layout draws nothing (`painted_runs`). An image's fills and strokes paint only where an effect -/// gives them a shape. +/// The names a paint is listed by in a Document Info category: its spot colors, its pattern or its +/// gradient. +type PaintNames<'a> = dyn Fn(&Paint) -> Vec + 'a; + +/// The spot colors, patterns or gradients (`label`: [`spot_labels`], [`pattern_label`] or +/// [`gradient_label`], each paint's names) that `n`'s own fills and strokes paint, each once. A +/// hidden fill or stroke and a stroke with no width paint nothing, and neither does any fill or +/// stroke of a guide (drawn in the guide color), of a compound path's member (`members`: the +/// compound path's own fills and strokes paint it) or of type whose layout draws nothing +/// (`painted_runs`). An image's fills and strokes paint only where an effect gives them a shape. fn own_paints( d: &Document, n: &Node, members: &HashSet<*const Node>, painted_runs: Option<&(&vectorcraft_doc::TextObject, BTreeSet)>, - label: fn(&Paint) -> Option, + label: &PaintNames<'_>, ) -> Vec { if matches!(n.kind, NodeKind::Path { guide: true, .. }) || members.contains(&std::ptr::from_ref(n)) || painted_runs.is_some_and(|(_, r)| r.is_empty()) - || !n.appearance.items.iter().any(|i| label(i.paint()).is_some()) + || n.appearance.items.iter().all(|i| label(i.paint()).is_empty()) { return vec![]; } - // The patterns or gradients an image's shaped fills and strokes paint (their effects can - // transform the paint, so they are matched by label). + // The names an image's shaped fills and strokes paint (their effects can transform the paint, + // so they are matched by name). let shaped: Option> = matches!(n.kind, NodeKind::Image(_)) - .then(|| vectorcraft_render::effects::image_paint(Some(d), n).into_iter().filter_map(|(_, _, item)| label(item.paint())).collect()); + .then(|| vectorcraft_render::effects::image_paint(Some(d), n).into_iter().flat_map(|(_, _, item)| label(item.paint())).collect()); let mut out: Vec = vec![]; for item in &n.appearance.items { - let Some(name) = label(item.paint()) else { continue }; - let paints = item.visible() - && !matches!(item, vectorcraft_doc::AppearanceItem::Stroke(s) if s.width <= 0.0) - && shaped.as_ref().is_none_or(|s| s.contains(&name)); - if paints && !out.contains(&name) { - out.push(name); + if !item.visible() || matches!(item, vectorcraft_doc::AppearanceItem::Stroke(s) if s.width <= 0.0) { + continue; + } + for name in label(item.paint()) { + if shaped.as_ref().is_none_or(|s| s.contains(&name)) && !out.contains(&name) { + out.push(name); + } } } out } +/// [`pattern_label`] or [`gradient_label`] as the names [`own_paints`] takes. +fn one_label(label: fn(&Paint) -> Option) -> impl Fn(&Paint) -> Vec { + move |p| label(p).into_iter().collect() +} + fn info(s: &mut Session, p: &Value) -> Result { const C: &str = "document.info"; let category = str_param(p, "category"); @@ -370,7 +417,13 @@ fn info(s: &mut Session, p: &Value) -> Result { }; let mut patterns = BTreeSet::new(); let mut symbols = BTreeSet::new(); + // `spot_objects`, `spot_rows` and `spot` hold the `spotObjects` entries, the rows of the objects filled or stroked with a spot + // color, in walk order, and the spot colors they use. `spot_swatches` names the document's spot colors once, for every paint. + let spot_swatches = spot_swatches(d); + let mut spot_objects: Vec = vec![]; + let mut spot_rows: Vec<(String, String)> = vec![]; let mut spot = BTreeSet::new(); + let (spot_label, pattern, gradient) = (|p: &Paint| spot_labels(&spot_swatches, p), one_label(pattern_label), one_label(gradient_label)); let mut styles: Vec<&str> = if selection_only { vec![] } else { d.graphic_styles.iter().map(|g| g.name.as_str()).collect() }; // `brushes` holds the Brushes panel's brushes, or with selectionOnly it holds the brushes that the selected objects' strokes use. let mut brushes: Vec = if selection_only { vec![] } else { vectorcraft_brush::library(d).into_iter().map(|b| b.name).collect() }; @@ -446,17 +499,11 @@ fn info(s: &mut Session, p: &Value) -> Result { if let Some(k) = k { *counts.entry(k).or_default() += 1; } - for item in &n.appearance.items { - if let Paint::Solid { swatch: Some(name), .. } = item.paint() - && d.swatch(name).is_some_and(|s| s.spot) - { - spot.insert(name.clone()); - } - } - // The patterns and the gradients this object's fills and strokes paint, each once. - let painted_runs = painted_runs(d, n); - let mut used_patterns = own_paints(d, n, &members, painted_runs.as_ref(), pattern_label); - let mut used_gradients = own_paints(d, n, &members, painted_runs.as_ref(), gradient_label); + // The spot colors, the patterns and the gradients this object's fills and strokes paint, each once. + let painted_runs = painted_runs(d, &spot_swatches, n); + let mut used_spots = own_paints(d, n, &members, painted_runs.as_ref(), &spot_label); + let mut used_patterns = own_paints(d, n, &members, painted_runs.as_ref(), &pattern); + let mut used_gradients = own_paints(d, n, &members, painted_runs.as_ref(), &gradient); // The characters' own paints: an inline graphic draws no outline and does not take its // run's paint (a list marker set in its run does), and a stroke with no width paints // nothing. @@ -464,19 +511,26 @@ fn info(s: &mut Session, p: &Value) -> Result { for run in painted.iter().filter_map(|i| t.runs.get(*i)) { let stroke = (run.style.stroke_width > 0.0).then_some(&run.style.stroke); for paint in std::iter::once(&run.style.fill).chain(stroke) { - for (used, label) in - [(&mut used_patterns, pattern_label as fn(&Paint) -> Option), (&mut used_gradients, gradient_label)] - { - if let Some(name) = label(paint) - && !used.contains(&name) - { - used.push(name); + let labels: [(&mut Vec, &PaintNames<'_>); 3] = + [(&mut used_spots, &spot_label), (&mut used_patterns, &pattern), (&mut used_gradients, &gradient)]; + for (used, label) in labels { + for name in label(paint) { + if !used.contains(&name) { + used.push(name); + } } } } } } - // Objects counts the objects that Pattern Objects and Gradient Objects list. + // Objects counts the objects that Spot Color Objects, Pattern Objects and Gradient Objects list. + if !used_spots.is_empty() { + let name = n.display_name(); + spot.extend(used_spots.iter().cloned()); + spot_rows.push((name.clone(), used_spots.join(", "))); + spot_objects.push(json!({ "id": n.id.0, "name": name, "spotColors": used_spots })); + *counts.entry("spots").or_default() += 1; + } if !used_patterns.is_empty() { let name = n.display_name(); patterns.extend(used_patterns.iter().cloned()); @@ -529,6 +583,8 @@ fn info(s: &mut Session, p: &Value) -> Result { "images": images, "symbols": symbols, "spotColors": spot, + "spotColorNames": spot, + "spotObjects": spot_objects, "swatches": d.swatches_iter().count(), "graphicStyles": d.graphic_styles.len(), "graphicStyleNames": styles, @@ -544,7 +600,7 @@ fn info(s: &mut Session, p: &Value) -> Result { }); // The document's name: its file's, else its title. out["document"]["name"] = json!(st.title()); - let sections = sections(d, &out, &fonts, &image_rows, &repeat_rows, &pattern_rows, &gradient_rows, category); + let sections = sections(d, &out, &fonts, &image_rows, &repeat_rows, &spot_rows, &pattern_rows, &gradient_rows, category); if text { return Ok(json!({ "text": report_text(&st.title(), selection_only, §ions) })); } @@ -997,6 +1053,127 @@ mod tests { assert!(!text.contains("Spare"), "{text}"); } + /// Document Info › Spot Color Objects lists each object filled or stroked with a spot color once, + /// with the spot colors it uses, as a solid color or in a gradient's stops, one in a group and a + /// type object whose characters use one included. A spot swatch no art uses adds no row; a + /// process color that looks the same, Registration and a tint of 0 are not spot colors; paint + /// that draws nothing is left out by the rules Pattern Objects follows; and Objects counts the + /// listed objects. With Selection Only it lists the objects in the selection, each once, and the + /// report writes the same rows. + #[test] + fn info_lists_the_spot_color_objects() { + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap(); + // An empty document lists none. + let i = s.execute("document.info", &json!({"category": "spotColors"})).unwrap(); + assert_eq!((i["spotObjects"].clone(), i["spotColorNames"].clone(), i["spotColors"].clone()), (json!([]), json!([]), json!([]))); + assert!(i["objects"]["spots"].is_null() && i["sections"][0]["rows"] == json!([]), "{i}"); + let text = s.execute("document.info", &json!({"category": "spotColors", "format": "text"})).unwrap()["text"].as_str().unwrap().to_string(); + assert!(text.contains("\nSPOT COLOR OBJECTS\nNone\n"), "{text}"); + let ink = |c: f64| json!({"model": "cmyk", "c": c, "m": 0.5, "y": 0.0, "k": 0.0}); + s.execute("swatch.new", &json!({"name": "Ink", "color": ink(1.0), "spot": true})).unwrap(); + s.execute("swatch.new", &json!({"name": "Gold", "color": ink(0.2), "spot": true})).unwrap(); + s.execute("swatch.new", &json!({"name": "Spare", "color": ink(0.4), "spot": true})).unwrap(); + // A global process color of the same color as Ink. + s.execute("swatch.new", &json!({"name": "Look", "color": ink(1.0), "global": true})).unwrap(); + let square = + |s: &mut Session, x: f64| s.execute("shape.rectangle", &json!({"x": x, "y": 0, "width": 10, "height": 10})).unwrap()["id"].clone(); + let ramp = |a: &str, b: &str| json!({"kind": "linear", "stops": [{"offset": 0, "color": "#000", "swatch": a}, {"offset": 1, "color": "#fff", "swatch": b}]}); + let (a, b, c, f, g, h) = + (square(&mut s, 0.0), square(&mut s, 20.0), square(&mut s, 40.0), square(&mut s, 60.0), square(&mut s, 80.0), square(&mut s, 100.0)); + s.execute("paint.setFill", &json!({"ids": [a], "swatch": "Ink"})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [a], "swatch": "Gold", "tint": 40})).unwrap(); + s.execute("layer.setProps", &json!({"id": a, "name": "Both"})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [b], "swatch": "Look"})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [b], "swatch": "Ink", "tint": 50})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [c], "swatch": "Ink", "tint": 0})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [f], "gradient": ramp("Gold", "Look")})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [g], "gradient": ramp("Look", "Look")})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [h], "swatch": "[Registration]"})).unwrap(); + for id in [&c, &g, &h] { + s.execute("paint.setStroke", &json!({"ids": [id], "none": true})).unwrap(); + } + s.execute("select.set", &json!({"ids": [b, c]})).unwrap(); + let group = s.execute("object.group", &json!({})).unwrap()["id"].clone(); + // A type object whose characters are filled with a spot color. + let t = s.execute("text.create", &json!({"x": 0, "y": 200, "text": "Hi"})).unwrap()["id"].clone(); + s.execute("paint.setFill", &json!({"ids": [t], "swatch": "Ink"})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [t], "none": true})).unwrap(); + s.execute("layer.setProps", &json!({"id": t, "name": "Type"})).unwrap(); + // A hidden spot fill and a spot stroke with no width paint nothing, so neither object is listed. + let (d, e) = (square(&mut s, 120.0), square(&mut s, 140.0)); + s.execute("paint.setStroke", &json!({"ids": [d], "none": true})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [d], "swatch": "Ink"})).unwrap(); + s.execute("appearance.setItem", &json!({"ids": [d], "index": 0, "visible": false})).unwrap(); + s.execute("paint.setFill", &json!({"ids": [e], "none": true})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [e], "swatch": "Ink"})).unwrap(); + s.execute("appearance.setItem", &json!({"ids": [e], "index": 1, "weight": 0})).unwrap(); + // Type with no characters, or of only spaces and a line break, paints nothing, and neither + // does a guide made from a path with a spot color. + let empty = s.execute("text.create", &json!({"x": 0, "y": 300, "text": ""})).unwrap()["id"].clone(); + s.execute("paint.setFill", &json!({"ids": [empty], "swatch": "Ink"})).unwrap(); + s.execute("appearance.addFill", &json!({"ids": [empty]})).unwrap(); + let blank = s.execute("text.create", &json!({"x": 0, "y": 320, "text": " \n"})).unwrap()["id"].clone(); + s.execute("paint.setFill", &json!({"ids": [blank], "swatch": "Ink"})).unwrap(); + let guide = square(&mut s, 300.0); + s.execute("paint.setFill", &json!({"ids": [guide], "swatch": "Ink"})).unwrap(); + s.execute("select.set", &json!({"ids": [guide]})).unwrap(); + s.execute("view.guides.make", &json!({})).unwrap(); + // Nor does a compound path's member's own spot fill, or an image's spot fill without an + // effect that gives it a shape. + let (m1, m2) = (square(&mut s, 320.0), square(&mut s, 340.0)); + s.execute("select.set", &json!({"ids": [m1, m2]})).unwrap(); + let compound = s.execute("object.compoundPath.make", &json!({})).unwrap()["id"].clone(); + s.execute("paint.setFill", &json!({"ids": [compound], "color": "#000000"})).unwrap(); + s.execute("paint.setStroke", &json!({"ids": [compound], "none": true})).unwrap(); + let node = |s: &Session, id: &Value| s.doc().unwrap().doc.node(vectorcraft_doc::NodeId(id.as_u64().unwrap())).unwrap().clone(); + let (member, paint) = (node(&s, &compound).children().unwrap()[0].id, node(&s, &a).appearance); + let pic = square(&mut s, 360.0); + s.execute("select.set", &json!({"ids": [pic]})).unwrap(); + s.execute("object.rasterize", &json!({"ppi": 72})).unwrap(); + let image = s.doc().unwrap().selection.objects[0]; + s.edit("own paint", |d, _| { + d.node_mut(member).unwrap().appearance = paint.clone(); + d.node_mut(image).unwrap().appearance = paint; + Ok(()) + }) + .unwrap(); + let i = s.execute("document.info", &json!({})).unwrap(); + assert_eq!( + i["spotObjects"], + json!([ + {"id": a, "name": "Both", "spotColors": ["Ink", "Gold"]}, + {"id": b, "name": "", "spotColors": ["Ink"]}, + {"id": f, "name": "", "spotColors": ["Gold"]}, + {"id": t, "name": "Type", "spotColors": ["Ink"]}, + ]) + ); + assert_eq!((i["spotColorNames"].clone(), i["spotColors"].clone()), (json!(["Gold", "Ink"]), json!(["Gold", "Ink"]))); + // Objects counts the objects that Spot Color Objects lists. + assert_eq!(i["objects"]["spots"].as_u64(), Some(4), "{}", i["objects"]); + let objects = s.execute("document.info", &json!({"category": "objects"})).unwrap()["sections"][0]["rows"].clone(); + assert!(objects.as_array().unwrap().contains(&json!(["Spot Color Objects", "4"])), "{objects}"); + s.execute("select.set", &json!({"ids": [c, g, h, d, e, empty, blank, guide, compound, image.0]})).unwrap(); + let i = s.execute("document.info", &json!({"selectionOnly": true})).unwrap(); + assert!(i["objects"]["spots"].is_null() && i["spotObjects"] == json!([]) && i["spotColors"] == json!([]), "{i}"); + let i = s.execute("document.info", &json!({"category": "spotColors"})).unwrap(); + assert_eq!((i["sections"][0]["id"].as_str(), i["sections"][0]["title"].as_str()), (Some("spotColors"), Some("Spot Color Objects"))); + assert_eq!(i["sections"][0]["rows"], json!([["Both", "Ink, Gold"], ["", "Ink"], ["", "Gold"], ["Type", "Ink"]])); + // A selection that holds the group and the object inside it lists that object once. + for ids in [json!([group, b]), json!([b, group])] { + s.execute("select.set", &json!({"ids": ids})).unwrap(); + let i = s.execute("document.info", &json!({"selectionOnly": true, "category": "spotColors"})).unwrap(); + assert_eq!(i["sections"][0]["rows"], json!([["", "Ink"]]), "{ids}"); + assert_eq!((i["objects"]["spots"].as_u64(), i["spotColorNames"].clone()), (Some(1), json!(["Ink"])), "{ids}"); + } + s.execute("select.set", &json!({"ids": [t]})).unwrap(); + let i = s.execute("document.info", &json!({"selectionOnly": true, "category": "spotColors"})).unwrap(); + assert_eq!((i["sections"][0]["rows"].clone(), i["spotColors"].clone()), (json!([["Type", "Ink"]]), json!(["Ink"]))); + let text = s.execute("document.info", &json!({"category": "spotColors", "format": "text"})).unwrap()["text"].as_str().unwrap().to_string(); + assert!(text.contains("\nSPOT COLOR OBJECTS\nBoth: Ink, Gold\n: Ink\n: Gold\nType: Ink\n"), "{text}"); + assert!(!text.contains("Spare") && !text.contains("Look"), "{text}"); + } + /// 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/crates/engine/src/tests/package.rs b/crates/engine/src/tests/package.rs index c1da365740..2e57ed048c 100644 --- a/crates/engine/src/tests/package.rs +++ b/crates/engine/src/tests/package.rs @@ -515,7 +515,7 @@ fn the_document_info_report_has_every_category() { "OBJECTS", "GRAPHIC STYLES", "BRUSHES", - "SPOT COLORS", + "SPOT COLOR OBJECTS", "PATTERN OBJECTS", "GRADIENT OBJECTS", "SYMBOLS", diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 97e5b335d3..3efba2679b 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -3319,6 +3319,9 @@ digits Malayalam Малаялам Split Path Разделяне на път spot петно Spot Color Спот цвят + Spot Color Objects Обекти със спот цветове + Pattern Objects Обекти с мостри + Gradient Objects Обекти с градиенти Spot Colors Спот цветове Spot Colors… Спот цветове… Spread: Разпространение: diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 75353321ff..76cb818576 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -3902,6 +3902,9 @@ theme Appearance Izgled Select All Unused Odaberi sve nekorišteno Sort by Kind Sortiraj po vrsti Sort by Name Sortiraj po nazivu + Spot Color Objects Objekti sa spot bojama + Pattern Objects Objekti s uzorcima + Gradient Objects Objekti s gradijentima Spot Colors Spot boje Spot Colors (0) Spot boje (0) Swatch Options Opcije uzorka boje diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 75e0b9ea20..4f3af5b168 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -4287,6 +4287,9 @@ digits Malayalam Malaiàlam Sponge… Esponja… spot directe Spot Color Color directe + Spot Color Objects Objectes amb colors directes + Pattern Objects Objectes amb motius + Gradient Objects Objectes amb degradats Spot Colors Colors directes Spot Colors (0) Colors directes (0) Spot Colors… Colors directes… diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 5d37ee136e..5c154ce842 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -3954,6 +3954,9 @@ theme Appearance Motiv Select All Unused Vybrat všechny nepoužité Sort by Kind Seřadit podle druhu Sort by Name Seřadit podle názvu + Spot Color Objects Objekty s přímými barvami + Pattern Objects Objekty se vzorky + Gradient Objects Objekty s přechody Spot Colors Přímé barvy Spot Colors (0) Přímé barvy (0) Swatch Options Možnosti položky vzorníku diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 8167f80138..1ed0154d45 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -3677,6 +3677,9 @@ theme Appearance Udseende Select All Unused Markér alle ubrugte Sort by Kind Sortér efter type Sort by Name Sortér efter navn + Spot Color Objects Objekter med spotfarver + Pattern Objects Objekter med mønstre + Gradient Objects Objekter med gradienter Spot Colors Spotfarver Spot Colors (0) Spotfarver (0) Swatch Options Indstillinger for farveprøve diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index bd4bb38b43..7c97be63f8 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -3378,6 +3378,9 @@ effect Points Punkte Split Path Pfad teilen spot Vollton Spot Color Volltonfarbe + Spot Color Objects Objekte mit Volltonfarben + Pattern Objects Objekte mit Mustern + Gradient Objects Objekte mit Verläufen Spot Colors Volltonfarben Spot Colors… Volltonfarben… Spread: Ausbreitung: diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 9860f8ad50..d52569ceec 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -3832,6 +3832,9 @@ digits Malayalam Μαλαγιάλαμ Split Into Grid… Διαίρεση σε πλέγμα… Split Path Διαίρεση διαδρομής spot πλακάτο + Spot Color Objects Αντικείμενα με χρώματα spot + Pattern Objects Αντικείμενα με μοτίβα + Gradient Objects Αντικείμενα με ντεγκραντέ Spot Colors Πλακάτα χρώματα Spot Colors… Πλακάτα χρώματα… Stacked Bar Graph Tool Στοιβαγμένο ραβδόγραμμα (Stacked Bar Graph Tool) diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 10c0c0bf87..002428f251 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3408,6 +3408,9 @@ digits Malayalam Malayálam Split Path Dividir trazado spot plana Spot Color Tinta plana + Spot Color Objects Objetos con tintas planas + Pattern Objects Objetos con motivos + Gradient Objects Objetos con degradados Spot Colors Tintas planas Spot Colors… Tintas planas… Spread: Extensión: diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 7845235f65..9e106dea76 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -3878,6 +3878,9 @@ theme Appearance Ulkoasu Select All Unused Valitse kaikki käyttämättömät Sort by Kind Lajittele lajin mukaan Sort by Name Lajittele nimen mukaan + Spot Color Objects Objektit, joissa on spottivärejä + Pattern Objects Objektit, joissa on kuvioita + Gradient Objects Objektit, joissa on liukuvärejä Spot Colors Spottivärit Spot Colors (0) Spottivärit (0) Swatch Options Värimallin asetukset diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 5e2895f9c2..b0b07185aa 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -3400,6 +3400,9 @@ digits Malayalam Malayalam Split Path Scinder le tracé spot ton direct Spot Color Ton direct + Spot Color Objects Objets avec tons directs + Pattern Objects Objets avec motifs + Gradient Objects Objets avec dégradés Spot Colors Tons directs Spot Colors… Tons directs… Spread: Étendue : diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index aca21d7eec..c06fda4262 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -4645,6 +4645,9 @@ antiAlias Sharp शार्प Sponge… स्पंज… spot स्पॉट Spot Color स्पॉट रंग + Spot Color Objects स्पॉट कलर्स के साथ ऑब्जेक्ट्स + Pattern Objects पैटर्न्स के साथ ऑब्जेक्ट्स + Gradient Objects ग्रेडिएंट्स के साथ ऑब्जेक्ट्स Spot Colors स्पॉट रंग Spot Colors (0) स्पॉट कलर्स (0) Spot Colors… स्पॉट कलर्स… diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 45d19b0ca9..ac85d4a968 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -4060,6 +4060,9 @@ mark-style Japanese Japán Split Path Útvonal kettévágása spot direkt Spot Color Direktszín + Spot Color Objects Direkt színeket használó objektumok + Pattern Objects Mintázatokat használó objektumok + Gradient Objects Színátmeneteket használó objektumok Spot Colors Direktszínek Spot Colors (0) Direktszínek (0) Spot Colors… Direktszínek… diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 8e2b383e2d..ac76a329b0 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -3677,6 +3677,9 @@ theme Appearance Tampilan Select All Unused Pilih semua yang tidak digunakan Sort by Kind Urutkan berdasarkan jenis Sort by Name Urutkan berdasarkan nama + Spot Color Objects Objek dengan Warna Spot + Pattern Objects Objek dengan Pola + Gradient Objects Objek dengan Gradien Spot Colors Warna spot Spot Colors (0) Warna spot (0) Swatch Options Opsi sampel diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 26e1f29651..7d2284d8c3 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -3453,6 +3453,9 @@ digits Malayalam Malayalam Split Path Dividi tracciato spot spot Spot Color Colore spot + Spot Color Objects Oggetti con colori spot + Pattern Objects Oggetti con motivi + Gradient Objects Oggetti con sfumature Spot Colors Colori spot Spot Colors… Colori spot… Spread: Estensione: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 30873028e8..a75330e19d 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -3228,6 +3228,9 @@ mark-style Japanese 日本式 Add Guides ガイドを追加 Split Path パスを分割 Spot Color 特色 + Spot Color Objects 特色を使用したオブジェクト + Pattern Objects パターンを使用したオブジェクト + Gradient Objects グラデーションを使用したオブジェクト Spot Colors 特色 Spot Colors… 特色… Spread: 拡散: diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 8e9d9d049d..00d54b057f 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -3513,6 +3513,9 @@ theme Appearance იერსახე Select All Unused ყველა გამოუყენებლის მონიშვნა Sort by Kind ტიპის მიხედვით დალაგება Sort by Name სახელის მიხედვით დალაგება + Spot Color Objects ობიექტები სპოტ-ფერებით + Pattern Objects ობიექტები პატერნებით + Gradient Objects ობიექტები გრადიენტებით Spot Colors სპოტ-ფერები Swatch Options ფერის ნიმუშის პარამეტრები Ungroup Color Group ფერების ჯგუფის დაშლა diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index f00e6cdeba..b581143b5f 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -3217,6 +3217,9 @@ mark-style Japanese 일본식 Add Guides 안내선 추가 Split Path 패스 분할 Spot Color 별색 + Spot Color Objects 별색을 사용한 개체 + Pattern Objects 패턴을 사용한 개체 + Gradient Objects 그레이디언트를 사용한 개체 Spot Colors 별색 Spot Colors… 별색… Spread: 확산: diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index bc00b76698..fd5aed2005 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -3691,6 +3691,9 @@ theme Appearance Vormgeving Select All Unused Alle ongebruikte selecteren Sort by Kind Sorteren op soort Sort by Name Sorteren op naam + Spot Color Objects Objecten met spotkleuren + Pattern Objects Objecten met patronen + Gradient Objects Objecten met verlopen Spot Colors Spotkleuren Spot Colors (0) Spotkleuren (0) Swatch Options Kleurstaalopties diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 23940fb80f..c5dcbded1b 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -3813,6 +3813,9 @@ theme Appearance Wygląd Select All Unused Zaznacz wszystkie nieużywane Sort by Kind Sortuj według rodzaju Sort by Name Sortuj według nazwy + Spot Color Objects Obiekty z kolorami dodatkowymi + Pattern Objects Obiekty ze wzorkami + Gradient Objects Obiekty z gradientami Spot Colors Kolory dodatkowe Swatch Options Opcje próbki Ungroup Color Group Rozgrupuj grupę kolorów diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 32c4b4bcc0..9b2a714815 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2860,6 +2860,9 @@ digits Malayalam Malaiala Split Into Grid… Dividir em Grade… Split Path Dividir Caminho Spot Color Cor Spot + Spot Color Objects Objetos com cores spot + Pattern Objects Objetos com padrões + Gradient Objects Objetos com gradientes Spot Colors Cores Spot Spot Colors… Cores Spot… Spread: Propagação: diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 15d3c3a380..7f4069d10c 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -3440,6 +3440,9 @@ digits Malayalam Малаялам Split Path Разделить контур spot плашечный Spot Color Плашечный цвет + Spot Color Objects Объекты с плашечными цветами + Pattern Objects Объекты с узорами + Gradient Objects Объекты с градиентами Spot Colors Плашечные цвета Spot Colors… Плашечные цвета… Spread: Разворот: diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 5ffc963fad..71056ff78c 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -3330,6 +3330,9 @@ digits Malayalam Malayalam Split Path Dela bana spot dekor Spot Color Dekorfärg + Spot Color Objects Objekt med dekorfärger + Pattern Objects Objekt med mönster + Gradient Objects Objekt med gradienter Spot Colors Dekorfärger Spot Colors… Dekorfärger… Spread: Spridning: diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index ade1efa8be..33e6d5a7e7 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -225,6 +225,28 @@ fn control_bar_panel_menu_labels_are_in_complete_catalogs() { } } +/// The titles of Document Info's Spot Color Objects, Pattern Objects and Gradient Objects +/// categories, which the panel, its menu and the Objects category's counts show. +#[test] +fn document_info_object_category_titles_are_in_complete_catalogs() { + let mut session = vectorcraft_engine::Session::new(); + session.execute("file.new", &serde_json::json!({"width": 100, "height": 100})).unwrap(); + let info = session.execute("document.info", &serde_json::json!({})).unwrap(); + let titles: Vec<&str> = info["sections"] + .as_array() + .unwrap() + .iter() + .filter(|s| matches!(s["id"].as_str(), Some("spotColors" | "patterns" | "gradients"))) + .filter_map(|s| s["title"].as_str()) + .collect(); + assert_eq!(titles, ["Spot Color Objects", "Pattern Objects", "Gradient Objects"]); + for lang in LANGUAGES.iter().filter(|lang| lang.complete_menus) { + for title in &titles { + assert!(lang.catalog().plain(title).is_some(), "{} is missing {title}", lang.code); + } + } +} + /// The Character panel's Anti-aliasing Method label and its choices, with the Sharp method's /// own `antiAlias` row apart from the plain "Sharp" corner row. #[test] diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 86356d74ba..7c5b920e37 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -3231,6 +3231,9 @@ digits Malayalam Malayalam Split Path Bölünmüş Yol spot nokta Spot Color Nokta Rengi + Spot Color Objects Spot Renkli Nesneler + Pattern Objects Desenli Nesneler + Gradient Objects Degradeli Nesneler Spot Colors Nokta Renkleri Spot Colors… Nokta Renkleri… Spread: Yayılma: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index ebba0f5c19..03873cf8cc 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -3446,6 +3446,9 @@ effect Points Точки Split Path Розділити контур spot плашковий Spot Color Плашковий колір + Spot Color Objects Об’єкти з плашковими кольорами + Pattern Objects Об’єкти з візерунками + Gradient Objects Об’єкти з градієнтами Spot Colors Плашкові кольори Spot Colors… Плашкові кольори… Spread: Розворот: diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 0ec0fd4e40..d95fc6fd3e 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -3792,6 +3792,9 @@ mark-style Japanese Kiểu Nhật Split Path Tách đường dẫn spot đặc biệt Spot Color Màu đặc biệt + Spot Color Objects Đối tượng có màu đặc biệt + Pattern Objects Đối tượng có họa tiết + Gradient Objects Đối tượng có chuyển sắc Spot Colors Màu đặc biệt Spot Colors… Màu đặc biệt… Spray Radius Bán kính phun diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 9fc1cdc4e8..1e1cf55490 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -3273,6 +3273,9 @@ digits Malayalam 马拉雅拉姆数字 Split Path 分割路径 spot 专色 Spot Color 专色 + Spot Color Objects 使用专色的对象 + Pattern Objects 使用图案的对象 + Gradient Objects 使用渐变的对象 Spot Colors 专色 Spot Colors… 专色… Spread: 扩散: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 685f7887fb..ba2925719a 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -4415,6 +4415,9 @@ digits Malayalam 馬來亞拉姆數字 Split Path 分割路徑 spot 特別色 Spot Color 特別色 + Spot Color Objects 使用特別色的物件 + Pattern Objects 使用圖樣的物件 + Gradient Objects 使用漸層的物件 Spot Colors 特別色 Spot Colors… 特別色… Spread: 擴散: diff --git a/crates/ui-egui/src/panels/doc_info.rs b/crates/ui-egui/src/panels/doc_info.rs index 4d6bf05ec1..e450da63aa 100644 --- a/crates/ui-egui/src/panels/doc_info.rs +++ b/crates/ui-egui/src/panels/doc_info.rs @@ -1,5 +1,5 @@ //! Document Info panel: the document's setup and what it contains (objects by kind, graphic -//! styles, brushes, spot colors, objects painted with patterns and with gradients, symbols, fonts and their files, +//! styles, brushes, objects painted with spot colors, with patterns and with gradients, symbols, fonts and their files, //! linked and embedded images, repeat objects), for the whole document or the selection only. The flyout picks one category (or all) //! and saves the text report (`docInfo.save`, the report File → Package writes too). @@ -192,6 +192,7 @@ mod tests { && menu_text.contains("Brushes") && menu_text.contains("Repeat Objects") && menu_text.contains("Gradient Objects") + && menu_text.contains("Spot Color Objects") && menu_text.contains("Save…"), "{menu_text}" ); @@ -205,6 +206,17 @@ mod tests { show(app, ui); }); assert!(gradients.contains("Gradient Objects (1)") && gradients.contains("Glow") && gradients.contains("Radial gradient"), "{gradients}"); + // Spot Color Objects lists the objects painted with a spot color by name. + let spot = json!({"model": "cmyk", "c": 0.0, "m": 0.8, "y": 0.6, "k": 0.0}); + app.session.execute("swatch.new", &json!({"name": "Signal Red", "color": spot, "spot": true})).unwrap(); + let badge = app.session.execute("shape.ellipse", &json!({"x": 50, "y": 50, "width": 20, "height": 20})).unwrap()["id"].clone(); + app.session.execute("paint.setFill", &json!({"ids": [badge], "swatch": "Signal Red"})).unwrap(); + app.session.execute("layer.setProps", &json!({"id": badge, "name": "Badge"})).unwrap(); + let spots = crate::tests_labels::painted_text(&mut app, |app, ui| { + set_pstate(ui.ctx(), "docinfo-category", "spotColors".to_string()); + show(app, ui); + }); + assert!(spots.contains("Spot Color Objects (1)") && spots.contains("Badge") && spots.contains("Signal Red"), "{spots}"); let filtered = crate::tests_labels::painted_text(&mut app, |app, ui| { set_pstate(ui.ctx(), "docinfo-category", "fonts".to_string()); show(app, ui); @@ -222,6 +234,7 @@ mod tests { let report = String::from_utf8(written.borrow().clone()).unwrap(); assert!(report.starts_with("Document Info: ") && report.contains("\nFONT DETAILS\n") && report.contains("\nREPEAT OBJECTS\n"), "{report}"); assert!(report.contains("\nGRADIENT OBJECTS\nGlow: Radial gradient\n"), "{report}"); + assert!(report.contains("\nSPOT COLOR OBJECTS\nBadge: Signal Red\n"), "{report}"); } /// The report the panel menu's Save… writes with category `category` shown ("" for All diff --git a/docs/mcp.md b/docs/mcp.md index 7bf5ae9956..05d6951cd2 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -6094,7 +6094,12 @@ the patterns it uses, which the patterns category lists as Pattern Objects and ` stroked with a gradient, by the same rules as `patternObjects`, with the gradients it uses: the name of the gradient 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) and `pathLength` (pt: the total length of the paths, compound paths' pieces included and guides left +`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 +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 out, which objects lists as Path Length in the document's units, say for a cut line or an LED strip along the letters); `format: "text"` returns the plain-text report Document Info › Save… (`docInfo.save {path?, selectionOnly?, category?}`: only `category` when given, else every category) and the package report write. diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index b1ceaf70ea..900787b283 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1352,6 +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 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.