M3.9: swatches in colour groups are first-class — Document Color Mode converts them, they can be spot colours with their own plate and PDF Separation, and Document Info counts them
Document::swatches_iter/_mut, swatch_mut, swatch_group_of, remove_swatch and a copy-on-write walk over linked solid paints (map_solid_paints) cover every colour group. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fb8c41d8e3
commit
00a0874d4d
|
|
@ -12,6 +12,7 @@ pub mod live;
|
|||
pub mod node;
|
||||
pub mod pattern;
|
||||
pub mod selection;
|
||||
pub mod swatches;
|
||||
pub mod text;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
|
@ -594,9 +595,6 @@ impl Document {
|
|||
pub fn pattern_mut(&mut self, name: &str) -> Option<&mut PatternDef> {
|
||||
self.patterns.iter_mut().find(|p| p.name == name)
|
||||
}
|
||||
pub fn swatch(&self, name: &str) -> Option<&Swatch> {
|
||||
self.swatches.iter().chain(self.swatch_groups.iter().flat_map(|g| g.swatches.iter())).find(|s| s.name == name)
|
||||
}
|
||||
/// Deep-clone `node` with fresh ids for it and all descendants.
|
||||
pub fn reid(&mut self, node: &Node) -> Node {
|
||||
let mut n = node.clone();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,279 @@
|
|||
//! Swatch lookups across colour groups and the walks over the paints that link to swatches.
|
||||
//!
|
||||
//! A swatch lives either in `Document::swatches` or in one of `Document::swatch_groups`; every
|
||||
//! lookup here covers both. Solid paints link to a global swatch by name (`Paint::Solid.swatch`);
|
||||
//! [`Document::map_solid_paints`] rewrites those links (and colours) wherever art can hold them,
|
||||
//! copying only the nodes that change.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use vectorcraft_color::{Color, Paint, Swatch};
|
||||
|
||||
use crate::{AppearanceItem, Document, Node, NodeId, NodeKind};
|
||||
|
||||
impl Document {
|
||||
/// Every swatch: the ungrouped ones first, then each colour group's in order.
|
||||
pub fn swatches_iter(&self) -> impl Iterator<Item = &Swatch> {
|
||||
self.swatches.iter().chain(self.swatch_groups.iter().flat_map(|g| g.swatches.iter()))
|
||||
}
|
||||
pub fn swatches_iter_mut(&mut self) -> impl Iterator<Item = &mut Swatch> {
|
||||
self.swatches.iter_mut().chain(self.swatch_groups.iter_mut().flat_map(|g| g.swatches.iter_mut()))
|
||||
}
|
||||
pub fn swatch(&self, name: &str) -> Option<&Swatch> {
|
||||
self.swatches_iter().find(|s| s.name == name)
|
||||
}
|
||||
pub fn swatch_mut(&mut self, name: &str) -> Option<&mut Swatch> {
|
||||
self.swatches_iter_mut().find(|s| s.name == name)
|
||||
}
|
||||
/// Index of the colour group holding swatch `name` (`None` when ungrouped or missing).
|
||||
pub fn swatch_group_of(&self, name: &str) -> Option<usize> {
|
||||
self.swatch_groups.iter().position(|g| g.swatches.iter().any(|s| s.name == name))
|
||||
}
|
||||
/// Is `name` used by a swatch or a colour group (they share one namespace)?
|
||||
pub fn swatch_name_taken(&self, name: &str) -> bool {
|
||||
self.swatch(name).is_some() || self.swatch_groups.iter().any(|g| g.name == name)
|
||||
}
|
||||
/// Remove swatch `name` from wherever it lives.
|
||||
pub fn remove_swatch(&mut self, name: &str) -> Option<Swatch> {
|
||||
if let Some(i) = self.swatches.iter().position(|s| s.name == name) {
|
||||
return Some(self.swatches.remove(i));
|
||||
}
|
||||
let g = self.swatch_group_of(name)?;
|
||||
let list = &mut self.swatch_groups[g].swatches;
|
||||
let i = list.iter().position(|s| s.name == name)?;
|
||||
Some(list.remove(i))
|
||||
}
|
||||
|
||||
/// Visit every solid paint (fills, strokes and text runs) in the art, symbol definitions,
|
||||
/// pattern tiles and graphic styles. `f(colour, link)` may change both and returns true when it
|
||||
/// did. Only nodes holding a changed paint (and the paths to them) are copied. Returns the
|
||||
/// number of changed paints.
|
||||
pub fn map_solid_paints(&mut self, f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> usize {
|
||||
let mut n = map_trees(&mut self.layers, f);
|
||||
for s in &mut self.symbols {
|
||||
n += map_tree(&mut s.art, f);
|
||||
}
|
||||
for p in &mut self.patterns {
|
||||
n += map_trees(&mut p.art, f);
|
||||
}
|
||||
for gs in &mut self.graphic_styles {
|
||||
for it in &mut gs.appearance.items {
|
||||
n += usize::from(map_solid(item_paint_mut(it), f));
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
|
||||
/// [`Document::map_solid_paints`] limited to the subtrees of `ids` (each paint visited once even
|
||||
/// when an id is inside another).
|
||||
pub fn map_solid_paints_in(&mut self, ids: &[NodeId], f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> usize {
|
||||
let roots: Vec<NodeId> =
|
||||
ids.iter().copied().filter(|id| !self.ancestry(*id).is_some_and(|a| a[..a.len() - 1].iter().any(|p| ids.contains(p)))).collect();
|
||||
let mut n = 0;
|
||||
for id in roots {
|
||||
let Some(node) = self.node(id) else { continue };
|
||||
if let Some((new, count)) = map_node(node, f)
|
||||
&& let Some(slot) = self.node_mut(id)
|
||||
{
|
||||
*slot = new;
|
||||
n += count;
|
||||
}
|
||||
}
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
/// Every colour in the subtree of `n` with the swatch it links to: solid fills, strokes and text
|
||||
/// runs (with their link) and gradient stops (unlinked), in paint order.
|
||||
pub fn node_colors(n: &Node, f: &mut dyn FnMut(&Color, Option<&str>)) {
|
||||
n.walk(&mut |m| {
|
||||
for p in node_paints(m) {
|
||||
match p {
|
||||
Paint::Solid { color, swatch } => f(color, swatch.as_deref()),
|
||||
Paint::Gradient(g) => g.gradient.stops.iter().for_each(|s| f(&s.color, None)),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn item_paint(it: &AppearanceItem) -> &Paint {
|
||||
match it {
|
||||
AppearanceItem::Fill(l) => &l.paint,
|
||||
AppearanceItem::Stroke(l) => &l.paint,
|
||||
}
|
||||
}
|
||||
|
||||
fn item_paint_mut(it: &mut AppearanceItem) -> &mut Paint {
|
||||
match it {
|
||||
AppearanceItem::Fill(l) => &mut l.paint,
|
||||
AppearanceItem::Stroke(l) => &mut l.paint,
|
||||
}
|
||||
}
|
||||
|
||||
/// The paints a node holds itself (not its children): appearance items, then text runs.
|
||||
fn node_paints(n: &Node) -> impl Iterator<Item = &Paint> {
|
||||
let runs = match &n.kind {
|
||||
NodeKind::Text(t) => t.runs.as_slice(),
|
||||
_ => &[],
|
||||
};
|
||||
n.appearance.items.iter().map(item_paint).chain(runs.iter().flat_map(|r| [&r.style.fill, &r.style.stroke]))
|
||||
}
|
||||
|
||||
/// Mutable [`node_paints`], in the same order.
|
||||
fn node_paints_mut(n: &mut Node) -> impl Iterator<Item = &mut Paint> {
|
||||
let runs = match &mut n.kind {
|
||||
NodeKind::Text(t) => t.runs.as_mut_slice(),
|
||||
_ => &mut [],
|
||||
};
|
||||
n.appearance.items.iter_mut().map(item_paint_mut).chain(runs.iter_mut().flat_map(|r| [&mut r.style.fill, &mut r.style.stroke]))
|
||||
}
|
||||
|
||||
/// Apply `f` to a solid paint; true when it changed.
|
||||
fn map_solid(p: &mut Paint, f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> bool {
|
||||
match p {
|
||||
Paint::Solid { color, swatch } => f(color, swatch),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// `n` with `f` applied to its subtree's solid paints, or `None` when nothing changed (then nothing
|
||||
/// was copied). Also returns the number of changed paints.
|
||||
fn map_node(n: &Node, f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> Option<(Node, usize)> {
|
||||
// Try each own paint on a scratch copy first, so unchanged nodes are never cloned.
|
||||
let mut changes: Vec<(usize, Color, Option<String>)> = vec![];
|
||||
for (i, p) in node_paints(n).enumerate() {
|
||||
if let Paint::Solid { color, swatch } = p {
|
||||
let (mut c, mut s) = (*color, swatch.clone());
|
||||
if f(&mut c, &mut s) {
|
||||
changes.push((i, c, s));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut count = changes.len();
|
||||
let mut out: Option<Node> = None;
|
||||
if !changes.is_empty() {
|
||||
let mut m = n.clone();
|
||||
let mut changes = changes.into_iter().peekable();
|
||||
for (i, p) in node_paints_mut(&mut m).enumerate() {
|
||||
if changes.peek().is_some_and(|c| c.0 == i)
|
||||
&& let (Some((_, c, s)), Paint::Solid { color, swatch }) = (changes.next(), p)
|
||||
{
|
||||
*color = c;
|
||||
*swatch = s;
|
||||
}
|
||||
}
|
||||
out = Some(m);
|
||||
}
|
||||
if let Some(ch) = n.children() {
|
||||
for (i, c) in ch.iter().enumerate() {
|
||||
if let Some((new, k)) = map_node(c, f) {
|
||||
let m = out.get_or_insert_with(|| n.clone());
|
||||
if let Some(slot) = m.children_mut().and_then(|v| v.get_mut(i)) {
|
||||
*slot = Arc::new(new);
|
||||
}
|
||||
count += k;
|
||||
}
|
||||
}
|
||||
}
|
||||
out.map(|m| (m, count))
|
||||
}
|
||||
|
||||
fn map_tree(n: &mut Arc<Node>, f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> usize {
|
||||
match map_node(n, f) {
|
||||
Some((new, count)) => {
|
||||
*n = Arc::new(new);
|
||||
count
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn map_trees(v: &mut [Arc<Node>], f: &mut dyn FnMut(&mut Color, &mut Option<String>) -> bool) -> usize {
|
||||
v.iter_mut().map(|n| map_tree(n, f)).sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use vectorcraft_color::SwatchGroup;
|
||||
use vectorcraft_geom::{Rect, shapes};
|
||||
|
||||
use super::*;
|
||||
use crate::Appearance;
|
||||
|
||||
fn linked(color: Color, name: &str) -> Paint {
|
||||
Paint::Solid { color, swatch: Some(name.into()) }
|
||||
}
|
||||
|
||||
/// A document with a grouped global swatch "Brand", a rectangle filled with it and an unlinked one.
|
||||
fn doc() -> (Document, NodeId, NodeId) {
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
let red = Color::rgb(1.0, 0.0, 0.0);
|
||||
d.swatch_groups.push(SwatchGroup {
|
||||
name: "Mine".into(),
|
||||
swatches: vec![Swatch { name: "Brand".into(), paint: Paint::solid(red), global: true, spot: false }],
|
||||
});
|
||||
let layer = d.layers[0].id;
|
||||
let mut ids = vec![];
|
||||
for paint in [linked(red, "Brand"), Paint::solid(red)] {
|
||||
let id = d.alloc_id();
|
||||
let r = shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0));
|
||||
d.insert(Some(layer), usize::MAX, Node::path(id, r, Appearance::basic(paint, Paint::None, 0.0))).unwrap();
|
||||
ids.push(id);
|
||||
}
|
||||
(d, ids[0], ids[1])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lookups_cover_colour_groups() {
|
||||
let (mut d, _, _) = doc();
|
||||
let total = d.swatches.len() + d.swatch_groups.iter().map(|g| g.swatches.len()).sum::<usize>();
|
||||
assert_eq!(d.swatches_iter().count(), total);
|
||||
assert!(d.swatch("Brand").is_some_and(|s| s.global));
|
||||
assert_eq!(d.swatch_group_of("Brand").map(|g| d.swatch_groups[g].name.as_str()), Some("Mine"));
|
||||
d.swatch_mut("Brand").unwrap().spot = true;
|
||||
assert!(d.swatch("Brand").unwrap().spot);
|
||||
assert!(d.swatch_name_taken("Mine") && d.swatch_name_taken("Brand") && !d.swatch_name_taken("Nope"));
|
||||
assert_eq!(d.remove_swatch("Brand").map(|s| s.name), Some("Brand".into()));
|
||||
assert!(d.swatch("Brand").is_none() && d.remove_swatch("Brand").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_copies_only_changed_nodes() {
|
||||
let (mut d, a, b) = doc();
|
||||
let before = d.clone();
|
||||
let blue = Color::rgb(0.0, 0.0, 1.0);
|
||||
let n = d.map_solid_paints(&mut |c, s| {
|
||||
if s.as_deref() != Some("Brand") {
|
||||
return false;
|
||||
}
|
||||
*c = blue;
|
||||
true
|
||||
});
|
||||
assert_eq!(n, 1);
|
||||
assert_eq!(d.node(a).unwrap().appearance.fill_paint(), linked(blue, "Brand"));
|
||||
assert_eq!(d.node(b).unwrap().appearance.fill_paint(), Paint::solid(Color::rgb(1.0, 0.0, 0.0)));
|
||||
let unchanged = |d: &Document| d.layers[0].children().unwrap()[1].clone();
|
||||
assert!(Arc::ptr_eq(&unchanged(&d), &unchanged(&before)), "the unlinked rectangle is shared, not copied");
|
||||
// Nothing to change: the tree is untouched.
|
||||
let again = d.clone();
|
||||
assert_eq!(d.map_solid_paints(&mut |_, _| false), 0);
|
||||
assert!(Arc::ptr_eq(&d.layers[0], &again.layers[0]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_within_ids_visits_each_paint_once() {
|
||||
let (mut d, a, b) = doc();
|
||||
let layer = d.layers[0].id;
|
||||
let mut seen = 0;
|
||||
let n = d.map_solid_paints_in(&[layer, a], &mut |_, s| {
|
||||
seen += 1;
|
||||
s.take().is_some()
|
||||
});
|
||||
assert_eq!((n, seen), (1, 2), "the layer covers `a`: each fill is visited once");
|
||||
assert_eq!(d.node(a).unwrap().appearance.fill_paint(), Paint::solid(Color::rgb(1.0, 0.0, 0.0)));
|
||||
let mut colors = vec![];
|
||||
node_colors(d.node(b).unwrap(), &mut |c, s| colors.push((*c, s.map(str::to_string))));
|
||||
assert_eq!(colors, [(Color::rgb(1.0, 0.0, 0.0), None)]);
|
||||
}
|
||||
}
|
||||
|
|
@ -257,7 +257,7 @@ fn convert_mode(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
}
|
||||
n
|
||||
});
|
||||
for sw in &mut d.swatches {
|
||||
for sw in d.swatches_iter_mut() {
|
||||
match &mut sw.paint {
|
||||
Paint::Solid { color, .. } => *color = conv(color),
|
||||
Paint::Gradient(g) => {
|
||||
|
|
@ -486,14 +486,14 @@ fn set_spot(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
const C: &str = "swatch.setSpot";
|
||||
let name = str_param(p, "name").ok_or_else(|| bad(C, "missing `name`"))?.to_string();
|
||||
let spot = bool_or(p, "spot", true);
|
||||
let sw = s.doc()?.doc.swatches.iter().find(|w| w.name == name).ok_or_else(|| bad(C, format!("no swatch `{name}`")))?;
|
||||
let sw = s.doc()?.doc.swatch(&name).ok_or_else(|| bad(C, format!("no swatch `{name}`")))?;
|
||||
if !matches!(sw.paint, Paint::Solid { .. }) {
|
||||
return Err(bad(C, "only solid-colour swatches can be spot colours"));
|
||||
}
|
||||
if sw.spot != spot {
|
||||
let n = name.clone();
|
||||
s.edit("Swatch Options", |d, _| {
|
||||
if let Some(w) = d.swatches.iter_mut().find(|w| w.name == n) {
|
||||
if let Some(w) = d.swatch_mut(&n) {
|
||||
w.spot = spot;
|
||||
if spot {
|
||||
w.global = true;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
"images": images,
|
||||
"symbols": symbols,
|
||||
"spotColors": spot,
|
||||
"swatches": d.swatches.len(),
|
||||
"swatches": d.swatches_iter().count(),
|
||||
"graphicStyles": d.graphic_styles.len(),
|
||||
"characterStyles": d.char_styles.len(),
|
||||
"paragraphStyles": d.para_styles.len(),
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ fn color_mode(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
for l in &mut d.layers {
|
||||
map_node_colors(Arc::make_mut(l), &f, true, true);
|
||||
}
|
||||
for sw in &mut d.swatches {
|
||||
for sw in d.swatches_iter_mut() {
|
||||
if let vectorcraft_color::Paint::Solid { color, .. } = &mut sw.paint {
|
||||
*color = f(*color);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -733,6 +733,8 @@ mod tests_pattern;
|
|||
#[cfg(test)]
|
||||
mod tests_prefs;
|
||||
#[cfg(test)]
|
||||
mod tests_swatches;
|
||||
#[cfg(test)]
|
||||
mod tests_textedit;
|
||||
#[cfg(test)]
|
||||
mod tests_xform;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,76 @@
|
|||
//! Swatches: colour groups as first-class swatch homes (colour mode, spot plates, PDF, Document
|
||||
//! Info), naming and kinds, Swatch Options edits reaching linked art, deleting and new groups.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::{Color, Paint};
|
||||
|
||||
use super::*;
|
||||
|
||||
fn session() -> Session {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
fn run(s: &mut Session, id: &str, p: Value) -> Value {
|
||||
s.execute(id, &p).unwrap_or_else(|e| panic!("{id} {p}: {e}"))
|
||||
}
|
||||
|
||||
fn rect(s: &mut Session) -> NodeId {
|
||||
NodeId(run(s, "shape.rectangle", json!({"x": 0, "y": 0, "width": 10, "height": 10}))["id"].as_u64().unwrap())
|
||||
}
|
||||
|
||||
fn doc(s: &Session) -> &vectorcraft_doc::Document {
|
||||
&s.doc().unwrap().doc
|
||||
}
|
||||
|
||||
/// Put a solid swatch into colour group `group` directly in the model (no command creates one in a
|
||||
/// group before M3.13).
|
||||
fn grouped_swatch(s: &mut Session, group: &str, name: &str, color: Color) {
|
||||
let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc);
|
||||
let g = d.swatch_groups.iter_mut().find(|g| g.name == group).unwrap();
|
||||
g.swatches.push(vectorcraft_color::Swatch { name: name.into(), paint: Paint::solid(color), global: true, spot: false });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_color_mode_converts_grouped_swatches() {
|
||||
let mut s = session();
|
||||
grouped_swatch(&mut s, "Grays", "Ink", Color::rgb(0.2, 0.4, 0.6));
|
||||
run(&mut s, "object.convertDocumentColorMode", json!({"mode": "cmyk"}));
|
||||
for g in &doc(&s).swatch_groups {
|
||||
for sw in &g.swatches {
|
||||
assert!(!matches!(sw.paint.color(), Some(Color::Rgb { .. })), "{} in {} is still RGB", sw.name, g.name);
|
||||
}
|
||||
}
|
||||
assert!(matches!(doc(&s).swatch("Ink").unwrap().paint.color(), Some(Color::Cmyk { .. })));
|
||||
// The File menu's mode switch converts them as well.
|
||||
let mut s = session();
|
||||
run(&mut s, "file.documentColorMode", json!({"mode": "cmyk"}));
|
||||
assert!(matches!(doc(&s).swatch("Bright Red").unwrap().paint.color(), Some(Color::Cmyk { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn grouped_spot_swatches_print_on_their_own_plate() {
|
||||
let mut s = session();
|
||||
grouped_swatch(&mut s, "Brights", "Signal Orange", Color::cmyk(0.0, 0.6, 1.0, 0.0));
|
||||
assert_eq!(run(&mut s, "swatch.setSpot", json!({"name": "Signal Orange"})), json!({"name": "Signal Orange", "spot": true}));
|
||||
assert!(doc(&s).swatch("Signal Orange").is_some_and(|w| w.spot && w.global));
|
||||
let plates = run(&mut s, "color.plates", json!({}));
|
||||
assert!(plates["plates"].as_array().unwrap().iter().any(|p| p["name"] == "Signal Orange" && p["spot"] == true), "{plates}");
|
||||
let a = rect(&mut s);
|
||||
run(&mut s, "paint.setFill", json!({"ids": [a.0], "swatch": "Signal Orange"}));
|
||||
let bytes = vectorcraft_pdf::export(doc(&s), &vectorcraft_pdf::PdfOptions { compress: false, created: Some(0), ..Default::default() }).unwrap();
|
||||
let pdf = String::from_utf8_lossy(&bytes);
|
||||
assert!(pdf.contains("/Separation") && pdf.contains("Signal#20Orange"), "a grouped spot swatch is a Separation");
|
||||
let info = run(&mut s, "document.info", json!({}));
|
||||
assert_eq!(info["spotColors"], json!(["Signal Orange"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_info_counts_grouped_swatches() {
|
||||
let mut s = session();
|
||||
let d = doc(&s);
|
||||
let all = d.swatches.len() + d.swatch_groups.iter().map(|g| g.swatches.len()).sum::<usize>();
|
||||
assert!(all > d.swatches.len());
|
||||
assert_eq!(run(&mut s, "document.info", json!({}))["swatches"], json!(all));
|
||||
}
|
||||
|
|
@ -215,9 +215,7 @@ impl Exporter<'_> {
|
|||
/// swatch's CMYK equivalent is the alternate space).
|
||||
fn solid(&mut self, c: &Color, swatch: Option<&str>) -> krilla::color::Color {
|
||||
use krilla::color::separation::{Color as SepColor, SeparationColorant, SeparationSpace};
|
||||
let spot = swatch
|
||||
.and_then(|n| self.doc.swatches.iter().find(|s| s.spot && s.name == n))
|
||||
.and_then(|s| s.paint.color().map(|sc| (s.name.clone(), sc)));
|
||||
let spot = swatch.and_then(|n| self.doc.swatch(n).filter(|s| s.spot)).and_then(|s| s.paint.color().map(|sc| (s.name.clone(), sc)));
|
||||
let Some((name, sc)) = spot else { return self.col(c) };
|
||||
let cms = vectorcraft_color::cms::active();
|
||||
let intent = cms.settings().intent;
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ pub fn plates(doc: &Document) -> Vec<Plate> {
|
|||
Plate { name: (*n).into(), spot: false, rgb: c.cmyk_to_srgb(ink, false) }
|
||||
})
|
||||
.collect();
|
||||
for sw in &doc.swatches {
|
||||
for sw in doc.swatches_iter() {
|
||||
if let (true, Paint::Solid { color, .. }) = (sw.spot, &sw.paint)
|
||||
&& !v.iter().any(|p| p.name == sw.name)
|
||||
{
|
||||
|
|
@ -69,7 +69,7 @@ pub fn plates(doc: &Document) -> Vec<Plate> {
|
|||
}
|
||||
|
||||
fn spot_swatch<'a>(doc: &'a Document, name: &str) -> Option<&'a Color> {
|
||||
doc.swatches.iter().find(|s| s.spot && s.name == name).and_then(|s| match &s.paint {
|
||||
doc.swatch(name).filter(|s| s.spot).and_then(|s| match &s.paint {
|
||||
Paint::Solid { color, .. } => Some(color),
|
||||
_ => None,
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue