Merge pull request #3975 from dotCooCoo/feat/trace-color-group

Image Trace: Document Library traces with a color group from the Swatches panel
This commit is contained in:
BFlat
2026-10-26 09:42:27 +07:00
committed by GitHub
26 changed files with 347 additions and 8 deletions
+37 -6
View File
@@ -129,7 +129,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Make",
["Object", "Image Trace"],
None,
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, white is always its own colour, at most ten; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down; Flat Logo names only the size it traced a larger image at)}",
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), colorGroup?: name (color mode, documentLibrary with library \"document\": the colors of that document color group instead of every swatch; \"\" is every swatch), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, white is always its own colour, at most ten; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white unless ignoreWhite makes them holes; fills/strokes are ignored; gradients and photos are refused)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down; Flat Logo names only the size it traced a larger image at)}",
has_selection_or_ids,
|s, p| trace_make(s, p, false)
),
@@ -1000,18 +1000,49 @@ fn trace_params(c: &str, p: &Value) -> Result<tr::TraceParams> {
if !(params.stroke_width.is_finite() && params.stroke_width > 0.0) {
return Err(bad(c, "strokeWidth must be a positive number of pixels"));
}
// A color group is refused unless the trace uses it: Color mode with Document Library and the
// document's swatches.
if params.color_group.as_deref().is_some_and(|g| !g.is_empty()) {
if params.palette != tr::Palette::DocumentLibrary {
return Err(bad(c, "`colorGroup` needs `palette: \"documentLibrary\"`"));
}
if params.library != super::swatchlib::DOCUMENT_SWATCHES {
return Err(bad(c, "`colorGroup` needs `library: \"document\"`"));
}
if params.mode != tr::Mode::Color {
return Err(bad(c, "`colorGroup` needs `mode: \"color\"`"));
}
}
Ok(params)
}
/// Document Library in Color mode: look the library's colours up into `params.swatches` → each
/// one as traced (sRGB) with the colour to fill with. Errors name command `c`.
/// Document Library with `colorGroup`: the solid colors of the active document's color group
/// `name` (so named exactly, else ignoring case). Errors name command `c`.
/// ([`trace_params`] has checked that the library is the document's swatches.)
fn trace_group_colors(c: &str, s: &Session, name: &str) -> Result<Vec<Color>> {
let groups = &s.doc()?.doc.swatch_groups;
let lower = name.to_lowercase();
let group = groups
.iter()
.find(|g| g.name == name)
.or_else(|| groups.iter().find(|g| g.name.to_lowercase() == lower))
.ok_or_else(|| bad(c, format!("no color group `{name}` in the document (see swatch.list)")))?;
Ok(group.swatches.iter().filter_map(|w| w.paint.color()).collect())
}
/// Document Library in Color mode: look the library's colors (or its color group's) up into
/// `params.swatches` → each one as traced (sRGB) with the color to fill with. Errors name command `c`.
fn trace_library(c: &str, s: &Session, params: &mut tr::TraceParams) -> Result<Vec<([u8; 3], Color)>> {
if params.mode != tr::Mode::Color || params.palette != tr::Palette::DocumentLibrary {
return Ok(vec![]);
}
let key = params.library.as_str();
let colors = super::swatchlib::library_colors(s, key)
.ok_or_else(|| bad(c, format!("no swatch library `{key}` (see swatch.library.list; \"document\": the document's swatches)")))?;
let group = params.color_group.as_deref().filter(|g| !g.is_empty());
let key = group.unwrap_or(params.library.as_str());
let colors = match group {
Some(name) => trace_group_colors(c, s, name)?,
None => super::swatchlib::library_colors(s, key)
.ok_or_else(|| bad(c, format!("no swatch library `{key}` (see swatch.library.list; \"document\": the document's swatches)")))?,
};
if colors.is_empty() {
return Err(bad(c, format!("`{key}` has no colours")));
}
+65
View File
@@ -2802,6 +2802,71 @@ fn image_trace_palettes_and_swatch_libraries() {
assert!(bad(json!({"palette": "rainbow"})));
}
#[test]
fn image_trace_document_library_uses_a_color_group() {
let mut s = session();
let r = vectorcraft_trace::Raster::from_fn(60, 60, |x, y| match (x < 30, y < 30) {
(true, _) => [200, 40, 40, 255],
(false, true) => [40, 160, 60, 255],
(false, false) => [50, 60, 200, 255],
});
add_image(&mut s, &r, Affine::IDENTITY);
// Ungrouped swatches of the image's own colors, and a group of colors a little off them.
s.execute("swatch.new", &json!({"colors": ["#c82828", "#28a03c", "#323cc8"]})).unwrap();
s.execute("swatch.newGroup", &json!({"name": "Brand", "colors": ["#b43c3c", "#3c8c50", "#4650b4"]})).unwrap();
s.execute("swatch.newGroup", &json!({"name": "Empty"})).unwrap();
s.execute("swatch.newGroup", &json!({"name": "Été", "colors": ["#b43c3c", "#3c8c50", "#4650b4"]})).unwrap();
let hex = |c: &Color| {
let [r, g, b, _] = c.to_rgba8(1.0);
format!("#{r:02x}{g:02x}{b:02x}")
};
let trace = |s: &mut Session, extra: Value| {
let mut params = json!({"mode": "color", "palette": "documentLibrary", "library": "document", "colors": 3});
params.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone());
s.execute("imageTrace.makeAndExpand", &json!({"params": params}))
};
let fills = |s: &Session, id: &Value| -> Vec<String> {
let g = node(s, NodeId(id.as_u64().unwrap()));
let mut v: Vec<String> = g.children().unwrap().iter().filter_map(|n| n.appearance.fill_paint().color()).map(|c| hex(&c)).collect();
v.sort();
v.dedup();
v
};
// Every swatch: the image's own colors win.
let r = trace(&mut s, json!({})).unwrap();
assert_eq!(fills(&s, &r["id"]), ["#28a03c", "#323cc8", "#c82828"]);
s.execute("edit.undo", &json!({})).unwrap();
// The group: its colors only, and its name matches whatever its case.
for name in ["Brand", "brand", "Été", "été", "ÉTÉ"] {
let r = trace(&mut s, json!({"colorGroup": name})).unwrap();
assert_eq!(fills(&s, &r["id"]), ["#3c8c50", "#4650b4", "#b43c3c"], "{name}");
s.execute("edit.undo", &json!({})).unwrap();
}
// An empty name is every swatch.
let r = trace(&mut s, json!({"colorGroup": ""})).unwrap();
assert_eq!(fills(&s, &r["id"]), ["#28a03c", "#323cc8", "#c82828"]);
s.execute("edit.undo", &json!({})).unwrap();
let err = |s: &mut Session, extra: Value| trace(s, extra).unwrap_err().to_string();
assert!(err(&mut s, json!({"colorGroup": "Nope"})).contains("no color group `Nope`"));
assert!(err(&mut s, json!({"colorGroup": "Empty"})).contains("`Empty` has no colours"));
assert!(err(&mut s, json!({"colorGroup": "Brand", "library": "web-safe-216"})).contains("needs `library: \"document\"`"));
// Every other palette refuses a group too, in any mode; an empty name is no group.
for palette in ["limited", "fullTone", "automatic"] {
let e = err(&mut s, json!({"colorGroup": "Brand", "palette": palette}));
assert!(e.contains("`colorGroup` needs `palette: \"documentLibrary\"`"), "{palette}: {e}");
}
let e = err(&mut s, json!({"colorGroup": "Brand", "mode": "grayscale", "palette": "limited"}));
assert!(e.contains("needs `palette: \"documentLibrary\"`"), "{e}");
// Document Library uses the group in Color mode only.
for mode in ["blackAndWhite", "grayscale", "logo"] {
let e = err(&mut s, json!({"colorGroup": "Brand", "mode": mode}));
assert!(e.contains("`colorGroup` needs `mode: \"color\"`"), "{mode}: {e}");
}
s.execute("imageTrace.makeAndExpand", &json!({"params": {"mode": "color", "palette": "limited", "colors": 3, "colorGroup": ""}})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
trace(&mut s, json!({"mode": "grayscale", "colorGroup": ""})).unwrap();
}
#[test]
fn image_trace_create_strokes_makes_stroked_centre_lines() {
let mut s = session();
+6 -1
View File
@@ -281,8 +281,12 @@ pub struct TraceParams {
/// Document Library: the swatch library, `"document"` (the document's swatches) or a library id
/// or name. The caller looks its colours up into `swatches`.
pub library: String,
/// Document Library with the document's swatches: the name of the color group whose colors are
/// used. `None` (or empty) uses every swatch. `imageTrace.make` refuses a name with any other
/// palette, library or mode.
pub color_group: Option<String>,
/// Document Library: the library's colours (sRGB). Not saved: they are looked up from
/// `library` at every trace.
/// `library` (and `color_group`) at every trace.
#[serde(skip)]
pub swatches: Vec<[u8; 3]>,
/// Paths (fidelity) 0–100: higher follows the pixels more tightly.
@@ -333,6 +337,7 @@ impl Default for TraceParams {
colors: 6,
color_detail: 50.0,
library: "document".into(),
color_group: None,
swatches: vec![],
paths: 50.0,
corners: 75.0,
+4
View File
@@ -5399,6 +5399,10 @@ mark-style Japanese Japonské
Snap to Perpendicular Přichytávat ke kolmici
@msg unknown snap option `{key}` (use {options}) neznámá možnost přichytávání `{key}` (použijte {options})
Gap Length: Délka mezery:
@msg `colorGroup` needs `library: "document"` `colorGroup` vyžaduje `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` vyžaduje `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` vyžaduje `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) skupina barev `{name}` v dokumentu neexistuje (viz swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Režim Ploché logo vektorizoval obraz v rozlišení {_1} × {_2} pixelů, zmenšený průměrováním z {_3} × {_4}.
Artboard {n} of {total} Pracovní plocha {n} z {total}
File {n} of {total} Soubor {n} z {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -4978,6 +4978,10 @@ effect Soft Weich
Snap to Perpendicular An Senkrechter ausrichten
@msg unknown snap option `{key}` (use {options}) unbekannte Einrastoption `{key}` (möglich: {options})
Gap Length: Lückenlänge:
@msg `colorGroup` needs `library: "document"` `colorGroup` erfordert `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` erfordert `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` erfordert `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) keine Farbgruppe `{name}` im Dokument (siehe swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Der Modus „Flaches Logo“ hat das Bild mit {_1} × {_2} Pixeln nachgezeichnet, durch Mittelung verkleinert von {_3} × {_4}.
Artboard {n} of {total} Zeichenfläche {n} von {total}
File {n} of {total} Datei {n} von {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5351,6 +5351,10 @@ effect Points Σημεία
Snap to Perpendicular Προσκόλληση σε κάθετο
@msg unknown snap option `{key}` (use {options}) άγνωστη επιλογή προσκόλλησης `{key}` (χρησιμοποιήστε {options})
Gap Length: Μήκος κενού:
@msg `colorGroup` needs `library: "document"` το `colorGroup` απαιτεί `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` το `colorGroup` απαιτεί `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` το `colorGroup` απαιτεί `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) δεν υπάρχει ομάδα χρωμάτων `{name}` στο έγγραφο (δείτε swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Η λειτουργία Επίπεδο λογότυπο ανίχνευσε την εικόνα σε {_1} × {_2} εικονοστοιχεία, σμικρυμένη με μέσο όρο από {_3} × {_4}.
Artboard {n} of {total} Πίνακας σχεδίασης {n} από {total}
File {n} of {total} Αρχείο {n} από {total}
Can't render this file because it contains an unexpected character in line 2070 and column 19.
+4
View File
@@ -5341,6 +5341,10 @@ mark-style Japanese Japonesas
Snap to Perpendicular Ajustar a perpendicular
@msg unknown snap option `{key}` (use {options}) opción de ajuste desconocida `{key}` (use {options})
Gap Length: Longitud del hueco:
@msg `colorGroup` needs `library: "document"` `colorGroup` requiere `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` requiere `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` requiere `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) no existe el grupo de colores `{name}` en el documento (consulta swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. El modo Logotipo plano calcó la imagen a {_1} × {_2} píxeles, reducida promediando desde {_3} × {_4}.
Artboard {n} of {total} Mesa de trabajo {n} de {total}
File {n} of {total} Archivo {n} de {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5363,6 +5363,10 @@ mark-style Japanese Japanilainen
Snap to Perpendicular Kohdista kohtisuoraan
@msg unknown snap option `{key}` (use {options}) tuntematon tartuntavalinta `{key}` (käytä: {options})
Gap Length: Aukon pituus:
@msg `colorGroup` needs `library: "document"` `colorGroup` edellyttää arvoa `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` edellyttää arvoa `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` edellyttää arvoa `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) asiakirjassa ei ole väriryhmää `{name}` (ks. swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Tasainen logo -tila jäljitti kuvan kokoon {_1} × {_2} kuvapistettä keskiarvoistamalla pienennettynä koosta {_3} × {_4}.
Artboard {n} of {total} Piirtoalusta {n}/{total}
File {n} of {total} Tiedosto {n}/{total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5398,6 +5398,10 @@ theme Appearance Apparence
Snap to Perpendicular Magnétisme de la perpendiculaire
@msg unknown snap option `{key}` (use {options}) option de magnétisme inconnue `{key}` (utilisez {options})
Gap Length: Longueur de lacune :
@msg `colorGroup` needs `library: "document"` `colorGroup` nécessite `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` nécessite `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` nécessite `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) aucun groupe de couleurs `{name}` dans le document (voir swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Le mode Logo en aplats a vectorisé l’image à {_1} × {_2} pixels, réduite par moyennage de {_3} × {_4}.
Artboard {n} of {total} Plan de travail {n} sur {total}
File {n} of {total} Fichier {n} sur {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5385,6 +5385,10 @@ imageTrace Outline Körvonal
Snap to Perpendicular Illesztés merőlegesre
@msg unknown snap option `{key}` (use {options}) ismeretlen illesztési beállítás: `{key}` (használható: {options})
Gap Length: Rés hossza:
@msg `colorGroup` needs `library: "document"` a `colorGroup` használatához `library: "document"` szükséges
@msg `colorGroup` needs `palette: "documentLibrary"` a `colorGroup` használatához `palette: "documentLibrary"` szükséges
@msg `colorGroup` needs `mode: "color"` a `colorGroup` használatához `mode: "color"` szükséges
@msg no color group `{name}` in the document (see swatch.list) nincs ilyen színcsoport a dokumentumban: `{name}` (lásd: swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Az Egyszínű logó mód {_1} × {_2} képponton vektorizálta a képet, {_3} × {_4} méretről átlagolással kicsinyítve.
Artboard {n} of {total} Rajztábla: {n} / {total}
File {n} of {total} Fájl: {n} / {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5012,6 +5012,10 @@ lighting Light Luce
Snap to Perpendicular Aggancia alla perpendicolare
@msg unknown snap option `{key}` (use {options}) opzione di aggancio sconosciuta `{key}` (usare {options})
Gap Length: Lunghezza lacuna:
@msg `colorGroup` needs `library: "document"` `colorGroup` richiede `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` richiede `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` richiede `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) nessun gruppo colori `{name}` nel documento (vedi swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. La modalità Logo piatto ha vettorializzato l’immagine a {_1} × {_2} pixel, ridotta per media da {_3} × {_4}.
Artboard {n} of {total} Tavola da disegno {n} di {total}
File {n} of {total} File {n} di {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5052,6 +5052,10 @@ lighting Light ライト
Snap to Perpendicular 垂線にスナップ
@msg unknown snap option `{key}` (use {options}) 不明なスナップオプション `{key}` ({options} を使用してください)
Gap Length: ギャップの長さ:
@msg `colorGroup` needs `library: "document"` `colorGroup` には `library: "document"` が必要です
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` には `palette: "documentLibrary"` が必要です
@msg `colorGroup` needs `mode: "color"` `colorGroup` には `mode: "color"` が必要です
@msg no color group `{name}` in the document (see swatch.list) ドキュメントにカラーグループ `{name}` が見つかりません (swatch.list を参照)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. フラットロゴモードは画像を {_3} × {_4} から平均化して縮小した {_1} × {_2} ピクセルでトレースしました。
Artboard {n} of {total} アートボード {n} / {total}
File {n} of {total} ファイル {n} / {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5237,6 +5237,10 @@ imageTrace Outline კონტური
@msg Save writes {_1}, not {_2}: use file.saveAsTemplate to write it „შენახვა“ წერს ფორმატს {_1} და არა {_2}: ამისთვის გამოიყენეთ file.saveAsTemplate
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. გამოსახულება არის {_1} × {_2} პიქსელი, ანუ ერთ მხარეზე {LARGE_TRACE_SIDE}-ზე მეტი: ის ვექტორიზდება, მაგრამ უფრო ნელა. ნაკლები სიზუსტის პრესეტი ან გამოსახულების უფრო პატარა ასლი უფრო სწრაფად ვექტორიზდება.
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). გამოსახულება ვექტორიზდა {_1} × {_2} პიქსელზე, შემცირდა {_3} × {_4}-დან (მაქსიმუმ {_5} მეგაპიქსელი).
@msg `colorGroup` needs `library: "document"` `colorGroup`-ს სჭირდება `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup`-ს სჭირდება `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup`-ს სჭირდება `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) დოკუმენტში ფერების ჯგუფი `{name}` არ არის (იხ. swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. ბრტყელი ლოგოს რეჟიმში გამოსახულება ვექტორიზდა {_1} × {_2} პიქსელზე, საშუალოს გამოთვლით შემცირდა {_3} × {_4}-დან.
@msg `{given}` isn't a library panel (see window.swatchLibrary) `{given}` ბიბლიოთეკის პანელი არ არის (იხ. window.swatchLibrary)
@msg `{key}` must be a #rrggbb color `{key}` უნდა იყოს #rrggbb ფერი
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -4975,6 +4975,10 @@ lighting Light 광원
Snap to Perpendicular 수직선에 스냅
@msg unknown snap option `{key}` (use {options}) 알 수 없는 스냅 옵션 `{key}` ({options} 중에서 사용하세요)
Gap Length: 간격 길이:
@msg `colorGroup` needs `library: "document"` `colorGroup`에는 `library: "document"`가 필요합니다
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup`에는 `palette: "documentLibrary"`가 필요합니다
@msg `colorGroup` needs `mode: "color"` `colorGroup`에는 `mode: "color"`가 필요합니다
@msg no color group `{name}` in the document (see swatch.list) 문서에 색상 그룹 `{name}`이(가) 없습니다(swatch.list 참조)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. 단색 로고 모드에서 이미지를 {_3} × {_4}에서 평균을 내어 축소한 {_1} × {_2}픽셀로 추적했습니다.
Artboard {n} of {total} 아트보드 {n} / {total}
File {n} of {total} 파일 {n} / {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5251,6 +5251,10 @@ mark-style Japanese Japońskie
Snap to Perpendicular Przyciągaj do prostopadłych
@msg unknown snap option `{key}` (use {options}) nieznana opcja przyciągania `{key}` (użyj: {options})
Gap Length: Długość przerwy:
@msg `colorGroup` needs `library: "document"` `colorGroup` wymaga `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` wymaga `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` wymaga `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) brak grupy kolorów `{name}` w dokumencie (patrz swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Tryb Płaskie logo obrysował obraz w rozdzielczości {_1} × {_2} pikseli, po zmniejszeniu przez uśrednienie z {_3} × {_4}.
Artboard {n} of {total} Obszar kompozycji {n} z {total}
File {n} of {total} Plik {n} z {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5316,6 +5316,10 @@ mark-style Japanese Japonesas
Snap to Perpendicular Ajustar à Perpendicular
@msg unknown snap option `{key}` (use {options}) opção de ajuste desconhecida `{key}` (use {options})
Gap Length: Comprimento da Lacuna:
@msg `colorGroup` needs `library: "document"` `colorGroup` requer `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` requer `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` requer `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) nenhum grupo de cores `{name}` no documento (veja swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. O modo Logotipo Plano traçou a imagem em {_1} × {_2} pixels, reduzida por média de {_3} × {_4}.
Artboard {n} of {total} Mesa de Trabalho {n} de {total}
File {n} of {total} Arquivo {n} de {total}
Can't render this file because it contains an unexpected character in line 6 and column 47.
+4
View File
@@ -5008,6 +5008,10 @@ lighting Light Свет
Snap to Perpendicular Привязать к перпендикуляру
@msg unknown snap option `{key}` (use {options}) неизвестный параметр привязки `{key}` (допустимо: {options})
Gap Length: Длина зазора:
@msg `colorGroup` needs `library: "document"` для `colorGroup` нужен `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` для `colorGroup` нужен `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` для `colorGroup` нужен `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) в документе нет цветовой группы `{name}` (см. swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Режим «Плоский логотип» трассировал изображение в размере {_1} × {_2} пикселей, уменьшенное усреднением с {_3} × {_4}.
Artboard {n} of {total} Монтажная область {n} из {total}
File {n} of {total} Файл {n} из {total}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -5422,6 +5422,10 @@ swatch Yellow Green Gulgrön
Snap to Perpendicular Fäst mot vinkelrät
@msg unknown snap option `{key}` (use {options}) okänt fästalternativ `{key}` (använd {options})
Gap Length: Lucklängd:
@msg `colorGroup` needs `library: "document"` `colorGroup` kräver `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` kräver `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` kräver `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) ingen färggrupp `{name}` i dokumentet (se swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Läget Platt logotyp vektoriserade bilden vid {_1} × {_2} pixlar, nedskalad genom medelvärdesbildning från {_3} × {_4}.
Artboard {n} of {total} Rityta {n} av {total}
File {n} of {total} Fil {n} av {total}
Can't render this file because it contains an unexpected character in line 5 and column 25.
+4
View File
@@ -5393,6 +5393,10 @@ theme Appearance Görünüm
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. Görüntü {_1} × {_2} piksel, bir kenarı {LARGE_TRACE_SIDE} değerinden büyük: izlenir, ancak daha yavaş. Daha düşük doğruluklu bir ön ayar veya görüntünün daha küçük bir kopyası daha hızlı izlenir.
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). Görüntü {_3} × {_4} boyutundan küçültülerek {_1} × {_2} pikselde izlendi (en fazla {_5} megapiksel).
Gap Length: Boşluk Uzunluğu:
@msg `colorGroup` needs `library: "document"` `colorGroup` için `library: "document"` gerekir
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` için `palette: "documentLibrary"` gerekir
@msg `colorGroup` needs `mode: "color"` `colorGroup` için `mode: "color"` gerekir
@msg no color group `{name}` in the document (see swatch.list) belgede `{name}` adlı renk grubu yok (bkz. swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Düz Logo modu görüntüyü {_3} × {_4} boyutundan ortalama alarak küçültüp {_1} × {_2} pikselde izledi.
@plural {n} more export waiting|{n} more exports waiting {n} dışa aktarma daha bekliyor|{n} dışa aktarma daha bekliyor
@msg Export cancelled Dışa aktarma iptal edildi
Can't render this file because it contains an unexpected character in line 5 and column 25.
+4
View File
@@ -4992,6 +4992,10 @@ lighting Light Світло
Snap to Perpendicular Прив’язати до перпендикуляра
@msg unknown snap option `{key}` (use {options}) невідомий параметр прив’язки `{key}` (використовуйте {options})
Gap Length: Довжина проміжку:
@msg `colorGroup` needs `library: "document"` для `colorGroup` потрібен `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` для `colorGroup` потрібен `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` для `colorGroup` потрібен `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) у документі немає колірної групи `{name}` (див. swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Режим «Плоский логотип» трасував зображення в розмірі {_1} × {_2} пікселів, зменшене усередненням з {_3} × {_4}.
Artboard {n} of {total} Монтажна область {n} з {total}
File {n} of {total} Файл {n} з {total}
Can't render this file because it contains an unexpected character in line 749 and column 40.
+4
View File
@@ -4992,6 +4992,10 @@ theme Appearance Giao diện
Snap to Perpendicular Hút vào đường vuông góc
@msg unknown snap option `{key}` (use {options}) tùy chọn hút không hợp lệ `{key}` (dùng {options})
Gap Length: Độ dài khoảng hở:
@msg `colorGroup` needs `library: "document"` `colorGroup` cần `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` cần `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` cần `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) không có nhóm màu `{name}` trong tài liệu (xem swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. Chế độ Logo phẳng đã dò ảnh ở {_1} × {_2} điểm ảnh, thu nhỏ bằng cách lấy trung bình từ {_3} × {_4}.
Artboard {n} of {total} Bảng vẽ {n} / {total}
File {n} of {total} Tệp {n} / {total}
Can't render this file because it contains an unexpected character in line 4053 and column 10.
+4
View File
@@ -5231,6 +5231,10 @@ lighting Light 光源
Snap to Perpendicular 对齐垂线
@msg unknown snap option `{key}` (use {options}) 未知的对齐选项 `{key}`(可用:{options})
Gap Length: 间隙长度:
@msg `colorGroup` needs `library: "document"` `colorGroup` 需要 `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` 需要 `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` 需要 `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) 文档中没有颜色组 `{name}` (请参阅 swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. “扁平徽标”模式已按 {_1} × {_2} 像素描摹图像,由 {_3} × {_4} 平均缩小而来。
# Links and Attributes: files and URLs handed to other apps.
@msg Edit Original opens image and document files only; “{name}” is not one 编辑原稿只能打开图像和文档文件;“{name}” 不是其中之一
Can't render this file because it contains an unexpected character in line 2 and column 73.
+4
View File
@@ -2377,6 +2377,10 @@ file-filter Templates 範本
@msg too many library panels were opened in this session 本次工作階段開啟的資料庫面板過多
@msg The image is {_1} × {_2} pixels, more than {LARGE_TRACE_SIDE} on a side: it is traced, but more slowly. A lower-fidelity preset or a smaller copy of the image traces faster. 影像為 {_1} × {_2} 像素,有一邊超過 {LARGE_TRACE_SIDE}:仍會描圖,但速度較慢。使用精確度較低的預設集或較小的影像拷貝,描圖會較快。
@msg The image was traced at {_1} × {_2} pixels, scaled down from {_3} × {_4} ({_5} megapixels at most). 影像已以 {_1} × {_2} 像素描圖,由 {_3} × {_4} 縮小而來 (最多 {_5} 百萬像素)。
@msg `colorGroup` needs `library: "document"` `colorGroup` 需要 `library: "document"`
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` 需要 `palette: "documentLibrary"`
@msg `colorGroup` needs `mode: "color"` `colorGroup` 需要 `mode: "color"`
@msg no color group `{name}` in the document (see swatch.list) 文件中沒有顏色群組 `{name}` (請參閱 swatch.list)
@msg Flat Logo traced the image at {_1} × {_2} pixels, averaged down from {_3} × {_4}. 「扁平標誌」模式已以 {_1} × {_2} 像素描圖,由 {_3} × {_4} 平均縮小而來。
# Links and Attributes: files and URLs handed to other apps.
@msg Edit Original opens image and document files only; “{name}” is not one 「編輯原稿」只能開啟影像與文件檔案;「{name}」不屬於這兩類
Can't render this file because it contains an unexpected character in line 6 and column 47.
+144
View File
@@ -61,6 +61,31 @@ fn library_list(app: &VectorcraftApp, ui: &mut Ui, current: &str) -> Option<Stri
library_panel::library_items(ui, &swatches::colour_libraries(app), &|id| id == current).or(document)
}
/// Document Library's Colors choices with the document's swatches: All, then the document's color
/// groups (`current` checked; `None` is All) → the choice, `Some(None)` for All.
fn color_group_list(app: &VectorcraftApp, ui: &mut Ui, current: Option<&str>) -> Option<Option<String>> {
let mut chosen = menu_item(ui, "All", true, current.is_none()).then_some(None);
let groups: Vec<String> = app.session.active().map(|d| d.doc.swatch_groups.iter().map(|g| g.name.clone()).collect()).unwrap_or_default();
if !groups.is_empty() {
ui.separator();
}
for g in groups {
if widgets::menu_item_name(ui, &g, true, current == Some(g.as_str())) {
chosen = Some(Some(g));
}
}
chosen
}
/// `params`' color group when the active document has it, found as `imageTrace.make` finds it (so
/// named exactly, else ignoring case); `None` is All. A group of another document, or a deleted
/// one, is All.
fn color_group<'a>(app: &VectorcraftApp, params: &'a Value) -> Option<&'a str> {
let name = params["colorGroup"].as_str().filter(|g| !g.is_empty())?;
let lower = name.to_lowercase();
app.session.active()?.doc.swatch_groups.iter().any(|g| g.name.to_lowercase() == lower).then_some(name)
}
/// Panel state: the preset name, the current parameters, the view and the last result's counts.
#[derive(Clone, Default)]
struct TraceUi {
@@ -258,6 +283,10 @@ pub fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) -> bool {
}
fn trace(app: &mut VectorcraftApp, st: &mut TraceUi) {
// A group the active document does not have traces with All, as the panel shows.
if color_group(app, &st.params).is_none() && !st.params["colorGroup"].is_null() {
st.params["colorGroup"] = Value::Null;
}
let preset = if st.preset == "Custom" { "Default" } else { st.preset.as_str() };
match app.run("imageTrace.make", json!({ "preset": preset, "params": st.params, "view": st.view.id() })) {
Ok(r) => {
@@ -378,6 +407,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let label = MODES.iter().find(|m| m.0 == mode).map_or("Black and White", |m| m.1);
let labels: Vec<&str> = MODES.iter().map(|m| m.1).collect();
if let Some(i) = widgets::dropdown(ui, "it-mode", label, &labels, 170.0) {
// Only Color mode uses a color group.
if MODES[i].0 != "color" {
st.params["colorGroup"] = Value::Null;
}
st.params["mode"] = json!(MODES[i].0);
st.preset = "Custom".into();
retrace = true;
@@ -390,6 +423,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
widgets::dim_label(ui, tl!("Palette:"));
let labels = PALETTES.map(palette_label);
if let Some(p) = widgets::dropdown_names(ui, "it-palette", palette_label(&palette), &labels, 170.0).and_then(|i| PALETTES.get(i)) {
// Every other palette refuses a color group.
if *p != "documentLibrary" {
st.params["colorGroup"] = Value::Null;
}
st.params["palette"] = json!(p);
st.preset = "Custom".into();
retrace = true;
@@ -400,11 +437,27 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
widgets::dim_label(ui, tl!("Library:"));
let key = st.params["library"].as_str().unwrap_or(DOCUMENT_SWATCHES).to_string();
if let Some(k) = widgets::combo(ui, "it-library", &library_label(app, &key), 170.0, false, |ui| library_list(app, ui, &key)) {
if k != DOCUMENT_SWATCHES {
st.params["colorGroup"] = Value::Null;
}
st.params["library"] = json!(k);
st.preset = "Custom".into();
retrace = true;
}
});
// The document's swatches: every one, or one color group's.
if st.params["library"].as_str().unwrap_or(DOCUMENT_SWATCHES) == DOCUMENT_SWATCHES {
ui.horizontal(|ui| {
widgets::dim_label(ui, tl!("Colors:"));
let group = color_group(app, &st.params).map(str::to_string);
let label = group.clone().unwrap_or_else(|| tl!("All").to_string());
if let Some(g) = widgets::combo(ui, "it-color-group", &label, 170.0, false, |ui| color_group_list(app, ui, group.as_deref())) {
st.params["colorGroup"] = json!(g);
st.preset = "Custom".into();
retrace = true;
}
});
}
}
}
// Full Tone and Automatic: how many colours (Less … More); else a count or the threshold.
@@ -1250,6 +1303,97 @@ mod tests {
assert!(has(&texts, "50") && !has(&texts, "Library:"), "{texts:?}");
}
#[test]
fn document_library_colors_lists_the_documents_color_groups() {
let mut app = traced_app();
app.run("swatch.newGroup", json!({"name": "Brand", "colors": ["#000000", "#ffffff"]})).unwrap();
app.run("imageTrace.make", json!({"preset": "6 Colors", "params": {"palette": "documentLibrary"}})).unwrap();
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let has = |texts: &[(String, Rect)], s: &str| texts.iter().any(|(t, _)| t == s);
let stored = |app: &VectorcraftApp, key: &str| target(app).2.map(|t| t["params"][key].clone());
let texts = frame(&mut app, &ctx, vec![]);
assert!(has(&texts, "Colors:") && has(&texts, "All"), "{texts:?}");
// The list holds All and the document's groups; a group traces again with its colors.
click(&mut app, &ctx, "All");
assert!(has(&frame(&mut app, &ctx, vec![]), " Brand"));
let steps = app.session.active().unwrap().history.undo.len();
click(&mut app, &ctx, " Brand");
assert_eq!(stored(&app, "colorGroup"), Some(json!("Brand")));
assert_eq!(target(&app).2.unwrap()["preset"], "Custom");
assert_eq!(app.session.active().unwrap().history.undo.len(), steps + 1);
assert!(has(&frame(&mut app, &ctx, vec![]), "Brand"));
// All again.
click(&mut app, &ctx, "Brand");
click(&mut app, &ctx, " All");
assert_eq!(stored(&app, "colorGroup"), Some(Value::Null));
// Another library clears the group, and the row goes.
click(&mut app, &ctx, "All");
click(&mut app, &ctx, " Brand");
let lib = swatches::colour_libraries(&app).into_iter().find(|l| l.submenu.is_none()).unwrap();
click(&mut app, &ctx, "Document Swatches");
click(&mut app, &ctx, &format!(" {}", library_panel::library_name(&lib.id, &lib.name)));
assert_eq!((stored(&app, "library"), stored(&app, "colorGroup")), (Some(json!(lib.id)), Some(Value::Null)));
assert!(!has(&frame(&mut app, &ctx, vec![]), "Colors:"));
// Another palette clears the group too, and traces again (the object stores the settings of
// a trace that was made).
click(&mut app, &ctx, library_panel::library_name(&lib.id, &lib.name));
click(&mut app, &ctx, " Document Swatches");
click(&mut app, &ctx, "All");
click(&mut app, &ctx, " Brand");
assert_eq!(stored(&app, "colorGroup"), Some(json!("Brand")));
click(&mut app, &ctx, "Document Library");
click(&mut app, &ctx, "Limited");
assert_eq!((stored(&app, "palette"), stored(&app, "colorGroup")), (Some(json!("limited")), Some(Value::Null)));
// Another mode clears it too, and traces again.
click(&mut app, &ctx, "Limited");
click(&mut app, &ctx, "Document Library");
click(&mut app, &ctx, "All");
click(&mut app, &ctx, " Brand");
assert_eq!(stored(&app, "colorGroup"), Some(json!("Brand")));
click(&mut app, &ctx, "Color");
click(&mut app, &ctx, "Grayscale");
assert_eq!((stored(&app, "mode"), stored(&app, "colorGroup")), (Some(json!("grayscale")), Some(Value::Null)));
}
#[test]
fn a_color_group_the_active_document_lacks_is_all() {
let mut app = traced_app();
app.run("swatch.newGroup", json!({"name": "Brand", "colors": ["#000000", "#ffffff"]})).unwrap();
app.run("imageTrace.make", json!({"preset": "6 Colors", "params": {"palette": "documentLibrary"}})).unwrap();
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
click(&mut app, &ctx, "All");
click(&mut app, &ctx, " Brand");
assert_eq!(target(&app).2.map(|t| t["params"]["colorGroup"].clone()), Some(json!("Brand")));
let shows_brand = |app: &mut VectorcraftApp| frame(app, &ctx, vec![]).iter().any(|(t, _)| t == "Brand");
// Without the group the row shows All; undoing its removal shows it again.
app.run("swatch.ungroup", json!({"name": "Brand"})).unwrap();
assert!(!shows_brand(&mut app));
app.run("edit.undo", json!({})).unwrap();
assert!(shows_brand(&mut app));
// Another document without the group, with an image that is not traced yet.
app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
let img = image::RgbaImage::from_fn(20, 20, |x, _| image::Rgba(if x < 10 { [0, 0, 0, 255] } else { [255, 255, 255, 255] }));
let mut png = vec![];
img.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
let data = vectorcraft_format::base64_encode(&png);
app.run("file.place", json!({"name": "half.png", "dataBase64": data, "link": false})).unwrap();
let texts = frame(&mut app, &ctx, vec![]);
let has = |s: &str| texts.iter().any(|(t, _)| t == s);
assert!(has("Colors:") && has("All") && !has("Brand"), "{texts:?}");
// Back in the first document, its Image Trace object shows its group again.
app.session.set_active(0);
assert!(shows_brand(&mut app));
app.session.set_active(1);
assert!(!shows_brand(&mut app));
// Trace uses every document swatch.
click(&mut app, &ctx, "Trace");
let stored = target(&app).2.unwrap_or_else(|| panic!("not traced: {}", app.ui.status));
assert!(stored["params"]["colorGroup"].as_str().is_none_or(str::is_empty), "{stored}");
assert_eq!(stored["params"]["palette"], "documentLibrary");
}
#[test]
fn create_strokes_traces_again_and_keeps_one_kind_on() {
let mut app = traced_app();
+14 -1
View File
@@ -32,11 +32,24 @@ white beside a cream), at most six; scattered specks of a colour add none. Adobe
its own palettes are chosen; this is VectorCraft's own method (see
[image-trace-quality.md](image-trace-quality.md#auto-color-keeps-rare-colours)).
**Document Library color groups.** With Palette › Document Library and Library › Document Swatches,
the panel's **Colors** menu traces with every document swatch (**All**) or with the colors of one
color group from the Swatches panel, as the public
[Image Trace panel options](https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/image-trace-panel-options.html)
page ([archived](https://web.archive.org/web/20251230195658/https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/image-trace-panel-options.html))
describes Document Library. In the params of `imageTrace.make` or `imageTrace.makeAndExpand`,
`"colorGroup": "<name>"` names the group. It needs `"mode": "color"`, `"palette": "documentLibrary"` and
`"library": "document"`; a `colorGroup` with a nonempty name and any other mode, palette or library is refused. An
empty name names no group and is accepted with any mode, palette or library; with Document Library and
`"library": "document"` it traces with every document swatch. A group whose name matches exactly is used first, then one whose name differs only in case. At
most `colors` of its colors are used. A missing group and a group with no solid colors are refused. Choosing another
mode, palette or library in the panel clears the group. A group the active document does not have shows as All
and traces as All.
**Document Library** traces with the library's colors as they are. Each pixel takes the library
color nearest to it in lightness and hue, with a difference in hue or chroma counted three times
as much as one in lightness, so a gray or a muted brown takes a black, white or other neutral
library color rather than a saturated color of the same lightness.
**Abutting and Overlapping.** Abutting is the default Method, and the Default, Auto Color, 3 Colors,
6 Colors and 16 Colors presets use it. With Abutting, two neighboring colors share one exact edge. The
canvas smooths the edges of each path on its own, so each of the two paths covers only part of the
+1
View File
@@ -793,6 +793,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Transparency:** opacity masks (clip/invert/disable/link), exported as SVG `<mask>` and PDF soft masks. The constant alpha soft mask that PDF and `.ai` export draw a CMYK page with transparency through is white, so applications that read every soft mask by its luminosity show the page too (#1359, part of #1286).
Disable, Enable, Unlink and Link Opacity Mask and Opacity Mask Options (`transparency.setOpacityMask`) record no undo step when every mask they act on already has the flags they set.
- **Image Trace, thin closed outlines:** generated `fit::snap_area_tests` verify nonzero interiors after straightening, opposite closed-boundary winding and Noise controls; see [quality notes](image-trace-quality.md#thin-closed-outlines-with-snap-curves-to-lines). Scores and hours unchanged; open-piece fitting is unchanged.
- **Image Trace, Document Library color groups:** with Document Swatches, the panel's Colors menu and `"colorGroup"` trace with one color group's colors instead of every swatch, as the public panel documentation describes Document Library; code-drawn tests in `crates/engine` and the panel.
- **Image Trace, flattened slivers:** in a Color, Grayscale or Black and White trace with Fills, a traced region whose outline straightens onto one line (a single pixel, or a one-pixel row along an edge, its pieces shared with the regions beside it) is left out instead of becoming a selectable path with no area, which paints nothing. Snap Curves To Lines straightens more outlines, so it leaves out more of these. Without Fills, the region's 1 px stroke draws it, and it is kept. Code-drawn tests in `crates/trace`.
- **Image Trace, Auto Color rare colours:** the Automatic palette keeps colours too rare for its area count when they are distinct (Oklab) and drawn as coherent regions, at most six, protected from mixed-colour merging and Noise; code-drawn stem, highlight, low-contrast and scattered-noise tests (see [quality notes](image-trace-quality.md#auto-color-keeps-rare-colours)).
- **Image Trace, Document Library keeps neutral tones neutral:** each pixel takes the library color nearest in Oklab with differences in hue and chroma counted three times, so a gray or a muted brown traces as a neutral library color instead of a saturated one of similar lightness; a code-drawn test of gray and brown patches with a black, light and blue library in `crates/trace`.