Merge pull request #4071 from dotCooCoo/feat/image-trace-saved-presets

Image Trace: Manage Presets saves, renames and deletes custom tracing presets
This commit is contained in:
BFlat
2026-10-29 20:19:14 +07:00
committed by GitHub
42 changed files with 1522 additions and 52 deletions
+6
View File
@@ -593,6 +593,12 @@ pub use flatten::FlattenOptions;
/// A built-in or saved flattener preset ([`crate::Prefs::flattener_presets`]).
pub use flatten::FlattenerPreset;
/// A saved Image Trace preset ([`crate::Prefs::trace_presets`]), and a change to the saved presets.
pub use buildcmds::{TracePreset, TracePresetChange};
/// The saved Image Trace presets' revision and latest changes ([`crate::Session`] keeps them).
pub(crate) use buildcmds::TracePresetChanges;
/// The shapes a group selected alone gives the Pathfinder panel.
pub use pathops::shape_count;
+26 -4
View File
@@ -40,7 +40,9 @@ mod trace;
pub use live_paint::live_paint_gaps;
use live_paint::{lp_expand, lp_fill, lp_gap_options, lp_info, lp_make, lp_merge, lp_release, lp_stroke_edge};
use shape_builder::{sb_merge, sb_regions};
use trace::{trace_expand, trace_make, trace_release, trace_set_view};
pub(crate) use trace::TracePresetChanges;
pub use trace::{TracePreset, TracePresetChange};
use trace::{trace_expand, trace_make, trace_presets_delete, trace_presets_save, trace_release, trace_set_view};
/// Name of an Image Trace group.
pub const TRACE_NAME: &str = "Image Trace";
@@ -142,7 +144,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), 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)}",
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), savedPreset?: {settings} (a saved preset's settings, which the journal records so a replay traces with them after the preset is changed, renamed or deleted; used in place of the saved preset `preset` names), 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)
),
@@ -182,9 +184,29 @@ pub fn specs() -> Vec<CommandSpec> {
has_selection_or_ids,
trace_set_view
),
cmd!(query "imageTrace.presets", "Image Trace Presets", [], None, "{} → {presets: [{name, params, enhanced: the params of the preset's enhanced version, null if it has none}]}", always, |_, _| {
Ok(json!({ "presets": tr::presets().into_iter().map(|(n, p)| json!({ "name": n, "params": p, "enhanced": tr::enhanced_preset(n) })).collect::<Vec<_>>() }))
cmd!(query "imageTrace.presets", "Image Trace Presets", [], None, "{} → {presets: [{name, builtIn, params, enhanced: the params of the preset's enhanced version, null if it has none}], revision: grows each time the saved presets change} the built-in presets, then the saved ones (builtIn false, enhanced null); any of these names works as imageTrace.make's `preset`", always, |s, _| {
let builtin = tr::presets().into_iter().map(|(n, p)| json!({ "name": n, "builtIn": true, "params": p, "enhanced": tr::enhanced_preset(n) }));
let saved = s.prefs.trace_presets.iter().map(|p| json!({ "name": p.name, "builtIn": false, "params": p.params, "enhanced": null }));
Ok(json!({ "presets": builtin.chain(saved).collect::<Vec<_>>(), "revision": s.trace_presets_revision() }))
}),
cmd!(
query "imageTrace.presets.save",
"Save Image Trace Preset",
[],
None,
"{name?: (default: a new \"Tracing Preset N\"), newName?: rename the saved preset `name`, preset?: the preset to start from (default: the saved preset `name`, else Default), enhanced?, params?: {settings, as imageTrace.make takes them, over the preset's}} create or change a saved tracing preset, kept with the preferences (built-in ones can't change) → {name, params, created, renamedFrom: the name before `newName` changed it, else null}",
always,
trace_presets_save
),
cmd!(
query "imageTrace.presets.delete",
"Delete Image Trace Preset",
[],
None,
"{name} delete a saved tracing preset (built-in ones stay) → {deleted: name}",
always,
trace_presets_delete
),
]
}
+190 -20
View File
@@ -20,24 +20,183 @@ fn view_param(cmd: &str, p: &Value) -> Result<Option<TraceView>> {
v.as_str().and_then(TraceView::from_id).map(Some).ok_or_else(|| bad(cmd, format!("view must be one of {}", ids())))
}
/// The parameters command `c` traces with: the preset's, with `params` over them.
fn trace_params(c: &str, p: &Value) -> Result<tr::TraceParams> {
let name = str_param(p, "preset").unwrap_or("Default");
/// A saved Image Trace preset ([`crate::Prefs::trace_presets`]): a name and its settings.
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TracePreset {
pub name: String,
#[serde(default)]
pub params: tr::TraceParams,
}
/// A change to the saved tracing presets: `imageTrace.presets.save` saved `name` (made it,
/// changed its settings, or renamed it from `renamed_from`), or `imageTrace.presets.delete`
/// deleted it. `revision` is [`Session::trace_presets_revision`] after the change.
#[derive(Clone, Debug, PartialEq)]
pub struct TracePresetChange {
pub revision: u64,
pub name: String,
pub renamed_from: Option<String>,
pub deleted: bool,
}
/// The revision of the saved tracing presets and their latest changes ([`TracePresetChange`]).
#[derive(Default)]
pub(crate) struct TracePresetChanges {
revision: u64,
/// Every change the commands made in this session, oldest first, so a panel that was closed
/// meanwhile still follows a rename (one record for each save or delete).
changes: Vec<TracePresetChange>,
}
impl TracePresetChanges {
/// A change the commands made: a new revision that records it.
fn note(&mut self, mut change: TracePresetChange) {
self.revision += 1;
change.revision = self.revision;
self.changes.push(change);
}
/// A change made by replacing the preferences: a new revision with no record.
pub(crate) fn replaced(&mut self) {
self.revision += 1;
}
}
impl Session {
/// A number that grows each time the saved tracing presets change: saved, renamed or deleted
/// by `imageTrace.presets.save` and `.delete`, or replaced with the preferences
/// ([`Session::apply_prefs`]). A list read at one revision holds until the number changes.
pub fn trace_presets_revision(&self) -> u64 {
self.trace_preset_changes.revision
}
/// The changes `imageTrace.presets.save` and `.delete` made after `revision`, oldest first.
pub fn trace_preset_changes_since(&self, revision: u64) -> impl Iterator<Item = &TracePresetChange> {
let changes = &self.trace_preset_changes.changes;
let from = changes.partition_point(|c| c.revision <= revision);
changes.get(from..).unwrap_or_default().iter()
}
/// The saved tracing preset `name`, any case.
pub fn trace_preset(&self, name: &str) -> Option<&TracePreset> {
self.prefs.trace_presets.iter().find(|q| q.name.eq_ignore_ascii_case(name.trim()))
}
/// The index of the saved tracing preset `name`.
fn trace_preset_index(&self, name: &str) -> Option<usize> {
self.prefs.trace_presets.iter().position(|q| q.name.eq_ignore_ascii_case(name.trim()))
}
/// Whether `name` is a built-in preset's, "Custom" (changed settings, as the panel and a traced
/// object show them), or a saved preset's other than the one at `except`.
fn trace_preset_taken(&self, name: &str, except: Option<usize>) -> bool {
tr::preset(name).is_some() || name.trim().eq_ignore_ascii_case("Custom") || self.trace_preset_index(name).is_some_and(|i| Some(i) != except)
}
/// The first free "Tracing Preset N": the name a new tracing preset gets.
pub fn new_trace_preset_name(&self) -> String {
(1..).map(|i| format!("Tracing Preset {i}")).find(|n| !self.trace_preset_taken(n, None)).unwrap_or_default()
}
}
pub(super) fn trace_presets_save(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "imageTrace.presets.save";
// A name or preset of the wrong type is an error, not dropped: dropping it would save other
// settings, or under another name.
let new_name = opt_str(C, p, "newName")?;
let preset = opt_str(C, p, "preset")?;
let name = match opt_str(C, p, "name")?.map(str::trim) {
Some("") => return Err(bad(C, "`name` is empty")),
Some(n) => n.to_string(),
None => s.new_trace_preset_name(),
};
if tr::preset(&name).is_some() {
return Err(bad(C, format!("`{name}` is a built-in preset and can't change: save it under another name")));
}
if name.eq_ignore_ascii_case("Custom") {
return Err(bad(C, "`Custom` stands for changed settings: save the preset under another name"));
}
let at = s.trace_preset_index(&name);
// The saved preset's name as it was saved.
let old = at.and_then(|i| s.prefs.trace_presets.get(i)).map(|q| q.name.clone());
if old.is_none() && new_name.is_some() {
return Err(bad(C, format!("no saved preset named `{name}` to rename")));
}
// A saved preset changes from its own settings unless `preset` says where to start.
let mut q = p.clone();
if let (Some(old), Some(o), None) = (&old, q.as_object_mut(), preset) {
o.insert("preset".into(), json!(old));
}
let params = trace_params(s, C, &q)?;
let name = match new_name.map(str::trim) {
Some("") => return Err(bad(C, "`newName` is empty")),
Some(n) if s.trace_preset_taken(n, at) => return Err(bad(C, format!("a preset named `{n}` exists"))),
Some(n) => n.to_string(),
None => old.clone().unwrap_or(name),
};
let json_params = serde_json::to_value(&params).map_err(|e| EngineError::Other(e.to_string()))?;
let preset = TracePreset { name: name.clone(), params };
match at.and_then(|i| s.prefs.trace_presets.get_mut(i)) {
Some(slot) => *slot = preset,
None => s.prefs.trace_presets.push(preset),
}
let renamed_from = old.as_ref().filter(|o| **o != name).cloned();
s.trace_preset_changes.note(TracePresetChange { revision: 0, name: name.clone(), renamed_from: renamed_from.clone(), deleted: false });
Ok(json!({"name": name, "params": json_params, "created": old.is_none(), "renamedFrom": renamed_from}))
}
pub(super) fn trace_presets_delete(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "imageTrace.presets.delete";
let name = opt_str(C, p, "name")?.ok_or_else(|| bad(C, "missing `name`"))?;
if tr::preset(name).is_some() {
return Err(bad(C, format!("`{name}` is a built-in preset and stays")));
}
let i = s.trace_preset_index(name).ok_or_else(|| bad(C, format!("no saved preset named `{name}`")))?;
let deleted = s.prefs.trace_presets.remove(i).name;
s.trace_preset_changes.note(TracePresetChange { revision: 0, name: deleted.clone(), renamed_from: None, deleted: true });
Ok(json!({ "deleted": deleted }))
}
/// The string `key` of `p` (`None` when it is missing or null); any other type is an error of
/// command `c`.
fn opt_str<'a>(c: &str, p: &'a Value, key: &str) -> Result<Option<&'a str>> {
match p.get(key) {
None | Some(Value::Null) => Ok(None),
Some(Value::String(v)) => Ok(Some(v)),
Some(_) => Err(bad(c, format!("`{key}` must be a string"))),
}
}
/// The parameters command `c` traces with: the preset's (built-in, else the `savedPreset`
/// settings a journal entry records, else saved), with `params` over them.
fn trace_params(s: &Session, c: &str, p: &Value) -> Result<tr::TraceParams> {
let name = opt_str(c, p, "preset")?.unwrap_or("Default");
let enhanced = switch_or(p, "enhanced", false, c)?;
// (A preset without an enhanced version is traced as it is.)
let recorded = match p.get("savedPreset").filter(|v| !v.is_null()) {
Some(v) => Some(serde_json::from_value::<tr::TraceParams>(v.clone()).map_err(|e| bad(c, format!("savedPreset: {e}")))?),
None => None,
};
// (A preset without an enhanced version, a saved one too, is traced as it is.)
let base = enhanced.then(|| tr::enhanced_preset(name)).flatten().or_else(|| tr::preset(name));
let base = base.ok_or_else(|| bad(c, format!("unknown preset `{name}` (see imageTrace.presets)")))?;
let Some(over) = p.get("params").filter(|v| !v.is_null()) else { return Ok(base) };
let mut v = serde_json::to_value(&base).map_err(|e| EngineError::Other(e.to_string()))?;
let (Some(o), Some(src)) = (v.as_object_mut(), over.as_object()) else { return Err(bad(c, "params must be an object")) };
// A key that isn't one of the settings is an error, with the nearest setting.
if let Some(key) = src.keys().find(|k| !o.contains_key(k.as_str())) {
return Err(crate::cmd::params::unknown(c, key, &o.keys().map(String::as_str).collect::<Vec<_>>()));
}
for (k, val) in src {
o.insert(k.clone(), val.clone());
}
let params: tr::TraceParams = serde_json::from_value(v).map_err(|e| bad(c, e.to_string()))?;
let base = base.or(recorded).or_else(|| s.trace_preset(name).map(|q| q.params.clone()));
let base = base.ok_or_else(|| bad(c, format!("unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets)")))?;
let params: tr::TraceParams = match p.get("params").filter(|v| !v.is_null()) {
None => base,
Some(over) => {
let mut v = serde_json::to_value(&base).map_err(|e| EngineError::Other(e.to_string()))?;
let (Some(o), Some(src)) = (v.as_object_mut(), over.as_object()) else { return Err(bad(c, "params must be an object")) };
// A key that isn't one of the settings is an error, with the nearest setting.
if let Some(key) = src.keys().find(|k| !o.contains_key(k.as_str())) {
return Err(crate::cmd::params::unknown(c, key, &o.keys().map(String::as_str).collect::<Vec<_>>()));
}
for (k, val) in src {
o.insert(k.clone(), val.clone());
}
serde_json::from_value(v).map_err(|e| bad(c, e.to_string()))?
}
};
// (Recorded and saved settings are checked too.)
if !params.fills && !params.strokes {
return Err(bad(c, "Create needs fills, strokes or both"));
}
@@ -212,14 +371,25 @@ fn grouped(d: &mut Document, nodes: Vec<Node>, parents: &[Option<usize>]) -> Vec
pub(super) fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> {
let c = if expand { "imageTrace.makeAndExpand" } else { "imageTrace.make" };
let mut params = trace_params(c, p)?;
let mut params = trace_params(s, c, p)?;
let view = view_param(c, p)?;
let params_json = serde_json::to_value(&params).map_err(|e| EngineError::Other(e.to_string()))?;
// The preset's name if the parameters are exactly that preset's or its enhanced version's,
// else "Custom".
// The preset's name if the parameters are exactly that preset's or its enhanced version's (a
// saved preset's under its saved name), else "Custom".
let named = str_param(p, "preset").unwrap_or("Default");
let is = |b: Option<tr::TraceParams>| b.and_then(|b| serde_json::to_value(&b).ok()).as_ref() == Some(&params_json);
let preset_label = if is(tr::preset(named)) || is(tr::enhanced_preset(named)) { named.to_string() } else { "Custom".to_string() };
let saved = s.trace_preset(named).filter(|q| tr::preset(named).is_none() && is(Some(q.params.clone()))).map(|q| q.name.clone());
let preset_label = match saved {
Some(name) => name,
None if is(tr::preset(named)) || is(tr::enhanced_preset(named)) => named.to_string(),
None => "Custom".to_string(),
};
// A saved preset's settings join the journal entry, so a replay traces as this run did after
// the preset is changed, renamed or deleted.
if let Some(q) = s.trace_preset(named).filter(|_| tr::preset(named).is_none()) {
let recorded = serde_json::to_value(&q.params).map_err(|e| EngineError::Other(e.to_string()))?;
s.note_journal("savedPreset", recorded);
}
let roots = root_ids(s, p)?;
// The image (inside a trace group, or selected directly).
let (target, img) = {
+3
View File
@@ -556,6 +556,9 @@ impl Session {
let fonts_folder_changed = p.fonts_folder != self.prefs.fonts_folder;
let hyphen_exceptions_changed = p.hyphenation_exceptions != self.prefs.hyphenation_exceptions;
let corner_radius_changed = p.corner_radius != self.prefs.corner_radius;
if p.trace_presets != self.prefs.trace_presets {
self.trace_preset_changes.replaced();
}
self.prefs = p;
if corner_radius_changed {
// General › Corner Radius is the Rounded Rectangle tool's radius (#394): a tool already
+2
View File
@@ -126,6 +126,8 @@ pub struct Session {
plugin_output: Option<cmd::plugin::HandedIn>,
/// URLs recently given in the Attributes panel (`attributes.set {url}`), newest first; not saved.
pub recent_urls: Vec<String>,
/// The saved tracing presets' revision and latest changes ([`Session::trace_presets_revision`]).
pub(crate) trace_preset_changes: cmd::TracePresetChanges,
/// The language the UI is drawn in (a language code, never `auto`), set by the UI each frame;
/// `None` without one (headless), where an explicit `interfaceLanguage` preference counts.
/// Japanese gives new type the Japanese defaults ([`Session::japanese_interface`]).
+6
View File
@@ -202,6 +202,11 @@ pub struct Prefs {
/// edit it.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub flattener_presets: Vec<cmd::FlattenerPreset>,
/// Image Trace panel › Manage Presets: the user's tracing presets (the built-in ones aren't
/// stored). Not a `prefs.set` key or group (resetting the preferences keeps them):
/// `imageTrace.presets.*` edit it.
#[serde(skip_serializing_if = "Vec::is_empty")]
pub trace_presets: Vec<cmd::TracePreset>,
/// Width profiles saved to the Stroke panel's Profile list (`stroke.widthProfile.*`). Not a
/// Preferences dialog field, so it has no [`cmd::prefscmds::PREF_SPECS`] row.
#[serde(skip_serializing_if = "Vec::is_empty")]
@@ -424,6 +429,7 @@ impl Default for Prefs {
eyedropper: Default::default(),
pathfinder: Default::default(),
flattener_presets: vec![],
trace_presets: vec![],
width_profiles: vec![],
japanese_crop_marks: false,
new_art_basic: true,
+1
View File
@@ -59,6 +59,7 @@ impl Session {
plugin_dir: None,
plugin_output: None,
recent_urls: vec![],
trace_preset_changes: Default::default(),
ui_language: None,
journal_note: Default::default(),
note_depth: 1,
+199
View File
@@ -3213,6 +3213,205 @@ fn image_trace_presets_query_and_errors() {
assert!(find_command("livePaint.make").unwrap().menu == ["Object", "Live Paint"]);
}
/// Manage Presets: a saved preset is listed after the built-in ones, traces by name in any case,
/// changes from its own settings, and can be renamed and deleted.
#[test]
fn image_trace_saved_presets_are_made_used_renamed_and_deleted() {
let mut s = session();
let img = image_doc(&mut s);
let r = s.execute("imageTrace.presets.save", &json!({"name": "Disc Only", "params": {"ignoreWhite": true, "paths": 70}})).unwrap();
assert_eq!((r["name"].as_str(), r["created"].as_bool(), r["params"]["ignoreWhite"].as_bool()), (Some("Disc Only"), Some(true), Some(true)));
let list = s.execute("imageTrace.presets", &json!({})).unwrap();
let list = list["presets"].as_array().unwrap();
assert_eq!(list.len(), 16);
assert!(list[..15].iter().all(|p| p["builtIn"] == true));
assert_eq!((list[15]["name"].as_str(), list[15]["builtIn"].as_bool()), (Some("Disc Only"), Some(false)));
assert!(list[15]["enhanced"].is_null());
assert_eq!(list[15]["params"]["paths"], 70.0);
// It traces by name, any case, and the object shows its saved name.
let r = s.execute("imageTrace.make", &json!({"id": img.0, "preset": "disc only"})).unwrap();
assert_eq!(r["paths"], 1, "the white is left out");
let g = NodeId(r["id"].as_u64().unwrap());
let t = node(&s, g).extras().trace.clone().unwrap();
assert_eq!((t["preset"].as_str(), t["params"]["ignoreWhite"].as_bool()), (Some("Disc Only"), Some(true)));
let r = s.execute("imageTrace.make", &json!({"id": g.0, "preset": "Disc Only", "params": {"paths": 20}})).unwrap();
let g = NodeId(r["id"].as_u64().unwrap());
assert_eq!(node(&s, g).extras().trace.clone().unwrap()["preset"], "Custom");
// Saving it again changes it from its own settings.
let r = s.execute("imageTrace.presets.save", &json!({"name": "disc only", "params": {"corners": 10}})).unwrap();
assert_eq!((r["name"].as_str(), r["created"].as_bool()), (Some("Disc Only"), Some(false)));
assert_eq!(
(r["params"]["ignoreWhite"].as_bool(), r["params"]["paths"].as_f64(), r["params"]["corners"].as_f64()),
(Some(true), Some(70.0), Some(10.0))
);
// Rename: the old name no longer traces.
s.execute("imageTrace.presets.save", &json!({"name": "Light", "preset": "6 Colors"})).unwrap();
let e = s.execute("imageTrace.presets.save", &json!({"name": "Disc Only", "newName": "light"})).unwrap_err().to_string();
assert!(e.contains("a preset named `light` exists"), "{e}");
assert!(s.execute("imageTrace.presets.save", &json!({"name": "Disc Only", "newName": "6 colors"})).is_err());
let e = s.execute("imageTrace.presets.save", &json!({"name": "Nothing", "newName": "Something"})).unwrap_err().to_string();
assert!(e.contains("no saved preset named `Nothing` to rename"), "{e}");
let r = s.execute("imageTrace.presets.save", &json!({"name": "Disc Only", "newName": "Disc"})).unwrap();
assert_eq!((r["name"].as_str(), r["params"]["corners"].as_f64()), (Some("Disc"), Some(10.0)));
let e = s.execute("imageTrace.make", &json!({"id": g.0, "preset": "Disc Only"})).unwrap_err().to_string();
assert!(e.contains("unknown preset `Disc Only`: not a built-in or saved preset"), "{e}");
assert_eq!(s.execute("imageTrace.make", &json!({"id": g.0, "preset": "Disc"})).unwrap()["paths"], 1);
// Delete.
assert_eq!(s.execute("imageTrace.presets.delete", &json!({"name": "DISC"})).unwrap()["deleted"], "Disc");
assert!(s.execute("imageTrace.presets.delete", &json!({"name": "Disc"})).is_err());
assert!(s.execute("imageTrace.make", &json!({"id": g.0, "preset": "Disc"})).is_err());
let names: Vec<String> = s.prefs.trace_presets.iter().map(|p| p.name.clone()).collect();
assert_eq!(names, ["Light"]);
}
/// The saved presets' revision grows with each save, rename and delete, and not with a refused
/// one; the changes are recorded, and the list and the save's reply carry them.
#[test]
fn image_trace_saved_presets_count_their_changes() {
let mut s = session();
let revision = |s: &mut Session| s.execute("imageTrace.presets", &json!({})).unwrap()["revision"].as_u64();
assert_eq!((s.trace_presets_revision(), revision(&mut s)), (0, Some(0)));
let r = s.execute("imageTrace.presets.save", &json!({"name": "A"})).unwrap();
assert!(r["renamedFrom"].is_null());
let r = s.execute("imageTrace.presets.save", &json!({"name": "a", "newName": "B", "params": {"paths": 30}})).unwrap();
assert_eq!(r["renamedFrom"], "A");
assert!(s.execute("imageTrace.presets.save", &json!({"name": "B", "newName": "Default"})).is_err());
assert!(s.execute("imageTrace.presets.delete", &json!({"name": "A"})).is_err());
assert!(s.execute("imageTrace.presets.save", &json!({"name": "b", "newName": "B"})).unwrap()["renamedFrom"].is_null());
s.execute("imageTrace.presets.delete", &json!({"name": "B"})).unwrap();
assert_eq!((s.trace_presets_revision(), revision(&mut s)), (4, Some(4)));
let changes: Vec<_> = s.trace_preset_changes_since(1).map(|c| (c.revision, c.name.as_str(), c.renamed_from.as_deref(), c.deleted)).collect();
assert_eq!(changes, [(2, "B", Some("A"), false), (3, "B", None, false), (4, "B", None, true)]);
// Preferences with other saved presets give a new revision; the same ones don't.
let mut prefs = s.prefs.clone();
s.apply_prefs(prefs.clone());
assert_eq!(s.trace_presets_revision(), 4);
s.execute("imageTrace.presets.save", &json!({"name": "C"})).unwrap();
prefs.trace_presets.clear();
s.apply_prefs(prefs);
assert_eq!(s.trace_presets_revision(), 6);
assert_eq!(s.trace_preset_changes_since(4).count(), 1, "replacing the preferences records no change");
// A rename stays on record after many later saves.
s.execute("imageTrace.presets.save", &json!({"name": "C"})).unwrap();
let seen = s.trace_presets_revision();
s.execute("imageTrace.presets.save", &json!({"name": "C", "newName": "D"})).unwrap();
for i in 0..40 {
s.execute("imageTrace.presets.save", &json!({"name": format!("E{i}")})).unwrap();
}
let first = s.trace_preset_changes_since(seen).next().map(|c| (c.name.clone(), c.renamed_from.clone()));
assert_eq!(first, Some(("D".to_string(), Some("C".to_string()))));
assert_eq!(s.trace_preset_changes_since(seen).count(), 41);
}
/// Built-in presets stay as they are, and a saved preset's settings are checked as imageTrace.make
/// checks them.
#[test]
fn image_trace_built_in_presets_cannot_be_saved_over_or_deleted() {
let mut s = session();
for name in ["6 Colors", "[Default]", "flat logo", "Custom"] {
let e = s.execute("imageTrace.presets.save", &json!({ "name": name })).unwrap_err().to_string();
assert!(e.contains("save it under another name") || e.contains("save the preset under another name"), "{name}: {e}");
}
for name in ["Default", "outline"] {
let e = s.execute("imageTrace.presets.delete", &json!({ "name": name })).unwrap_err().to_string();
assert!(e.contains("is a built-in preset and stays"), "{e}");
}
assert!(s.execute("imageTrace.presets.save", &json!({"name": " "})).is_err());
assert!(s.execute("imageTrace.presets.delete", &json!({})).is_err());
let e = s.execute("imageTrace.presets.save", &json!({"params": {"fills": false, "strokes": false}})).unwrap_err().to_string();
assert!(e.contains("`imageTrace.presets.save`") && e.contains("Create needs fills, strokes or both"), "{e}");
assert!(s.execute("imageTrace.presets.save", &json!({"preset": "No Such"})).is_err());
assert!(s.prefs.trace_presets.is_empty(), "nothing saved");
// Without a name, the first free "Tracing Preset N"; a preset and its enhanced version as a start.
let r = s.execute("imageTrace.presets.save", &json!({"preset": "16 Colors", "enhanced": true})).unwrap();
assert_eq!(r["name"], "Tracing Preset 1");
assert_eq!(r["params"], serde_json::to_value(vectorcraft_trace::enhanced_preset("16 Colors").unwrap()).unwrap());
assert_eq!(s.execute("imageTrace.presets.save", &json!({})).unwrap()["name"], "Tracing Preset 2");
// Built-in presets come first in the list and are unchanged.
let list = s.execute("imageTrace.presets", &json!({})).unwrap();
let six = list["presets"].as_array().unwrap().iter().find(|p| p["name"] == "6 Colors").unwrap().clone();
assert_eq!(six["params"], serde_json::to_value(vectorcraft_trace::preset("6 Colors").unwrap()).unwrap());
}
/// Saved tracing presets are kept with the preferences, are not a preference key, and survive
/// resetting the preferences.
#[test]
fn image_trace_saved_presets_persist_with_the_preferences() {
assert!(Prefs::default().to_json().get("tracePresets").is_none(), "nothing saved while there are none");
assert!(serde_json::from_value::<Prefs>(json!({"keyboardIncrement": 2.0})).unwrap().trace_presets.is_empty());
let mut s = session();
s.execute("imageTrace.presets.save", &json!({"name": "Keep", "preset": "3 Colors", "params": {"noise": 40}})).unwrap();
let back: Prefs = serde_json::from_str(&serde_json::to_string(&s.prefs).unwrap()).unwrap();
assert_eq!(back.trace_presets, s.prefs.trace_presets);
let mut t = session();
t.apply_prefs(back);
image_doc(&mut t);
let r = t.execute("imageTrace.make", &json!({"preset": "keep"})).unwrap();
let t_node = node(&t, NodeId(r["id"].as_u64().unwrap()));
let stored = t_node.extras().trace.clone().unwrap();
assert_eq!(
(stored["preset"].as_str(), stored["params"]["noise"].as_u64(), stored["params"]["colors"].as_u64()),
(Some("Keep"), Some(40), Some(3))
);
assert!(t.execute("prefs.set", &json!({"key": "tracePresets", "value": []})).is_err());
t.execute("prefs.reset", &json!({})).unwrap();
assert_eq!(t.prefs.trace_presets.len(), 1);
assert_eq!(t.execute("prefs.get", &json!({})).unwrap()["prefs"]["tracePresets"][0]["name"], "Keep");
}
/// A preset or name that isn't a string is an error, and changes no saved preset.
#[test]
fn image_trace_presets_reject_names_that_are_not_strings() {
let mut s = session();
let img = image_doc(&mut s);
s.execute("imageTrace.presets.save", &json!({"name": "Keep", "preset": "3 Colors"})).unwrap();
let kept = s.prefs.trace_presets.clone();
for p in [json!({"name": "Keep", "preset": 123}), json!({"name": 7}), json!({"name": "Keep", "newName": 5}), json!({"name": ["Keep"]})] {
let e = s.execute("imageTrace.presets.save", &p).unwrap_err().to_string();
assert!(e.contains("must be a string"), "{p}: {e}");
}
assert_eq!(s.prefs.trace_presets, kept, "nothing saved or changed");
let e = s.execute("imageTrace.presets.delete", &json!({"name": 1})).unwrap_err().to_string();
assert!(e.contains("`name` must be a string"), "{e}");
let e = s.execute("imageTrace.make", &json!({"id": img.0, "preset": 3})).unwrap_err().to_string();
assert!(e.contains("`preset` must be a string"), "{e}");
}
/// The journal records a saved preset's settings, so a replay traces with them after the preset is
/// changed or deleted.
#[test]
fn image_trace_replays_a_saved_preset_with_the_settings_it_had() {
let mut s = session();
let img = image_doc(&mut s);
s.execute("imageTrace.presets.save", &json!({"name": "Keep", "params": {"ignoreWhite": true}})).unwrap();
assert_eq!(s.execute("imageTrace.make", &json!({"id": img.0, "preset": "Keep"})).unwrap()["paths"], 1, "the white is left out");
let (cmd, p) = s.journal.last().unwrap().clone();
assert_eq!((cmd.as_str(), &p["savedPreset"]["ignoreWhite"]), ("imageTrace.make", &json!(true)));
s.execute("edit.undo", &json!({})).unwrap();
s.execute("imageTrace.presets.save", &json!({"name": "Keep", "params": {"ignoreWhite": false}})).unwrap();
assert_ne!(s.execute("imageTrace.make", &json!({"id": img.0, "preset": "Keep"})).unwrap()["paths"], 1);
s.execute("edit.undo", &json!({})).unwrap();
// Changed: the replay traces as the first run did, and the object no longer names Keep.
let r = s.execute(&cmd, &p).unwrap();
assert_eq!(r["paths"], 1);
assert_eq!(node(&s, NodeId(r["id"].as_u64().unwrap())).extras().trace.clone().unwrap()["preset"], "Custom");
s.execute("edit.undo", &json!({})).unwrap();
// Deleted.
s.execute("imageTrace.presets.delete", &json!({"name": "Keep"})).unwrap();
assert_eq!(s.execute(&cmd, &p).unwrap()["paths"], 1);
s.execute("edit.undo", &json!({})).unwrap();
assert!(s.execute("imageTrace.make", &json!({"id": img.0, "preset": "Keep", "savedPreset": 5})).is_err());
// Recorded settings are checked as `params` are.
let e = s
.execute("imageTrace.make", &json!({"id": img.0, "preset": "Keep", "savedPreset": {"fills": false, "strokes": false}}))
.unwrap_err()
.to_string();
assert!(e.contains("Create needs fills, strokes or both"), "{e}");
// A built-in preset records nothing.
s.execute("imageTrace.make", &json!({"id": img.0, "preset": "6 Colors"})).unwrap();
assert!(s.journal.last().unwrap().1.get("savedPreset").is_none());
}
/// A soft-edged red disc (r = 30 px at (50.3, 49.6)) on transparency, placed at (50, 50) scaled ×2.
fn soft_logo_doc(s: &mut Session) -> NodeId {
let r = vectorcraft_trace::Raster::from_fn(100, 100, |x, y| {
+2
View File
@@ -111,6 +111,7 @@ pub mod text_import;
mod tiff_bmp_tga;
pub mod tile_edge_color;
mod tools;
pub mod trace_presets;
pub(crate) mod transform;
pub mod transform_each;
pub mod variables;
@@ -306,6 +307,7 @@ registry! {
EyedropperOptions: [eyedropper::KIND] => eyedropper::SPEC,
FlattenTransparency: [flatten::KIND] => flatten::SPEC,
FlattenerPresets: [flattener_presets::KIND] => flattener_presets::SPEC,
ImageTracePreset: [trace_presets::KIND] => trace_presets::SPEC,
SaveStyleLibrary: [save_style_library::KIND] => save_style_library::SPEC,
SaveSymbolLibrary: [save_symbol_library::KIND] => save_symbol_library::SPEC,
SaveBrushLibrary: [save_brush_library::KIND] => save_brush_library::SPEC,
@@ -0,0 +1,71 @@
//! Image Trace panel › Manage Presets: Save as New Preset… keeps the panel's settings as a saved
//! tracing preset under the name entered, and Rename… gives a saved preset another name. OK runs
//! `imageTrace.presets.save`.
//!
//! Fields: `name`, `rename` (the saved preset to rename; empty to save a new one) and `params` (the
//! settings a new preset keeps).
use serde_json::{Value, json};
use super::{DialogSpec, form};
use crate::state::Dialog;
use crate::{VectorcraftApp, widgets};
/// The dialog kind of Save as New Preset… and Rename… (Image Trace).
pub const KIND: &str = "imageTracePreset";
pub(super) const SPEC: DialogSpec = DialogSpec {
heading: |d| if d.str("rename").is_empty() { tl!("Save Preset") } else { tl!("Rename Preset") }.into(),
body,
confirm,
ok_enabled: Some(|_, d| !d.str("name").trim().is_empty()),
min_width: 320.0,
..DialogSpec::FORM
};
/// Open Save as New Preset… for `params` (default: the Default preset's settings), or, with
/// `rename`, Rename… for that saved preset (OK reports a name that isn't one).
pub fn open(app: &mut VectorcraftApp, p: &Value) -> Result<Value, String> {
let rename = p.get("rename").and_then(Value::as_str).map(str::trim).unwrap_or_default();
let fields = if rename.is_empty() {
json!({"name": app.session.new_trace_preset_name(), "rename": "", "params": p.get("params").cloned().unwrap_or(Value::Null)})
} else {
let name = app.session.trace_preset(rename).map_or(rename, |saved| saved.name.as_str());
json!({"name": name, "rename": name, "params": null})
};
app.ui.dialog = Some(Dialog::new(KIND, fields));
Ok(Value::Null)
}
fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
ui.horizontal(|ui| {
widgets::dim_label(ui, tl!("Name:"));
form::text(ui, d, "name", 220.0);
});
let name = d.str("name");
let rename = d.str("rename");
if rename.is_empty() && app.session.trace_preset(&name).is_some() {
widgets::dim_label(ui, tl!("Replaces the saved preset of that name."));
}
false
}
/// `imageTrace.presets.save` parameters from the fields.
fn params(d: &Dialog) -> Value {
let rename = d.str("rename");
if rename.is_empty() {
json!({"name": d.str("name"), "params": d.fields.get("params").cloned().unwrap_or(Value::Null)})
} else {
json!({"name": rename, "newName": d.str("name")})
}
}
/// A name the save refuses (a taken one when renaming, a built-in preset's) keeps the dialog open,
/// to be changed.
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let r = app.run("imageTrace.presets.save", params(d));
if r.is_ok() {
app.ui.dialog = None;
}
r
}
+6
View File
@@ -1489,6 +1489,12 @@ font-technology Variable Променливи
Image Trace Трасиране на изображение
Image Tracing Трасиране на изображения
Tracing Presets Предварителни настройки за трасиране
Manage Presets Управление на предварителните настройки
Save as New Preset… Запази като нова предварителна настройка…
Rename… Преименуване…
Rename Preset Преименуване на предварителна настройка
Save Image Trace Preset Запази предварителна настройка за трасиране на изображения
Delete Image Trace Preset Изтриване на предварителна настройка за трасиране на изображения
Image Trace Presets Предварителни настройки за трасиране на изображения
Image: Изображение:
Images: Изображения:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1016,6 +1016,7 @@
Delete Group Suprimeix el grup
Delete Guide Suprimeix la guia
Delete Guides Suprimeix les guies
Delete Image Trace Preset Suprimeix el preajust de traçat d’imatge
Delete Layer Suprimeix la capa
Delete Library Suprimeix la biblioteca
Delete Library Group Suprimeix el grup de biblioteca
@@ -2283,6 +2284,7 @@
Make with Top Object Crea amb l’objecte superior
Make with Warp… Crea amb deformació…
Make/Release Clipping Mask Crea/Allibera una màscara de retall
Manage Presets Gestiona els preajustos
Manage Workspaces Gestiona els espais de treball
Manage Workspaces… Gestiona els espais de treball…
Manually Manualment
@@ -3260,10 +3262,12 @@
Rename Library Group Canvia el nom del grup de la biblioteca
Rename Library… Canvia el nom de la biblioteca…
Rename Paragraph Style Canvia el nom de l’estil de paràgraf
Rename Preset Canvia el nom del preajust
Rename Style Canvia el nom de l’estil
Rename Variable Canvia el nom de la variable
Rename View Canvia el nom de la vista
Rename Workspace Canvia el nom de l’espai de treball
Rename… Canvia el nom…
Render Threads (-1 = Automatic) Fils de renderització (-1 = automàtic)
Rendering intent: Intenció de renderització:
Reorder Reordena
@@ -3445,6 +3449,7 @@
Save a Copy… Desa una còpia…
Save and Close Desa i tanca
Save As Desa com a
Save as New Preset… Desa com a preajust nou…
Save as PDF… Desa com a PDF…
Save as preset: Desa com a preajust:
Save as Template Desa com a plantilla
@@ -3471,6 +3476,7 @@
Save Graphic Style Library… Desa la biblioteca d’estils gràfics…
Save Grid as Preset Desa la quadrícula com a preajust
Save Grid as Preset… Desa la quadrícula com a preajust…
Save Image Trace Preset Desa el preajust de traçat d’imatge
Save PDF Desa el PDF
Save PDF Dialog Diàleg Desa el PDF
Save PDF Preset Desa el preajust de PDF
@@ -4696,6 +4702,7 @@
@msg `colorGroup` needs `palette: "documentLibrary"` `colorGroup` necessita `palette: "documentLibrary"`
@msg `command` must be a command whose menu item opens a dialog (see ui.menuDialog) `command` ha de ser una ordre que obri un diàleg des del seu element de menú (vegeu ui.menuDialog)
@msg `corners` must be an array of anchor indices, not {v} `corners` ha de ser una matriu d’índexs de punts d’ancoratge, no {v}
@msg `Custom` stands for changed settings: save the preset under another name `Custom` representa una configuració modificada: deseu el preajust amb un altre nom
@msg `dither` applies to gradient fills (paint.setFill) `dither` s’aplica als farciments de degradat (paint.setFill)
@msg `dither` applies to gradient fills (stroke: false) `dither` s’aplica als farciments de degradat (stroke: false)
@msg `dither` must be true or false `dither` ha de ser cert o fals
@@ -4710,6 +4717,8 @@
@msg `minAnchors` and `maxAnchors` must be whole numbers `minAnchors` i `maxAnchors` han de ser nombres enters
@msg `mode` applies to freeform gradients `mode` s’aplica als degradats de forma lliure
@msg `mode` must be grayscale, rgb, hsb, cmyk or websafe `mode` ha de ser grayscale, rgb, hsb, cmyk o websafe
@msg `name` is empty `name` és buit
@msg `newName` is empty `newName` és buit
@msg `pathfinder` must be an object `pathfinder` ha de ser un objecte
@msg `sampleSize` must be 1, 3 or 5 `sampleSize` ha de ser 1, 3 o 5
@msg `select` must be true or false `select` ha de ser cert o fals
@@ -4743,6 +4752,8 @@
@msg `{k}` must be [x, y] `{k}` ha de ser [x, y]
@msg `{min_key}` is greater than `{max_key}` `{min_key}` és més gran que `{max_key}`
@msg `{name}` has no options `{name}` no té opcions
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` és un preajust integrat i no es pot canviar: deseu-lo amb un altre nom
@msg `{name}` is a built-in preset and stays `{name}` és un preajust integrat i no es pot suprimir
@msg `{name}` is a built-in workspace; choose another name `{name}` és un espai de treball integrat; trieu un altre nom
@msg `{name}` is a process colour: tints apply to global and spot colours `{name}` és un color de procés: els tons s’apliquen als colors globals i directes
@msg `{name}` is no recovery entry name `{name}` no és un nom d’entrada de recuperació
@@ -5118,6 +5129,8 @@
@msg no print service no hi ha servei d’impressió
@msg no revert command no hi ha cap ordre de reversió
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) no hi ha cap preajust de PDF desat anomenat `{name}` (els preajustos integrats són de només lectura: creeu-ne un de nou a partir d’ells)
@msg no saved preset named `{name}` no hi ha cap preajust desat anomenat `{name}`
@msg no saved preset named `{name}` to rename no hi ha cap preajust desat anomenat `{name}` per canviar-ne el nom
@msg no saved presets no hi ha preajustos desats
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) no hi ha cap preajust d’impressió desat anomenat `{name}` ({DEFAULT_PRESET} està protegit: creeu-ne un de nou a partir d’ell)
@msg no saved profiles no hi ha perfils desats
@@ -5493,6 +5506,7 @@
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) preajust desconegut `{name}` (high, medium, low o un de desat: consulta flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) preajust desconegut `{name}` (consulta flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) preajust desconegut `{name}` (consulta webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) preajust desconegut `{name}`: no és integrat ni desat (consulta imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) costat desconegut `{other}` (left|right|top|bottom)
@msg unknown snap option `{key}` (use {options}) opció d’ajustament desconeguda `{key}` (fes servir {options})
@msg unknown tool `{tool}` eina desconeguda `{tool}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1659,6 +1659,12 @@ lighting Light Světlo
Document Library Knihovna dokumentu
Library: Knihovna:
Tracing Presets Přednastavení vektorizace
Manage Presets Spravovat přednastavení
Save as New Preset… Uložit jako nové přednastavení…
Rename… Přejmenovat…
Rename Preset Přejmenovat přednastavení
Save Image Trace Preset Uložit přednastavení vektorizace obrazu
Delete Image Trace Preset Odstranit přednastavení vektorizace obrazu
Method: Metoda:
Mode: Režim:
Noise Šum
@@ -1690,6 +1696,7 @@ lighting Light Světlo
Locked Zamčené
Hidden Skryté
Filter Layers Panel Rows Filtrovat řádky panelu Vrstvy
@msg `Custom` stands for changed settings: save the preset under another name `Custom` označuje změněná nastavení: uložte přednastavení pod jiným názvem
@msg `{key}` must be an array of these names: {_1} `{key}` musí být pole těchto názvů: {_1}
Graphics Grafika
Local Libraries Místní knihovny
@@ -4295,6 +4302,10 @@ imageTrace Outline Obrys
@msg a page tree node has no kids uzel stromu stránek nemá potomky
@msg a page's placement is not finite umístění stránky není konečné
@msg a preset named `{_1}` exists přednastavení s názvem `{_1}` existuje
@msg `name` is empty `name` je prázdný
@msg `newName` is empty `newName` je prázdný
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` je vestavěné přednastavení a nelze ho změnit: uložte ho pod jiným názvem
@msg `{name}` is a built-in preset and stays `{name}` je vestavěné přednastavení a nelze ho odstranit
@msg a swatch or color group named `{n}` exists položka vzorníku nebo skupina barev s názvem `{n}` existuje
@msg a slice size must be a positive number of points velikost řezu musí být kladný počet bodů
@msg a spine keeps at least two points páteř si ponechává alespoň dva body
@@ -4454,6 +4465,8 @@ imageTrace Outline Obrys
@msg no print service chybí tisková služba
@msg no revert command chybí příkaz pro návrat k uložené verzi
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) uložené přednastavení PDF s názvem `{name}` neexistuje (vestavěná přednastavení jsou jen pro čtení: vytvořte z nich nové)
@msg no saved preset named `{name}` uložené přednastavení s názvem `{name}` neexistuje
@msg no saved preset named `{name}` to rename uložené přednastavení s názvem `{name}` k přejmenování neexistuje
@msg no saved presets žádná uložená přednastavení
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) uložené přednastavení tisku s názvem `{name}` neexistuje ({DEFAULT_PRESET} je chráněné: vytvořte z něj nové)
@msg no saved profiles žádné uložené profily
@@ -4683,6 +4696,7 @@ imageTrace Outline Obrys
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) neznámé přednastavení `{name}` (high, medium, low nebo uložené: viz flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) neznámé přednastavení `{name}` (viz flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) neznámé přednastavení `{name}` (viz webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) neznámé přednastavení `{name}`: není vestavěné ani uložené (viz imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) neznámá strana `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` neznámý nástroj `{tool}`
@msg unknown tool `{t}` neznámý nástroj `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -1497,6 +1497,12 @@ lighting Light Lys
Document Library Dokumentbibliotek
Library: Bibliotek:
Tracing Presets Vektoriseringsforudindstillinger
Manage Presets Administrer forudindstillinger
Save as New Preset… Gem som ny forudindstilling…
Rename… Omdøb…
Rename Preset Omdøb forudindstilling
Save Image Trace Preset Gem forudindstilling til billedsporing
Delete Image Trace Preset Slet forudindstilling til billedsporing
Method: Metode:
Mode: Tilstand:
Noise Støj
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1524,6 +1524,12 @@ file-filter Templates Vorlagen
Image Trace Bildnachzeichner
Image Tracing Bildnachzeichnung
Tracing Presets Nachzeichnervorgaben
Manage Presets Vorgaben verwalten
Save as New Preset… Als neue Vorgabe speichern…
Rename… Umbenennen…
Rename Preset Vorgabe umbenennen
Save Image Trace Preset Bildnachzeichner-Vorgabe speichern
Delete Image Trace Preset Bildnachzeichner-Vorgabe löschen
Image Trace Presets Bildnachzeichner-Vorgaben
Image: Bild:
Images: Bilder:
@@ -2840,6 +2846,7 @@ mark-style Japanese Japanisch
Locked Gesperrt
Hidden Ausgeblendet
Filter Layers Panel Rows Zeilen im Ebenenbedienfeld filtern
@msg `Custom` stands for changed settings: save the preset under another name `Custom` steht für geänderte Einstellungen: Speichern Sie die Vorgabe unter einem anderen Namen
@msg `{key}` must be an array of these names: {_1} `{key}` muss ein Array mit diesen Namen sein: {_1}
Search commands and tools Befehle und Werkzeuge suchen
Search commands and tools… Befehle und Werkzeuge suchen…
@@ -3908,6 +3915,10 @@ theme Appearance Erscheinungsbild
@msg a page tree node has no kids ein Knoten des Seitenbaums hat keine untergeordneten Einträge
@msg a page's placement is not finite die Platzierung einer Seite ist nicht endlich
@msg a preset named `{_1}` exists eine Vorgabe namens `{_1}` ist bereits vorhanden
@msg `name` is empty `name` ist leer
@msg `newName` is empty `newName` ist leer
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` ist eine integrierte Vorgabe und kann nicht geändert werden: Speichern Sie sie unter einem anderen Namen
@msg `{name}` is a built-in preset and stays `{name}` ist eine integrierte Vorgabe und kann nicht gelöscht werden
@msg a swatch or color group named `{n}` exists ein Farbfeld oder eine Farbgruppe namens `{n}` ist bereits vorhanden
@msg a slice size must be a positive number of points eine Slice-Größe muss eine positive Anzahl von Punkt sein
@msg a spine keeps at least two points eine Achse behält mindestens zwei Punkte
@@ -4068,6 +4079,8 @@ theme Appearance Erscheinungsbild
@msg no print service kein Druckdienst
@msg no revert command kein Befehl zum Zurücksetzen auf die gespeicherte Version
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) keine gespeicherte PDF-Vorgabe namens `{name}` (integrierte Vorgaben sind schreibgeschützt: Erstellen Sie daraus eine neue)
@msg no saved preset named `{name}` keine gespeicherte Vorgabe namens `{name}`
@msg no saved preset named `{name}` to rename keine gespeicherte Vorgabe namens `{name}` zum Umbenennen
@msg no saved presets keine gespeicherten Vorgaben
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) keine gespeicherte Druckvorgabe namens `{name}` ({DEFAULT_PRESET} ist geschützt: Erstellen Sie daraus eine neue)
@msg no saved profiles keine gespeicherten Profile
@@ -4298,6 +4311,7 @@ theme Appearance Erscheinungsbild
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) unbekannte Vorgabe `{name}` (high, medium, low oder eine gespeicherte: siehe flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) unbekannte Vorgabe `{name}` (siehe flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) unbekannte Vorgabe `{name}` (siehe webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) unbekannte Vorgabe `{name}`: weder integriert noch gespeichert (siehe imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) unbekannte Seite `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` unbekanntes Werkzeug `{tool}`
@msg unknown tool `{t}` unbekanntes Werkzeug `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1451,6 +1451,12 @@ font-technology Variable Μεταβλητές
Image Trace Ανίχνευση περιγραμμάτων εικόνας
Image Tracing Ανίχνευση περιγραμμάτων εικόνας
Tracing Presets Προεπιλογές ανίχνευσης περιγραμμάτων
Manage Presets Διαχείριση προεπιλογών
Save as New Preset… Αποθήκευση ως νέας προεπιλογής…
Rename… Μετονομασία…
Rename Preset Μετονομασία προεπιλογής
Save Image Trace Preset Αποθήκευση προεπιλογής ανίχνευσης περιγραμμάτων εικόνας
Delete Image Trace Preset Διαγραφή προεπιλογής ανίχνευσης περιγραμμάτων εικόνας
Image Trace Presets Προεπιλογές ανίχνευσης περιγραμμάτων εικόνας
Image: Εικόνα:
Images: Εικόνες:
@@ -2068,6 +2074,7 @@ axis Both Και τα δύο
@msg its type is to come in as outlines, which only its page has Το κείμενό του θα εισαχθεί ως περιγράμματα, τα οποία υπάρχουν μόνο στη σελίδα του
@msg the art on them differs from the page's in {_1}% of it Τα γραφικά τους διαφέρουν από εκείνα της σελίδας κατά {_1}%
@msg the art on them differs from the page's in {_1}% of it: they come from its editing data all the same Τα γραφικά τους διαφέρουν από εκείνα της σελίδας κατά {_1}%, αλλά θα χρησιμοποιηθούν τα δεδομένα επεξεργασίας
@msg `Custom` stands for changed settings: save the preset under another name Το `Custom` αντιστοιχεί σε αλλαγμένες ρυθμίσεις: αποθηκεύστε την προεπιλογή με άλλο όνομα
@msg `to` must be "corner" or "smooth" Το `to` πρέπει να είναι "corner" ή "smooth"
@msg BMP files are RGB or grayscale: colorModel rgb or gray Τα αρχεία BMP πρέπει να είναι RGB ή σε κλίμακα του γκρι: colorModel rgb ή gray
@msg Blend colors: select at least three objects with filled colours Ανάμειξη χρωμάτων: επιλέξτε τουλάχιστον τρία αντικείμενα με χρώμα γεμίσματος
@@ -3956,6 +3963,10 @@ theme Appearance Εμφάνιση
@msg a page tree node has no kids ο κόμβος δέντρου σελίδων δεν έχει θυγατρικά στοιχεία
@msg a page's placement is not finite η τοποθέτηση της σελίδας δεν έχει πεπερασμένες τιμές
@msg a preset named `{_1}` exists υπάρχει ήδη προεπιλογή με όνομα `{_1}`
@msg `name` is empty το `name` είναι κενό
@msg `newName` is empty το `newName` είναι κενό
@msg `{name}` is a built-in preset and can't change: save it under another name η `{name}` είναι ενσωματωμένη προεπιλογή και δεν μπορεί να αλλάξει: αποθηκεύστε την με άλλο όνομα
@msg `{name}` is a built-in preset and stays η `{name}` είναι ενσωματωμένη προεπιλογή και δεν μπορεί να διαγραφεί
@msg a swatch or color group named `{n}` exists υπάρχει ήδη δείγμα χρώματος ή ομάδα χρωμάτων με όνομα `{n}`
@msg a slice size must be a positive number of points το μέγεθος τομής πρέπει να είναι θετικός αριθμός στιγμών
@msg a spine keeps at least two points απαιτούνται τουλάχιστον δύο σημεία κεντρικής καμπύλης
@@ -4110,6 +4121,8 @@ theme Appearance Εμφάνιση
@msg no print service δεν υπάρχει υπηρεσία εκτύπωσης
@msg no revert command δεν υπάρχει εντολή επαναφοράς
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) δεν υπάρχει αποθηκευμένη προεπιλογή PDF με όνομα `{name}` (οι ενσωματωμένες προεπιλογές είναι μόνο για ανάγνωση: δημιουργήστε νέα από αυτές)
@msg no saved preset named `{name}` δεν υπάρχει αποθηκευμένη προεπιλογή με όνομα `{name}`
@msg no saved preset named `{name}` to rename δεν υπάρχει αποθηκευμένη προεπιλογή με όνομα `{name}` για μετονομασία
@msg no saved presets δεν υπάρχουν αποθηκευμένες προεπιλογές
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) δεν υπάρχει αποθηκευμένη προεπιλογή εκτύπωσης με όνομα `{name}` (η {DEFAULT_PRESET} προστατεύεται: δημιουργήστε νέα από αυτήν)
@msg no saved profiles δεν υπάρχουν αποθηκευμένα προφίλ
@@ -4334,6 +4347,7 @@ theme Appearance Εμφάνιση
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) άγνωστη προεπιλογή `{name}` (high, medium, low ή αποθηκευμένη: δείτε flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) άγνωστη προεπιλογή `{name}` (δείτε flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) άγνωστη προεπιλογή `{name}` (δείτε webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) άγνωστη προεπιλογή `{name}`: δεν είναι ενσωματωμένη ούτε αποθηκευμένη (δείτε imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) άγνωστη πλευρά `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` άγνωστο εργαλείο `{tool}`
@msg unknown tool `{t}` άγνωστο εργαλείο `{t}`
Can't render this file because it contains an unexpected character in line 2071 and column 19.
+14
View File
@@ -1524,6 +1524,12 @@ font-technology Variable Variables
Image Trace Calco de imagen
Image Tracing Objeto de calco de imagen
Tracing Presets Preajustes de calco
Manage Presets Gestionar preajustes
Save as New Preset… Guardar como preajuste nuevo…
Rename… Cambiar nombre…
Rename Preset Cambiar nombre del preajuste
Save Image Trace Preset Guardar preajuste de calco de imagen
Delete Image Trace Preset Eliminar preajuste de calco de imagen
Image Trace Presets Preajustes de calco de imagen
Image: Imagen:
Images: Imágenes:
@@ -2875,6 +2881,7 @@ font-technology Variable Variables
Locked Bloqueados
Hidden Ocultos
Filter Layers Panel Rows Filtrar filas del panel Capas
@msg `Custom` stands for changed settings: save the preset under another name `Custom` representa ajustes modificados: guarda el preajuste con otro nombre
@msg `{key}` must be an array of these names: {_1} `{key}` debe ser una lista de estos nombres: {_1}
Search commands and tools Buscar comandos y herramientas
Search commands and tools… Buscar comandos y herramientas…
@@ -3988,6 +3995,10 @@ axis Both Ambos
@msg a page tree node has no kids un nodo del árbol de páginas no tiene hijos
@msg a page's placement is not finite la posición de una página no es finita
@msg a preset named `{_1}` exists ya existe un preajuste llamado `{_1}`
@msg `name` is empty `name` está vacío
@msg `newName` is empty `newName` está vacío
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` es un preajuste integrado y no se puede cambiar: guárdalo con otro nombre
@msg `{name}` is a built-in preset and stays `{name}` es un preajuste integrado y no se puede eliminar
@msg a swatch or color group named `{n}` exists ya existe una muestra o un grupo de colores llamado `{n}`
@msg a slice size must be a positive number of points el tamaño de un sector debe ser un número positivo de puntos
@msg a spine keeps at least two points un eje conserva al menos dos puntos
@@ -4147,6 +4158,8 @@ axis Both Ambos
@msg no print service no hay servicio de impresión
@msg no revert command no hay comando para volver a la versión guardada
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) no hay ningún preajuste de PDF guardado llamado `{name}` (los preajustes integrados son de solo lectura: crea uno nuevo a partir de ellos)
@msg no saved preset named `{name}` no hay ningún preajuste guardado llamado `{name}`
@msg no saved preset named `{name}` to rename no hay ningún preajuste guardado llamado `{name}` para cambiarle el nombre
@msg no saved presets no hay preajustes guardados
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) no hay ningún preajuste de impresión guardado llamado `{name}` ({DEFAULT_PRESET} está protegido: crea uno nuevo a partir de él)
@msg no saved profiles no hay perfiles guardados
@@ -4377,6 +4390,7 @@ axis Both Ambos
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) preajuste desconocido `{name}` (high, medium, low o uno guardado: consulta flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) preajuste desconocido `{name}` (consulta flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) preajuste desconocido `{name}` (consulta webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) preajuste desconocido `{name}`: no es integrado ni guardado (consulta imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) lado desconocido `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` herramienta desconocida `{tool}`
@msg unknown tool `{t}` herramienta desconocida `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1633,6 +1633,12 @@ lighting Light Valo
Document Library Asiakirjakirjasto
Library: Kirjasto:
Tracing Presets Jäljityksen esiasetukset
Manage Presets Hallitse esiasetuksia
Save as New Preset… Tallenna uutena esiasetuksena…
Rename… Nimeä uudelleen…
Rename Preset Nimeä esiasetus uudelleen
Save Image Trace Preset Tallenna kuvan jäljityksen esiasetus
Delete Image Trace Preset Poista kuvan jäljityksen esiasetus
Method: Menetelmä:
Mode: Tila:
Noise Kohina
@@ -4050,6 +4056,7 @@ imageTrace Outline Ääriviiva
@msg its type is to come in as outlines, which only its page has sen teksti tuodaan ääriviivoina, jotka ovat vain sen sivulla
@msg the art on them differs from the page's in {_1}% of it niiden kuvitus eroaa sivun kuvituksesta {_1} %:n osalta
@msg the art on them differs from the page's in {_1}% of it: they come from its editing data all the same niiden kuvitus eroaa sivun kuvituksesta {_1} %:n osalta: ne tuodaan silti sen muokkaustiedoista
@msg `Custom` stands for changed settings: save the preset under another name `Custom` tarkoittaa muutettuja asetuksia: tallenna esiasetus toisella nimellä
@msg `to` must be "corner" or "smooth" `to` on oltava "corner" tai "smooth"
@msg BMP files are RGB or grayscale: colorModel rgb or gray BMP-tiedostot ovat RGB- tai harmaasävykuvia: colorModel rgb tai gray
@msg Blend colors: select at least three objects with filled colours Sulauta värit: valitse vähintään kolme objektia, joilla on väritäyttö
@@ -4212,6 +4219,10 @@ imageTrace Outline Ääriviiva
@msg a page tree node has no kids sivupuun solmulla ei ole lapsia
@msg a page's placement is not finite sivun sijainti ei ole äärellinen
@msg a preset named `{_1}` exists esiasetus nimeltä `{_1}` on olemassa
@msg `name` is empty `name` on tyhjä
@msg `newName` is empty `newName` on tyhjä
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` on sisäänrakennettu esiasetus, eikä sitä voi muuttaa: tallenna se toisella nimellä
@msg `{name}` is a built-in preset and stays `{name}` on sisäänrakennettu esiasetus, eikä sitä voi poistaa
@msg a swatch or color group named `{n}` exists värimalli tai väriryhmä nimeltä `{n}` on jo olemassa
@msg a slice size must be a positive number of points viipaleen koon on oltava positiivinen määrä pisteitä
@msg a spine keeps at least two points selkärangassa on oltava vähintään kaksi pistettä
@@ -4366,6 +4377,8 @@ imageTrace Outline Ääriviiva
@msg no print service ei tulostuspalvelua
@msg no revert command ei palautuskomentoa
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) ei tallennettua PDF-esiasetusta nimeltä `{name}` (sisäänrakennetut esiasetukset ovat vain luku -tilassa: luo uusi niiden pohjalta)
@msg no saved preset named `{name}` tallennettua esiasetusta nimeltä `{name}` ei ole
@msg no saved preset named `{name}` to rename ei uudelleennimettävää tallennettua esiasetusta nimeltä `{name}`
@msg no saved presets ei tallennettuja esiasetuksia
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) ei tallennettua tulostuksen esiasetusta nimeltä `{name}` ({DEFAULT_PRESET} on suojattu: luo uusi sen pohjalta)
@msg no saved profiles ei tallennettuja profiileja
@@ -4589,6 +4602,7 @@ imageTrace Outline Ääriviiva
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) tuntematon esiasetus `{name}` (high, medium, low tai tallennettu: ks. flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) tuntematon esiasetus `{name}` (ks. flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) tuntematon esiasetus `{name}` (ks. webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) tuntematon esiasetus `{name}`: ei sisäänrakennettu eikä tallennettu (ks. imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) tuntematon puoli `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` tuntematon työkalu `{tool}`
@msg unknown tool `{t}` tuntematon työkalu `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1523,6 +1523,12 @@ font-technology Variable Variables
Image Trace Vectorisation de l’image
Image Tracing Objet de vectorisation de l’image
Tracing Presets Paramètres prédéfinis de vectorisation
Manage Presets Gérer les paramètres prédéfinis
Save as New Preset… Enregistrer comme nouveau paramètre prédéfini…
Rename… Renommer…
Rename Preset Renommer le paramètre prédéfini
Save Image Trace Preset Enregistrer le paramètre prédéfini de vectorisation de l’image
Delete Image Trace Preset Supprimer le paramètre prédéfini de vectorisation de l’image
Image Trace Presets Paramètres prédéfinis de vectorisation de l’image
Image: Image :
Images: Images :
@@ -2872,6 +2878,7 @@ font-technology Variable Variables
Locked Verrouillés
Hidden Masqués
Filter Layers Panel Rows Filtrer les lignes du panneau Calques
@msg `Custom` stands for changed settings: save the preset under another name `Custom` désigne des réglages modifiés : enregistrez le paramètre prédéfini sous un autre nom
@msg `{key}` must be an array of these names: {_1} `{key}` doit être un tableau de noms parmi {_1}
Search commands and tools Chercher commandes et outils
Search commands and tools… Chercher commandes et outils…
@@ -3985,6 +3992,10 @@ axis Both Les deux
@msg a page tree node has no kids un nœud de l’arborescence des pages n’a pas d’enfants
@msg a page's placement is not finite la position d’une page n’est pas finie
@msg a preset named `{_1}` exists un paramètre prédéfini nommé `{_1}` existe déjà
@msg `name` is empty `name` est vide
@msg `newName` is empty `newName` est vide
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` est un paramètre prédéfini intégré et ne peut pas être modifié : enregistrez-le sous un autre nom
@msg `{name}` is a built-in preset and stays `{name}` est un paramètre prédéfini intégré et ne peut pas être supprimé
@msg a swatch or color group named `{n}` exists une nuance ou un groupe de couleurs nommé `{n}` existe déjà
@msg a slice size must be a positive number of points la taille d’une tranche doit être un nombre de points positif
@msg a spine keeps at least two points une dorsale garde au moins deux points
@@ -4144,6 +4155,8 @@ axis Both Les deux
@msg no print service aucun service d’impression
@msg no revert command aucune commande Version précédente
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) aucun paramètre prédéfini PDF enregistré nommé `{name}` (les paramètres prédéfinis intégrés sont en lecture seule : créez-en un nouveau à partir d’eux)
@msg no saved preset named `{name}` aucun paramètre prédéfini enregistré nommé `{name}`
@msg no saved preset named `{name}` to rename aucun paramètre prédéfini enregistré nommé `{name}` à renommer
@msg no saved presets aucun paramètre prédéfini enregistré
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) aucun paramètre prédéfini d’impression enregistré nommé `{name}` ({DEFAULT_PRESET} est protégé : créez-en un nouveau à partir de lui)
@msg no saved profiles aucun profil enregistré
@@ -4374,6 +4387,7 @@ axis Both Les deux
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) paramètre prédéfini inconnu `{name}` (high, medium, low ou un paramètre enregistré : voir flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) paramètre prédéfini inconnu `{name}` (voir flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) paramètre prédéfini inconnu `{name}` (voir webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) paramètre prédéfini inconnu `{name}` : ni intégré ni enregistré (voir imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) côté inconnu `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` outil inconnu `{tool}`
@msg unknown tool `{t}` outil inconnu `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -3473,6 +3473,7 @@ mark-style Japanese Japán
Locked Zárolt
Hidden Rejtett
Filter Layers Panel Rows Rétegek panel sorainak szűrése
@msg `Custom` stands for changed settings: save the preset under another name a `Custom` a módosított beállításokat jelöli: mentsd az előbeállítást más néven
@msg `{key}` must be an array of these names: {_1} `{key}`: ezen nevek tömbje kell: {_1}
Search commands and tools Parancsok és eszközök keresése
Search commands and tools… Parancsok és eszközök keresése…
@@ -4060,6 +4061,12 @@ mark-style Japanese Japán
Trace Vektorizálás
Trace the image with the Default preset Kép vektorizálása az alapértelmezett előbeállítással
Tracing Presets Vektorizálási előbeállítások
Manage Presets Előbeállítások kezelése
Save as New Preset… Mentés új előbeállításként…
Rename… Átnevezés…
Rename Preset Előbeállítás átnevezése
Save Image Trace Preset Képvektorizálási előbeállítás mentése
Delete Image Trace Preset Képvektorizálási előbeállítás törlése
Tracing Result Vektorizálás eredménye
Tracing Result with Outlines Vektorizálás eredménye vázlattal
Tracking Betűköz
@@ -4531,6 +4538,10 @@ imageTrace Outline Körvonal
@msg a calculation has no result, as in a division by zero egy számításnak nincs eredménye, például nullával való osztáskor
@msg the operator looks for a mark on the stack and finds none az operátor jelölőt keres a veremben, de nem talál
@msg a preset named `{_1}` exists már van ilyen nevű előbeállítás: `{_1}`
@msg `name` is empty a `name` üres
@msg `newName` is empty a `newName` üres
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` beépített előbeállítás, nem módosítható: mentsd más néven
@msg `{name}` is a built-in preset and stays `{name}` beépített előbeállítás, nem törölhető
@msg a swatch or color group named `{n}` exists már van ilyen nevű színminta vagy színcsoport: `{n}`
@msg a slice size must be a positive number of points a szelet mérete pozitív pontérték kell legyen
@msg a spine keeps at least two points a gerincen legalább két pont marad
@@ -4816,6 +4827,8 @@ imageTrace Outline Körvonal
@msg no print service nincs nyomtatási szolgáltatás
@msg no revert command nincs visszaállító parancs
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) nincs ilyen nevű mentett PDF-előbeállítás: `{name}` (a beépített előbeállítások csak olvashatók: újat indíthatsz belőlük)
@msg no saved preset named `{name}` nincs ilyen nevű mentett előbeállítás: `{name}`
@msg no saved preset named `{name}` to rename nincs ilyen nevű átnevezhető mentett előbeállítás: `{name}`
@msg no saved presets nincs mentett előbeállítás
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) nincs ilyen nevű mentett nyomtatási előbeállítás: `{name}` (védett: {DEFAULT_PRESET}; új előbeállítást indíthatsz belőle)
@msg no saved profiles nincs mentett profil
@@ -5128,6 +5141,7 @@ imageTrace Outline Körvonal
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) ismeretlen előbeállítás: `{name}` (high, medium, low vagy egy mentett; lásd: flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) ismeretlen előbeállítás: `{name}` (lásd: flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) ismeretlen előbeállítás: `{name}` (lásd: webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) ismeretlen előbeállítás: `{name}`, sem beépített, sem mentett (lásd: imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) ismeretlen oldal: `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` ismeretlen eszköz: `{tool}`
@msg unknown tool `{t}` ismeretlen eszköz: `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -1497,6 +1497,12 @@ lighting Light Cahaya
Document Library Pustaka dokumen
Library: Pustaka:
Tracing Presets Preset penelusuran
Manage Presets Kelola preset
Save as New Preset… Simpan sebagai preset baru…
Rename… Ganti nama…
Rename Preset Ganti nama preset
Save Image Trace Preset Simpan preset telusur gambar
Delete Image Trace Preset Hapus preset telusur gambar
Method: Metode:
Mode: Mode:
Noise Derau
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
+14
View File
@@ -1548,6 +1548,12 @@ file-filter Templates Modelli
Image Trace Ricalco immagine
Image Tracing Oggetto ricalco immagine
Tracing Presets Preset di ricalco
Manage Presets Gestisci preset
Save as New Preset… Salva come nuovo preset…
Rename… Rinomina…
Rename Preset Rinomina preset
Save Image Trace Preset Salva preset ricalco immagine
Delete Image Trace Preset Elimina preset ricalco immagine
Image Trace Presets Preset ricalco immagine
Image: Immagine:
Images: Immagini:
@@ -2909,6 +2915,7 @@ mark-style Japanese Giapponese
Locked Bloccati
Hidden Nascosti
Filter Layers Panel Rows Filtra righe del pannello Livelli
@msg `Custom` stands for changed settings: save the preset under another name `Custom` indica impostazioni modificate: salva il preset con un altro nome
@msg `{key}` must be an array of these names: {_1} `{key}` deve essere un array di questi nomi: {_1}
Search commands and tools Cerca comandi e strumenti
Search commands and tools… Cerca comandi e strumenti…
@@ -4028,6 +4035,10 @@ axis Both Entrambi
@msg a page tree node has no kids un nodo dell'albero delle pagine non ha elementi figli
@msg a page's placement is not finite la posizione di una pagina non è finita
@msg a preset named `{_1}` exists esiste già un preset denominato `{_1}`
@msg `name` is empty `name` è vuoto
@msg `newName` is empty `newName` è vuoto
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` è un preset integrato e non può essere modificato: salvalo con un altro nome
@msg `{name}` is a built-in preset and stays `{name}` è un preset integrato e non può essere eliminato
@msg a swatch or color group named `{n}` exists esiste già un campione o un gruppo colori denominato `{n}`
@msg a slice size must be a positive number of points la dimensione di una sezione deve essere un numero positivo di punti
@msg a spine keeps at least two points una dorsale mantiene almeno due punti
@@ -4187,6 +4198,8 @@ axis Both Entrambi
@msg no print service nessun servizio di stampa
@msg no revert command nessun comando Ripristina
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) nessun preset PDF salvato denominato `{name}` (i preset integrati sono di sola lettura: creane uno nuovo a partire da essi)
@msg no saved preset named `{name}` nessun preset salvato denominato `{name}`
@msg no saved preset named `{name}` to rename nessun preset salvato denominato `{name}` da rinominare
@msg no saved presets nessun preset salvato
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) nessun preset di stampa salvato denominato `{name}` ({DEFAULT_PRESET} è protetto: creane uno nuovo a partire da esso)
@msg no saved profiles nessun profilo salvato
@@ -4417,6 +4430,7 @@ axis Both Entrambi
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) preset sconosciuto `{name}` (high, medium, low o uno salvato: vedi flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) preset sconosciuto `{name}` (vedi flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) preset sconosciuto `{name}` (vedi webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) preset sconosciuto `{name}`: né integrato né salvato (vedi imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) lato sconosciuto `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` strumento sconosciuto `{tool}`
@msg unknown tool `{t}` strumento sconosciuto `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -283,6 +283,12 @@ font-technology Variable バリアブル
Image Trace 画像トレース
Image Tracing 画像トレースオブジェクト
Tracing Presets トレースプリセット
Manage Presets プリセットを管理
Save as New Preset… 新規プリセットとして保存…
Rename… 名前を変更…
Rename Preset プリセット名を変更
Save Image Trace Preset 画像トレースプリセットを保存
Delete Image Trace Preset 画像トレースプリセットを削除
Text Wrap テキストの回り込み
Text Wrap Options… テキストの回り込みオプション…
Clipping Mask クリッピングマスク
@@ -2765,6 +2771,7 @@ mark-style Japanese 日本式
Locked ロック
Hidden 非表示
Filter Layers Panel Rows レイヤーパネルの行をフィルター
@msg `Custom` stands for changed settings: save the preset under another name `Custom` は変更した設定を表します: プリセットを別の名前で保存してください
@msg `{key}` must be an array of these names: {_1} `{key}` には {_1} の中の名前の配列を指定してください
Search commands and tools コマンドとツールを検索
Search commands and tools… コマンドとツールを検索…
@@ -4422,6 +4429,10 @@ swatch Bright Violet ビビッドバイオレット
@msg {_1} is an exporter (File › Export › Export As…) {_1} は書き出しプラグインです (「ファイル › 書き出し › 形式を指定して書き出し…」から使用してください)
@msg `documentData` is {size} bytes (limit {MAX_DOCUMENT_DATA}) `documentData` は {size} バイトです (上限 {MAX_DOCUMENT_DATA})
@msg a preset named `{_1}` exists `{_1}` という名前のプリセットはすでに存在します
@msg `name` is empty `name` が空です
@msg `newName` is empty `newName` が空です
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` は組み込みのプリセットのため変更できません: 別の名前で保存してください
@msg `{name}` is a built-in preset and stays `{name}` は組み込みのプリセットのため削除できません
@msg a swatch or color group named `{n}` exists `{n}` という名前のスウォッチまたはカラーグループはすでに存在します
@msg no print preset named `{base}` `{base}` という名前のプリントプリセットが見つかりません
@msg no print preset named `{name}` `{name}` という名前のプリントプリセットが見つかりません
@@ -4440,6 +4451,8 @@ swatch Bright Violet ビビッドバイオレット
@msg PDF settings are not an object PDF 設定がオブジェクト形式ではありません
@msg PDF security settings are not an object PDF のセキュリティ設定がオブジェクト形式ではありません
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) `{name}` という名前の保存済み PDF プリセットが見つかりません (組み込みのプリセットは読み取り専用です: それを元に新しいプリセットを作成してください)
@msg no saved preset named `{name}` `{name}` という名前の保存済みプリセットが見つかりません
@msg no saved preset named `{name}` to rename 名前を変更する `{name}` という保存済みプリセットが見つかりません
@msg Saved to User Defined: {_1} 「ユーザー定義」に保存しました: {_1}
@msg choose a direction to divide in 分割する方向を選択してください
@msg choice must be "merge" or "add", not `{choice}` choice には "merge" または "add" を指定してください (`{choice}` は指定できません)
@@ -4525,6 +4538,7 @@ swatch Bright Violet ビビッドバイオレット
@msg unknown highlight `{h}` `{h}` は不明なハイライトの指定です
@msg unknown overprints `{o}` (preserve, simulate or discard) `{o}` は不明なオーバープリントの指定です (preserve、simulate、discard のいずれか)
@msg unknown preset `{name}` (see flattener.presets.list) `{name}` は不明なプリセットです (flattener.presets.list を参照してください)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) `{name}` は不明なプリセットです: 組み込みのプリセットでも保存済みのプリセットでもありません (imageTrace.presets を参照してください)
@msg Select text or click into text with the Type tool to insert glyphs 字形を挿入するには、テキストを選択するか、文字ツールでテキスト内をクリックしてください
@msg `library` is a library id or name (see {list}) `library` にはライブラリの ID または名前を指定してください ({list} を参照)
@msg no library `{key}` (see {list}) ライブラリ `{key}` が見つかりません ({list} を参照)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1407,6 +1407,12 @@ lighting Light სინათლე
Document Library დოკუმენტის ბიბლიოთეკა
Library: ბიბლიოთეკა:
Tracing Presets ვექტორიზაციის პრესეტები
Manage Presets პრესეტების მართვა
Save as New Preset… ახალ პრესეტად შენახვა…
Rename… სახელის შეცვლა…
Rename Preset პრესეტის სახელის შეცვლა
Save Image Trace Preset გამოსახულების ვექტორიზაციის პრესეტის შენახვა
Delete Image Trace Preset გამოსახულების ვექტორიზაციის პრესეტის წაშლა
Method: მეთოდი:
Mode: რეჟიმი:
Noise ხმაური
@@ -3625,6 +3631,7 @@ font-technology Variable ცვლადი
@msg its page has text, and its layers have no text objects to put it in მის გვერდზე ტექსტია, შრეებში კი არ არის ტექსტური ობიექტები, სადაც ის ჩაჯდებოდა
@msg its type is to come in as outlines, which only its page has მისი ტექსტი კონტურებად უნდა შემოვიდეს, ასეთი კი მხოლოდ მის გვერდზეა
@msg the art on them differs from the page's in {_1}% of it მათზე არსებული ნახატი გვერდისას {_1}%-ით განსხვავდება
@msg `Custom` stands for changed settings: save the preset under another name `Custom` შეცვლილ პარამეტრებს ნიშნავს: შეინახეთ პრესეტი სხვა სახელით
@msg `to` must be "corner" or "smooth" `to` უნდა იყოს "corner" ან "smooth"
@msg BMP files are RGB or grayscale: colorModel rgb or gray BMP ფაილები RGB-ა ან ნაცრისფერი: colorModel rgb ან gray
@msg Blend colors: select at least three objects with filled colours ფერების შერევა: მონიშნეთ მინიმუმ სამი ობიექტი ფერადი შევსებით
@@ -3781,6 +3788,10 @@ font-technology Variable ცვლადი
@msg a page tree node has no kids გვერდების ხის კვანძს შვილობილები არ აქვს
@msg a page's placement is not finite გვერდის განლაგება სასრული არ არის
@msg a preset named `{_1}` exists პრესეტი სახელით `{_1}` არსებობს
@msg `name` is empty `name` ცარიელია
@msg `newName` is empty `newName` ცარიელია
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` ჩაშენებული პრესეტია და მისი შეცვლა შეუძლებელია: შეინახეთ სხვა სახელით
@msg `{name}` is a built-in preset and stays `{name}` ჩაშენებული პრესეტია და მისი წაშლა შეუძლებელია
@msg a swatch or color group named `{n}` exists ფერის ნიმუში ან ფერების ჯგუფი სახელით `{n}` არსებობს
@msg a slice size must be a positive number of points ნაჭრის ზომა წერტილების დადებითი რიცხვი უნდა იყოს
@msg a stream runs past the end ნაკადი ბოლოს სცილდება
@@ -3928,6 +3939,8 @@ font-technology Variable ცვლადი
@msg no print service ბეჭდვის სერვისი არ არის
@msg no revert command დაბრუნების ბრძანება არ არის
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) შენახული PDF პრესეტი სახელით `{name}` არ არის (ჩაშენებული პრესეტები მხოლოდ წასაკითხია: მათგან ახალი შექმენით)
@msg no saved preset named `{name}` შენახული პრესეტი სახელით `{name}` არ არის
@msg no saved preset named `{name}` to rename გადასარქმევი შენახული პრესეტი სახელით `{name}` არ არის
@msg no saved presets შენახული პრესეტები არ არის
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) შენახული ბეჭდვის პრესეტი სახელით `{name}` არ არის ({DEFAULT_PRESET} დაცულია: მისგან ახალი შექმენით)
@msg no saved profiles შენახული პროფილები არ არის
@@ -4139,6 +4152,7 @@ font-technology Variable ცვლადი
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) უცნობი პრესეტი `{name}` (high, medium, low ან შენახული: იხ. flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) უცნობი პრესეტი `{name}` (იხ. flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) უცნობი პრესეტი `{name}` (იხ. webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) უცნობი პრესეტი `{name}`: არც ჩაშენებულია და არც შენახული (იხ. imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) უცნობი მხარე `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` უცნობი ხელსაწყო `{tool}`
@msg unknown tool `{t}` უცნობი ხელსაწყო `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -279,6 +279,12 @@ font-technology Variable 가변
Image Trace 이미지 추적
Image Tracing 이미지 추적
Tracing Presets 추적 프리셋
Manage Presets 프리셋 관리
Save as New Preset… 새 프리셋으로 저장…
Rename… 이름 바꾸기…
Rename Preset 프리셋 이름 바꾸기
Save Image Trace Preset 이미지 추적 프리셋 저장
Delete Image Trace Preset 이미지 추적 프리셋 삭제
Text Wrap 문자 감싸기
Text Wrap Options… 문자 감싸기 옵션…
Clipping Mask 클리핑 마스크
@@ -2759,6 +2765,7 @@ mark-style Japanese 일본식
Locked 잠김
Hidden 숨김
Filter Layers Panel Rows 레이어 패널 행 필터링
@msg `Custom` stands for changed settings: save the preset under another name `Custom`은 변경된 설정을 나타냅니다. 프리셋을 다른 이름으로 저장하세요
@msg `{key}` must be an array of these names: {_1} `{key}`은(는) 다음 이름의 배열이어야 합니다: {_1}
Search commands and tools 명령 및 도구 검색
Search commands and tools… 명령 및 도구 검색…
@@ -4354,6 +4361,10 @@ theme Appearance 외관
@msg {_1} is an exporter (File › Export › Export As…) {_1}은(는) 내보내기 플러그인입니다(파일 › 내보내기 › 다른 이름으로 내보내기…)
@msg `documentData` is {size} bytes (limit {MAX_DOCUMENT_DATA}) `documentData`가 {size}바이트입니다(한도 {MAX_DOCUMENT_DATA})
@msg a preset named `{_1}` exists `{_1}`이라는 프리셋이 이미 있습니다
@msg `name` is empty `name`이(가) 비어 있습니다
@msg `newName` is empty `newName`이(가) 비어 있습니다
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}`은(는) 기본 제공 프리셋이라 변경할 수 없습니다. 다른 이름으로 저장하세요
@msg `{name}` is a built-in preset and stays `{name}`은(는) 기본 제공 프리셋이라 삭제할 수 없습니다
@msg a swatch or color group named `{n}` exists `{n}`이라는 견본 또는 색상 그룹이 이미 있습니다
@msg no print preset named `{base}` `{base}`라는 인쇄 프리셋이 없습니다
@msg no print preset named `{name}` `{name}`이라는 인쇄 프리셋이 없습니다
@@ -4372,6 +4383,8 @@ theme Appearance 외관
@msg PDF settings are not an object PDF 설정이 개체가 아닙니다
@msg PDF security settings are not an object PDF 보안 설정이 개체가 아닙니다
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) `{name}`이라는 저장된 PDF 프리셋이 없습니다(기본 제공 프리셋은 읽기 전용이니 이를 바탕으로 새로 만드세요)
@msg no saved preset named `{name}` `{name}`이라는 저장된 프리셋이 없습니다
@msg no saved preset named `{name}` to rename 이름을 바꿀 `{name}`이라는 저장된 프리셋이 없습니다
@msg Saved to User Defined: {_1} 사용자 정의에 저장했습니다: {_1}
@msg choose a direction to divide in 나눌 방향을 선택하세요
@msg choice must be "merge" or "add", not `{choice}` 선택은 `{choice}`이(가) 아니라 "merge" 또는 "add"여야 합니다
@@ -4457,6 +4470,7 @@ theme Appearance 외관
@msg unknown highlight `{h}` 알 수 없는 강조 `{h}`
@msg unknown overprints `{o}` (preserve, simulate or discard) 알 수 없는 오버프린트 `{o}`(preserve, simulate 또는 discard)
@msg unknown preset `{name}` (see flattener.presets.list) 알 수 없는 프리셋 `{name}`(flattener.presets.list 참조)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) 알 수 없는 프리셋 `{name}`: 기본 제공 프리셋도 저장된 프리셋도 아닙니다(imageTrace.presets 참조)
@msg Select text or click into text with the Type tool to insert glyphs 글리프를 삽입하려면 텍스트를 선택하거나 문자 도구로 텍스트 안을 클릭하세요
@msg `library` is a library id or name (see {list}) `library`는 라이브러리 id 또는 이름입니다({list} 참조)
@msg no library `{key}` (see {list}) 라이브러리 `{key}`이(가) 없습니다({list} 참조)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+6
View File
@@ -1511,6 +1511,12 @@ lighting Light Licht
Document Library Documentbibliotheek
Library: Bibliotheek:
Tracing Presets Traceervoorinstellingen
Manage Presets Voorinstellingen beheren
Save as New Preset… Opslaan als nieuwe voorinstelling…
Rename… Hernoemen…
Rename Preset Voorinstelling hernoemen
Save Image Trace Preset Voorinstelling voor afbeeldingtracering opslaan
Delete Image Trace Preset Voorinstelling voor afbeeldingtracering verwijderen
Method: Methode:
Mode: Modus:
Noise Ruis
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1579,6 +1579,12 @@ lighting Light Światło
Document Library Biblioteka dokumentu
Library: Biblioteka:
Tracing Presets Ustawienia obrysu
Manage Presets Zarządzaj ustawieniami
Save as New Preset… Zapisz jako nowe ustawienie…
Rename… Zmień nazwę…
Rename Preset Zmień nazwę ustawienia
Save Image Trace Preset Zapisz ustawienie obrysu obrazu
Delete Image Trace Preset Usuń ustawienie obrysu obrazu
Method: Metoda:
Mode: Tryb:
Noise Szum
@@ -1610,6 +1616,7 @@ lighting Light Światło
Locked Zablokowane
Hidden Ukryte
Filter Layers Panel Rows Filtruj wiersze panelu Warstwy
@msg `Custom` stands for changed settings: save the preset under another name `Custom` oznacza zmienione ustawienia: zapisz ustawienie pod inną nazwą
@msg `{key}` must be an array of these names: {_1} `{key}` musi być tablicą tych nazw: {_1}
Graphics Grafiki
Local Libraries Biblioteki lokalne
@@ -4119,6 +4126,10 @@ imageTrace Outline Kontur
@msg a page tree node has no kids węzeł drzewa stron nie ma elementów potomnych
@msg a page's placement is not finite położenie strony nie jest skończone
@msg a preset named `{_1}` exists ustawienie o nazwie `{_1}` już istnieje
@msg `name` is empty `name` jest puste
@msg `newName` is empty `newName` jest puste
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` to wbudowane ustawienie i nie można go zmienić: zapisz je pod inną nazwą
@msg `{name}` is a built-in preset and stays `{name}` to wbudowane ustawienie i nie można go usunąć
@msg a swatch or color group named `{n}` exists próbka lub grupa kolorów o nazwie `{n}` już istnieje
@msg a slice size must be a positive number of points rozmiar plasterka musi być dodatnią liczbą punktów
@msg a spine keeps at least two points grzbiet musi mieć co najmniej dwa punkty
@@ -4278,6 +4289,8 @@ imageTrace Outline Kontur
@msg no print service brak usługi drukowania
@msg no revert command brak polecenia przywracania
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) brak zapisanego ustawienia PDF o nazwie `{name}` (wbudowane ustawienia są tylko do odczytu: utwórz na ich podstawie nowe)
@msg no saved preset named `{name}` brak zapisanego ustawienia o nazwie `{name}`
@msg no saved preset named `{name}` to rename brak zapisanego ustawienia o nazwie `{name}` do zmiany nazwy
@msg no saved presets brak zapisanych ustawień
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) brak zapisanego ustawienia drukowania o nazwie `{name}` ({DEFAULT_PRESET} jest chronione: utwórz na jego podstawie nowe)
@msg no saved profiles brak zapisanych profili
@@ -4510,6 +4523,7 @@ imageTrace Outline Kontur
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) nieznane ustawienie `{name}` (high, medium, low lub zapisane: patrz flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) nieznane ustawienie `{name}` (patrz flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) nieznane ustawienie `{name}` (patrz webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) nieznane ustawienie `{name}`: ani wbudowane, ani zapisane (patrz imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) nieznana strona `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` nieznane narzędzie `{tool}`
@msg unknown tool `{t}` nieznane narzędzie `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -2382,6 +2382,7 @@
Locked Bloqueados
Hidden Ocultos
Filter Layers Panel Rows Filtrar linhas do painel Camadas
@msg `Custom` stands for changed settings: save the preset under another name `Custom` representa configurações alteradas: salve a predefinição com outro nome
@msg `{key}` must be an array of these names: {_1} `{key}` deve ser uma matriz destes nomes: {_1}
Search Commands… Pesquisar Comandos…
Search commands and tools Pesquisar comandos e ferramentas
@@ -3205,6 +3206,12 @@ font-technology Variable Variáveis
Library: Biblioteca:
Image Tracing Vetorização de imagem
Tracing Presets Predefinições de vetorização
Manage Presets Gerenciar Predefinidos
Save as New Preset… Salvar como Novo Predefinido…
Rename… Renomear…
Rename Preset Renomear Predefinido
Save Image Trace Preset Salvar Predefinido de Rastreamento de Imagem
Delete Image Trace Preset Excluir Predefinido de Rastreamento de Imagem
Japanese Japonês
Keep a link to an image file instead of only embedding it Manter um link para o arquivo de imagem em vez de apenas incorporá-lo
Latin Latim
@@ -3572,6 +3579,10 @@ axis Both Ambos
@msg a page tree node has no kids um nó da árvore de páginas não tem filhos
@msg a page's placement is not finite a posição de uma página não é finita
@msg a preset named `{_1}` exists já existe uma predefinição chamada `{_1}`
@msg `name` is empty `name` está vazio
@msg `newName` is empty `newName` está vazio
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` é uma predefinição integrada e não pode ser alterada: salve-a com outro nome
@msg `{name}` is a built-in preset and stays `{name}` é uma predefinição integrada e não pode ser excluída
@msg a swatch or color group named `{n}` exists já existe uma amostra ou um grupo de cores chamado `{n}`
@msg a slice size must be a positive number of points o tamanho de uma fatia deve ser um número positivo de pontos
@msg a spine keeps at least two points uma coluna mantém pelo menos dois pontos
@@ -3724,6 +3735,8 @@ axis Both Ambos
@msg no print service sem serviço de impressão
@msg no revert command sem comando de reverter
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) nenhuma predefinição de PDF salva chamada `{name}` (as predefinições integradas são somente leitura: crie uma nova a partir delas)
@msg no saved preset named `{name}` nenhuma predefinição salva chamada `{name}`
@msg no saved preset named `{name}` to rename nenhuma predefinição salva chamada `{name}` para renomear
@msg no saved presets nenhuma predefinição salva
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) nenhuma predefinição de impressão salva chamada `{name}` ({DEFAULT_PRESET} é protegida: crie uma nova a partir dela)
@msg no saved profiles nenhum perfil salvo
@@ -3952,6 +3965,7 @@ axis Both Ambos
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) predefinição desconhecida `{name}` (high, medium, low ou uma salva: veja flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) predefinição desconhecida `{name}` (veja flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) predefinição desconhecida `{name}` (veja webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) predefinição desconhecida `{name}`: não é integrada nem salva (veja imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) lado desconhecido `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` ferramenta desconhecida `{tool}`
@msg unknown tool `{t}` ferramenta desconhecida `{t}`
Can't render this file because it contains an unexpected character in line 6 and column 47.
+14
View File
@@ -1546,6 +1546,12 @@ file-filter Templates Шаблоны
Image Trace Трассировка изображения
Image Tracing Объект трассировки изображения
Tracing Presets Наборы трассировки
Manage Presets Управление наборами
Save as New Preset… Сохранить как новый набор…
Rename… Переименовать…
Rename Preset Переименовать набор
Save Image Trace Preset Сохранить набор трассировки изображения
Delete Image Trace Preset Удалить набор трассировки изображения
Image Trace Presets Наборы трассировки изображения
Image: Изображение:
Images: Изображения:
@@ -2905,6 +2911,7 @@ mark-style Japanese Японский
Locked Заблокированные
Hidden Скрытые
Filter Layers Panel Rows Фильтровать строки панели «Слои»
@msg `Custom` stands for changed settings: save the preset under another name `Custom` обозначает изменённые параметры: сохраните набор под другим именем
@msg `{key}` must be an array of these names: {_1} `{key}` должно быть массивом из этих имён: {_1}
Search commands and tools Поиск команд и инструментов
Search commands and tools… Поиск команд и инструментов…
@@ -4024,6 +4031,10 @@ axis Both Оба
@msg a page tree node has no kids узел дерева страниц не имеет потомков
@msg a page's placement is not finite размещение страницы не конечно
@msg a preset named `{_1}` exists набор с именем `{_1}` уже существует
@msg `name` is empty параметр `name` пуст
@msg `newName` is empty параметр `newName` пуст
@msg `{name}` is a built-in preset and can't change: save it under another name встроенный набор `{name}` нельзя изменить: сохраните его под другим именем
@msg `{name}` is a built-in preset and stays встроенный набор `{name}` нельзя удалить
@msg a swatch or color group named `{n}` exists образец или цветовая группа с именем `{n}` уже существует
@msg a slice size must be a positive number of points размер фрагмента должен быть положительным числом пунктов
@msg a spine keeps at least two points остов сохраняет как минимум две точки
@@ -4183,6 +4194,8 @@ axis Both Оба
@msg no print service нет службы печати
@msg no revert command нет команды возврата
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) нет сохранённого набора PDF с именем `{name}` (встроенные наборы доступны только для чтения: создайте новый на их основе)
@msg no saved preset named `{name}` нет сохранённого набора с именем `{name}`
@msg no saved preset named `{name}` to rename нет сохранённого набора с именем `{name}` для переименования
@msg no saved presets нет сохранённых наборов
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) нет сохранённого набора печати с именем `{name}` ({DEFAULT_PRESET} защищён: создайте новый на его основе)
@msg no saved profiles нет сохранённых профилей
@@ -4413,6 +4426,7 @@ axis Both Оба
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) неизвестный набор `{name}` (high, medium, low или сохранённый: см. flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) неизвестный набор `{name}` (см. flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) неизвестный набор `{name}` (см. webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) неизвестный набор `{name}`: не встроенный и не сохранённый (см. imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) неизвестная сторона `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` неизвестный инструмент `{tool}`
@msg unknown tool `{t}` неизвестный инструмент `{t}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1491,6 +1491,12 @@ font-technology Variable Variabla
Image Trace Bildkalkering
Image Tracing Bildkalkering
Tracing Presets Förinställningar för vektorisering
Manage Presets Hantera förinställningar
Save as New Preset… Spara som ny förinställning…
Rename… Byt namn…
Rename Preset Byt namn på förinställning
Save Image Trace Preset Spara förinställning för bildkalkering
Delete Image Trace Preset Radera förinställning för bildkalkering
Image Trace Presets Förinställningar för bildkalkering
Image: Bild:
Images: Bilder:
@@ -3744,6 +3750,7 @@ axis Both Båda
@msg its type is to come in as outlines, which only its page has dess text ska importeras som konturer, vilket bara dess sida har
@msg the art on them differs from the page's in {_1}% of it grafiken på dem skiljer sig från sidans i {_1} % av ytan
@msg the art on them differs from the page's in {_1}% of it: they come from its editing data all the same grafiken på dem skiljer sig från sidans i {_1} % av ytan: de hämtas ändå från dess redigeringsdata
@msg `Custom` stands for changed settings: save the preset under another name `Custom` står för ändrade inställningar: spara förinställningen med ett annat namn
@msg `to` must be "corner" or "smooth" `to` måste vara "corner" eller "smooth"
@msg BMP files are RGB or grayscale: colorModel rgb or gray BMP-filer är RGB eller gråskala: colorModel rgb eller gray
@msg Blend colors: select at least three objects with filled colours Blanda färger: markera minst tre objekt med färgfyllning
@@ -3907,6 +3914,10 @@ axis Both Båda
@msg a page tree node has no kids en nod i sidträdet har inga barn
@msg a page's placement is not finite en sidas placering är inte ändlig
@msg a preset named `{_1}` exists det finns redan en förinställning med namnet `{_1}`
@msg `name` is empty `name` är tomt
@msg `newName` is empty `newName` är tomt
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` är en inbyggd förinställning och kan inte ändras: spara den med ett annat namn
@msg `{name}` is a built-in preset and stays `{name}` är en inbyggd förinställning och kan inte tas bort
@msg a swatch or color group named `{n}` exists det finns redan en färgruta eller färggrupp med namnet `{n}`
@msg a slice size must be a positive number of points en skivstorlek måste vara ett positivt antal punkter
@msg a spine keeps at least two points en ryggrad behåller minst två punkter
@@ -4061,6 +4072,8 @@ axis Both Båda
@msg no print service ingen utskriftstjänst
@msg no revert command inget kommando för att återgå
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) ingen sparad PDF-förinställning med namnet `{name}` (inbyggda förinställningar är skrivskyddade: skapa en ny utifrån dem)
@msg no saved preset named `{name}` ingen sparad förinställning med namnet `{name}`
@msg no saved preset named `{name}` to rename ingen sparad förinställning med namnet `{name}` att byta namn på
@msg no saved presets inga sparade förinställningar
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) ingen sparad utskriftsförinställning med namnet `{name}` ({DEFAULT_PRESET} är skyddad: skapa en ny utifrån den)
@msg no saved profiles inga sparade profiler
@@ -4285,6 +4298,7 @@ axis Both Båda
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) okänd förinställning `{name}` (high, medium, low eller en sparad: se flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) okänd förinställning `{name}` (se flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) okänd förinställning `{name}` (se webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) okänd förinställning `{name}`: varken inbyggd eller sparad (se imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) okänd sida `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` okänt verktyg `{tool}`
@msg unknown tool `{t}` okänt verktyg `{t}`
Can't render this file because it contains an unexpected character in line 5 and column 25.
+31
View File
@@ -1616,6 +1616,37 @@ fn blend_spine_param_errors_say_what_is_wrong() {
assert!(matches!(&e, EngineError::BadParams { msg, .. } if msg == "select a blend or give `id`"), "{e}");
}
/// The errors of saving, renaming and deleting a tracing preset are translated in every language
/// that translates every message.
#[test]
fn saved_tracing_preset_errors_are_translated() {
use serde_json::json;
use vectorcraft_engine::{EngineError, Session};
let mut s = Session::new();
s.execute("imageTrace.presets.save", &json!({"name": "Mine"})).unwrap();
s.execute("imageTrace.presets.save", &json!({"name": "Other"})).unwrap();
let mut errors = vec![];
for (cmd, p, want) in [
("imageTrace.presets.save", json!({"name": ""}), "`name` is empty"),
("imageTrace.presets.save", json!({"name": "Mine", "newName": ""}), "`newName` is empty"),
("imageTrace.presets.save", json!({"name": "Other", "newName": "Mine"}), "a preset named `Mine` exists"),
("imageTrace.presets.save", json!({"name": "Default"}), "`Default` is a built-in preset and can't change: save it under another name"),
("imageTrace.presets.delete", json!({"name": "Default"}), "`Default` is a built-in preset and stays"),
("imageTrace.presets.delete", json!({"name": "Gone"}), "no saved preset named `Gone`"),
] {
let e = s.execute(cmd, &p).unwrap_err();
assert!(matches!(&e, EngineError::BadParams { msg, .. } if msg == want), "{cmd} {p}: {e}");
errors.push((e.to_string(), want));
}
for code in COMPLETE_MESSAGES {
let lang = Lang::from_code(code).expect("registered");
for (e, want) in &errors {
let shown = message(lang, e);
assert!(!shown.contains(want), "{code}: {shown}");
}
}
}
/// The status bar's line for an EPS file whose PostScript stops on an error, opened partly or
/// refused, and the refusal's dialog keep the error's name as PostScript spells it and its meaning,
/// which each language shows as its catalog translates the meaning.
+14
View File
@@ -1375,6 +1375,12 @@ font-technology Variable Değişken
Image Trace Görüntü İzleme
Image Tracing Görüntü İzleme
Tracing Presets İzleme Ön Ayarları
Manage Presets Ön Ayarları Yönet
Save as New Preset… Yeni Ön Ayar Olarak Kaydet…
Rename… Yeniden adlandır…
Rename Preset Ön Ayarı Yeniden Adlandır
Save Image Trace Preset Görüntü İzleme Ön Ayarını Kaydet
Delete Image Trace Preset Görüntü İzleme Ön Ayarını Sil
Image Trace Presets Görüntü İzleme Ön Ayarları
Image: Resim:
Images: Görseller:
@@ -2716,6 +2722,7 @@ font-technology Variable Değişken
X11 (XWayland) When a Pen Is Attached Kalem Takıldığında X11 (XWayland)
Apply {name} “{name}” Stilini Uygula
Redefine {name} “{name}” Stilini Yeniden Tanımla
@msg `Custom` stands for changed settings: save the preset under another name `Custom` değiştirilmiş ayarları ifade eder: ön ayarı başka bir adla kaydedin
@msg `{key}` must be an array of these names: {_1} "{key}" şu adlardan oluşan bir dizi olmalıdır: {_1}
Search commands and tools Komutları ve araçları arayın
Search commands and tools… Komutları ve araçları arayın…
@@ -3770,6 +3777,10 @@ axis Both Her İkisi
@msg a page tree node has no kids sayfa ağacı düğümünün çocuğu yok
@msg a page's placement is not finite bir sayfanın yerleşimi sınırlı değildir
@msg a preset named `{_1}` exists `{_1}` adında bir ön ayar mevcut
@msg `name` is empty `name` boş
@msg `newName` is empty `newName` boş
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` yerleşik bir ön ayardır ve değiştirilemez: başka bir adla kaydedin
@msg `{name}` is a built-in preset and stays `{name}` yerleşik bir ön ayardır ve silinemez
@msg a slice size must be a positive number of points dilim boyutu pozitif sayıda nokta olmalıdır
@msg a stream runs past the end bir akış sonundan geçiyor
@msg a stream's length is wrong bir akışın uzunluğu yanlış
@@ -3921,6 +3932,8 @@ axis Both Her İkisi
@msg no print service baskı hizmeti yok
@msg no revert command geri alma komutu yok
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) `{name}` adında kayıtlı bir PDF hazır ayarı yok (yerleşik hazır ayarlar salt okunurdur: onlardan yeni bir tane başlatın)
@msg no saved preset named `{name}` `{name}` adında kayıtlı bir ön ayar yok
@msg no saved preset named `{name}` to rename yeniden adlandırılacak `{name}` adında kayıtlı bir ön ayar yok
@msg no saved presets kayıtlı ön ayar yok
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) `{name}` adında kayıtlı bir yazdırma ön ayarı yok ({DEFAULT_PRESET} korumalı: ondan yeni bir tane başlatın)
@msg no saved profiles kayıtlı profil yok
@@ -4137,6 +4150,7 @@ axis Both Her İkisi
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) bilinmeyen ön ayar `{name}` (yüksek, orta, düşük veya kaydedilmiş bir ayar: bkz. flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) bilinmeyen ön ayar `{name}` (bkz. düzleştirici.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) bilinmeyen ön ayar `{name}` (bkz. webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) bilinmeyen ön ayar `{name}`: yerleşik ya da kayıtlı bir ön ayar değil (bkz. imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) bilinmeyen taraf `{other}` (sol|sağ|üst|alt)
@msg unknown tool `{tool}` bilinmeyen araç `{tool}`
@msg unknown tool `{t}` bilinmeyen araç `{t}`
Can't render this file because it contains an unexpected character in line 5 and column 25.
+14
View File
@@ -1542,6 +1542,12 @@ file-filter Templates Шаблони
Image Trace Трасування зображення
Image Tracing Об’єкт трасування зображення
Tracing Presets Набори трасування
Manage Presets Керування наборами
Save as New Preset… Зберегти як новий набір…
Rename… Перейменувати…
Rename Preset Перейменувати набір
Save Image Trace Preset Зберегти набір трасування зображення
Delete Image Trace Preset Видалити набір трасування зображення
Image Trace Presets Набір трасування зображення
Image: Зображення:
Images: Зображення:
@@ -2901,6 +2907,7 @@ mark-style Japanese Японський
Locked Заблоковані
Hidden Приховані
Filter Layers Panel Rows Фільтрувати рядки панелі «Шари»
@msg `Custom` stands for changed settings: save the preset under another name `Custom` позначає змінені параметри: збережіть набір під іншим ім’ям
@msg `{key}` must be an array of these names: {_1} `{key}` має бути масивом цих назв: {_1}
Search commands and tools Пошук команд та інструментів
Search commands and tools… Пошук команд та інструментів…
@@ -4012,6 +4019,10 @@ axis Both Обидві
@msg a page tree node has no kids вузол дерева сторінок не має нащадків
@msg a page's placement is not finite розміщення сторінки не звісно
@msg a preset named `{_1}` exists набір з іменем `{_1}` вже існує
@msg `name` is empty параметр `name` порожній
@msg `newName` is empty параметр `newName` порожній
@msg `{name}` is a built-in preset and can't change: save it under another name вбудований набір `{name}` не можна змінити: збережіть його під іншим ім’ям
@msg `{name}` is a built-in preset and stays вбудований набір `{name}` не можна видалити
@msg a swatch or color group named `{n}` exists зразок або колірна група з ім’ям `{n}` вже існує
@msg a slice size must be a positive number of points розмір фрагмента має бути позитивним числом пунктів
@msg a spine keeps at least two points вісь зберігає як мінімум дві точки
@@ -4171,6 +4182,8 @@ axis Both Обидві
@msg no print service немає служби друку
@msg no revert command немає команди повернення
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) немає збереженого набору PDF з ім’ям `{name}` (вбудовані набори доступні лише для читання: створіть новий на їх основі)
@msg no saved preset named `{name}` немає збереженого набору з ім’ям `{name}`
@msg no saved preset named `{name}` to rename немає збереженого набору з ім’ям `{name}` для перейменування
@msg no saved presets немає збережених наборів
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) немає збереженого набору друку з іменем `{name}` ({DEFAULT_PRESET} захищений: створіть новий на його основі)
@msg no saved profiles немає збережених профілів
@@ -4401,6 +4414,7 @@ axis Both Обидві
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) невідомий набір `{name}` (high, medium, low або збережений: див. flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) невідомий набір `{name}` (див. flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) невідомий набір `{name}` (див. webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) невідомий набір `{name}`: не вбудований і не збережений (див. imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) невідома сторона `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` невідомий інструмент `{tool}`
@msg unknown tool `{t}` невідомий інструмент `{t}`
Can't render this file because it contains an unexpected character in line 749 and column 40.
+14
View File
@@ -3222,6 +3222,7 @@ mark-style Japanese Kiểu Nhật
Locked Đã khóa
Hidden Đã ẩn
Filter Layers Panel Rows Lọc hàng bảng Lớp
@msg `Custom` stands for changed settings: save the preset under another name `Custom` biểu thị các cài đặt đã thay đổi: hãy lưu thiết lập sẵn với tên khác
@msg `{key}` must be an array of these names: {_1} `{key}` phải là một mảng gồm các tên sau: {_1}
Search commands and tools Tìm kiếm lệnh và công cụ
Search commands and tools… Tìm kiếm lệnh và công cụ…
@@ -3760,6 +3761,12 @@ mark-style Japanese Kiểu Nhật
Trace Vector hóa
Trace the image with the Default preset Vector hóa ảnh với thiết lập sẵn mặc định
Tracing Presets Thiết lập sẵn vector hóa
Manage Presets Quản lý thiết lập sẵn
Save as New Preset… Lưu thành thiết lập sẵn mới…
Rename… Đổi tên…
Rename Preset Đổi tên thiết lập sẵn
Save Image Trace Preset Lưu thiết lập sẵn vector hóa ảnh
Delete Image Trace Preset Xóa thiết lập sẵn vector hóa ảnh
Tracing Result Kết quả vector hóa
Tracing Result with Outlines Kết quả vector hóa với đường bao
Tracking Giãn chữ
@@ -4181,6 +4188,10 @@ theme Appearance Giao diện
@msg a calculation has no result, as in a division by zero một phép tính không có kết quả, như khi chia cho không
@msg the operator looks for a mark on the stack and finds none toán tử tìm một dấu mốc trên ngăn xếp nhưng không thấy
@msg a preset named `{_1}` exists đã có thiết lập sẵn tên `{_1}`
@msg `name` is empty `name` trống
@msg `newName` is empty `newName` trống
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` là thiết lập sẵn tích hợp và không thể thay đổi: hãy lưu với tên khác
@msg `{name}` is a built-in preset and stays `{name}` là thiết lập sẵn tích hợp và không thể xóa
@msg a swatch or color group named `{n}` exists đã có mẫu màu hoặc nhóm màu tên `{n}`
@msg a slice size must be a positive number of points kích thước lát cắt phải là số điểm dương
@msg a spine keeps at least two points trục phải giữ ít nhất hai điểm
@@ -4465,6 +4476,8 @@ theme Appearance Giao diện
@msg no print service không có dịch vụ in
@msg no revert command không có lệnh hoàn nguyên
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) không có thiết lập sẵn PDF đã lưu tên `{name}` (thiết lập sẵn tích hợp là chỉ đọc: tạo mới từ chúng)
@msg no saved preset named `{name}` không có thiết lập sẵn đã lưu tên `{name}`
@msg no saved preset named `{name}` to rename không có thiết lập sẵn đã lưu tên `{name}` để đổi tên
@msg no saved presets không có thiết lập sẵn đã lưu
@msg no saved print preset named `{name}` ({DEFAULT_PRESET} is protected: start a new one from it) không có thiết lập sẵn in đã lưu tên `{name}` ({DEFAULT_PRESET} được bảo vệ: tạo mới từ nó)
@msg no saved profiles không có hồ sơ đã lưu
@@ -4788,6 +4801,7 @@ theme Appearance Giao diện
@msg unknown preset `{name}` (high, medium, low or a saved one: see flattener.presets.list) thiết lập sẵn không xác định `{name}` (high, medium, low hoặc thiết lập đã lưu: xem flattener.presets.list)
@msg unknown preset `{name}` (see flattener.presets.list) thiết lập sẵn không xác định `{name}` (xem flattener.presets.list)
@msg unknown preset `{name}` (see webExport.presets.list) thiết lập sẵn không xác định `{name}` (xem webExport.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) thiết lập sẵn không xác định `{name}`: không phải thiết lập sẵn tích hợp hay đã lưu (xem imageTrace.presets)
@msg unknown side `{other}` (left|right|top|bottom) cạnh không xác định `{other}` (left|right|top|bottom)
@msg unknown tool `{tool}` công cụ không xác định `{tool}`
@msg unknown tool `{t}` công cụ không xác định `{t}`
Can't render this file because it contains an unexpected character in line 4091 and column 10.
+14
View File
@@ -1475,6 +1475,12 @@ file-filter Templates 模板
Image Trace 图像描摹
Image Tracing 图像描摹对象
Tracing Presets 描摹预设
Manage Presets 管理预设
Save as New Preset… 存储为新预设…
Rename… 重命名…
Rename Preset 重命名预设
Save Image Trace Preset 保存图像描摹预设
Delete Image Trace Preset 删除图像描摹预设
Image Trace Presets 图像描摹预设
Image: 图像:
Images: 图像:
@@ -3646,6 +3652,7 @@ imageTrace Outline 轮廓
# names and values in backticks or code (`colorModel rgb`, `document.export`) stay as they are.
# Simplified Chinese: no final 。 unless the English ends with a period; a space between Chinese text and
# an adjacent Latin word, number or {placeholder}; 、 between the items of a list.
@msg `Custom` stands for changed settings: save the preset under another name `Custom` 表示已更改的设置:请以其他名称保存预设
@msg `{key}` must be an array of these names: {_1} `{key}` 必须是由以下名称组成的数组:{_1}
@msg Reset {_1} 重置 {_1}
@msg Saving 正在保存
@@ -4146,6 +4153,10 @@ imageTrace Outline 轮廓
@msg {_1} is an exporter (File › Export › Export As…) {_1} 是导出增效工具 (文件 › 导出 › 导出为…)
@msg `documentData` is {size} bytes (limit {MAX_DOCUMENT_DATA}) `documentData` 为 {size} 字节(上限 {MAX_DOCUMENT_DATA})
@msg a preset named `{_1}` exists 名为 `{_1}` 的预设已存在
@msg `name` is empty `name` 为空
@msg `newName` is empty `newName` 为空
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` 是内置预设,无法更改:请以其他名称保存
@msg `{name}` is a built-in preset and stays `{name}` 是内置预设,无法删除
@msg a swatch or color group named `{n}` exists 名为 `{n}` 的色板或颜色组已存在
@msg no print preset named `{base}` 没有名为 `{base}` 的打印预设
@msg no print preset named `{name}` 没有名为 `{name}` 的打印预设
@@ -4164,6 +4175,8 @@ imageTrace Outline 轮廓
@msg PDF settings are not an object PDF 设置不是对象
@msg PDF security settings are not an object PDF 安全性设置不是对象
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) 没有名为 `{name}` 的已保存 PDF 预设 (内置预设为只读:请以它们为基础新建一个)
@msg no saved preset named `{name}` 没有名为 `{name}` 的已保存预设
@msg no saved preset named `{name}` to rename 没有名为 `{name}` 的已保存预设可供重命名
@msg Saved to User Defined: {_1} 已保存到“用户定义”:{_1}
@msg choose a direction to divide in 请选择分割方向
@msg choice must be "merge" or "add", not `{choice}` choice 必须是 "merge" 或 "add",而不是 `{choice}`
@@ -4246,6 +4259,7 @@ imageTrace Outline 轮廓
@msg unknown highlight `{h}` 未知的高亮显示 `{h}`
@msg unknown overprints `{o}` (preserve, simulate or discard) 未知的叠印方式 `{o}` (preserve、simulate 或 discard)
@msg unknown preset `{name}` (see flattener.presets.list) 未知预设 `{name}` (请参阅 flattener.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) 未知预设 `{name}`:既不是内置预设,也不是已保存的预设 (请参阅 imageTrace.presets)
@msg Select text or click into text with the Type tool to insert glyphs 请选择文字,或用文字工具在文字中单击,以插入字形
@msg `library` is a library id or name (see {list}) `library` 是库 id 或名称 (请参阅 {list})
@msg no library `{key}` (see {list}) 没有库 `{key}` (请参阅 {list})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+14
View File
@@ -1513,6 +1513,12 @@ file-filter Templates 範本
Image Trace 影像描圖
Image Tracing 影像描圖物件
Tracing Presets 描圖預設集
Manage Presets 管理預設集
Save as New Preset… 儲存為新預設集…
Rename… 重新命名…
Rename Preset 重新命名預設集
Save Image Trace Preset 儲存影像描圖預設集
Delete Image Trace Preset 刪除影像描圖預設集
Image Trace Presets 影像描圖預設集
Image: 影像:
Images: 影像:
@@ -1602,6 +1608,7 @@ file-filter Templates 範本
# Traditional Chinese: no final 。 unless the English ends with a period; a space between Chinese text and
# an adjacent Latin word, number or {placeholder}; 、 between the items of a list; 「」 around the name of
# a panel, menu item or setting.
@msg `Custom` stands for changed settings: save the preset under another name `Custom` 代表已變更的設定:請以其他名稱儲存預設集
@msg `{key}` must be an array of these names: {_1} `{key}` 必須是由下列名稱組成的陣列:{_1}
@msg Reset {_1} 重設 {_1}
@msg Saving 正在儲存
@@ -2100,6 +2107,10 @@ file-filter Templates 範本
@msg {_1} is an exporter (File › Export › Export As…) {_1} 是匯出增效模組 (檔案 › 匯出 › 匯出為…)
@msg `documentData` is {size} bytes (limit {MAX_DOCUMENT_DATA}) `documentData` 為 {size} 位元組(上限 {MAX_DOCUMENT_DATA})
@msg a preset named `{_1}` exists 名為 `{_1}` 的預設集已存在
@msg `name` is empty `name` 為空
@msg `newName` is empty `newName` 為空
@msg `{name}` is a built-in preset and can't change: save it under another name `{name}` 是內建預設集,無法變更:請以其他名稱儲存
@msg `{name}` is a built-in preset and stays `{name}` 是內建預設集,無法刪除
@msg a swatch or color group named `{n}` exists 名為 `{n}` 的色票或顏色群組已存在
@msg no print preset named `{base}` 沒有名為 `{base}` 的列印預設集
@msg no print preset named `{name}` 沒有名為 `{name}` 的列印預設集
@@ -2118,6 +2129,8 @@ file-filter Templates 範本
@msg PDF settings are not an object PDF 設定不是物件
@msg PDF security settings are not an object PDF 安全性設定不是物件
@msg no saved PDF preset named `{name}` (built-in presets are read-only: start a new one from them) 沒有名為 `{name}` 的已儲存 PDF 預設集 (內建預設集為唯讀:請以它們為基礎新增一個)
@msg no saved preset named `{name}` 沒有名為 `{name}` 的已儲存預設集
@msg no saved preset named `{name}` to rename 沒有名為 `{name}` 的已儲存預設集可供重新命名
@msg Saved to User Defined: {_1} 已儲存至「使用者定義」:{_1}
@msg choose a direction to divide in 請選擇分割方向
@msg choice must be "merge" or "add", not `{choice}` choice 必須是 "merge" 或 "add",而非 `{choice}`
@@ -2200,6 +2213,7 @@ file-filter Templates 範本
@msg unknown highlight `{h}` 未知的反白顯示 `{h}`
@msg unknown overprints `{o}` (preserve, simulate or discard) 未知的疊印方式 `{o}` (preserve、simulate 或 discard)
@msg unknown preset `{name}` (see flattener.presets.list) 未知的預設集 `{name}` (請參閱 flattener.presets.list)
@msg unknown preset `{name}`: not a built-in or saved preset (see imageTrace.presets) 未知的預設集 `{name}`:既不是內建預設集,也不是已儲存的預設集 (請參閱 imageTrace.presets)
@msg Select text or click into text with the Type tool to insert glyphs 請選取文字,或使用文字工具在文字中按一下,以插入字符
@msg `library` is a library id or name (see {list}) `library` 是資料庫 id 或名稱 (請參閱 {list})
@msg no library `{key}` (see {list}) 沒有資料庫 `{key}` (請參閱 {list})
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -650,6 +650,7 @@ fn run_ui_part9(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<Result<
crate::dialogs::flattener_presets::open(app, s("selected").as_deref());
Ok(Value::Null)
}
"ui.imageTracePresetDialog" => crate::dialogs::trace_presets::open(app, p),
"ui.flattenerPreview" => crate::panels::flattener_preview::command(app, p),
"window.graphicStyleLibrary" => crate::panels::graphic_styles::open_library(app, p),
"ui.libraryPanel" => crate::panels::library_panel::command(app, p),
+6
View File
@@ -715,6 +715,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"",
"{selected?: preset name} open the presets manager (dialog `flattenerPresets`, fields `selected`, then the selected preset's `name` and option keys: setting them on a saved preset saves it, a new `name` renames it; built-in presets don't change). New, Delete, Import… and Export… run flattener.presets.save / delete / import / export",
),
(
"ui.imageTracePresetDialog",
"Save as New Preset…",
"",
"{params?: tracing settings (default: the Default preset's), rename?: a saved preset's name} the Image Trace panel's Manage Presets: open Save as New Preset… for those settings, or with `rename`, Rename… for that saved preset (dialog `imageTracePreset`, field `name`: a new \"Tracing Preset N\" or the preset's name); OK runs imageTrace.presets.save with the name (as `newName` when renaming), and the panel shows the preset it made from its settings",
),
(
"ui.flattenerPreview",
"Flattener Preview",
+225 -28
View File
@@ -9,9 +9,12 @@
//! The public documentation of the panel describes Preview as showing the result of the current
//! settings (<https://web.archive.org/web/20250831090623/https://helpx.adobe.com/illustrator/using/image-trace.html>).
use std::sync::Arc;
use egui::Ui;
use serde_json::{Value, json};
use vectorcraft_doc::{Node, NodeId, NodeKind, TraceView};
use vectorcraft_engine::cmd::TracePresetChange;
use vectorcraft_engine::cmd::swatchlib::DOCUMENT_SWATCHES;
use super::{first_selected, library_panel, pstate, set_pstate, swatches};
@@ -93,6 +96,9 @@ struct TraceUi {
params: Value,
view: TraceView,
info: Option<(u64, u64, u64)>,
/// The revision of the saved presets at the panel's last frame
/// ([`vectorcraft_engine::Session::trace_presets_revision`]).
revision: Option<u64>,
/// The selected Image Trace object's stored settings last adopted (so edits in progress
/// aren't overwritten until the object changes).
synced: Value,
@@ -105,13 +111,54 @@ struct TraceUi {
drafts: std::collections::HashMap<(u64, NodeId), (String, Value, Value)>,
}
/// Each preset: its name, its parameters, and those of its enhanced version (null if it has none).
fn presets(app: &mut VectorcraftApp) -> Vec<(String, Value, Value)> {
/// A preset: its name, its parameters, those of its enhanced version (null if it has none), and
/// whether it is built in (else the user saved it).
type Preset = (String, Value, Value, bool);
/// The presets as `imageTrace.presets` listed them at a revision of the saved presets.
#[derive(Clone)]
struct PresetList {
revision: u64,
presets: Arc<Vec<Preset>>,
}
#[cfg(test)]
thread_local! {
/// How many times [`presets`] ran `imageTrace.presets` on this thread.
static PRESET_READS: std::cell::Cell<u32> = const { std::cell::Cell::new(0) };
}
/// Every preset, built-in then saved. `imageTrace.presets` runs once for each revision of the saved
/// presets ([`vectorcraft_engine::Session::trace_presets_revision`]); the list is kept in `ctx`'s
/// memory, which the panel, the Control bar and Properties share.
fn presets(app: &mut VectorcraftApp, ctx: &egui::Context) -> Arc<Vec<Preset>> {
let id = egui::Id::unique("trace-presets");
let revision = app.session.trace_presets_revision();
if let Some(list) = ctx.data(|d| d.get_temp::<PresetList>(id)).filter(|l| l.revision == revision) {
return list.presets;
}
#[cfg(test)]
PRESET_READS.with(|n| n.set(n.get() + 1));
let v = app.session.execute("imageTrace.presets", &json!({})).unwrap_or_default();
v["presets"]
let presets: Vec<Preset> = v["presets"]
.as_array()
.map(|a| a.iter().map(|p| (p["name"].as_str().unwrap_or("").to_string(), p["params"].clone(), p["enhanced"].clone())).collect())
.unwrap_or_default()
.map(|a| {
a.iter()
.map(|p| (p["name"].as_str().unwrap_or("").to_string(), p["params"].clone(), p["enhanced"].clone(), p["builtIn"] != false))
.collect()
})
.unwrap_or_default();
let presets = Arc::new(presets);
ctx.data_mut(|d| d.insert_temp(id, PresetList { revision, presets: presets.clone() }));
presets
}
/// The listed preset that `name` finds, as the engine finds presets: a built-in one by its name in
/// any case, also in brackets (`[Default]`), else a saved one by its exact name in any case.
fn find_preset<'a>(all: &'a [Preset], name: &str) -> Option<&'a Preset> {
let name = name.trim();
let key = name.trim_start_matches('[').trim_end_matches(']');
all.iter().find(|p| p.3 && p.0.eq_ignore_ascii_case(key)).or_else(|| all.iter().find(|p| !p.3 && p.0.eq_ignore_ascii_case(name)))
}
/// Whether `n` is an Image Trace object: a group so named whose first child is the traced image.
@@ -153,6 +200,13 @@ pub(crate) fn selected_trace(app: &VectorcraftApp) -> Option<(String, TraceView)
Some((n.extras().trace.as_ref().and_then(|t| t["preset"].as_str()).unwrap_or("Custom").to_string(), n.trace_view()))
}
/// The trace parameters the one selected Image Trace object records (null without one).
fn selected_trace_params(app: &VectorcraftApp) -> Value {
let Some(st) = app.session.active() else { return Value::Null };
let [id] = st.selection.objects[..] else { return Value::Null };
st.doc.node(id).filter(|n| is_trace(n)).and_then(|n| n.extras().trace.as_ref()).map_or(Value::Null, |t| t["params"].clone())
}
/// Image Trace › View, in the UI language.
fn view_label(v: TraceView) -> &'static str {
match v {
@@ -205,7 +259,7 @@ pub(crate) fn view_row(app: &mut VectorcraftApp, ui: &mut Ui, current: TraceView
}
}
/// Trace the selected image, or trace the selected Image Trace object again, with built-in `preset`.
/// Trace the selected image, or trace the selected Image Trace object again, with `preset`.
fn trace_with(app: &mut VectorcraftApp, preset: &str) {
// A failure is reported in the status bar. The panel stays as it was: the Image Trace
// button (Properties, Control bar) opens it.
@@ -224,7 +278,7 @@ pub(crate) fn open_settings(app: &mut VectorcraftApp) {
app.ui.image_trace_advanced = true;
}
/// The built-in presets to choose from (`current` highlighted) → the one chosen. Listed only
/// The presets to choose from, built-in then saved (`current` highlighted) → the one chosen. Listed only
/// while the menu is open.
/// A preset's name as shown: translated as a preset's name where the catalog has it so (the
/// Outline preset is not the Outline view), as any other string else.
@@ -234,14 +288,38 @@ fn preset_label(name: &str) -> &str {
fn preset_list(app: &mut VectorcraftApp, ui: &mut Ui, current: &str) -> Option<String> {
let mut chosen = None;
for (name, ..) in presets(app) {
if ui.add(egui::Button::selectable(name == current, preset_label(&name))).clicked() {
chosen = Some(name);
let list = presets(app, ui.ctx());
for (name, _, _, built_in) in list.iter() {
// A saved preset's name is the user's, shown as it is.
let label = if *built_in { preset_label(name) } else { name.as_str() };
if ui.add(egui::Button::selectable(name == current, label)).clicked() {
chosen = Some(name.clone());
}
}
chosen
}
/// Manage Presets, next to the Image Trace panel's Preset dropdown: Save as New Preset… names the
/// panel's settings; Rename… and Delete act on the saved preset the dropdown shows (`saved`).
/// Built-in presets don't change. From the public documentation of saving tracing presets
/// (<https://web.archive.org/web/20260902004121/https://helpx.adobe.com/illustrator/desktop/manage-objects/traces-mockups-symbols/save-image-trace-presets.html>).
fn manage_presets(app: &mut VectorcraftApp, ui: &mut Ui, st: &TraceUi, saved: bool) {
let run = if menu_item(ui, tl!("Save as New Preset…"), true, false) {
Some(("ui.imageTracePresetDialog", json!({ "params": st.params })))
} else if menu_item(ui, tl!("Rename…"), saved, false) {
Some(("ui.imageTracePresetDialog", json!({ "rename": st.preset })))
} else if menu_item(ui, tl!("Delete"), saved, false) {
Some(("imageTrace.presets.delete", json!({ "name": st.preset })))
} else {
None
};
if let Some((id, p)) = run
&& let Err(e) = app.run(id, p)
{
app.ui.status = e;
}
}
/// Width of the Control bar's Image Trace button.
pub(crate) const TRACE_BUTTON_W: f32 = 104.0;
@@ -257,10 +335,16 @@ pub(crate) fn trace_button(app: &mut VectorcraftApp, ui: &mut Ui, width: f32) {
}
}
/// The selected Image Trace object's preset (Control bar, Properties): choosing another traces it
/// again with that preset.
pub(crate) fn preset_dropdown(app: &mut VectorcraftApp, ui: &mut Ui, current: &str, width: f32) {
if let Some(p) = widgets::combo(ui, "trace-preset", tl!(current), width, |ui| preset_list(app, ui, current)) {
/// The selected Image Trace object's preset (Control bar, Properties), traced with the preset
/// `traced_with`, shown as the panel shows it: choosing another traces it again with that preset.
pub(crate) fn preset_dropdown(app: &mut VectorcraftApp, ui: &mut Ui, traced_with: &str, width: f32) {
let all = presets(app, ui.ctx());
let params = selected_trace_params(app);
let changes: Vec<&TracePresetChange> = app.session.trace_preset_changes_since(0).collect();
let current = listed_preset(traced_with, &params, &all, &changes);
// A saved preset's name is the user's, shown as it is.
let shown = if app.session.trace_preset(&current).is_some() { current.as_str() } else { tl!(current.as_str()) };
if let Some(p) = widgets::combo(ui, "trace-preset", shown, width, |ui| preset_list(app, ui, &current)) {
trace_with(app, &p);
}
}
@@ -282,12 +366,16 @@ pub fn control_bar(app: &mut VectorcraftApp, ui: &mut Ui) -> bool {
true
}
fn trace(app: &mut VectorcraftApp, st: &mut TraceUi) {
fn trace(app: &mut VectorcraftApp, ctx: &egui::Context, 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() };
// The settings are complete, so with a preset that no longer exists (a deleted saved one) they
// are traced over Default, and the object shows Custom.
let all = presets(app, ctx);
let known = find_preset(&all, &st.preset).map(|p| p.0.as_str());
let preset = known.filter(|_| st.preset != "Custom").unwrap_or("Default");
match app.run("imageTrace.make", json!({ "preset": preset, "params": st.params, "view": st.view.id() })) {
Ok(r) => {
st.info = Some((r["paths"].as_u64().unwrap_or(0), r["anchors"].as_u64().unwrap_or(0), r["colors"].as_u64().unwrap_or(0)));
@@ -319,9 +407,51 @@ fn slider(ui: &mut Ui, label: &str, v: &mut f64, range: std::ops::RangeInclusive
out
}
/// Bring the panel's settings `st` in line with the selected object `object`, whose trace settings are `stored`;
/// whether the object came back with the settings that waited for it.
fn sync_with_object(st: &mut TraceUi, stored: Option<Value>, object: Option<(u64, NodeId)>) -> bool {
/// The preset `name` an object was traced with, or, when no preset in `all` has that name, the name that the saved
/// preset renames in `changes` (oldest first) gave it since. None when the preset was deleted, or when more than one
/// preset had the name in this session (a preset made or renamed to it after the first one was renamed or deleted),
/// since the object may have been traced with any of them.
fn renamed_preset(name: &str, all: &[Preset], changes: &[&TracePresetChange]) -> Option<String> {
let name = name.trim();
if find_preset(all, name).is_some() {
return Some(name.to_string());
}
// Each preset that had the name: its name now, or None once deleted.
let mut had: Vec<Option<String>> = vec![Some(name.to_string())];
let named = |had: &[Option<String>], n: &str| had.iter().flatten().any(|h| h.eq_ignore_ascii_case(n));
for c in changes {
if c.deleted {
had.iter_mut().filter(|h| h.as_deref().is_some_and(|h| h.eq_ignore_ascii_case(&c.name))).for_each(|h| *h = None);
} else if let Some(from) = c.renamed_from.as_deref() {
if c.name.eq_ignore_ascii_case(name) && !named(&had, name) && !named(&had, from) {
had.push(Some(c.name.clone()));
}
had.iter_mut().flatten().filter(|h| h.eq_ignore_ascii_case(from)).for_each(|h| h.clone_from(&c.name));
} else if c.name.eq_ignore_ascii_case(name) && !named(&had, name) {
had.push(Some(c.name.clone()));
}
}
match had.as_slice() {
[only] => only.clone(),
_ => None,
}
}
/// The name the preset `name` an object was traced with `params` shows under, with the presets `all` and the saved
/// preset changes `changes` of this session (oldest first): its listed name (`[sketched art]` is Sketched Art), the
/// new name of a saved preset renamed since, or Custom when that preset was deleted or its settings changed since.
fn listed_preset(name: &str, params: &Value, all: &[Preset], changes: &[&TracePresetChange]) -> String {
if name == "Custom" {
return name.to_string();
}
let found = renamed_preset(name, all, changes).and_then(|n| find_preset(all, &n)).filter(|p| p.3 || p.1 == *params);
found.map_or_else(|| "Custom".into(), |p| p.0.clone())
}
/// Bring the panel's settings `st` in line with the selected object `object`, whose trace settings are `stored`,
/// with the presets `all` and the saved preset changes `changes` of this session (oldest first); whether the object
/// came back with the settings that waited for it.
fn sync_with_object(st: &mut TraceUi, stored: Option<Value>, object: Option<(u64, NodeId)>, all: &[Preset], changes: &[&TracePresetChange]) -> bool {
let same = |a: &Value, b: &Value| a["preset"] == b["preset"] && a["params"] == b["params"];
let mut restored = false;
// Another object: the settings that wait for the one left are kept until it is selected again,
@@ -344,6 +474,10 @@ fn sync_with_object(st: &mut TraceUi, stored: Option<Value>, object: Option<(u64
if object != st.object || !same(&t, &st.synced) {
st.preset = t["preset"].as_str().unwrap_or("Custom").to_string();
st.params = t["params"].clone();
// The preset shows under its listed name (`[sketched art]` is Sketched Art). An object
// traced with a saved preset that was renamed since shows the new name; one traced with a
// saved preset that was deleted since, or whose settings changed since, shows Custom.
st.preset = listed_preset(&st.preset, &st.params, all, changes);
}
// The object selected again shows the settings that wait for it, unless its own settings
// changed since (Undo, or a trace from elsewhere).
@@ -362,8 +496,56 @@ fn sync_with_object(st: &mut TraceUi, stored: Option<Value>, object: Option<(u64
restored
}
/// Bring the panel's preset in `st`, and the preset of each setting that waits for an object no longer selected, in
/// line with the saved presets made, changed, renamed or deleted since the last frame (Manage Presets, or
/// imageTrace.presets.save and .delete), as the commands recorded them, with the presets `all`.
fn follow_preset_changes(app: &VectorcraftApp, st: &mut TraceUi, all: &[Preset]) {
let revision = app.session.trace_presets_revision();
if let Some(seen) = st.revision.filter(|seen| *seen != revision) {
let changes: Vec<&TracePresetChange> = app.session.trace_preset_changes_since(seen).collect();
follow_preset(&mut st.preset, &st.params, &changes, all);
for (preset, params, _) in st.drafts.values_mut() {
follow_preset(preset, params, &changes, all);
}
}
st.revision = Some(revision);
}
/// Bring `preset`, shown with the settings `params`, in line with the saved preset changes `changes`, with the
/// presets `all`: it follows its own preset's new name, becomes a preset saved with its settings, and becomes Custom
/// when its preset no longer exists or its preset's settings changed away from `params`.
fn follow_preset(preset: &mut String, params: &Value, changes: &[&TracePresetChange], all: &[Preset]) {
// The presets saved meanwhile, under the names later renames gave them.
let mut saved: Vec<String> = vec![];
let mut followed = false;
for c in changes {
match c.renamed_from.as_deref() {
Some(from) => {
if *preset == from {
*preset = c.name.clone();
followed = true;
}
for n in saved.iter_mut().filter(|n| n.as_str() == from) {
*n = c.name.clone();
}
}
None if !c.deleted => saved.push(c.name.clone()),
None => {}
}
}
let as_shown = saved.iter().rev().find(|n| all.iter().any(|p| !p.3 && p.0 == **n && p.1 == *params));
if let Some(name) = as_shown.filter(|_| !followed) {
*preset = name.clone();
}
// A save or rename of the preset can also change its settings.
let changed_away = (followed || saved.contains(preset)) && all.iter().any(|p| !p.3 && p.0 == *preset && p.1 != *params);
if changed_away || !all.iter().any(|p| p.0 == *preset) {
*preset = "Custom".into();
}
}
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let all = presets(app);
let all = presets(app, ui.ctx());
let mut st: TraceUi = pstate(ui.ctx(), "image-trace");
if st.params.is_null() {
st.preset = "Default".into();
@@ -372,20 +554,35 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let (is_trace, is_image, stored) = target(app);
let any_trace = any_trace(app);
let object = target_key(app);
let restored = sync_with_object(&mut st, stored, object);
// The saved preset changes since the last frame apply to the panel's settings before the selected object's
// settings, which `sync_with_object` resolves against every change, are adopted.
follow_preset_changes(app, &mut st, &all);
let changes: Vec<&TracePresetChange> = app.session.trace_preset_changes_since(0).collect();
let restored = sync_with_object(&mut st, stored, object, &all, &changes);
let mut retrace = false;
ui.horizontal(|ui| {
widgets::dim_label(ui, tl!("Preset:"));
let names: Vec<&str> = all.iter().map(|p| p.0.as_str()).collect();
if let Some(i) = widgets::dropdown(ui, "it-preset", &st.preset, &names, 170.0) {
st.preset = all[i].0.clone();
st.params = all[i].1.clone();
let manage = 24.0;
let width = (ui.available_width() - manage - ui.spacing().item_spacing.x).min(170.0);
let is_saved = all.iter().any(|p| !p.3 && p.0 == st.preset);
let shown = if is_saved { st.preset.as_str() } else { preset_label(&st.preset) };
let current = st.preset.clone();
if let Some(name) = widgets::combo(ui, "it-preset", shown, width, |ui| preset_list(app, ui, &current))
&& let Some(p) = all.iter().find(|p| p.0 == name)
{
st.preset = name;
st.params = p.1.clone();
retrace = true;
}
let r = widgets::icon_button(ui, "ellipsis", tl!("Manage Presets"), false, manage);
if cfg!(test) {
ui.ctx().data_mut(|d| d.insert_temp(egui::Id::unique("trace-manage-presets"), r.rect));
}
egui::Popup::menu(&r).show(|ui| manage_presets(app, ui, &st, is_saved));
});
// The preset's enhanced version (with Shapes and Auto Grouping) or its legacy one.
if let Some((_, legacy, better)) = all.iter().find(|p| p.0 == st.preset && !p.2.is_null()) {
if let Some((_, legacy, better, _)) = all.iter().find(|p| p.0 == st.preset && !p.2.is_null()) {
let on = *better == st.params;
if widgets::check(ui, tl!("Enhanced"), on, true) {
st.params = if on { legacy.clone() } else { better.clone() };
@@ -605,7 +802,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
}
let r = ui.add_enabled_ui(is_trace || is_image, |ui| widgets::flat_button(ui, tl!("Trace"), 80.0)).inner;
if r.on_disabled_hover_text(tl!("Select an image to trace")).clicked() {
trace(app, &mut st);
trace(app, ui.ctx(), &mut st);
}
if ui.add_enabled_ui(any_trace, |ui| widgets::flat_button(ui, tl!("Expand"), 80.0)).inner.clicked() {
app.run("imageTrace.expand", json!({})).ok();
@@ -621,7 +818,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
st.preview_off = !preview;
}
if retrace && is_trace && preview {
trace(app, &mut st);
trace(app, ui.ctx(), &mut st);
}
set_pstate(ui.ctx(), "image-trace", st);
}
@@ -767,6 +767,430 @@ fn create_strokes_traces_again_and_keeps_one_kind_on() {
assert_eq!(create(&app), Some((json!(false), json!(true))), "Strokes alone stays on");
}
/// Click at `at`, then draw a frame.
fn click_at(app: &mut VectorcraftApp, ctx: &egui::Context, at: Pos2) {
let press = |pressed| Event::PointerButton { pos: at, button: PointerButton::Primary, pressed, modifiers: Default::default() };
frame(app, ctx, vec![Event::PointerMoved(at), press(true)]);
frame(app, ctx, vec![press(false)]);
frame(app, ctx, vec![]);
}
/// Choose `item` from Manage Presets, next to the Preset dropdown. Whether it was enabled.
fn manage(app: &mut VectorcraftApp, ctx: &egui::Context, item: &str) -> bool {
frame(app, ctx, vec![]);
let button = ctx.data(|d| d.get_temp::<Rect>(egui::Id::unique("trace-manage-presets"))).unwrap();
click_at(app, ctx, button.center());
let texts = frame(app, ctx, vec![]);
let at = texts.iter().find(|(t, _)| t.trim() == item).map(|(_, r)| r.center()).unwrap_or_else(|| panic!("no `{item}` in {texts:?}"));
let state = |app: &VectorcraftApp| (app.ui.dialog.as_ref().map(|d| (d.kind.clone(), d.fields.clone())), app.session.prefs.trace_presets.clone());
let before = state(app);
click_at(app, ctx, at);
// Escape closes the menu when the item was disabled.
frame(app, ctx, vec![Event::Key { key: egui::Key::Escape, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() }]);
before != state(app)
}
/// The preset the panel shows.
fn shown_preset(ctx: &egui::Context) -> String {
pstate::<TraceUi>(ctx, "image-trace").preset
}
#[test]
fn manage_presets_saves_renames_and_deletes_a_preset_of_the_panel_settings() {
let mut app = traced_app_with_ink(100);
trace_and_open(&mut app, "Sketched Art");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
// A built-in preset can't be renamed or deleted.
assert!(!manage(&mut app, &ctx, "Rename…"));
assert!(!manage(&mut app, &ctx, "Delete"));
// Save as New Preset… asks for a name, and the panel then shows the preset.
assert!(manage(&mut app, &ctx, "Save as New Preset…"));
let d = app.ui.dialog.clone().unwrap();
assert_eq!((d.kind.as_str(), d.str("name")), (crate::dialogs::trace_presets::KIND, "Tracing Preset 1".to_string()));
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("Heavy Ink"));
crate::dialogs::confirm(&mut app).unwrap();
assert!(app.ui.dialog.is_none());
let texts = frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Heavy Ink");
assert!(texts.iter().any(|(t, _)| t == "Heavy Ink"), "{texts:?}");
let saved = &app.session.prefs.trace_presets;
assert_eq!((saved.len(), saved[0].name.as_str(), saved[0].params.threshold), (1, "Heavy Ink", 150));
// It follows the built-in presets in the preset list (the Preset dropdown, Control bar and
// Properties).
let list_ctx = egui::Context::default();
crate::theme::install_fonts(&list_ctx);
let screen = Rect::from_min_size(Pos2::ZERO, vec2(300.0, 1200.0));
let mut out = list_ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| {
preset_list(&mut app, ui, "Heavy Ink");
});
out.textures_delta.clear();
let shapes: Vec<_> = out.shapes.iter().map(|c| c.shape.clone()).collect();
let listed: Vec<String> = crate::tests_removeanchors::shapes_text(&shapes).into_iter().map(|(t, _)| t).collect();
assert_eq!(listed.iter().position(|t| t == "Heavy Ink"), Some(presets(&mut app, &list_ctx).len() - 1), "{listed:?}");
assert_eq!(listed.len(), presets(&mut app, &list_ctx).len(), "{listed:?}");
// Rename… starts from its name, and the panel follows the new one.
assert!(manage(&mut app, &ctx, "Rename…"));
assert_eq!(app.ui.dialog.as_ref().unwrap().str("name"), "Heavy Ink");
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("Dark Ink"));
crate::dialogs::confirm(&mut app).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Dark Ink");
assert_eq!(app.session.prefs.trace_presets[0].name, "Dark Ink");
// Tracing with it gives the object its name.
click(&mut app, &ctx, "Trace");
assert_eq!(selected_trace(&app).unwrap().0, "Dark Ink");
// Delete: the settings stay, as Custom.
assert!(manage(&mut app, &ctx, "Delete"));
assert!(app.session.prefs.trace_presets.is_empty());
assert_eq!(shown_preset(&ctx), "Custom");
assert!(!manage(&mut app, &ctx, "Delete"));
}
#[test]
fn renaming_another_saved_preset_with_the_same_settings_keeps_the_panel_preset() {
let mut app = traced_app_with_ink(100);
trace_and_open(&mut app, "Sketched Art");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
let save = |app: &mut VectorcraftApp, p: Value| {
app.session.execute("imageTrace.presets.save", &p).unwrap();
frame(app, &ctx, vec![]);
shown_preset(&ctx)
};
assert_eq!(save(&mut app, json!({ "name": "A", "preset": "Sketched Art" })), "A");
assert_eq!(save(&mut app, json!({ "name": "B", "preset": "Sketched Art" })), "B");
// A, with the same settings as B, is renamed: the panel still shows B.
assert_eq!(save(&mut app, json!({ "name": "A", "newName": "C" })), "B");
// B itself is renamed: the panel follows it.
assert_eq!(save(&mut app, json!({ "name": "B", "newName": "E" })), "E");
}
/// A saved preset renamed or deleted while the settings that wait for another object name it: that object, selected
/// again, shows the preset's new name, or Custom.
#[test]
fn settings_that_wait_follow_a_renamed_or_deleted_preset() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "preset": "Sketched Art", "params": {"threshold": 40}})).unwrap();
trace_and_open(&mut app, "Sketched Art");
let a = first_selected(&app).unwrap().id;
app.run("edit.duplicate", json!({})).unwrap();
let b = first_selected(&app).unwrap().id;
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
app.run("select.set", json!({"ids": [a.0]})).unwrap();
frame(&mut app, &ctx, vec![]);
click(&mut app, &ctx, "Preview");
// Ink is chosen for A with Preview off, so it waits for A.
let ink = presets(&mut app, &ctx).iter().find(|p| p.0 == "Ink").unwrap().1.clone();
let mut st: TraceUi = pstate(&ctx, "image-trace");
st.preset = "Ink".into();
st.params = ink;
set_pstate(&ctx, "image-trace", st);
let select = |app: &mut VectorcraftApp, id: NodeId| {
app.run("select.set", json!({"ids": [id.0]})).unwrap();
frame(app, &ctx, vec![]);
shown_preset(&ctx)
};
assert_eq!(select(&mut app, b), "Sketched Art");
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "newName": "Dark Ink"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(select(&mut app, a), "Dark Ink", "A's waiting settings follow the rename");
assert_eq!(select(&mut app, b), "Sketched Art");
app.session.execute("imageTrace.presets.delete", &json!({"name": "Dark Ink"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(select(&mut app, a), "Custom", "A's waiting settings name a deleted preset");
assert_eq!(pstate::<TraceUi>(&ctx, "image-trace").params["threshold"], json!(40), "and keep their settings");
}
#[test]
fn a_refused_preset_name_keeps_the_dialog_open() {
let mut app = traced_app_with_ink(100);
for name in ["A", "B"] {
app.session.execute("imageTrace.presets.save", &json!({ "name": name })).unwrap();
}
crate::dialogs::trace_presets::open(&mut app, &json!({"rename": "A"})).unwrap();
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("b"));
assert!(crate::dialogs::confirm(&mut app).is_err());
assert_eq!(app.ui.dialog.as_ref().map(|d| d.str("name")).as_deref(), Some("b"), "the name entered stays, to be changed");
app.ui.dialog.as_mut().unwrap().fields.insert("name".into(), json!("C"));
crate::dialogs::confirm(&mut app).unwrap();
assert!(app.ui.dialog.is_none());
let names: Vec<String> = app.session.prefs.trace_presets.iter().map(|p| p.name.clone()).collect();
assert_eq!(names, ["C", "B"]);
}
#[test]
fn selecting_an_object_traced_with_a_deleted_preset_shows_custom() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "Keep", "preset": "Sketched Art"})).unwrap();
trace_and_open(&mut app, "Keep");
let a = first_selected(&app).unwrap().id;
app.run("edit.duplicate", json!({})).unwrap();
let b = first_selected(&app).unwrap().id;
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Keep");
// A, traced again with a built-in preset, is shown while Keep is deleted.
app.run("select.set", json!({"ids": [a.0]})).unwrap();
app.run("imageTrace.make", json!({"preset": "Sketched Art"})).unwrap();
frame(&mut app, &ctx, vec![]);
app.session.execute("imageTrace.presets.delete", &json!({"name": "Keep"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Sketched Art");
// B still records Keep.
app.run("select.set", json!({"ids": [b.0]})).unwrap();
assert_eq!(selected_trace(&app).unwrap().0, "Keep");
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Custom");
// A built-in preset named in another case or in brackets shows under its own name, and Trace
// with the settings unchanged keeps it.
app.run("imageTrace.make", json!({"preset": "[sketched art]"})).unwrap();
assert_eq!(selected_trace(&app).unwrap().0, "[sketched art]");
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Sketched Art");
click(&mut app, &ctx, "Trace");
assert_eq!(selected_trace(&app).unwrap().0, "Sketched Art");
}
/// An object traced with a saved preset that was renamed since shows the new name when it is selected again, and
/// when the panel opens again.
#[test]
fn selecting_an_object_traced_with_a_renamed_preset_shows_the_new_name() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "preset": "Sketched Art"})).unwrap();
trace_and_open(&mut app, "Ink");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Ink");
app.session.execute("imageTrace.presets.save", &json!({"name": "ink", "newName": "Dark Ink"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Dark Ink");
app.run("select.set", json!({"ids": []})).unwrap();
frame(&mut app, &ctx, vec![]);
app.run("select.all", json!({})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(selected_trace(&app).unwrap().0, "Ink", "the object still records the old name");
assert_eq!(shown_preset(&ctx), "Dark Ink");
let reopened = egui::Context::default();
crate::theme::install_fonts(&reopened);
frame(&mut app, &reopened, vec![]);
assert_eq!(shown_preset(&reopened), "Dark Ink");
// Dark Ink is deleted, and another preset with the same settings is made under that name: the object shows
// Custom, also after that preset is renamed.
app.session.execute("imageTrace.presets.delete", &json!({"name": "Dark Ink"})).unwrap();
app.session.execute("imageTrace.presets.save", &json!({"name": "Dark Ink", "preset": "Sketched Art"})).unwrap();
let shown = |app: &mut VectorcraftApp| {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(app, &ctx, vec![]);
shown_preset(&ctx)
};
assert_eq!(shown(&mut app), "Custom");
app.session.execute("imageTrace.presets.save", &json!({"name": "Dark Ink", "newName": "Other Ink"})).unwrap();
assert_eq!(shown(&mut app), "Custom");
}
/// The Control bar's and Properties' Preset dropdowns show an object traced with a saved preset under the name the
/// panel shows: the new name after a rename, and Custom once the preset's settings changed or it was deleted.
#[test]
fn the_control_bar_and_properties_show_a_renamed_or_changed_preset_as_the_panel_does() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "preset": "Sketched Art"})).unwrap();
trace_with(&mut app, "Ink");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let texts = |app: &mut VectorcraftApp, properties: bool| {
let screen = Rect::from_min_size(Pos2::ZERO, vec2(900.0, 700.0));
let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| {
if properties {
crate::panels::properties::show(app, ui);
} else {
ui.horizontal(|ui| control_bar(app, ui));
}
});
out.textures_delta.clear();
crate::tests_removeanchors::shapes_text(&out.shapes.iter().map(|c| c.shape.clone()).collect::<Vec<_>>())
};
let shows = |app: &mut VectorcraftApp, name: &str| {
for properties in [false, true] {
let t = texts(app, properties);
assert!(t.iter().any(|(s, _)| s == name), "{} shows {name}: {t:?}", if properties { "Properties" } else { "Control bar" });
}
};
shows(&mut app, "Ink");
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "newName": "Dark Ink"})).unwrap();
shows(&mut app, "Dark Ink");
app.session.execute("imageTrace.presets.save", &json!({"name": "Dark Ink", "preset": "Silhouettes"})).unwrap();
shows(&mut app, "Custom");
app.session.execute("imageTrace.presets.delete", &json!({"name": "Dark Ink"})).unwrap();
shows(&mut app, "Custom");
}
/// An object traced with a preset whose name another saved preset had earlier in the session, renamed since: the
/// panel can't tell which of the two the object was traced with, and shows Custom.
#[test]
fn an_object_traced_with_a_reused_preset_name_that_was_renamed_shows_custom() {
let mut app = traced_app_with_ink(100);
let save = |app: &mut VectorcraftApp, p: Value| {
app.session.execute("imageTrace.presets.save", &p).unwrap();
};
save(&mut app, json!({"name": "A", "preset": "Sketched Art"}));
save(&mut app, json!({"name": "A", "newName": "B"}));
save(&mut app, json!({"name": "A", "preset": "Sketched Art"}));
trace_and_open(&mut app, "A");
save(&mut app, json!({"name": "A", "newName": "C"}));
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(selected_trace(&app).unwrap().0, "A");
assert_eq!(shown_preset(&ctx), "Custom");
}
/// An object traced with a preset that got its name from a rename, renamed again since: the panel shows the new name.
#[test]
fn an_object_traced_between_two_renames_shows_the_last_name() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "A", "preset": "Sketched Art"})).unwrap();
app.session.execute("imageTrace.presets.save", &json!({"name": "A", "newName": "B"})).unwrap();
trace_and_open(&mut app, "B");
app.session.execute("imageTrace.presets.save", &json!({"name": "B", "newName": "C"})).unwrap();
let shown = |app: &mut VectorcraftApp| {
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(app, &ctx, vec![]);
shown_preset(&ctx)
};
assert_eq!(selected_trace(&app).unwrap().0, "B");
assert_eq!(shown(&mut app), "C");
// Renamed back to B and on to D, it is still the same preset.
app.session.execute("imageTrace.presets.save", &json!({"name": "C", "newName": "B"})).unwrap();
app.session.execute("imageTrace.presets.save", &json!({"name": "B", "newName": "D"})).unwrap();
assert_eq!(shown(&mut app), "D");
}
/// Presets renamed between two frames while the object is not selected: the object, selected again, shows its own
/// preset's new name, not the name a rename of another preset gave.
#[test]
fn renames_between_frames_leave_a_reselected_object_on_its_own_preset() {
let mut app = traced_app_with_ink(100);
for name in ["A", "B"] {
app.session.execute("imageTrace.presets.save", &json!({"name": name, "preset": "Sketched Art"})).unwrap();
}
trace_and_open(&mut app, "A");
let a = first_selected(&app).unwrap().id;
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "A");
app.run("select.set", json!({"ids": []})).unwrap();
frame(&mut app, &ctx, vec![]);
app.session.execute("imageTrace.presets.save", &json!({"name": "B", "newName": "C"})).unwrap();
app.session.execute("imageTrace.presets.save", &json!({"name": "A", "newName": "B"})).unwrap();
app.run("select.set", json!({"ids": [a.0]})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "B");
}
/// Saved presets whose names differ only by brackets are different presets: an object traced with
/// a deleted one shows Custom.
#[test]
fn a_saved_preset_in_brackets_is_not_another_saved_preset() {
let mut app = traced_app_with_ink(100);
for name in ["Ink", "[Ink]"] {
app.session.execute("imageTrace.presets.save", &json!({"name": name, "preset": "Sketched Art"})).unwrap();
}
trace_and_open(&mut app, "[Ink]");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "[Ink]");
app.session.execute("imageTrace.presets.delete", &json!({"name": "[Ink]"})).unwrap();
app.run("select.set", json!({"ids": []})).unwrap();
frame(&mut app, &ctx, vec![]);
app.run("select.all", json!({})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(selected_trace(&app).unwrap().0, "[Ink]");
assert_eq!(shown_preset(&ctx), "Custom");
}
/// A saved preset whose settings change outside the panel while the panel shows it with other
/// settings: the panel shows Custom, and Trace records Custom with the panel's settings.
#[test]
fn a_saved_preset_changed_elsewhere_leaves_the_panel_settings_as_custom() {
let mut app = traced_app_with_ink(100);
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "preset": "Sketched Art"})).unwrap();
trace_and_open(&mut app, "Ink");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Ink");
let before = pstate::<TraceUi>(&ctx, "image-trace").params;
// A save with the same settings keeps the name.
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Ink");
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink", "params": {"threshold": 40}})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Custom");
assert_eq!(pstate::<TraceUi>(&ctx, "image-trace").params, before, "the panel's settings stay");
// A rename that also changes the settings.
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink 2", "preset": "Sketched Art"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Ink 2");
app.session.execute("imageTrace.presets.save", &json!({"name": "Ink 2", "newName": "Ink 3", "params": {"threshold": 40}})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!(shown_preset(&ctx), "Custom");
// The object, traced with Ink before its settings changed, shows Custom when the panel opens
// again.
assert_eq!(selected_trace(&app).unwrap().0, "Ink");
let reopened = egui::Context::default();
crate::theme::install_fonts(&reopened);
frame(&mut app, &reopened, vec![]);
assert_eq!(shown_preset(&reopened), "Custom");
}
#[test]
fn the_preset_list_is_read_once_for_each_change_of_the_saved_presets() {
let mut app = traced_app_with_ink(100);
trace_and_open(&mut app, "Sketched Art");
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let reads = || PRESET_READS.with(std::cell::Cell::get);
frame(&mut app, &ctx, vec![]);
let start = reads();
for _ in 0..5 {
frame(&mut app, &ctx, vec![]);
}
assert_eq!(reads(), start, "frames with the same saved presets read the list again");
// A preset saved outside the panel is read once, and the panel shows it.
app.session.execute("imageTrace.presets.save", &json!({"name": "A", "preset": "Sketched Art"})).unwrap();
for _ in 0..3 {
frame(&mut app, &ctx, vec![]);
}
assert_eq!((reads(), shown_preset(&ctx)), (start + 1, "A".to_string()));
// The rename's reply names the old name, and the panel follows the new one.
let r = app.session.execute("imageTrace.presets.save", &json!({"name": "a", "newName": "B"})).unwrap();
assert_eq!(r["renamedFrom"], "A");
frame(&mut app, &ctx, vec![]);
assert_eq!((reads(), shown_preset(&ctx)), (start + 2, "B".to_string()));
// Preferences that replace the saved presets are read again too.
let mut prefs = app.session.prefs.clone();
prefs.trace_presets.clear();
app.session.apply_prefs(prefs);
frame(&mut app, &ctx, vec![]);
assert_eq!((reads(), shown_preset(&ctx)), (start + 3, "Custom".to_string()));
// A preset saved with the panel's settings and renamed before the next frame is shown under
// its new name.
app.session.execute("imageTrace.presets.save", &json!({"name": "C", "preset": "Sketched Art"})).unwrap();
app.session.execute("imageTrace.presets.save", &json!({"name": "C", "newName": "D"})).unwrap();
frame(&mut app, &ctx, vec![]);
assert_eq!((reads(), shown_preset(&ctx)), (start + 4, "D".to_string()));
}
#[test]
fn panel_and_menu_draw_headless() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
+10
View File
@@ -1582,6 +1582,16 @@ already walks Image Trace. Short agent entry points:
- Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`),
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`. A key inside `params` that is not one of the trace
settings, such as `colours`, is an error that names it and the nearest setting (`colors`), as for a command's own keys.
`imageTrace.presets.save {name?, newName?, preset?, enhanced?, params?}` saves tracing settings as a preset kept with
the preferences: `params` over `preset`'s (default: the saved preset `name`, else Default), checked as `imageTrace.make`
checks them; without `name` it is a new "Tracing Preset N", and `newName` renames a saved preset (the reply's
`renamedFrom` holds the old name). `imageTrace.presets.delete {name}` deletes one. Built-in presets and the name
`Custom` can't be saved over, renamed or deleted. `imageTrace.presets` lists the built-in presets with
`builtIn: true`, then the saved ones with `builtIn: false`, and returns a `revision` that grows each time the saved
presets change. `imageTrace.make {preset}` takes a saved preset's name, with ASCII letters matching in either case.
Its journal entry records the saved preset's settings as `savedPreset`, so a replay traces with them after the preset
is changed, renamed or deleted. In the app,
`ui.imageTracePresetDialog {params?, rename?}` opens the Image Trace panel's Save as New Preset… or Rename… dialog.
- Live Paint: `livePaint.make`, `livePaint.merge`, `livePaint.fill {group?, point}`, `livePaint.strokeEdge`,
`livePaint.info {group}`, `livePaint.release`, `livePaint.expand`, and `livePaint.gapOptions {ids?, detect?, size?,
color?, closeGaps?}` (Object › Live Paint › Gap Options). With `detect: true`, paint stops at the gaps that run
+1
View File
@@ -847,6 +847,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Image Trace, Flat Logo working size:** Flat Logo traces at most 1024 px on a side; a larger image is averaged down by a whole factor first, and the trace's `warnings` name the size it was traced at and the image's own size, in place of the slower-trace and 64-megapixel notes; code-drawn 1024 and 1025 px tests in `crates/trace` and `crates/engine`.
- **Image Trace, Preview:** the Image Trace panel's Preview checkbox, before Trace, is on by default, and a setting change traces the selected Image Trace object again at once. Off, changes wait in the panel until Trace applies them as one undo step, or until Preview is turned on again. Changes stay with the object they were made for while another object is selected. When Preview is turned on with that object not selected, the object is traced with its changes when it is selected again. Preview is kept with the UI preferences, and Reset to Default leaves it. The `imageTrace.preview` command turns it on or off.
- **Image Trace small contours:** closed fits too small to form an editable subpath retain their positioned outline, including holes. Source-generated island, hole/winding and stepped-region regressions cover Corners and Noise; defaults, parity scores and estimates are unchanged.
- **Image Trace, custom presets:** Manage Presets next to the Image Trace panel's Preset dropdown saves the panel's settings as a new preset under a name (Save as New Preset…) and renames or deletes the saved preset shown; built-in presets don't change. Saved presets are kept with the preferences and follow the built-in ones in the panel, Control bar and Properties preset lists. `imageTrace.presets.save` and `imageTrace.presets.delete` save, rename and delete them, `imageTrace.presets` lists them, and `imageTrace.make {preset}` traces with one by name.
- **Create Strokes baseline (2026-10-17, measured):** the ignored centre-lines class covers 23 exact code-drawn scenes in aa and hard variants, including junctions, crossings, fills meeting strokes and varying widths. [Baseline and metric definitions](image-trace-quality.md#centre-lines). Topology is recovered from emitted geometry; width-profile metrics remain unavailable. No behaviour change or score increase.
- **Image Trace, collapsed loops keep few segments:** a loop whose fit collapses to one cubic keeps the fewest segments that hold its area and course, so a higher Paths gives no fewer segments; a code-drawn Paths sweep in `crates/trace` (see [quality notes](image-trace-quality.md#collapsed-loops-keep-few-segments)).
- **Image Trace, stacked layers only under their own areas:** with Method Overlapping, a layer reaches under the colors above it only within the connected areas that hold its own color, so a shape that sits apart from a color is no longer written again, at the same bounds, in that color, and the edge of that color's layer no longer shows through that shape's anti-aliased rim. *Measured* by `crates/trace/tests/quality` on each preset's own inputs: High Fidelity Photo 121 paths to 69.6 with ΔE 1.74 to 1.72, Low Fidelity Photo 37.1 paths to 26.1. Code-drawn disc, square and line tests in `crates/trace`.