From 3c463fa5fb03f1f7cb0c381f268b2794b5195a36 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 08:34:40 -0800 Subject: [PATCH 01/33] Brushes: strokes draw with the first of two brushes with one name When a document holds two brushes with the same name, the canvas drew a stroke that names it with the last of them, while brush.get, Brush Options, expanding, outlining and the SVG, PDF, EPS and .ai exports took the first. Document Info gave the last one's type, and every row of that name in the Brushes panel previewed and dragged the last one. The renderer, Document Info and the Brushes panel now take the first brush of a name, so the canvas, the panel and the exports agree. --- crates/engine/src/cmd/docinfo.rs | 11 ++++++---- crates/render/src/brush_fx.rs | 25 +++++++++++++++++++++- crates/ui-egui/src/panels/brushes.rs | 12 ++++++++++- crates/ui-egui/src/panels/brushes/tests.rs | 24 +++++++++++++++++++++ docs/target-app-parity.md | 2 ++ 5 files changed, 68 insertions(+), 6 deletions(-) diff --git a/crates/engine/src/cmd/docinfo.rs b/crates/engine/src/cmd/docinfo.rs index a43325becd..a58ff8e1ee 100644 --- a/crates/engine/src/cmd/docinfo.rs +++ b/crates/engine/src/cmd/docinfo.rs @@ -633,10 +633,13 @@ fn info(s: &mut Session, p: &Value) -> Result { fonts.extend(super::fonts::art_texts_of(d).into_iter().flat_map(|(_, t, _)| t.runs.iter().map(|r| super::fonts::font_of(&r.style)))); } let font_names: Vec = fonts.iter().map(super::fonts::font_label).collect(); - // Each brush the art uses with its type, found by name in the Brushes panel as the renderer finds it (the last of two + // Each brush the art uses with its type, found by name in the Brushes panel as the renderer finds it (the first of two // brushes with one name); a brush the panel does not hold draws as a plain stroke and has no type. A row names the type // as the New Brush dialog does (`Art Brush`), and the panel translates these values when it shows them. - let kinds: HashMap<&str, &vectorcraft_brush::BrushKind> = library.iter().map(|b| (b.name.as_str(), &b.kind)).collect(); + let mut kinds: HashMap<&str, &vectorcraft_brush::BrushKind> = HashMap::new(); + for b in &library { + kinds.entry(b.name.as_str()).or_insert(&b.kind); + } let brush_entries: Vec = brushes.iter().map(|name| json!({ "name": name, "type": kinds.get(name.as_str()).map(|k| k.type_id()) })).collect(); let brush_rows: Vec<(String, String)> = brushes @@ -910,7 +913,7 @@ mod tests { let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc); d.node_mut(NodeId(missing)).unwrap().appearance.stroke_mut().unwrap().paint = Paint::None; assert_eq!(s.execute("document.info", &json!({"category": "brushes"})).unwrap()["brushes"], json!([])); - // Two panel brushes with one name: the type is the one the renderer draws with, the last one's. + // Two panel brushes with one name: the type is the one the renderer draws with, the first one's. let twin = square(&mut s, 140.0); s.execute("brush.apply", &json!({"ids": [twin], "name": "Charcoal"})).unwrap(); let d = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc); @@ -920,7 +923,7 @@ mod tests { lib.extend(kinds.into_iter().map(|kind| vectorcraft_brush::Brush { name: "Twin".into(), kind })); vectorcraft_brush::store(d, &lib); d.node_mut(NodeId(twin)).unwrap().appearance.stroke_mut().unwrap().brush = Some("Twin".into()); - assert_eq!(s.execute("document.info", &json!({"category": "brushes"})).unwrap()["brushes"], json!([{"name": "Twin", "type": "scatter"}])); + assert_eq!(s.execute("document.info", &json!({"category": "brushes"})).unwrap()["brushes"], json!([{"name": "Twin", "type": "art"}])); // Type: a brushed stroke on drawn characters is listed, one on spaces alone is not. let blank = s.execute("text.create", &json!({"x": 10, "y": 60, "text": " "})).unwrap()["id"].as_u64().unwrap(); let word = s.execute("text.create", &json!({"x": 10, "y": 90, "text": "Hi"})).unwrap()["id"].as_u64().unwrap(); diff --git a/crates/render/src/brush_fx.rs b/crates/render/src/brush_fx.rs index b1b46b3327..3c6ea922d4 100644 --- a/crates/render/src/brush_fx.rs +++ b/crates/render/src/brush_fx.rs @@ -107,7 +107,12 @@ impl BrushCache { vectorcraft_brush::serde_json::to_string(v).unwrap_or_default().hash(&mut h); h.finish() }); - self.lib = vectorcraft_brush::library(doc).into_iter().map(|b| (b.name.clone(), Arc::new(b))).collect(); + // Of two brushes with one name, a stroke that names it draws with the first, as + // `vectorcraft_brush::find` and the exports take it. + self.lib.clear(); + for b in vectorcraft_brush::library(doc) { + self.lib.entry(b.name.clone()).or_insert_with(|| Arc::new(b)); + } } /// [`Self::key`] for the brush library `doc` holds (checked once a frame, `stamp`). @@ -388,6 +393,24 @@ mod tests { assert!(!dark(a.pixel(100, 58)) && dark(b.pixel(100, 58))); } + /// Of two brushes with one name, a stroke that names it draws with the first. + #[test] + fn a_stroke_draws_with_the_first_of_two_brushes_with_its_name() { + use vectorcraft_brush::{Brush, BrushKind, Calligraphic}; + let nib = |size: f64| Brush { name: "Twin".into(), kind: BrushKind::Calligraphic(Calligraphic { size, ..Default::default() }) }; + let doc = |lib: &[Brush]| { + let mut d = doc_with_line(Some("Twin"), 1.0); + vectorcraft_brush::store(&mut d, lib); + d + }; + let (thin, wide) = (nib(1.0), nib(20.0)); + let first = render(&mut Renderer::new(), &doc(std::slice::from_ref(&thin))).pixels; + let second = render(&mut Renderer::new(), &doc(std::slice::from_ref(&wide))).pixels; + assert_ne!(first, second, "the two nibs paint differently"); + let both = render(&mut Renderer::new(), &doc(&[thin, wide])).pixels; + assert!(both == first, "the stroke draws with the first brush named Twin"); + } + #[test] fn stained_symbol_instance() { let mut d = Document::new(100.0, 100.0); diff --git a/crates/ui-egui/src/panels/brushes.rs b/crates/ui-egui/src/panels/brushes.rs index 8f29c70475..969fa14aeb 100644 --- a/crates/ui-egui/src/panels/brushes.rs +++ b/crates/ui-egui/src/panels/brushes.rs @@ -69,6 +69,16 @@ pub(crate) fn document_brushes(st: &DocState) -> Rc> { MEMO.with(|m| m.borrow_mut().get(&st.doc, st.revision, || vectorcraft_brush::library(&st.doc).iter().map(|b| Shown::of(&st.doc, b)).collect())) } +/// The brushes of `shown` by name: of two with one name, the first, the one a stroke that names +/// it draws with and `brush.get` gives. +fn by_name(shown: &[Shown]) -> HashMap<&str, &Shown> { + let mut map = HashMap::new(); + for b in shown { + map.entry(b.name.as_str()).or_insert(b); + } + map +} + /// The cache key of the stroke preview of brush definition `def` in `doc` (`art`: its art): the /// definition and every resource of `doc` the art uses ([`art_resources`]: patterns with their /// options, symbols, swatches and colour groups, gradients, images, styles…), so the preview @@ -298,7 +308,7 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { // definitions and preview keys are the document version's, worked out once. let doc = app.session.active().map(|d| d.doc.clone()); let memo = app.session.active().map(document_brushes).unwrap_or_default(); - let defs: HashMap<&str, &Shown> = memo.iter().map(|b| (b.name.as_str(), b)).collect(); + let defs = by_name(&memo); let mut clicked: Option<(String, egui::Modifiers)> = None; let mut options: Option = None; let mut moved: Option = None; diff --git a/crates/ui-egui/src/panels/brushes/tests.rs b/crates/ui-egui/src/panels/brushes/tests.rs index fcab4373ce..2d9305fc52 100644 --- a/crates/ui-egui/src/panels/brushes/tests.rs +++ b/crates/ui-egui/src/panels/brushes/tests.rs @@ -821,3 +821,27 @@ fn previews_follow_the_swatches_images_and_pattern_options_their_art_uses() { assert_eq!(change(&mut app, what, f), (brushes, symbols, what.to_string())); } } + +/// Of two brushes with one name, both rows preview and drag the first, the one `brush.get` gives +/// and a stroke that names it draws with. +#[test] +fn rows_of_two_brushes_with_one_name_show_the_first() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 200, "height": 100})).unwrap(); + app.session + .edit("Brushes", |d, _| { + let mut lib = vectorcraft_brush::library(d); + let kinds: Vec<_> = ["Charcoal", "Dots"].iter().map(|n| lib.iter().find(|b| b.name == *n).unwrap().kind.clone()).collect(); + lib.extend(kinds.into_iter().map(|kind| Brush { name: "Twin".into(), kind })); + vectorcraft_brush::store(d, &lib); + Ok(()) + }) + .unwrap(); + let twins: Vec = brushes(&mut app).0.into_iter().filter(|b| b.0 == "Twin").map(|b| b.1).collect(); + assert_eq!(twins, ["art", "scatter"], "the panel lists both"); + let got = app.run("brush.get", json!({"name": "Twin"})).unwrap(); + let memo = document_brushes(app.session.active().unwrap()); + let shown = by_name(&memo)["Twin"]; + assert_eq!(shown.ty, "art"); + assert_eq!(shown.def, got, "the row shows and drags what brush.get gives"); +} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 65b58c6dc5..4cd01e8945 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1766,6 +1766,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Brush art of copies:** symbol instances, inline graphics and repeat copies of a brushed path keep their own brush art between frames, as the same path on the page does. The renderer also keeps the stroke outlines of brush art and the evaluation of a live object in it, such as an envelope that a scatter brush places. The brush art is made again when the path, its stroke or the brush changes, and when the art of a symbol or pattern that the brush art can show changes. On one thread at 1000 × 1000 pixels (the median frame, with the median fastest frame in brackets), 2,000 paths on the page whose scatter brush art is an envelope take 94 ms (80 ms) a frame instead of 400 ms (306 ms), and 2,000 instances of a symbol whose art is a brushed path take 13.4 ms (12.0 ms) instead of 19.3 ms (15.6 ms), with the same pixels. +- **Two brushes with one name:** when a document's Brushes panel holds two brushes with the same name, a stroke that names it draws on the canvas with the first of them, as `brush.get`, Brush Options, expanding, outlining and the SVG, PDF, EPS and `.ai` exports take it. Document Info gives that brush's type, and both rows of the Brushes panel preview and drag the first brush. + - **Live objects in symbols and masks:** symbol instances and inline graphics whose symbol is a blend, an envelope, a repeat, an Intertwine object or a gradient mesh, or a group that holds one, draw the evaluation the renderer keeps for that object between frames, as the same object on the page does. So do opacity masks whose art is such an object, and such objects in clip groups in Outline view. The object is evaluated again when the symbols, patterns, fonts or color settings it shows change. Symbol art with effects of its own, or that holds a symbol instance, an image, a placed file, type with an inline graphic or a raster effect, is still placed and evaluated again in every frame. At 1000 × 1000 pixels, a page of 2,000 instances of a symbol that holds an envelope takes 28 ms a frame instead of 126 ms, with the same pixels. - **Live objects in documents with many symbols:** a live object, or an object with effects, that shows symbols or patterns is not walked again in a later frame to find them while the document's symbols and patterns stay the same. One that shows a placed file is walked in every frame. At 1000 × 1000 pixels, a page of 2,000 envelopes around instances of the last 2,000 of 10,000 symbols takes 30 ms a frame, and 83 ms when every frame walks them again, with the same pixels. From 453d869c984694c91e1c3af9d292ac0df3229ed4 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 10:18:09 -0800 Subject: [PATCH 02/33] Tests: brush art tests replace the default Dots brush The two brush art tests in render's memory tests added a scatter brush named Dots next to the default library's own Dots. Strokes now draw with the first brush of a name, so the stroke drew the default brush and the edits to the symbols and patterns its art shows no longer changed the frame. The tests now remove the default Dots before adding their own. --- crates/render/src/tests/memory.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/render/src/tests/memory.rs b/crates/render/src/tests/memory.rs index 011f9fd7fe..d286fa7e3c 100644 --- a/crates/render/src/tests/memory.rs +++ b/crates/render/src/tests/memory.rs @@ -931,6 +931,8 @@ fn brush_art_draws_edits_of_the_symbols_and_patterns_it_shows() { painted.transform(Affine::translate((0.0, 8.0)), true); let scatter = Node::group(d.alloc_id(), vec![Arc::new(instance), Arc::new(painted)]); let mut lib = vectorcraft_brush::library(&d); + // The default library has its own `Dots`, which a stroke would draw with first. + lib.retain(|b| b.name != "Dots"); lib.push(Brush { name: "Dots".into(), kind: BrushKind::Scatter(Scatter { art: scatter, spacing: (150.0, 150.0), ..Default::default() }) }); vectorcraft_brush::store(&mut d, &lib); let mut ap = vectorcraft_doc::Appearance::basic(Paint::None, Paint::solid(Color::BLACK), 1.0); @@ -997,6 +999,8 @@ fn brush_art_draws_renamed_symbols_and_patterns_it_shows() { painted.transform(Affine::translate((0.0, 8.0)), true); let scatter = Node::group(d.alloc_id(), vec![Arc::new(instance), Arc::new(painted)]); let mut lib = vectorcraft_brush::library(&d); + // The default library has its own `Dots`, which a stroke would draw with first. + lib.retain(|b| b.name != "Dots"); lib.push(Brush { name: "Dots".into(), kind: BrushKind::Scatter(Scatter { art: scatter, spacing: (150.0, 150.0), ..Default::default() }) }); vectorcraft_brush::store(&mut d, &lib); let mut ap = vectorcraft_doc::Appearance::basic(Paint::None, Paint::solid(Color::BLACK), 1.0); From 1ed7785bc5d677a8b66aa0241ebd0f277ae5fcea Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 11:37:49 -0800 Subject: [PATCH 03/33] Actions: keep folder preferences out of saved sets and confined replies A prefs.set step, recorded inside a command.batch or loaded from a .vcactions file, can hold a folder preference such as scratchPrimary. - ui.inspect under automation read roots now checks such a step's value for {key, value} and each entry of values, also in a batch step's commands at any depth, as written and trimmed, including bare names, subfolders and file: URLs. An empty value and the scratch disk choices Startup and None stay. - Any other step text written as an absolute path or a file: URL outside the read root is left out also when whitespace surrounds it. - Save Actions refuses a set with a prefs.set step that holds a folder value and names the action and the preference instead of writing the file. An empty folder and the scratch disk choices save. - prefs.export leaves out scratchPrimary and scratchSecondary, like the other folder preferences. prefs.import refuses a scratch disk set to a folder and still reads Startup, None and an empty value. The folder list, the scratch disk choices and the folder check now live in one place in the engine. Docs: docs/mcp.md, params text and a progress line in docs/target-app-parity.md. --- crates/engine/src/cmd/prefscmds.rs | 34 +++++---- crates/engine/src/tests/prefs_file.rs | 11 ++- crates/ui-egui/src/control.rs | 84 +++++++++++++++------- crates/ui-egui/src/control/tests.rs | 18 +++++ crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/bs.tsv | 1 + crates/ui-egui/src/i18n/ca.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/da.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hi.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/id.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ka.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/nl.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/tr.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + crates/ui-egui/src/menus/ui_commands.rs | 2 +- crates/ui-egui/src/panels/actions.rs | 42 +++++++++++ crates/ui-egui/src/panels/actions/files.rs | 33 ++++++++- crates/ui-egui/src/tests/automation.rs | 56 +++++++++++++++ docs/mcp.md | 23 +++--- docs/target-app-parity.md | 1 + 37 files changed, 283 insertions(+), 48 deletions(-) diff --git a/crates/engine/src/cmd/prefscmds.rs b/crates/engine/src/cmd/prefscmds.rs index 748e0a125a..8f7b90156f 100644 --- a/crates/engine/src/cmd/prefscmds.rs +++ b/crates/engine/src/cmd/prefscmds.rs @@ -792,7 +792,7 @@ pub fn specs() -> Vec { "Export Preferences", [], None, - "{path?} write the preferences as a JSON file {format: \"vectorcraft-preferences\", version: 1, prefs: {key: value, …}}: every preference prefs.list lists and the eyedropper, perspectiveWidget, pathfinder, glyphSnap and pixelSnap groups, without the folder preferences fontsFolder, pluginsFolder, recoveryFolder and templatesFolder and without saved presets; a file over 1 MB, which prefs.import would refuse, is an error and is not written → {path, count}; without path → {data: the file's text, count}", + "{path?} write the preferences as a JSON file {format: \"vectorcraft-preferences\", version: 1, prefs: {key: value, …}}: every preference prefs.list lists and the eyedropper, perspectiveWidget, pathfinder, glyphSnap and pixelSnap groups, without the folder preferences fontsFolder, pluginsFolder, scratchPrimary, scratchSecondary, recoveryFolder and templatesFolder and without saved presets; a file over 1 MB, which prefs.import would refuse, is an error and is not written → {path, count}; without path → {data: the file's text, count}", always, export ), @@ -801,7 +801,7 @@ pub fn specs() -> Vec { "Import Preferences", [], None, - "{path? | data?: the file's text} apply the preferences of a file prefs.export wrote (at most 1 MB). Every value is checked first, as prefs.set checks it: another format, a newer version, an unknown preference, a value of the wrong type or out of range, or a folder preference is an error, and nothing changes. Preferences the file leaves out keep their values; the open document's units stay as they are; no undo step; not a command.batch step → {imported: count}", + "{path? | data?: the file's text} apply the preferences of a file prefs.export wrote (at most 1 MB). Every value is checked first, as prefs.set checks it: another format, a newer version, an unknown preference, a value of the wrong type or out of range, or a folder preference (for scratchPrimary and scratchSecondary, a folder; the choices Startup and None are read) is an error, and nothing changes. Preferences the file leaves out keep their values; the open document's units stay as they are; no undo step; not a command.batch step → {imported: count}", always, import ), @@ -817,8 +817,20 @@ pub const PREFS_VERSION: u64 = 1; /// The largest preferences file `prefs.import` reads. const MAX_PREFS_FILE_BYTES: usize = 1 << 20; -/// Folder preferences, which name folders on one computer: a preferences file leaves them out. -const FOLDER_PREFS: &[&str] = &["fontsFolder", "pluginsFolder", "recoveryFolder", "templatesFolder"]; +/// Folder preferences, which name folders on one computer: a preferences file leaves them out, and +/// under automation read roots one outside them is not shown. +pub const FOLDER_PREFS: [&str; 6] = ["fontsFolder", "pluginsFolder", "scratchPrimary", "scratchSecondary", "recoveryFolder", "templatesFolder"]; + +/// The scratch disk choices that are not folders. +pub const SCRATCH_CHOICES: [&str; 2] = ["Startup", "None"]; + +/// Does `value`, set as preference `key`, name a folder: is `key` a folder preference +/// ([`FOLDER_PREFS`]) and `value`, trimmed as the app uses it, neither empty (the default folder) +/// nor a scratch disk choice ([`SCRATCH_CHOICES`])? +pub fn names_a_folder(key: &str, value: &str) -> bool { + let path = value.trim(); + FOLDER_PREFS.contains(&key) && !path.is_empty() && !(key.starts_with("scratch") && SCRATCH_CHOICES.contains(&path)) +} /// The preferences `prefs.export` writes: every [`PREF_SPECS`] key but the [`FOLDER_PREFS`], and /// the [`PREF_GROUPS`]. @@ -867,7 +879,9 @@ fn read_prefs_file(bytes: &[u8]) -> std::result::Result, Stri if prefs.is_empty() { return Err("the file holds no preferences".into()); } - if let Some(key) = prefs.keys().find(|k| FOLDER_PREFS.contains(&k.as_str())) { + // A scratch disk choice or an empty scratch disk names no folder, and is read. + let folder = |k: &str, v: &Value| FOLDER_PREFS.contains(&k) && (!k.starts_with("scratch") || v.as_str().is_some_and(|v| names_a_folder(k, v))); + if let Some(key) = prefs.iter().find(|(k, v)| folder(k, v)).map(|(k, _)| k) { return Err(format!("`{key}` names a folder, which preferences files leave out")); } Ok(prefs.clone()) @@ -892,14 +906,8 @@ fn import(s: &mut Session, p: &Value) -> Result { Ok(json!({ "imported": values.len() })) } -/// The preferences that name a folder: the [`FOLDER_PREFS`] and the scratch disks. -const PATH_PREFS: [&str; 6] = ["fontsFolder", "pluginsFolder", "scratchPrimary", "scratchSecondary", "recoveryFolder", "templatesFolder"]; - -/// The scratch disk choices that are not folders. -const SCRATCH_CHOICES: [&str; 2] = ["Startup", "None"]; - /// The preferences as `prefs.get`, `prefs.list`, `prefs.set` and `prefs.reset` show them to the -/// request being served. Under automation read roots, a folder preference ([`PATH_PREFS`]) outside +/// request being served. Under automation read roots, a folder preference ([`FOLDER_PREFS`]) outside /// them is `null`, as `ui.inspect` leaves it out, and an export preset's `folder` outside them is /// left out. An empty value (the default folder) and a scratch /// disk choice ([`SCRATCH_CHOICES`]) are shown. Without roots the preferences are shown as they are. @@ -910,7 +918,7 @@ fn shown_prefs(prefs: &Prefs) -> Value { } // The app trims a folder before it uses it. let hidden = |path: &str| !path.trim().is_empty() && crate::file_access::check_read(path.trim()).is_err(); - for key in PATH_PREFS { + for key in FOLDER_PREFS { let Some(path) = all[key].as_str() else { continue }; if hidden(path) && !(key.starts_with("scratch") && SCRATCH_CHOICES.contains(&path.trim())) { all[key] = Value::Null; diff --git a/crates/engine/src/tests/prefs_file.rs b/crates/engine/src/tests/prefs_file.rs index 7693ad0d5c..c2822ba937 100644 --- a/crates/engine/src/tests/prefs_file.rs +++ b/crates/engine/src/tests/prefs_file.rs @@ -12,7 +12,7 @@ fn is_bad_params(r: Result) -> bool { } /// Folder preferences, which a preferences file leaves out. -const FOLDERS: [&str; 4] = ["fontsFolder", "pluginsFolder", "recoveryFolder", "templatesFolder"]; +const FOLDERS: [&str; 6] = ["fontsFolder", "pluginsFolder", "scratchPrimary", "scratchSecondary", "recoveryFolder", "templatesFolder"]; /// A preferences file holding `prefs`, as `prefs.export` writes one. fn file(prefs: Value) -> String { @@ -46,7 +46,9 @@ fn in_file(s: &Session) -> Value { fn exported_preferences_import_unchanged() { let mut s = changed(); s.prefs.fonts_folder = "/fonts/here".into(); + s.prefs.scratch_primary = "/scratch/here".into(); let r = s.execute("prefs.export", &json!({})).unwrap(); + assert!(!r["data"].as_str().unwrap().contains("/scratch/here"), "a scratch disk folder is not written"); let text = r["data"].as_str().unwrap().to_string(); let written: Value = serde_json::from_str(&text).unwrap(); assert_eq!((written["format"].as_str(), written["version"].as_u64()), (Some(PREFS_FORMAT), Some(1))); @@ -71,6 +73,7 @@ fn exported_preferences_import_unchanged() { assert_eq!(t.doc().unwrap().doc.units, Unit::Millimeters, "the document's units stay as they are"); assert_eq!(t.doc().unwrap().history.undo.len(), undo, "no undo step"); assert_eq!(t.prefs.fonts_folder, "", "folders are not carried over"); + assert_eq!(t.prefs.scratch_primary, "Startup", "nor is a scratch disk"); // Through a file, too. let path = std::env::temp_dir().join(format!("vc-prefs-{}.json", std::process::id())).to_string_lossy().to_string(); @@ -96,6 +99,10 @@ fn a_hand_written_file_sets_what_it_names_and_leaves_the_rest() { assert_eq!(s.prefs.keyboard_increment, 3.0); assert_eq!(s.prefs.guide_color, "#abcdef"); assert_eq!(s.prefs.history_states, 77, "a preference the file leaves out keeps its value"); + // A scratch disk choice names no folder, and is read. + let choices = file(json!({"scratchPrimary": "None", "scratchSecondary": "Startup"})); + assert_eq!(s.execute("prefs.import", &json!({"data": choices})).unwrap(), json!({"imported": 2})); + assert_eq!((s.prefs.scratch_primary.as_str(), s.prefs.scratch_secondary.as_str()), ("None", "Startup")); } #[test] @@ -119,6 +126,8 @@ fn a_bad_preferences_file_is_refused_and_nothing_changes() { (json!({"data": file(json!({"uiAccent": "blue", "historyStates": 5000}))}), "`historyStates` must be a whole number between 5 and 1000"), (json!({"data": file(json!({"uiAccent": "blue", "eyedropper": 5}))}), "`eyedropper` must be an object"), (json!({"data": file(json!({"uiAccent": "blue", "fontsFolder": "/fonts"}))}), "`fontsFolder` names a folder"), + (json!({"data": file(json!({"uiAccent": "blue", "scratchPrimary": "/scratch"}))}), "`scratchPrimary` names a folder"), + (json!({"data": file(json!({"uiAccent": "blue", "scratchSecondary": " scratch "}))}), "`scratchSecondary` names a folder"), (json!({"data": too_large}), "larger than 1 MB"), (json!({"data": 5}), "`data` must be a string"), (json!({"path": "/a.json", "data": file(json!({"uiAccent": "blue"}))}), "give `path` or `data`"), diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index 89ab7e37b4..cc5ed91cb6 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -944,12 +944,6 @@ fn names_a_loaded_library(v: &Value) -> bool { } } -/// The preferences that hold a folder, by their keys in `ui.engine_prefs` and the Preferences dialog. -const PREF_FOLDERS: [&str; 6] = ["fontsFolder", "pluginsFolder", "scratchPrimary", "scratchSecondary", "recoveryFolder", "templatesFolder"]; - -/// The scratch disk choices that are not paths. -const SCRATCH_CHOICES: [&str; 2] = ["Startup", "None"]; - /// May the request being served read `path`? Under an automation read root (a control client's /// request), `ui.inspect` leaves out a path outside the root, as `ui.menu.list` leaves out a recent /// file or a listed script outside it. Another platform's absolute path @@ -972,11 +966,11 @@ fn unreadable_absolute(s: &str) -> bool { } /// Is `s` written as a path that the request may not read: an absolute path or a `file:` URL -/// ([`unreadable_absolute`]), `~` or a path under it, or a path that starts with `.` or `..` and a -/// separator? It is checked as the file access check reads it, relative to the working folder. Other -/// text with a separator (`a/b`) stays. +/// ([`unreadable_absolute`]), as written or with surrounding whitespace trimmed, `~` or a path under +/// it, or a path that starts with `.` or `..` and a separator? It is checked as the file access check +/// reads it, relative to the working folder. Other text with a separator (`a/b`) stays. fn unreadable_path_like(s: &str) -> bool { - if unreadable_absolute(s) { + if unreadable_absolute(s) || unreadable_absolute(s.trim()) { return true; } let t = s.trim_start(); @@ -1045,11 +1039,49 @@ fn percent_decoded(s: &str) -> String { String::from_utf8_lossy(&out).into_owned() } -/// The params of each step of each action of action set `set` (`ui.action_sets`). -fn steps_mut(set: &mut Value) -> impl Iterator> { +/// The command and params of each step of each action of action set `set` (`ui.action_sets`). +fn steps_mut(set: &mut Value) -> impl Iterator)> { let actions = set.get_mut("actions").and_then(Value::as_array_mut).into_iter().flatten(); let steps = actions.filter_map(|action| action.get_mut("steps").and_then(Value::as_array_mut)).flatten(); - steps.filter_map(|step| step.get_mut(1).and_then(Value::as_object_mut)) + steps.filter_map(|step| { + let command = step.get(0).and_then(Value::as_str).unwrap_or_default().to_string(); + step.get_mut(1).and_then(Value::as_object_mut).map(|params| (command, params)) + }) +} + +/// Takes out of the params of a recorded step running `command` the paths the request may not read: +/// the folder preferences' values [`leave_out_unreadable_pref_steps`] takes out, and what +/// [`leave_out_unreadable_params`] takes out, each in one pass over the params. +fn leave_out_unreadable_step(command: &str, params: &mut serde_json::Map) { + leave_out_unreadable_pref_steps(command, params); + leave_out_unreadable_params(params); +} + +/// Takes out of the params of a step running `command` the folder preferences' values the request +/// may not read ([`shows_folder`]): in a `prefs.set` step, given as `{key, value}` or in `values`; in +/// a `command.batch` step, the same in each of its commands, at any depth. +fn leave_out_unreadable_pref_steps(command: &str, params: &mut serde_json::Map) { + match command { + "prefs.set" => { + if let Some(Value::Object(values)) = params.get_mut("values") { + leave_out_unreadable_folders(values); + } + if let (Some(key), Some(value)) = (params.get("key").and_then(Value::as_str), params.get("value").and_then(Value::as_str)) + && !shows_folder(key, value) + { + params.remove("value"); + } + } + "command.batch" => { + for step in params.get_mut("commands").and_then(Value::as_array_mut).into_iter().flatten().filter_map(Value::as_object_mut) { + let command = step.get("command").and_then(Value::as_str).unwrap_or_default().to_string(); + if let Some(Value::Object(params)) = step.get_mut("params") { + leave_out_unreadable_pref_steps(&command, params); + } + } + } + _ => {} + } } /// Takes out of a recorded step's params, at any depth (the commands of a `command.batch` step), the @@ -1103,8 +1135,8 @@ fn leave_out_unreadable_paths(ui: &mut serde_json::Map) { } } } - for step in ui.get_mut("action_sets").and_then(Value::as_array_mut).into_iter().flatten().flat_map(steps_mut) { - leave_out_unreadable_params(step); + for (command, params) in ui.get_mut("action_sets").and_then(Value::as_array_mut).into_iter().flatten().flat_map(steps_mut) { + leave_out_unreadable_step(&command, params); } if let Some(Value::Object(prefs)) = ui.get_mut("engine_prefs") { leave_out_unreadable_folders(prefs); @@ -1117,20 +1149,24 @@ fn leave_out_unreadable_paths(ui: &mut serde_json::Map) { } } -/// Takes out of `prefs` the folder preferences ([`PREF_FOLDERS`]) the request may not read, as written -/// or trimmed as the app uses it. An empty value (no folder) and a scratch disk choice -/// ([`SCRATCH_CHOICES`]) stay. +/// Takes out of `prefs` the folder preferences +/// ([`vectorcraft_engine::cmd::prefscmds::FOLDER_PREFS`]) the request may not read ([`shows_folder`]). fn leave_out_unreadable_folders(prefs: &mut serde_json::Map) { - for key in PREF_FOLDERS { - let Some(written) = prefs.get(key).and_then(Value::as_str) else { continue }; - let path = written.trim(); - if path.is_empty() || (key.starts_with("scratch") && SCRATCH_CHOICES.contains(&path)) || (readable(written) && readable(path)) { - continue; + for key in vectorcraft_engine::cmd::prefscmds::FOLDER_PREFS { + if prefs.get(key).and_then(Value::as_str).is_some_and(|written| !shows_folder(key, written)) { + prefs.remove(key); } - prefs.remove(key); } } +/// May the request see `written` as the value of preference `key`? Yes unless it names a folder +/// ([`vectorcraft_engine::cmd::prefscmds::names_a_folder`]: an empty value and a scratch disk choice +/// do not) that the request may not read as written, or trimmed as the app uses it, also as a `file:` +/// URL ([`readable_value`]). +fn shows_folder(key: &str, written: &str) -> bool { + !vectorcraft_engine::cmd::prefscmds::names_a_folder(key, written) || (readable(written) && readable_value(written.trim())) +} + fn key_from(name: &str) -> Option { egui::Key::from_name(name).or(match name.to_ascii_lowercase().as_str() { "enter" | "return" => Some(egui::Key::Enter), diff --git a/crates/ui-egui/src/control/tests.rs b/crates/ui-egui/src/control/tests.rs index ac8fbfa3eb..4d43339a85 100644 --- a/crates/ui-egui/src/control/tests.rs +++ b/crates/ui-egui/src/control/tests.rs @@ -293,6 +293,24 @@ fn an_absolute_path_outside_the_read_root_is_unreadable() { }); } +/// Under a read root, an absolute path or a `file:` URL outside it is written as a path also with +/// whitespace around it, and a recorded step's text param loses it; a path inside the root stays. +#[test] +fn a_spaced_absolute_path_outside_the_read_root_is_left_out() { + let base = crate::tests_folder::Folder::new("unreadable-spaced"); + std::fs::create_dir_all(base.join("in")).unwrap(); + let roots = vectorcraft_engine::file_access::AutomationRoots::new(&[&base.join("in")], &[]).unwrap(); + let inside = format!(" {} ", base.join("in").join("a.svg").to_string_lossy()); + let mut params = json!({"target": " /outside/root/x", "name": "\tfile:///outside/root/x ", "keep": inside, "list": [" /outside/root/y"]}); + vectorcraft_engine::file_access::confine(roots.as_ref(), || { + assert!(super::unreadable_path_like(" /outside/root/x")); + assert!(super::unreadable_path_like("\tfile:///outside/root/x ")); + assert!(!super::unreadable_path_like(&inside), "{inside}"); + super::leave_out_unreadable_params(params.as_object_mut().unwrap()); + }); + assert_eq!(params, json!({"keep": inside, "list": []})); +} + /// Without a read root, a `file:` URL that names another host may be read, and a recorded step keeps /// it. #[test] diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 4c2785956f..a5e8b6efd0 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5844,6 +5844,7 @@ theme Appearance Външен вид @msg no such color няма такъв цвят @msg {_1} cannot be used on Windows {_1} не може да се използва в Windows @msg more than one action is named “{_1}” повече от едно действие се казва „{_1}“ +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set действието „{_1}“ задава предпочитанието за папка {_2}; изтрийте тази стъпка, за да запазите набора @msg no symbol named `{n}` няма символ с име `{n}` Smudge Stick… Размазваща пръчка… Smudge Stick Размазваща пръчка diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 9ccc94d3a4..4cd869081f 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5847,6 +5847,7 @@ mark-style Japanese Japanski @msg no such color nema takve boje @msg {_1} cannot be used on Windows {_1} se ne može koristiti na Windowsu @msg more than one action is named “{_1}” više od jedne akcije ima naziv „{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set akcija „{_1}” postavlja postavku foldera {_2}; izbrišite taj korak da biste sačuvali skup @msg no symbol named `{n}` nema simbola pod nazivom `{n}` Smudge Stick… Štapić za razmazivanje… Smudge Stick Štapić za razmazivanje diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index b0159ebc14..522abdab79 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -5722,6 +5722,7 @@ digits Malayalam Malaiàlam @msg no such color no existeix aquest color @msg {_1} cannot be used on Windows {_1} no es pot fer servir al Windows @msg more than one action is named “{_1}” més d’una acció es diu “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set l’acció “{_1}” defineix la preferència de carpeta {_2}; suprimiu aquest pas per desar el conjunt @msg name the file poseu un nom al fitxer @msg New Window isn't available yet: a document opens in one window Finestra nova encara no està disponible: un document s’obre en una sola finestra @msg no active document no hi ha cap document actiu diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 27b2d32771..04b292ed8b 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -6153,6 +6153,7 @@ mark-style Japanese Japonské @msg no such color taková barva neexistuje @msg {_1} cannot be used on Windows Zkratku {_1} nelze ve Windows použít @msg more than one action is named “{_1}” název „{_1}“ má více než jedna akce +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set akce „{_1}“ nastavuje předvolbu složky {_2}; chcete-li sadu uložit, tento krok smažte @msg no symbol named `{n}` žádný symbol s názvem `{n}` Smudge Stick… Rozmazávací tyčinka… Smudge Stick Rozmazávací tyčinka diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 3810bb2ac8..0f3865bb2c 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5726,6 +5726,7 @@ swatch Bright Violet Kraftig violet @msg no such color ingen sådan farve @msg {_1} cannot be used on Windows {_1} kan ikke bruges i Windows @msg more than one action is named “{_1}” mere end én handling hedder »{_1}« +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set handlingen »{_1}« angiver mappeindstillingen {_2}; slet det trin for at gemme sættet @msg no symbol named `{n}` intet symbol med navnet `{n}` Remove Color Profile… Fjern farveprofil… Color wheel Farvehjul diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 7b6cd3e8a6..93cdda7c63 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5739,6 +5739,7 @@ effect Soft Weich @msg no such color keine solche Farbe @msg {_1} cannot be used on Windows {_1} kann unter Windows nicht verwendet werden @msg more than one action is named “{_1}” mehr als eine Aktion heißt „{_1}“ +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set die Aktion „{_1}“ legt die Ordnervoreinstellung {_2} fest; löschen Sie diesen Schritt, um den Satz zu speichern @msg no symbol named `{n}` kein Symbol namens `{n}` Smudge Stick… Wischstift… Smudge Stick Wischstift diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 8620169f21..b407274593 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -6105,6 +6105,7 @@ effect Points Σημεία @msg no such color δεν υπάρχει τέτοιο χρώμα @msg {_1} cannot be used on Windows Το {_1} δεν μπορεί να χρησιμοποιηθεί στα Windows @msg more than one action is named “{_1}” περισσότερες από μία ενέργειες ονομάζονται «{_1}» +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set η ενέργεια «{_1}» ορίζει την προτίμηση φακέλου {_2}· διαγράψτε αυτό το βήμα για να αποθηκεύσετε το σύνολο @msg no symbol named `{n}` κανένα σύμβολο με όνομα `{n}` Smudge Stick… Μουτζούρα… Smudge Stick Μουτζούρα diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index a82db20a99..e181c9a86e 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -6096,6 +6096,7 @@ mark-style Japanese Japonesas @msg no such color no existe ese color @msg {_1} cannot be used on Windows {_1} no se puede usar en Windows @msg more than one action is named “{_1}” más de una acción se llama “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set la acción “{_1}” establece la preferencia de carpeta {_2}; elimine ese paso para guardar el conjunto @msg no symbol named `{n}` no hay ningún símbolo llamado `{n}` Smudge Stick… Difuminar con barra… Smudge Stick Difuminar con barra diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index db90a36316..741f782599 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -6116,6 +6116,7 @@ mark-style Japanese Japanilainen @msg no such color väriä ei ole @msg {_1} cannot be used on Windows {_1} ei ole käytettävissä Windowsissa @msg more than one action is named “{_1}” useamman kuin yhden toiminnon nimi on ”{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set toiminto ”{_1}” asettaa kansioasetuksen {_2}; poista tämä vaihe, jotta voit tallentaa sarjan @msg no symbol named `{n}` symbolia nimeltä `{n}` ei ole Smudge Stick… Sumennuskynä… Smudge Stick Sumennuskynä diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 938e43dd48..23bf37a539 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -6148,6 +6148,7 @@ theme Appearance Apparence @msg no such color couleur inexistante @msg {_1} cannot be used on Windows {_1} ne peut pas être utilisé sous Windows @msg more than one action is named “{_1}” plusieurs scripts s’appellent « {_1} » +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set le script « {_1} » définit la préférence de dossier {_2} ; supprimez cette étape pour enregistrer le jeu @msg no symbol named `{n}` aucun symbole nommé `{n}` Smudge Stick… Estompe… Smudge Stick Estompe diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 0eacf9629b..5522cc31bd 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -7170,6 +7170,7 @@ antiAlias Sharp शार्प @msg more than 10000 linked files: package dependencies in smaller parts 10000 से अधिक लिंक्ड फ़ाइलें: पैकेज निर्भरता छोटे भागों में @msg more than 256 commands 256 से अधिक कमांड @msg more than one action is named “{_1}” एक से अधिक कार्रवाई का नाम "{_1}" रखा गया है। +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set कार्रवाई "{_1}" फ़ोल्डर प्राथमिकता {_2} सेट करती है; सेट सेव करने के लिए वह चरण हटाएँ @msg Move Panel to New Window is not available: floating panels stay in the main window नई विंडो में पैनल को स्थानांतरित करना उपलब्ध नहीं है: अस्थायी पैनल मुख्य विंडो में रहते हैं @msg move requires a destination panel move: एक गंतव्य पैनल की आवश्यकता होती है @msg move requires an existing destination panel move: चाल एक मौजूदा गंतव्य पैनल की आवश्यकता है diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 3dadc4d3b6..8361c405f3 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -6136,6 +6136,7 @@ imageTrace Outline Körvonal @msg no such color nincs ilyen szín @msg {_1} cannot be used on Windows A(z) {_1} nem használható Windows rendszeren @msg more than one action is named “{_1}” több művelet neve is „{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set a(z) „{_1}” művelet beállítja a(z) {_2} mappabeállítást; a készlet mentéséhez törölje ezt a lépést @msg no symbol named `{n}` nincs `{n}` nevű szimbólum Smudge Stick… Elmosó rúd… Smudge Stick Elmosó rúd diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index e670840b2c..bd7a906986 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5726,6 +5726,7 @@ swatch Bright Violet Violet cerah @msg no such color tidak ada warna seperti itu @msg {_1} cannot be used on Windows {_1} tidak dapat digunakan di Windows @msg more than one action is named “{_1}” lebih dari satu tindakan bernama “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set tindakan “{_1}” mengatur preferensi folder {_2}; hapus langkah itu untuk menyimpan set @msg no symbol named `{n}` tidak ada simbol bernama `{n}` Remove Color Profile… Hapus profil warna… Color wheel Roda warna diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 91d1e24bf7..765a2aa6aa 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5774,6 +5774,7 @@ lighting Light Luce @msg no such color colore inesistente @msg {_1} cannot be used on Windows {_1} non può essere usato su Windows @msg more than one action is named “{_1}” più di un’azione si chiama “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set l’azione “{_1}” imposta la preferenza della cartella {_2}; elimina quel passaggio per salvare il set @msg no symbol named `{n}` nessun simbolo denominato `{n}` Smudge Stick… Sfumino… Smudge Stick Sfumino diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 628d1ef3de..7d725b5352 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5818,6 +5818,7 @@ lighting Light ライト @msg no such color 指定されたカラーが見つかりません @msg {_1} cannot be used on Windows {_1} は Windows では使用できません @msg more than one action is named “{_1}” 「{_1}」という名前のアクションが複数あります +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set アクション「{_1}」がフォルダーの環境設定 {_2} を設定しています。セットを保存するにはそのステップを削除してください @msg no symbol named `{n}` `{n}` という名前のシンボルはありません Smudge Stick… スマッジスティック… Smudge Stick スマッジスティック diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index aa2c27c296..ea3cf5a668 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5880,6 +5880,7 @@ imageTrace Outline კონტური @msg no such color ასეთი ფერი არ არის @msg {_1} cannot be used on Windows {_1} ვერ გამოიყენება Windows-ში @msg more than one action is named “{_1}” „{_1}“ ერთზე მეტი მოქმედების სახელია +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set მოქმედება „{_1}“ აყენებს საქაღალდის პარამეტრს {_2}; ნაკრების შესანახად წაშალეთ ეს ნაბიჯი @msg no symbol named `{n}` სიმბოლო სახელით `{n}` არ არის @msg no saved selection recalled შენახული მონიშვნა გამოძახებული არ არის @msg The last start of VectorCraft ended on {name} before its window appeared, so it started on {summary}. To try that graphics processor again, change Preferences › Performance › Graphics Processor or start with WGPU_BACKEND set VectorCraft-ის ბოლო გაშვება ფანჯრის გამოჩენამდე შეწყდა (გრაფიკული პროცესორი: {name}), ამიტომ VectorCraft გაეშვა: {summary}. ამ გრაფიკული პროცესორის ხელახლა საცდელად შეცვალეთ პარამეტრები › წარმადობა › გრაფიკული პროცესორი ან გაუშვით WGPU_BACKEND ცვლადის მითითებით diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index ca5c2ac8d1..98be548ced 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5735,6 +5735,7 @@ lighting Light 광원 @msg no such color 해당 색상이 없습니다 @msg {_1} cannot be used on Windows {_1}은(는) Windows에서 사용할 수 없습니다 @msg more than one action is named “{_1}” “{_1}”(이)라는 이름의 액션이 둘 이상 있습니다 +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set 액션 “{_1}”이(가) 폴더 환경 설정 {_2}을(를) 설정합니다. 세트를 저장하려면 해당 단계를 삭제하세요 @msg no symbol named `{n}` 이름이 `{n}`인 심볼이 없습니다 Smudge Stick… 문지르기 막대… Smudge Stick 문지르기 막대 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index d46d7e8e87..441dca9c77 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -6300,6 +6300,7 @@ swatch Bright Violet Felviolet @msg no such color kleur bestaat niet @msg {_1} cannot be used on Windows {_1} kan niet worden gebruikt in Windows @msg more than one action is named “{_1}” meer dan één actie heet „{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set de actie „{_1}” stelt de mapvoorkeur {_2} in; verwijder die stap om de set op te slaan @msg no symbol named `{n}` geen symbool met de naam `{n}` Remove Color Profile… Kleurprofiel verwijderen… Color wheel Kleurenwiel diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 38d5e2f497..9a689ba5e2 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -6008,6 +6008,7 @@ mark-style Japanese Japońskie @msg no such color brak takiego koloru @msg {_1} cannot be used on Windows Skrótu {_1} nie można używać w systemie Windows @msg more than one action is named “{_1}” więcej niż jedna operacja nazywa się „{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set operacja „{_1}” ustawia preferencję folderu {_2}; usuń ten krok, aby zapisać zestaw @msg no symbol named `{n}` brak symbolu o nazwie `{n}` Smudge Stick… Rozmazywacz… Smudge Stick Rozmazywacz diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 2a45bc8e68..ce2a694be7 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -6068,6 +6068,7 @@ mark-style Japanese Japonesas @msg no such color cor inexistente @msg {_1} cannot be used on Windows {_1} não pode ser usado no Windows @msg more than one action is named “{_1}” mais de uma ação se chama “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set a ação “{_1}” define a preferência de pasta {_2}; exclua essa etapa para salvar o conjunto @msg no symbol named `{n}` nenhum símbolo chamado `{n}` Smudge Stick… Esfuminho… Smudge Stick Esfuminho diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index af93b86974..f2646a0bb2 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5759,6 +5759,7 @@ lighting Light Свет @msg no such color такого цвета нет @msg {_1} cannot be used on Windows Сочетание {_1} нельзя использовать в Windows @msg more than one action is named “{_1}” несколько действий называются «{_1}» +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set действие «{_1}» задаёт настройку папки {_2}; удалите этот шаг, чтобы сохранить набор @msg no symbol named `{n}` нет символа с именем `{n}` Smudge Stick… Растушёвка… Smudge Stick Растушёвка diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index f26438a201..08c988a3d4 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -6107,6 +6107,7 @@ theme Appearance Utseende @msg no such color färgen finns inte @msg {_1} cannot be used on Windows {_1} kan inte användas i Windows @msg more than one action is named “{_1}” fler än en åtgärd heter ”{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set åtgärden ”{_1}” anger mappinställningen {_2}; ta bort det steget för att spara uppsättningen @msg no symbol named `{n}` ingen symbol med namnet `{n}` Angular Vinkelmått Away from the Line Bort från linjen diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index a91aab6f0e..58d8cf1546 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5804,6 +5804,7 @@ theme Appearance Görünüm @msg no such color öyle bir renk yok @msg {_1} cannot be used on Windows {_1} Windows'ta kullanılamaz @msg more than one action is named “{_1}” birden fazla eylemin adı “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set “{_1}” eylemi {_2} klasör tercihini ayarlıyor; seti kaydetmek için o adımı silin @msg no symbol named `{n}` `{n}` adında bir sembol yok Artboard Options: edit with the Artboard tool Çalışma Yüzeyi Seçenekleri: Çalışma Yüzeyi aracıyla düzenleme @msg this is a PostScript file (an .ai saved in an older format or without PDF compatibility, or an EPS file), which can't be opened yet: save it as PDF, PDF-compatible .ai or SVG bu henüz açılamayan bir PostScript dosyasıdır (eski bir formatta kaydedilmiş veya PDF uyumluluğu olmayan bir .ai veya bir EPS dosyası): PDF, PDF uyumlu .ai veya SVG olarak kaydedin diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 17ddeb2086..37c56e1881 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5754,6 +5754,7 @@ lighting Light Світло @msg no such color такого кольору немає @msg {_1} cannot be used on Windows Сполучення {_1} не можна використовувати у Windows @msg more than one action is named “{_1}” кілька дій мають назву «{_1}» +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set дія «{_1}» задає параметр папки {_2}; видаліть цей крок, щоб зберегти набір @msg no symbol named `{n}` немає символу з назвою `{n}` Smudge Stick… Розтушовування… Smudge Stick Розтушовування diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 7d3d89a82d..4c3fff5f31 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5755,6 +5755,7 @@ theme Appearance Giao diện @msg no such color không có màu như vậy @msg {_1} cannot be used on Windows không thể dùng {_1} trên Windows @msg more than one action is named “{_1}” có nhiều hành động tên là “{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set hành động “{_1}” đặt tùy chọn thư mục {_2}; hãy xóa bước đó để lưu bộ @msg no symbol named `{n}` không có biểu tượng nào tên `{n}` Smudge Stick… Bút chì xóa mờ… Smudge Stick Bút chì xóa mờ diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 34ffeb5d6b..cf912fb369 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -5987,6 +5987,7 @@ lighting Light 光源 @msg no such color 没有此颜色 @msg {_1} cannot be used on Windows {_1} 在 Windows 上不能使用 @msg more than one action is named “{_1}” 有多个动作名为“{_1}” +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set 动作“{_1}”设置了文件夹首选项 {_2};请删除该步骤后再存储动作组 @msg no symbol named `{n}` 没有名为 `{n}` 的符号 Smudge Stick… 涂抹棒… Smudge Stick 涂抹棒 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 309e00346c..2fc05b8c7c 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5818,6 +5818,7 @@ lighting Light 光源 @msg no such color 沒有這個顏色 @msg {_1} cannot be used on Windows {_1} 無法在 Windows 上使用 @msg more than one action is named “{_1}” 有多個動作名為「{_1}」 +@msg the action “{_1}” sets the folder preference {_2}; delete that step to save the set 動作「{_1}」設定了資料夾偏好設定 {_2};請刪除該步驟後再儲存動作組 @msg no symbol named `{n}` 沒有名為 `{n}` 的符號 Smudge Stick… 塗抹棒… Smudge Stick 塗抹棒 diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 37cdcb2d56..b29918c878 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -1538,7 +1538,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ crate::panels::actions::SAVE_SET, "Save Actions…", "", - "{set?: index, id?, path?} write the Actions panel's set at set (0 is the top set; default: the current set) with its actions to an action set file (.vcactions) at path, else a picked file named after the set (the web downloads it). id holds the panel's own set id, which its menu passes: the set with this id is saved wherever it is now, and set is not read. A set over 8 MiB as a file, with more than 1,000 actions or with an action of more than 10,000 steps is not written; the panel menu's Save Actions… → {path}", + "{set?: index, id?, path?} write the Actions panel's set at set (0 is the top set; default: the current set) with its actions to an action set file (.vcactions) at path, else a picked file named after the set (the web downloads it). id holds the panel's own set id, which its menu passes: the set with this id is saved wherever it is now, and set is not read. A set over 8 MiB as a file, with more than 1,000 actions or with an action of more than 10,000 steps is not written, nor is a set with a prefs.set step (also in a batch step) that sets a folder preference to a folder (an error names the action and the preference; an empty folder and the scratch disk choices Startup and None save); the panel menu's Save Actions… → {path}", ), ( crate::panels::actions::LOAD_SET, diff --git a/crates/ui-egui/src/panels/actions.rs b/crates/ui-egui/src/panels/actions.rs index 1971834a07..630d81e95f 100644 --- a/crates/ui-egui/src/panels/actions.rs +++ b/crates/ui-egui/src/panels/actions.rs @@ -3512,6 +3512,48 @@ mod tests { } } + /// `ui.actionSet.save` refuses a set with a step that sets a folder preference to a folder, as + /// `{key, value}`, in `values` or in a batch step's commands at any depth, with an error that names + /// the action and the preference, and writes no file. An empty folder, a scratch disk choice and + /// other preferences save. + #[test] + fn save_actions_refuses_a_step_that_sets_a_folder_preference() { + let mut app = app_with_sets(); + let files = with_files(&mut app, "/sets/x.vcactions"); + let step = |command: &str, params: Value| (command.to_string(), params); + let nested = json!({"commands": [{"command": "command.batch", "params": {"commands": [ + {"command": "prefs.set", "params": {"values": {"fontsFolder": "fonts"}}}, + ]}}]}); + for (steps, key) in [ + (vec![step("prefs.set", json!({"key": "scratchPrimary", "value": "scratch"}))], "scratchPrimary"), + (vec![step("prefs.set", json!({"values": {"uiAccent": "green", "templatesFolder": "/Users/me/Templates"}}))], "templatesFolder"), + ( + vec![ + step("object.move", json!({"dx": 1, "dy": 0})), + step( + "command.batch", + json!({"commands": [{"command": "prefs.set", "params": {"key": "scratchSecondary", "value": " /Volumes/Fast "}}]}), + ), + ], + "scratchSecondary", + ), + (vec![step("command.batch", nested)], "fontsFolder"), + ] { + app.ui.action_sets[1] = ActionSet::new("Other", vec![Action::new("Scratch", steps)]); + let e = app.run(SAVE_SET, json!({"set": 1, "path": "/sets/x.vcactions"})).unwrap_err(); + assert_eq!(e, format!("the action “Scratch” sets the folder preference {key}; delete that step to save the set")); + assert!(files.borrow().is_empty(), "no file is written for {key}"); + } + let kept = vec![ + step("prefs.set", json!({"values": {"scratchPrimary": "None", "scratchSecondary": " Startup ", "fontsFolder": "", "uiAccent": "green"}})), + step("prefs.set", json!({"key": "pluginsFolder", "value": ""})), + step("x.y", json!({"key": "fontsFolder", "value": "fonts"})), + ]; + app.ui.action_sets[1] = ActionSet::new("Other", vec![Action::new("Scratch", kept)]); + assert_eq!(app.run(SAVE_SET, json!({"set": 1, "path": "/sets/x.vcactions"})).unwrap(), json!({"path": "/sets/x.vcactions"})); + assert!(files.borrow().contains_key("/sets/x.vcactions")); + } + /// A file that is not an action set file, or is too large, is refused with a message, and no /// set is added. #[test] diff --git a/crates/ui-egui/src/panels/actions/files.rs b/crates/ui-egui/src/panels/actions/files.rs index a8ae79e713..b5805f9b2a 100644 --- a/crates/ui-egui/src/panels/actions/files.rs +++ b/crates/ui-egui/src/panels/actions/files.rs @@ -169,6 +169,30 @@ fn ui_steps_in<'a>(command: &str, params: &'a Value) -> Vec<&'a Value> { .collect() } +/// A folder preference whose value in a step running `command` with `params` names a folder +/// ([`vectorcraft_engine::cmd::prefscmds::names_a_folder`]): in a `prefs.set` step, given as +/// `{key, value}` or in `values`, and in a `command.batch` step, in any of its commands at any depth. +/// Every such value is found, also one under `values` that `{key, value}` replaces when the step runs, +/// because [`write_set`] writes the params as they are. +fn folder_pref_in(command: &str, params: &Value) -> Option { + use vectorcraft_engine::cmd::prefscmds::names_a_folder; + match command { + "prefs.set" => { + let names = |key: &str, value: &Value| value.as_str().is_some_and(|v| names_a_folder(key, v)); + let pair = params.get("key").and_then(Value::as_str).filter(|key| params.get("value").is_some_and(|v| names(key, v))); + let values = params.get("values").and_then(Value::as_object).into_iter().flatten(); + pair.map(str::to_string).or_else(|| values.filter(|(key, v)| names(key, v)).map(|(key, _)| key.clone()).next()) + } + "command.batch" => { + let mut steps = params.get("commands").and_then(Value::as_array).into_iter().flatten(); + steps.find_map(|step| { + folder_pref_in(step.get("command").and_then(Value::as_str).unwrap_or_default(), step.get("params").unwrap_or(&Value::Null)) + }) + } + _ => None, + } +} + /// `set` as the text of an action set file. pub fn write_set(set: &ActionSet) -> String { let actions = set @@ -281,7 +305,9 @@ pub(super) fn set_file_stem(name: &str) -> String { /// the set (the web downloads it) → `{path}`. `id` is the panel's own set id ([`ActionSet::id`]), /// which the panel menu passes: the set with this id is saved wherever it is when the file dialog /// answers, and `set` is not read. A set that [`read_set`] would refuse (larger than 8 MiB as a -/// file, more than 1,000 actions, an action with more than 10,000 steps) is not written. +/// file, more than 1,000 actions, an action with more than 10,000 steps) is not written, nor is a set +/// with a step that sets a folder preference to a folder ([`folder_pref_in`]), which names a folder +/// on this computer: the error names the action and the preference. pub fn save_set(app: &mut VectorcraftApp, p: &Value) -> Result { let len = app.ui.action_sets.len(); let set = match (p.get("id"), p.get("set")) { @@ -292,6 +318,11 @@ pub fn save_set(app: &mut VectorcraftApp, p: &Value) -> Result { (_, Some(v)) => v.as_u64().and_then(|i| usize::try_from(i).ok()).filter(|&i| i < len).ok_or("no such action set")?, }; let Some(s) = app.ui.action_sets.get(set) else { return Err("no such action set".into()) }; + let folder = + s.actions.iter().find_map(|a| a.steps.iter().find_map(|(command, params)| folder_pref_in(command, params)).map(|key| (&a.name, key))); + if let Some((action, key)) = folder { + return Err(format!("the action “{}” sets the folder preference {}; delete that step to save the set", action, key)); + } let text = write_set(s); // What Save Actions… writes, Load Actions… reads back. read_set(&text, "")?; diff --git a/crates/ui-egui/src/tests/automation.rs b/crates/ui-egui/src/tests/automation.rs index 6d7c7075c5..2364a00a56 100644 --- a/crates/ui-egui/src/tests/automation.rs +++ b/crates/ui-egui/src/tests/automation.rs @@ -891,6 +891,62 @@ fn ui_inspect_leaves_out_relative_paths_in_recorded_steps() { let _ = std::fs::remove_dir_all(base); } +/// `ui.inspect` leaves out of a recorded `prefs.set` step the value of a folder preference outside the +/// read root, given as `{key, value}` or in `values`, also in a batch step's commands at any depth: a +/// bare name or a subfolder (in the working folder, outside the root), a path with spaces around it and +/// a `file:` URL. A folder inside the root, an empty value, a scratch disk choice, other preferences and +/// another command's params stay, and the UI state is not changed. +#[test] +fn ui_inspect_leaves_out_folder_preferences_in_recorded_prefs_steps() { + let (mut app, tx, base) = confined("inspect-prefs-steps"); + let (inside, outside) = (s(base.join("in")), s(base.join("outside"))); + let url = if outside.starts_with('/') { format!("file://{outside}") } else { format!("file:///{}", outside.replace('\\', "/")) }; + let folders = ["fontsFolder", "pluginsFolder", "scratchPrimary", "scratchSecondary", "recoveryFolder", "templatesFolder"]; + let mut steps: Vec = folders.iter().map(|k| json!(["prefs.set", {"key": k, "value": "scratch"}])).collect(); + steps.push(json!(["prefs.set", {"values": folders.iter().map(|k| (k.to_string(), json!("tmp/scratch"))).collect::>()}])); + steps.push(json!(["prefs.set", {"values": { + "scratchPrimary": format!(" {outside} "), "fontsFolder": url, "pluginsFolder": format!(" {url}"), "recoveryFolder": "Startup", + "templatesFolder": inside, "scratchSecondary": " None ", "uiAccent": "green", + }}])); + steps.push(json!(["prefs.set", {"key": "scratchPrimary", "value": "Startup"}])); + steps.push(json!(["prefs.set", {"key": "recoveryFolder", "value": ""}])); + steps.push(json!(["prefs.set", {"key": "fontsFolder", "value": inside}])); + let batch = |commands: Value| json!({"commands": commands}); + let inner = batch(json!([{"command": "prefs.set", "params": {"values": {"scratchSecondary": "tmp/scratch", "uiAccent": "red"}}}])); + steps.push(json!([ + "command.batch", + batch(json!([ + {"command": "prefs.set", "params": {"key": "scratchPrimary", "value": "scratch"}}, + {"command": "command.batch", "params": inner}, + ])) + ])); + steps.push(json!(["x.y", {"key": "scratchPrimary", "value": "scratch"}])); + let set: crate::panels::actions::ActionSet = + serde_json::from_value(json!({"name": "set", "actions": [{"name": "prefs", "steps": steps}]})).unwrap(); + app.ui.action_sets = vec![set]; + let r = request(&mut app, &tx, "ui.inspect", json!({})); + let shown = &r["result"]["ui"]["action_sets"][0]["actions"][0]["steps"]; + let mut want: Vec = folders.iter().map(|k| json!(["prefs.set", {"key": k}])).collect(); + want.push(json!(["prefs.set", {"values": {}}])); + want.push(json!(["prefs.set", {"values": {"templatesFolder": inside, "scratchSecondary": " None ", "uiAccent": "green"}}])); + want.push(json!(["prefs.set", {"key": "scratchPrimary", "value": "Startup"}])); + want.push(json!(["prefs.set", {"key": "recoveryFolder", "value": ""}])); + want.push(json!(["prefs.set", {"key": "fontsFolder", "value": inside}])); + let inner = batch(json!([{"command": "prefs.set", "params": {"values": {"uiAccent": "red"}}}])); + want.push(json!([ + "command.batch", + batch(json!([ + {"command": "prefs.set", "params": {"key": "scratchPrimary"}}, + {"command": "command.batch", "params": inner}, + ])) + ])); + want.push(json!(["x.y", {"key": "scratchPrimary", "value": "scratch"}])); + assert_eq!(shown, &json!(want), "{r}"); + assert!(!r.to_string().contains("tmp/scratch") && !r.to_string().contains(&outside.replace('\\', "\\\\")), "{r}"); + assert_eq!(app.ui.action_sets[0].actions[0].steps[0].1, json!({"key": "fontsFolder", "value": "scratch"}), "the UI state is not changed"); + let _ = std::fs::remove_dir_all(base); +} + /// Two libraries loaded from outside the read root under the same file name, only the second of them /// in a library panel: `ui.inspect` and `swatch.library.list` show the second under the same id, and /// that id finds it. diff --git a/docs/mcp.md b/docs/mcp.md index 2b20cfc588..752e501e50 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -148,12 +148,14 @@ the suite) keep the agent to the folders you give it: in a recorded action's steps in `ui.action_sets`, also in a batch step's commands, the `path`, `paths`, `file`, `files` and `folder` params, a param whose name ends in `Path`, `Paths`, `File`, `Files` or `Folder` (`diffPath`, `destinationFolder`), the entries of a list under such a param, and under any other param, as a key or in any - list, an absolute path or a `file:` URL outside the roots (any case of `file:`, `%XX` escapes decoded; a URL that + list, an absolute path or a `file:` URL outside the roots, also with whitespace around it (any case of `file:`, `%XX` escapes decoded; a URL that names another host is always left out), and `~`, a path under `~` or a path that starts with `./` or `../` (either separator) that resolves outside the roots from the app's working folder; and the folder - preferences in `ui.engine_prefs` and in an open Preferences dialog's `ui.dialog.fields` - (`fontsFolder`, `pluginsFolder`, `scratchPrimary`, `scratchSecondary`, `recoveryFolder`, `templatesFolder`; an - empty folder and the scratch disk choices `Startup` and `None` stay). A library loaded from a file outside the + preferences in `ui.engine_prefs`, in an open Preferences dialog's `ui.dialog.fields` and in a recorded + `prefs.set` step (its `value` for such a `key`, and the entries of `values`), also in a batch step's commands + (`fontsFolder`, `pluginsFolder`, `scratchPrimary`, `scratchSecondary`, `recoveryFolder`, `templatesFolder`, checked + as written and trimmed, a bare name or a subfolder from the app's working folder; an empty folder and the scratch + disk choices `Startup` and `None` stay). A library loaded from a file outside the roots shows as `loaded/` wherever `ui_inspect` and `ui.docking` name it: its id, its panel's id, the Color Guide's `ui.color_guide_limit`, a closed panel's key in `ui.docking_hidden` and the `library` and `panel` params of a recorded action's steps in `ui.action_sets`, and the keys of shortcut overrides (`panel:` @@ -3152,7 +3154,11 @@ already walks Image Trace. Short agent entry points: after the set (the web downloads it), and returns `{path}`. `id` holds the panel's own set id, which the panel menu passes: the set with this id is saved wherever it is when the file dialog answers, and `set` is not read. A set that would be larger than 8 MiB as a file, has more than 1,000 actions or has an action of more than 10,000 steps is not - written, and the command returns the same error as a load of such a file. `ui.actionSet.load {path? | data?}` reads + written, and the command returns the same error as a load of such a file. Nor is a set with a `prefs.set` step, also + in a batch step's commands, that sets a folder preference (`fontsFolder`, `pluginsFolder`, `scratchPrimary`, + `scratchSecondary`, `recoveryFolder`, `templatesFolder`) to a folder, which names a folder on this computer: the + error `the action “” sets the folder preference ; delete that step to save the set` names the first such + action and preference. An empty folder and the scratch disk choices `Startup` and `None` save. `ui.actionSet.load {path? | data?}` reads the file at `path`, else a picked one, or the file's text `data`, adds its set after the other sets and returns `{set, name, actions}`. A set named like one already in the panel gets the next free number (`Print 2`), and the other sets and the current set stay as they are. An action set file (`.vcactions`) is JSON: @@ -6118,12 +6124,13 @@ Preferences. `prefs.reset` resets the preferences at once, without the question. `prefs.export {path?}` writes the preferences as a JSON file, `{"format": "vectorcraft-preferences", "version": 1, "prefs": {…}}`, and replies `{path, count}`; without `path` it replies `{data, count}` with the file's text. The file holds every preference `prefs.list` lists and the `eyedropper`, `perspectiveWidget`, `pathfinder`, `glyphSnap` and -`pixelSnap` groups. It leaves out the folder preferences (`fontsFolder`, `pluginsFolder`, `recoveryFolder`, `templatesFolder`) and -saved presets. An export over 1 MB, the most `prefs.import` reads, is an error and writes nothing. +`pixelSnap` groups. It leaves out the folder preferences (`fontsFolder`, `pluginsFolder`, `scratchPrimary`, `scratchSecondary`, +`recoveryFolder`, `templatesFolder`) and saved presets. An export over 1 MB, the most `prefs.import` reads, is an error and writes nothing. `prefs.import {path | data}` applies such a file and replies `{imported}`, the number of preferences it set. It checks every value first, as `prefs.set` does: a file over 1 MB, malformed JSON, another `format`, a newer `version`, an empty `prefs`, an unknown preference, a value of the wrong type or out of range, or a folder preference -is an error, and nothing changes. Preferences the file leaves out keep their values. An import leaves the open +is an error, and nothing changes. For `scratchPrimary` and `scratchSecondary` only a folder is an error: an empty +value and the choices `Startup` and `None` are read. Preferences the file leaves out keep their values. An import leaves the open document's units as they are, records no undo step, and is saved with the other preferences. An open Preferences dialog shows the imported values. `prefs.import` is refused as a `command.batch` step. The dialog's Export Preferences… and Import Preferences… buttons run the two commands with a save or open dialog; the web build downloads the exported file and has no Import Preferences… button. diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index f7782bdfcd..2c0b7cab1e 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -747,6 +747,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Batch…: Override Save and Override Export name each file with the extension of the format the step writes, however the step spells the format (`.svg` and `SVG` give `.svg`, `png8` gives `.png`), in `action.batch`. - Batch…: Override Export writes the files of an Export for Screens or Export Assets step into a folder for each source file or data set inside the destination folder, under the names the export gives them (`action.batch`). The folder and file names drop trailing periods and spaces (`a..svg` gives `a`). When two source files would get the same name that way (compared without case), or a later file's folder or file would take the name of an output an earlier file wrote, of a source file in the destination folder or, for data sets, of a file already there, Batch adds `-2`, `-3`… to the later one's name. - Actions panel menu: Save Actions… writes the current action set with all its actions to a file, and Load Actions… adds the set from such a file to the panel ([Manage a set of actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/manage-a-set-of-actions.html); `ui.actionSet.save`, `ui.actionSet.load`). The file is VectorCraft's own action set file (`.vcactions`); a loaded set named like one in the panel gets the next free number. + - Actions panel menu: Save Actions… does not write a set with a step that sets a folder preference (the Fonts, Plug-ins, Templates and Recovery folders and the two scratch disks) to a folder, and names the action and the preference instead; an empty folder and the scratch disk choices Startup and None save. Export Preferences leaves out both scratch disks, and `ui.inspect` under automation read roots leaves such a folder outside them out of recorded steps. These are VectorCraft's own rules (`ui.actionSet.save`, `prefs.export`). - Actions panel menu: Load Actions… also reads action sets saved as `.aia` files ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html); `actionSet.read`, `ui.actionSet.load`). VectorCraft's own rules: the set comes in with all its actions. A step with a label and no recorded values comes in as the one menu command with the step's label, when that command is directly in a top menu, is what its menu bar item runs with no values and opens no dialog. A step that is off in the file stays excluded from playback. The other steps, recorded menu items included, are left out, and the status bar says how many. - Actions panel: the panel menu's Action Options… renames the selected action and keeps its steps ([Actions panel overview](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/actions-panel-overview.html): the menu item opens a dialog that renames the action or gives it a function key and color; `ui.action.options`). It also sets the action's function key and color. - Actions panel: Create New Action (bottom bar) and the panel menu's New Action… ask for the new action's name, set, function key and color, and Record adds the action and starts recording into it ([Create new actions](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/automate-actions/record-actions.html): the New Action dialog takes the action's name and the set it goes in; `ui.action.new`, `ui.action.play`). The function key (F1 to F15, with Ctrl or Command and Shift) plays the action. An action's function key plays the action even when a command has the same shortcut, and the command keeps it. VectorCraft's own rules, which the page does not cover: when several actions have the same key the first in panel order plays; a copied action has no key; New Action does not open while an action is recording. The color is kept with the action for Button Mode, which VectorCraft does not have yet. From dd3438cb7f192e1b741ad82f537d53091f75ad86 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 12:09:16 -0800 Subject: [PATCH 04/33] Live Paint Bucket: Cursor Swatch Preview and arrow keys pick swatches Live Paint Bucket Options gets a Cursor Swatch Preview checkbox (on at first, saved with the other options). With the Bucket selected, Left and Right set the active fill, or the stroke when the Stroke proxy is in front, to the color swatch before or after it in the Swatches panel, wrapping around at the ends, with the preview on or off. The selected art keeps its paint. The preview shows the active paint above the pointer between the swatches before and after it; a color that is no swatch shows alone until Right picks the first swatch or Left the last, and a paint that is not a solid color shows no chip. The chips hide during a fill drag and while Alt samples with the eyedropper. The fill and the stroke each remember the swatch picked for them, so swatches of the same color are each reached in turn. The Bucket and the Shape Builder now take the arrows for swatches only without Shift, Alt or Cmd/Ctrl, judged by the modifiers held when the key went down, so modified arrows keep their shortcuts (Tool::claims_key_with, Session::tool_claims_key_with). Docs: docs/mcp.md, docs/target-app-parity.md, tool.options params text. --- crates/engine/src/tests/build.rs | 197 ++++++++++++++++++ crates/engine/src/tooling.rs | 6 + crates/mcp/src/ui_catalog.rs | 2 +- crates/tools/src/builder.rs | 62 ++++-- crates/tools/src/builder/live_paint.rs | 75 ++++++- crates/tools/src/builder/tests.rs | 16 +- crates/tools/src/lib.rs | 5 + crates/tools/src/settings.rs | 2 +- crates/ui-egui/src/canvas/tests.rs | 8 + .../ui-egui/src/dialogs/live_paint_bucket.rs | 20 +- crates/ui-egui/src/menus/ui_commands.rs | 2 +- crates/ui-egui/src/shortcuts.rs | 19 +- crates/ui-egui/src/shortcuts/tests.rs | 31 +++ docs/mcp.md | 15 +- docs/target-app-parity.md | 1 + 15 files changed, 418 insertions(+), 43 deletions(-) diff --git a/crates/engine/src/tests/build.rs b/crates/engine/src/tests/build.rs index 6eeace1073..30956a6e4e 100644 --- a/crates/engine/src/tests/build.rs +++ b/crates/engine/src/tests/build.rs @@ -1748,6 +1748,203 @@ fn shape_builder_arrow_keys_pass_swatches_of_the_same_color() { assert_eq!(s.paint.fill, after, "the swatch after Twin B"); } +/// The colors of the cursor swatch preview's chips, left to right, and which one is ringed. +fn swatch_chips(s: &mut Session, v: ViewInfo) -> Vec<([u8; 4], bool)> { + let mut chips: Vec<(f64, [u8; 4], bool)> = s + .overlays(v) + .into_iter() + .filter_map(|o| match o { + vectorcraft_tools::Overlay::Swatch { p, color, selected } => Some((p.x, color, selected)), + _ => None, + }) + .collect(); + chips.sort_by(|a, b| a.0.total_cmp(&b.0)); + chips.into_iter().map(|(_, c, sel)| (c, sel)).collect() +} + +/// A solid paint's color as the chips draw it. +fn chip_color(p: &Paint) -> [u8; 4] { + match p { + Paint::Solid { color, .. } => color.to_rgba8(1.0), + _ => panic!("not a solid color: {p:?}"), + } +} + +/// Live Paint Bucket: Left and Right step the active paint through the document's color swatches +/// (the selected art keeps its paint), the Cursor Swatch Preview (on at first) shows the active +/// paint ringed between the swatches before and after it, and with it off the arrows still step. +#[test] +fn live_paint_bucket_arrow_keys_step_the_active_paint_through_the_swatches() { + let mut s = session(); + let (a, _) = two(&mut s); + let v = ViewInfo::default(); + s.select_tool("livePaintBucket", v).unwrap(); + assert_eq!(s.tool_options_of("livePaintBucket")["cursorSwatchPreview"], json!(true)); + assert!(s.tool_claims_key(ToolKey::Right, v) && s.tool_claims_key(ToolKey::Left, v)); + assert!(!s.tool_claims_key(ToolKey::Up, v), "Up and Down keep their own use"); + let list: Vec<(String, Paint)> = + vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc).iter().map(|sw| (sw.name.clone(), sw.paint.clone())).collect(); + assert!(list.len() >= 4); + let n = list.len(); + let stroke = s.paint.stroke.clone(); + s.execute("paint.setFill", &json!({"swatch": list[0].0, "ids": []})).unwrap(); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[2].1); + s.tool_key(ToolKey::Left, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[1].1); + assert_eq!(s.paint.stroke, stroke, "the stroke stays"); + assert_eq!(node(&s, a).appearance.fill_paint(), Paint::solid(Color::rgb(1.0, 0.0, 0.0)), "A keeps its fill"); + s.pointer(&PointerEvent::new(PointerKind::Move, 300.0, 300.0), v).unwrap(); + assert_eq!(swatch_chips(&mut s, v), vec![(chip_color(&list[0].1), false), (chip_color(&list[1].1), true), (chip_color(&list[2].1), false)]); + let alt = Mods { alt: true, ..Default::default() }; + s.pointer(&PointerEvent::new(PointerKind::Move, 300.0, 300.0).with_mods(alt), v).unwrap(); + assert!(swatch_chips(&mut s, v).is_empty(), "Alt samples with the eyedropper and shows no chips"); + s.pointer(&PointerEvent::new(PointerKind::Move, 300.0, 300.0), v).unwrap(); + // Past the first swatch, Left wraps around to the last. + s.tool_key(ToolKey::Left, Mods::default(), v).unwrap(); + s.tool_key(ToolKey::Left, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[n - 1].1); + assert_eq!(swatch_chips(&mut s, v).iter().map(|c| c.0).collect::>(), [&list[n - 2].1, &list[n - 1].1, &list[0].1].map(chip_color)); + // With the Stroke proxy in front, the arrows step the stroke and the chips show it. + s.execute("paint.toggleActive", &json!({"fill": false})).unwrap(); + let fill = s.paint.fill.clone(); + s.execute("paint.setStroke", &json!({"swatch": list[1].0, "ids": []})).unwrap(); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!((&s.paint.fill, &s.paint.stroke), (&fill, &list[2].1)); + assert!(!s.fill_active, "the Stroke proxy stays in front"); + assert_eq!(swatch_chips(&mut s, v)[1], (chip_color(&list[2].1), true)); + assert_eq!(node(&s, a).appearance.fill_paint(), Paint::solid(Color::rgb(1.0, 0.0, 0.0)), "A keeps its paint"); + // Cursor Swatch Preview off hides the chips; the arrows still step. + s.set_tool_option_cmd(&json!({"tool": "livePaintBucket", "values": {"cursorSwatchPreview": false}})).unwrap(); + assert!(swatch_chips(&mut s, v).is_empty()); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.stroke, list[3].1); +} + +/// The Live Paint Bucket and the Shape Builder take Left and Right for swatches only without +/// modifiers: Shift, Alt and Cmd/Ctrl arrows are left to their shortcuts and change no paint. +#[test] +fn swatch_arrow_keys_leave_modified_arrows_to_the_shortcuts() { + let mut s = session(); + two(&mut s); + let v = ViewInfo::default(); + let first = vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc)[0].name.clone(); + s.execute("paint.setFill", &json!({"swatch": first, "ids": []})).unwrap(); + for tool in ["livePaintBucket", "shapeBuilder"] { + s.select_tool(tool, v).unwrap(); + if tool == "shapeBuilder" { + s.set_tool_option_cmd(&json!({"tool": tool, "values": {"pickColorFrom": "swatches"}})).unwrap(); + } + let fill = s.paint.fill.clone(); + assert!(s.tool_claims_key_with(ToolKey::Right, Mods::default(), v), "{tool}"); + for m in [Mods { shift: true, ..Default::default() }, Mods { alt: true, ..Default::default() }, Mods { cmd: true, ..Default::default() }] { + for key in [ToolKey::Left, ToolKey::Right] { + assert!(!s.tool_claims_key_with(key, m, v), "{tool} {key:?} {m:?}"); + s.tool_key(key, m, v).unwrap(); + assert_eq!(s.paint.fill, fill, "{tool} {key:?} {m:?}"); + } + } + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_ne!(s.paint.fill, fill, "{tool}: a plain Right steps"); + } +} + +/// Two swatches of the same color: the Bucket's arrows step through both, and the fill and the +/// stroke each step on from the swatch picked for them. +#[test] +fn live_paint_bucket_arrow_keys_pass_swatches_of_the_same_color_for_fill_and_stroke() { + let mut s = session(); + two(&mut s); + for name in ["Twin A", "Twin B"] { + s.execute("swatch.new", &json!({"name": name, "color": "#123456"})).unwrap(); + } + let v = ViewInfo::default(); + s.select_tool("livePaintBucket", v).unwrap(); + let names: Vec = vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc).iter().map(|sw| sw.name.clone()).collect(); + let a = names.iter().position(|n| n == "Twin A").unwrap(); + assert_eq!(names[a + 1], "Twin B"); + let after = vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc)[(a + 2) % names.len()].paint.clone(); + // The fill goes to Twin B. + s.execute("paint.setFill", &json!({"swatch": "Twin A", "ids": []})).unwrap(); + let twin = s.paint.fill.clone(); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, twin, "Twin B"); + // The stroke starts at Twin A: Right goes to Twin B, then past it. + s.execute("paint.setStroke", &json!({"swatch": "Twin A", "ids": []})).unwrap(); + assert!(!s.fill_active); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.stroke, twin, "Twin B for the stroke"); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.stroke, after, "the swatch after Twin B"); + // Back on the fill, which is still Twin B: Right goes past it. + s.execute("paint.toggleActive", &json!({"fill": true})).unwrap(); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, after, "the fill steps on from Twin B"); +} + +/// A color that is no swatch shows alone and Right picks the first swatch, Left the last; a paint +/// that is not a solid color shows no chip; one swatch steps to itself; with no color swatch the +/// tool leaves the arrows alone; during a fill drag the arrows change nothing. +#[test] +fn live_paint_bucket_arrow_keys_handle_unmatched_paint_few_swatches_and_drags() { + let mut s = session(); + live(&mut s); + let v = ViewInfo::default(); + s.select_tool("livePaintBucket", v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Move, 300.0, 300.0), v).unwrap(); + let list: Vec = vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc).iter().map(|sw| sw.paint.clone()).collect(); + let odd = Paint::solid(Color::rgb8(0x12, 0x34, 0x57)); + assert!(!list.contains(&odd)); + s.paint.fill = odd.clone(); + assert_eq!(swatch_chips(&mut s, v), vec![(chip_color(&odd), true)]); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[0]); + s.paint.fill = odd.clone(); + s.tool_key(ToolKey::Left, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[list.len() - 1]); + s.paint.fill = Paint::None; + assert!(swatch_chips(&mut s, v).is_empty()); + + // During a fill drag the arrows are not taken and change nothing. + s.paint.fill = list[0].clone(); + s.pointer(&PointerEvent::new(PointerKind::Down, 25.0, 50.0), v).unwrap(); + assert!(s.tool_busy()); + assert!(!s.tool_claims_key(ToolKey::Right, v)); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, list[0]); + assert!(swatch_chips(&mut s, v).is_empty(), "no chips during a drag"); + s.pointer(&PointerEvent::new(PointerKind::Up, 25.0, 50.0), v).unwrap(); + + // One color swatch: both arrows pick it, and it stands on both sides of itself. + let one = list[0].clone(); + s.edit("t", |d, _| { + d.swatch_groups.clear(); + d.swatches.retain(|sw| sw.paint == one); + d.swatches.truncate(1); + Ok(()) + }) + .unwrap(); + assert_eq!(vectorcraft_tools::builder::color_swatches(&s.doc().unwrap().doc).len(), 1); + s.paint.fill = odd; + s.tool_key(ToolKey::Left, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, one); + s.pointer(&PointerEvent::new(PointerKind::Move, 301.0, 300.0), v).unwrap(); + assert_eq!(swatch_chips(&mut s, v), vec![(chip_color(&one), false), (chip_color(&one), true), (chip_color(&one), false)]); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, one); + + // No color swatch: the arrows are left to the canvas and the paint stays. + s.edit("t", |d, _| { + d.swatches.clear(); + Ok(()) + }) + .unwrap(); + assert!(!s.tool_claims_key(ToolKey::Right, v) && !s.tool_claims_key(ToolKey::Left, v)); + s.tool_key(ToolKey::Right, Mods::default(), v).unwrap(); + assert_eq!(s.paint.fill, one); +} + #[test] fn shape_builder_tool_alt_deletes_line_pieces() { let mut s = session(); diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index ae0c55ac76..c974a119e2 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -1164,6 +1164,12 @@ impl Session { self.plane_key(key).is_some() || self.with_tool_cx(view, |t, cx| t.claims_key(cx, key)) } + /// Does the active tool take `key`, pressed with `mods` held, ahead of the shortcuts bound to it + /// (see `Tool::claims_key_with`)? + pub fn tool_claims_key_with(&mut self, key: ToolKey, mods: Mods, view: ViewInfo) -> bool { + self.plane_key(key).is_some() || self.with_tool_cx(view, |t, cx| t.claims_key_with(cx, key, mods)) + } + /// Has the active tool started something that Escape ends while no button is pressed (see /// `Tool::ends_on_escape`)? pub fn tool_ends_on_escape(&mut self, view: ViewInfo) -> bool { diff --git a/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index dcde7c5076..db63f1c96b 100644 --- a/crates/mcp/src/ui_catalog.rs +++ b/crates/mcp/src/ui_catalog.rs @@ -925,7 +925,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "tool.options", "Tool Options…", "", - "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x, y and preview (on initially), previews one there and accepts it as one Undo step; Cancel rolls back the preview), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), typeOnPath|verticalTypeOnPath → Type on a Path Options for the selected type on a path (dialog `command` running type.pathOptions; error `nothing selected` without a selection, and the command's error when no type on a path is selected),symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (px for pencil and paintbrush, the same at any zoom; pt for smooth and blobBrush), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), livePreview (pencil, on: off draws a thin line while the stroke is drawn, and the path's fill and stroke on release), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), livePaintBucket → Live Paint Bucket Options (dialog `livePaintBucketOptions`, fields paintFills (on) and paintStrokes (off), highlight (on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the face highlight's width, 1–20 px; 4; an edge's highlight is half as wide); OK runs tool.setOption {tool: \"livePaintBucket\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values}; a highlightColor with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), objectsOnPath → Objects on Path Tool Options (dialog `objectsOnPathOptions`, fields pivot (0–8, row by row from the top left; 4, the center) and rotate (°, −360–360; 0); Reset puts 4 and 0 back; Save runs tool.setOption {tool: \"objectsOnPath\", values}: the objects attached next use them, groups attached before keep theirs)", + "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x, y and preview (on initially), previews one there and accepts it as one Undo step; Cancel rolls back the preview), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), typeOnPath|verticalTypeOnPath → Type on a Path Options for the selected type on a path (dialog `command` running type.pathOptions; error `nothing selected` without a selection, and the command's error when no type on a path is selected),symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (px for pencil and paintbrush, the same at any zoom; pt for smooth and blobBrush), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), livePreview (pencil, on: off draws a thin line while the stroke is drawn, and the path's fill and stroke on release), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), livePaintBucket → Live Paint Bucket Options (dialog `livePaintBucketOptions`, fields paintFills (on) and paintStrokes (off), cursorSwatchPreview (on: the active fill or stroke shows above the pointer between the color swatches before and after it; Left and Right without modifiers step it through the color swatches whether it is on or off), highlight (on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the face highlight's width, 1–20 px; 4; an edge's highlight is half as wide); OK runs tool.setOption {tool: \"livePaintBucket\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values}; a highlightColor with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), objectsOnPath → Objects on Path Tool Options (dialog `objectsOnPathOptions`, fields pivot (0–8, row by row from the top left; 4, the center) and rotate (°, −360–360; 0); Reset puts 4 and 0 back; Save runs tool.setOption {tool: \"objectsOnPath\", values}: the objects attached next use them, groups attached before keep theirs)", ), ( "ui.colorGuideOptions", diff --git a/crates/tools/src/builder.rs b/crates/tools/src/builder.rs index 1c5a895161..8b9157bc25 100644 --- a/crates/tools/src/builder.rs +++ b/crates/tools/src/builder.rs @@ -143,6 +143,38 @@ fn step_swatch(list: &[&Swatch], fill: &Paint, last: Option, forward: boo } } +/// Is no Shift, Alt, Cmd or Ctrl held? The arrows step swatches only then. +fn plain(mods: Mods) -> bool { + !(mods.shift || mods.alt || mods.cmd || mods.ctrl) +} + +/// The color swatch the Right (`forward`) or Left arrow key picks for the current `paint`, with its +/// index in [`color_swatches`] (see [`step_swatch`]); None when the document has no color swatch. +fn pick_swatch(doc: &Document, paint: &Paint, last: Option, forward: bool) -> Option<(usize, String)> { + let list = color_swatches(doc); + step_swatch(&list, paint, last, forward).and_then(|i| Some((i, list.get(i)?.name.clone()))) +} + +/// The cursor swatch preview for the current `paint` with the pointer at `at`: the swatch before +/// it, `paint` itself (ringed, while it is a solid color) and the swatch after it, side by side above +/// the pointer. With `matched_only`, the swatches beside it show only while `paint` is one of the +/// color swatches; otherwise the last and the first swatch stand beside a color that is none of them. +fn swatch_chips(cx: &ToolContext, at: Point, paint: &Paint, last: Option, matched_only: bool) -> Vec { + let list = color_swatches(cx.doc); + let rgba = |p: &Paint| match p { + Paint::Solid { color, .. } => Some(color.to_rgba8(1.0)), + _ => None, + }; + let beside = !matched_only || swatch_index(&list, paint, last).is_some(); + let chip = |forward| beside.then(|| step_swatch(&list, paint, last, forward)).flatten().and_then(|i| rgba(&list.get(i)?.paint)); + let (before, after) = (chip(false), chip(true)); + let y = at.y - cx.tol(22.0); + [(before, -16.0, false), (rgba(paint), 0.0, true), (after, 16.0, false)] + .into_iter() + .filter_map(|(color, dx, selected)| Some(Overlay::Swatch { p: Point::new(at.x + cx.tol(dx), y), color: color?, selected })) + .collect() +} + /// Shape Builder tool: drag across regions to merge them. Holding Alt (Option) switches to erase /// mode while it is held: the pointer shows a minus, what a click or drag would delete highlights in /// red, and the gesture deletes the regions it touches, the pieces of open paths and the edges @@ -425,20 +457,7 @@ impl ShapeBuilderTool { /// The cursor swatch preview: the swatch before the current fill, the current fill (ringed) /// and the swatch after it, side by side above the pointer. fn swatch_chips(&self, cx: &ToolContext) -> Vec { - let Some(at) = self.at else { return vec![] }; - let list = color_swatches(cx.doc); - let fill = &cx.paint.fill; - let rgba = |p: &Paint| match p { - Paint::Solid { color, .. } => Some(color.to_rgba8(1.0)), - _ => None, - }; - let chip = |forward| step_swatch(&list, fill, self.picked, forward).and_then(|i| rgba(&list.get(i)?.paint)); - let (before, after) = (chip(false), chip(true)); - let y = at.y - cx.tol(22.0); - [(before, -16.0, false), (rgba(fill), 0.0, true), (after, 16.0, false)] - .into_iter() - .filter_map(|(color, dx, selected)| Some(Overlay::Swatch { p: Point::new(at.x + cx.tol(dx), y), color: color?, selected })) - .collect() + self.at.map(|at| swatch_chips(cx, at, &cx.paint.fill, self.picked, false)).unwrap_or_default() } } @@ -712,9 +731,15 @@ impl Tool for ShapeBuilderTool { (self.opts.swatches && !self.dragging && matches!(key, ToolKey::Left | ToolKey::Right)) || (self.dragging && key == ToolKey::Escape) } - fn key(&mut self, cx: &ToolContext, key: ToolKey, _mods: Mods) -> Vec { + /// The arrows pick swatches only without modifiers; Shift, Alt and Cmd/Ctrl arrows keep their + /// shortcuts. + fn claims_key_with(&self, cx: &ToolContext, key: ToolKey, mods: Mods) -> bool { + self.claims_key(cx, key) && (key == ToolKey::Escape || plain(mods)) + } + + fn key(&mut self, cx: &ToolContext, key: ToolKey, mods: Mods) -> Vec { // While a merge is being computed, the arrows leave the current fill it takes as it was. - if !self.claims_key(cx, key) || self.editing() { + if !self.claims_key_with(cx, key, mods) || self.editing() { return vec![]; } if key == ToolKey::Escape { @@ -726,13 +751,12 @@ impl Tool for ShapeBuilderTool { (self.marquee, self.hover, self.far) = (None, None, false); return vec![]; } - let list = color_swatches(cx.doc); - let Some((i, s)) = step_swatch(&list, &cx.paint.fill, self.picked, key == ToolKey::Right).and_then(|i| Some((i, list.get(i)?))) else { + let Some((i, name)) = pick_swatch(cx.doc, &cx.paint.fill, self.picked, key == ToolKey::Right) else { return vec![]; }; self.picked = Some(i); // The current fill only (no `ids`): the selected art keeps its own. - vec![Action::Exec("paint.setFill".into(), json!({ "swatch": s.name, "ids": [] }))] + vec![Action::Exec("paint.setFill".into(), json!({ "swatch": name, "ids": [] }))] } fn options(&self) -> Value { diff --git a/crates/tools/src/builder/live_paint.rs b/crates/tools/src/builder/live_paint.rs index e2d6c9f77e..885d82526a 100644 --- a/crates/tools/src/builder/live_paint.rs +++ b/crates/tools/src/builder/live_paint.rs @@ -3,7 +3,7 @@ use super::lookup::{is_live_paint, live_paint_at, live_paint_at_where, visible_e use super::map::{HIGHLIGHT_RED, sorted_roots}; use super::{ Action, Arc, BezPath, CLICK_TOL_PX, Cursor, Document, EDGE_NAME, FACE_NAME, FillRule, Mods, Node, NodeId, Overlay, Point, PointerEvent, - PointerKind, Rect, Tool, ToolContext, ToolKey, Value, json, json_ids, path_contains, + PointerKind, Rect, Tool, ToolContext, ToolKey, Value, color_swatches, json, json_ids, path_contains, pick_swatch, plain, swatch_chips, }; // ---------- Live Paint Bucket ---------- @@ -171,6 +171,14 @@ enum LpPaint { /// click paints nothing. With Highlight off, the face or edge under the pointer is not /// highlighted. The cursor is a filled bucket where a click fills a face, a brush where it paints /// an edge and an empty bucket where it paints nothing; with Alt it is the Eyedropper. +/// +/// The Left and Right arrow keys without Shift, Alt or Cmd/Ctrl set the active paint (the fill, +/// or the stroke when the Stroke proxy is in front) to the color swatch before or after it in the +/// Swatches panel, wrapping around; from a color that is no swatch, Right picks the first swatch +/// and Left the last. The selected art keeps its paint. With `cursorSwatchPreview` (on), the +/// active paint shows as a chip above the pointer between the swatches before and after it, +/// except during a fill drag or with Alt held; a solid color that is no swatch shows alone, and a +/// paint that is not a solid color shows no chip. pub struct LivePaintBucketTool { pub(super) cache: RegionCache, pub(super) hover: LpHover, @@ -183,6 +191,11 @@ pub struct LivePaintBucketTool { highlight: bool, color: [u8; 3], width: f64, + /// Cursor Swatch Preview. + preview: bool, + /// The index in [`color_swatches`] of the swatch the arrow keys picked last for the fill and for + /// the stroke. + picked: [Option; 2], } /// Maximum points in one Live Paint fill path chunk. @@ -200,11 +213,24 @@ impl Default for LivePaintBucketTool { highlight: true, color: HIGHLIGHT_RED, width: LP_BUCKET_WIDTH, + preview: true, + picked: [None; 2], } } } impl LivePaintBucketTool { + /// The active paint the arrow keys step and the preview shows: the fill, or the stroke when the + /// Stroke proxy is in front. + fn active_paint<'a>(cx: &'a ToolContext) -> &'a vectorcraft_color::Paint { + if cx.fill_active { &cx.paint.fill } else { &cx.paint.stroke } + } + + /// The slot of [`Self::picked`] for the active paint. + fn picked_slot(cx: &ToolContext) -> usize { + usize::from(!cx.fill_active) + } + fn begin_fill(&mut self, params: Value, p: Point) -> Vec { self.fill_drag = Some((params.clone(), vec![vec![p]])); vec![Action::Begin("Live Paint Bucket".into()), Action::Preview("livePaint.fill".into(), params)] @@ -387,24 +413,57 @@ impl Tool for LivePaintBucketTool { } } - fn key(&mut self, _cx: &ToolContext, key: ToolKey, _mods: Mods) -> Vec { - if key == ToolKey::Escape && self.fill_drag.take().is_some() { vec![Action::Cancel] } else { vec![] } + /// Escape during a fill drag cancels it. Left and Right pick the color swatch before or after + /// the active paint, while no drag is on. + fn key(&mut self, cx: &ToolContext, key: ToolKey, mods: Mods) -> Vec { + if key == ToolKey::Escape { + return if self.fill_drag.take().is_some() { vec![Action::Cancel] } else { vec![] }; + } + if !self.claims_key_with(cx, key, mods) { + return vec![]; + } + let slot = Self::picked_slot(cx); + let Some((i, name)) = pick_swatch(cx.doc, Self::active_paint(cx), self.picked[slot], key == ToolKey::Right) else { + return vec![]; + }; + self.picked[slot] = Some(i); + // The active paint only (no `ids`): the selected art keeps its own. + let cmd = if cx.fill_active { "paint.setFill" } else { "paint.setStroke" }; + vec![Action::Exec(cmd.into(), json!({ "swatch": name, "ids": [] }))] } fn busy(&self) -> bool { self.fill_drag.is_some() } - fn claims_key(&self, _cx: &ToolContext, key: ToolKey) -> bool { - key == ToolKey::Escape && self.busy() + fn claims_key(&self, cx: &ToolContext, key: ToolKey) -> bool { + match key { + ToolKey::Escape => self.busy(), + ToolKey::Left | ToolKey::Right => !self.busy() && !color_swatches(cx.doc).is_empty(), + _ => false, + } + } + + /// The arrows pick swatches only without modifiers; Shift, Alt and Cmd/Ctrl arrows keep their + /// shortcuts. + fn claims_key_with(&self, cx: &ToolContext, key: ToolKey, mods: Mods) -> bool { + self.claims_key(cx, key) && (key == ToolKey::Escape || plain(mods)) } fn deactivate(&mut self, _cx: &ToolContext) -> Vec { if self.fill_drag.take().is_some() { vec![Action::Commit] } else { vec![] } } - fn overlays(&self, _cx: &ToolContext) -> Vec { - lp_overlays(&self.hover, &self.cache, self.color, self.width as f32, (self.width * LP_BUCKET_EDGE_FRACTION) as f32) + fn overlays(&self, cx: &ToolContext) -> Vec { + let mut out = lp_overlays(&self.hover, &self.cache, self.color, self.width as f32, (self.width * LP_BUCKET_EDGE_FRACTION) as f32); + // No chips during a fill drag, or while Alt makes the tool sample with the eyedropper. + if self.preview + && !self.busy() + && let Some((at, _, false)) = self.at + { + out.extend(swatch_chips(cx, at, Self::active_paint(cx), self.picked[Self::picked_slot(cx)], true)); + } + out } fn cursor(&self, cx: &ToolContext, p: Point, m: Mods) -> Cursor { @@ -416,6 +475,7 @@ impl Tool for LivePaintBucketTool { json!({ "paintFills": self.fills, "paintStrokes": self.strokes, + "cursorSwatchPreview": self.preview, "highlight": self.highlight, "color": format!("#{r:02x}{g:02x}{b:02x}"), "width": self.width, @@ -426,6 +486,7 @@ impl Tool for LivePaintBucketTool { match key { "paintFills" => self.fills = v.as_bool().unwrap_or(self.fills), "paintStrokes" => self.strokes = v.as_bool().unwrap_or(self.strokes), + "cursorSwatchPreview" => self.preview = v.as_bool().unwrap_or(self.preview), "highlight" => self.highlight = v.as_bool().unwrap_or(self.highlight), "color" => self.color = lp_color(v).unwrap_or(self.color), "width" => { diff --git a/crates/tools/src/builder/tests.rs b/crates/tools/src/builder/tests.rs index 298e6a1907..14976f03c9 100644 --- a/crates/tools/src/builder/tests.rs +++ b/crates/tools/src/builder/tests.rs @@ -1113,7 +1113,12 @@ fn bucket_options_choose_what_a_click_paints() { }; let (face, edge) = (Some(Rect::new(50.0, 0.0, 100.0, 50.0)), Some(Rect::new(50.0, 0.0, 50.0, 50.0))); let mut t = LivePaintBucketTool::default(); - assert_eq!(t.options(), json!({"paintFills": true, "paintStrokes": false, "highlight": true, "color": "#ff2a2a", "width": 4.0})); + assert_eq!( + t.options(), + json!({"paintFills": true, "paintStrokes": false, "cursorSwatchPreview": true, "highlight": true, "color": "#ff2a2a", "width": 4.0}) + ); + // The highlight alone, without the swatch chips. + t.set_option("cursorSwatchPreview", &json!(false)); // Paint Fills alone: 1 pt from the edge, a click fills the face and Shift-click strokes the edge. assert_eq!((click(&mut t, 51.0, Mods::default()), hovered(&mut t, 51.0, Mods::default())), (fill(51.0), face)); assert_eq!((click(&mut t, 51.0, shift), hovered(&mut t, 51.0, shift)), (stroke(51.0), edge)); @@ -1231,6 +1236,7 @@ fn bucket_fills_a_region_of_a_selection_that_mixes_a_group_and_paths() { s.set([g]); let live = cx(&d, &s, &p); let mut t = LivePaintBucketTool::default(); + t.set_option("cursorSwatchPreview", &json!(false)); t.pointer(&live, &ev(PointerKind::Move, pos.x, pos.y)); t.poll(&live, true); assert!(t.overlays(&live).is_empty()); @@ -1245,6 +1251,7 @@ fn bucket_strokes_alone_paints_and_highlights_no_region_of_the_selection() { let p = paint(); let cx = cx(&d, &s, &p); let mut t = LivePaintBucketTool::default(); + t.set_option("cursorSwatchPreview", &json!(false)); t.pointer(&cx, &ev(PointerKind::Move, 75.0, 50.0)); t.poll(&cx, true); assert!(!t.overlays(&cx).is_empty(), "Paint Fills highlights the region"); @@ -1263,6 +1270,7 @@ fn bucket_options_set_the_highlight() { let p = paint(); let cx = cx(&d, &s, &p); let mut t = LivePaintBucketTool::default(); + t.set_option("cursorSwatchPreview", &json!(false)); let highlight = |t: &mut LivePaintBucketTool| { t.pointer(&cx, &ev(PointerKind::Move, 75.0, 25.0)); match t.overlays(&cx).as_slice() { @@ -1299,6 +1307,7 @@ fn bucket_highlights_an_edge_thinner_than_a_face() { let p = paint(); let cx = cx(&d, &s, &p); let mut t = LivePaintBucketTool::default(); + t.set_option("cursorSwatchPreview", &json!(false)); let width = |t: &mut LivePaintBucketTool, x: f64, shift: bool| { t.pointer(&cx, &ev(PointerKind::Move, x, 25.0).with_mods(Mods { shift, ..Default::default() })); match t.overlays(&cx).as_slice() { @@ -1517,7 +1526,7 @@ fn bucket_hover_builds_a_large_map_on_another_thread() { poll_until_ready(&mut t, &cx); assert!(matches!(t.hover, LpHover::Region(_)), "{:?}", t.hover); assert_eq!((t.cache.builds, t.cache.background), (1, 1)); - assert_eq!(t.overlays(&cx).len(), 1); + assert_eq!(t.overlays(&cx).iter().filter(|o| matches!(o, Overlay::Path { .. })).count(), 1); } #[test] @@ -1883,6 +1892,7 @@ fn live_paint_edges_outside_the_faces_come_from_the_nearest_visible_group() { }; assert_eq!(lp_click(&mut sel, &cx, ev(PointerKind::Down, x, y).with_mods(alt)), vec![picked]); let mut bucket = LivePaintBucketTool::default(); + bucket.set_option("cursorSwatchPreview", &json!(false)); bucket.pointer(&cx, &ev(PointerKind::Move, x, y).with_mods(shift)); assert_eq!(edge_x(&bucket.overlays(&cx)), want.map(|w| w.2)); let stroked = want.map(|(g, ..)| Action::Exec("livePaint.strokeEdge".into(), json!({ "group": g.0, "point": [x, y], "tolerance": 4.0 }))); @@ -1926,6 +1936,7 @@ fn live_paint_tools_skip_hidden_edges() { }; assert_eq!(lp_click(&mut sel, &cx, ev(PointerKind::Down, x, 50.0).with_mods(alt)), vec![picked], "x {x}"); let mut bucket = LivePaintBucketTool::default(); + bucket.set_option("cursorSwatchPreview", &json!(false)); bucket.pointer(&cx, &ev(PointerKind::Move, x, 50.0).with_mods(shift)); assert_eq!(edge_x(&bucket.overlays(&cx)), want.map(|w| w.2), "x {x}"); let stroked = want.map(|(g, ..)| Action::Exec("livePaint.strokeEdge".into(), json!({ "group": g.0, "point": [x, 50.0], "tolerance": 4.0 }))); @@ -1953,6 +1964,7 @@ fn live_paint_tools_skip_hidden_faces() { }; // Where the faces overlap, both tools take the back group's face under the hidden one. let mut bucket = LivePaintBucketTool::default(); + bucket.set_option("cursorSwatchPreview", &json!(false)); bucket.pointer(&cx, &ev(PointerKind::Move, 75.0, 50.0)); assert_eq!(part(&bucket.overlays(&cx)), Some(Rect::new(0.0, 0.0, 100.0, 100.0))); let fill = Action::Preview("livePaint.fill".into(), json!({ "group": back.0, "point": [75.0, 50.0] })); diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 17e6f038e9..33f2b7bd3c 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -182,6 +182,11 @@ pub trait Tool: Send { fn claims_key(&self, _cx: &ToolContext, _key: ToolKey) -> bool { false } + /// [`Self::claims_key`] for a press with `mods` held. By default the modifiers do not matter; + /// a tool that takes a key only without modifiers leaves the modified presses to the shortcuts. + fn claims_key_with(&self, cx: &ToolContext, key: ToolKey, _mods: Mods) -> bool { + self.claims_key(cx, key) + } /// Has the tool started something that Escape ends while no button is pressed (a path being /// drawn, the Blend tool's first pick)? That Escape ends nothing else, such as isolation mode or /// a rotated view. diff --git a/crates/tools/src/settings.rs b/crates/tools/src/settings.rs index e7b9f9a5c4..a4d1b1a64f 100644 --- a/crates/tools/src/settings.rs +++ b/crates/tools/src/settings.rs @@ -46,7 +46,7 @@ const ROWS: &[Row] = &[ // Live Paint Selection Tool Options (`builder::LivePaintSelectionTool`). (&["livePaintSelection"], None, &["selectFills", "selectStrokes", "color", "width"]), // Live Paint Bucket Options (`builder::LivePaintBucketTool`). - (&["livePaintBucket"], None, &["paintFills", "paintStrokes", "highlight", "color", "width"]), + (&["livePaintBucket"], None, &["paintFills", "paintStrokes", "cursorSwatchPreview", "highlight", "color", "width"]), (&["polygon"], None, &["sides"]), (&["star"], None, &["points"]), (&["freeTransform"], None, &["constrain"]), diff --git a/crates/ui-egui/src/canvas/tests.rs b/crates/ui-egui/src/canvas/tests.rs index d6df378c87..c4d324ad2b 100644 --- a/crates/ui-egui/src/canvas/tests.rs +++ b/crates/ui-egui/src/canvas/tests.rs @@ -680,9 +680,17 @@ fn bucket_modifier_only_hover_updates_the_visible_cursor() { frame(app, &ctx, events); app.canvas.hover_cursor.map(|(_, _, cursor)| cursor) }; + // The cursor swatch preview's chips go and come back with Alt too. + let chips = |app: &mut VectorcraftApp| { + let view = app.view_info(); + app.session.overlays(view).iter().filter(|o| matches!(o, vectorcraft_tools::Overlay::Swatch { .. })).count() + }; assert_eq!(shown(&mut app, vec![Event::PointerMoved(at)]), Some(Cursor::LivePaintBucket)); + assert!(chips(&mut app) > 0); assert_eq!(shown(&mut app, vec![Event::ModifiersChanged(Modifiers::ALT)]), Some(Cursor::Eyedropper)); + assert_eq!(chips(&mut app), 0); assert_eq!(shown(&mut app, vec![Event::ModifiersChanged(Modifiers::NONE)]), Some(Cursor::LivePaintBucket)); + assert!(chips(&mut app) > 0); } /// The Image Trace panel's eye-button hold ends when the pointer is released, even when the diff --git a/crates/ui-egui/src/dialogs/live_paint_bucket.rs b/crates/ui-egui/src/dialogs/live_paint_bucket.rs index c040cdcc31..f515c3d904 100644 --- a/crates/ui-egui/src/dialogs/live_paint_bucket.rs +++ b/crates/ui-egui/src/dialogs/live_paint_bucket.rs @@ -4,7 +4,8 @@ //! `tool.setOption {tool: "livePaintBucket", values}`. //! //! Fields: `paintFills` and `paintStrokes` (the faces and the edges of Live Paint groups), -//! `highlight` (whether the face or edge under the pointer is outlined), `color` (the highlight +//! `cursorSwatchPreview` (whether the active paint and the color swatches beside it show above the +//! pointer), `highlight` (whether the face or edge under the pointer is outlined), `color` (the highlight //! color: `#rrggbb`, or a layer color's name such as "Red") and `width` (the width of a face's //! highlight in screen pixels; an edge's highlight is half as wide). @@ -25,7 +26,7 @@ pub const KIND: &str = "livePaintBucketOptions"; const TOOL: &str = "livePaintBucket"; /// The options the dialog edits. -const KEYS: [&str; 5] = ["paintFills", "paintStrokes", "highlight", "color", "width"]; +const KEYS: [&str; 6] = ["paintFills", "paintStrokes", "cursorSwatchPreview", "highlight", "color", "width"]; /// Width of the label column. const LABEL_W: f32 = 60.0; @@ -48,6 +49,7 @@ pub fn open(app: &mut VectorcraftApp) -> Result { fn body(_app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { form::check(ui, d, "paintFills", tl!("Paint Fills")); form::check(ui, d, "paintStrokes", tl!("Paint Strokes")); + form::check(ui, d, "cursorSwatchPreview", tl!("Cursor Swatch Preview")); ui.add_space(6.0); form::check(ui, d, "highlight", tl!("Highlight")); let color = resolve(&d.str("color")).unwrap_or(Color::BLACK); @@ -120,17 +122,18 @@ mod tests { crate::toolbar::open_options(&mut app, TOOL).unwrap(); let text = painted(&mut app); - for label in ["Live Paint Bucket Options", "Paint Fills", "Paint Strokes", "Highlight", "Color:", "Width:"] { + for label in ["Live Paint Bucket Options", "Paint Fills", "Paint Strokes", "Cursor Swatch Preview", "Highlight", "Color:", "Width:"] { assert!(text.contains(label), "{label} in {text}"); } let d = app.ui.dialog.as_mut().unwrap(); assert_eq!(d.kind, KIND); assert_eq!( - (d.bool("paintFills"), d.bool("paintStrokes"), d.bool("highlight"), d.str("color"), d.f64("width", 0.0)), - (true, false, true, "#ff2a2a".to_string(), 4.0) + (d.bool("paintFills"), d.bool("paintStrokes"), d.bool("cursorSwatchPreview"), d.bool("highlight"), d.str("color"), d.f64("width", 0.0)), + (true, false, true, true, "#ff2a2a".to_string(), 4.0) ); d.fields.insert("paintFills".into(), json!(false)); d.fields.insert("paintStrokes".into(), json!(true)); + d.fields.insert("cursorSwatchPreview".into(), json!(false)); d.fields.insert("color".into(), json!("Green")); d.fields.insert("width".into(), json!(8)); super::super::confirm(&mut app).unwrap(); @@ -141,10 +144,13 @@ mod tests { // They last while another tool is chosen, and the dialog opens on them again. app.select_tool("selection"); let o = app.session.tool_options_of(TOOL); - assert_eq!((o["paintFills"].as_bool(), o["paintStrokes"].as_bool(), o["color"].as_str()), (Some(false), Some(true), Some("#4fff4f"))); + assert_eq!( + (o["paintFills"].as_bool(), o["paintStrokes"].as_bool(), o["cursorSwatchPreview"].as_bool(), o["color"].as_str()), + (Some(false), Some(true), Some(false), Some("#4fff4f")) + ); app.run("tool.options", json!({"tool": TOOL})).unwrap(); let d = app.ui.dialog.as_mut().unwrap(); - assert_eq!((d.bool("paintStrokes"), d.str("color")), (true, "#4fff4f".to_string())); + assert_eq!((d.bool("paintStrokes"), d.bool("cursorSwatchPreview"), d.str("color")), (true, false, "#4fff4f".to_string())); // Highlight off: the tool outlines nothing under the pointer. d.fields.insert("highlight".into(), json!(false)); super::super::confirm(&mut app).unwrap(); diff --git a/crates/ui-egui/src/menus/ui_commands.rs b/crates/ui-egui/src/menus/ui_commands.rs index 925d7adf3b..a565bcaaf2 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -926,7 +926,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "tool.options", "Tool Options…", "", - "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x, y and preview (on initially), previews one there and accepts it as one Undo step; Cancel rolls back the preview), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), typeOnPath|verticalTypeOnPath → Type on a Path Options for the selected type on a path (dialog `command` running type.pathOptions; error `nothing selected` without a selection, and the command's error when no type on a path is selected),symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (px for pencil and paintbrush, the same at any zoom; pt for smooth and blobBrush), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), livePreview (pencil, on: off draws a thin line while the stroke is drawn, and the path's fill and stroke on release), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), livePaintBucket → Live Paint Bucket Options (dialog `livePaintBucketOptions`, fields paintFills (on) and paintStrokes (off), highlight (on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the face highlight's width, 1–20 px; 4; an edge's highlight is half as wide); OK runs tool.setOption {tool: \"livePaintBucket\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values}; a highlightColor with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), objectsOnPath → Objects on Path Tool Options (dialog `objectsOnPathOptions`, fields pivot (0–8, row by row from the top left; 4, the center) and rotate (°, −360–360; 0); Reset puts 4 and 0 back; Save runs tool.setOption {tool: \"objectsOnPath\", values}: the objects attached next use them, groups attached before keep theirs)", + "{tool: id} what double-clicking a tool button opens: hand → fits the artboard in the window (view.fitArtboard), zoom → 100% (view.actualSize), rotate|scale|reflect|shear → that Object › Transform dialog (dialog `rotate`, `scale`, `reflect` or `shear`; error `nothing selected` without a selection), selection|directSelection|groupSelection → the Move dialog (dialog `move`; the same error), gradient|magicWand → the Gradient or Magic Wand panel (window.panel), artboard → Artboard Options of the active artboard (dialog `artboardOptions`, fields index, name, x, y, width, height, showCenterMark, showCrossHairs; OK runs artboard.setProps, and with other artboards selected with the Artboard tool a new width or height goes to each of them too, in its `artboards` list, each resized about the reference point the form used for that size, or about its top-left corner when the size was set in the fields directly), flare → Flare Tool Options (dialog `flareOptions`, fields diameter and pathLength (pt), opacity, brightness, growth, fuzziness, longest, rayFuzziness, largest (%), rays, rings, direction (°), raysOn, ringsOn; OK runs tool.setOption {tool: \"flare\", values}: the next flare dragged out uses them, and a Flare click's dialog, which also has x, y and preview (on initially), previews one there and accepts it as one Undo step; Cancel rolls back the preview), lineSegment|arc|rectangularGrid|polarGrid → that tool's Tool Options, its click dialog without x and y (dialog kind = the tool id, `__toolOptions: true`; lineSegment: length (pt), angle (°), fillLine; arc: lengthX, lengthY (pt), reference (0–8), arcType: open|closed, baseAlong: x|y, slope (−100…100), fillArc; rectangularGrid: width, height (pt, the Default Size), reference (0–8), rows, columns, rowSkew, columnSkew (%), frame, fill; polarGrid: the same with concentric, radial, concentricSkew, radialSkew and compound for rows, columns, the skews and frame; OK runs tool.setOption {tool, values} and draws nothing: the next drag uses them (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and a click's dialog opens at them), columnGraph|stackedColumnGraph|barGraph|stackedBarGraph|lineGraph|areaGraph|scatterGraph|pieGraph|radarGraph → Graph Type for the selected graph (dialog `command` running graph.setType; an error without one), typeOnPath|verticalTypeOnPath → Type on a Path Options for the selected type on a path (dialog `command` running type.pathOptions; error `nothing selected` without a selection, and the command's error when no type on a path is selected),symbolSprayer|symbolShifter|symbolScruncher|symbolSizer|symbolSpinner|symbolStainer|symbolScreener|symbolStyler → Symbolism Tools Options (dialog `symbolismOptions`, fields tool, diameter (pt), intensity and density (1–10), shared by the eight tools; OK runs tool.setOption {tool, values}), blend → Blend Options, perspectiveGrid → Perspective Grid Options, eyedropper → Eyedropper Options (dialog `eyedropperOptions`, fields sampleSize, pickUp, apply; OK runs eyedropper.setOptions), printTiling → resets the print tiling (print.tiling.set {reset: true}), warp|twirl|pucker|bloat|scallop|crystallize|wrinkle → that tool's Tool Options (dialog `liquifyOptions`, fields tool, width, height, angle, intensity %, usePressure, detail, simplify, simplifyOn, rate, complexity, horizontal %, vertical %, affectAnchors, affectIn, affectOut, showBrush; OK runs tool.setOption {tool, values}), pencil|paintbrush|smooth|blobBrush|eraser → that tool's Tool Options (dialog `freehandOptions`, fields tool and the options the tool keeps: fidelity (px for pencil and paintbrush, the same at any zoom; pt for smooth and blobBrush), fill, keepSelected (on for pencil and paintbrush, off for blobBrush), mergeOnlySelection (off), closeWithin and editWithin (px, 0 is off), livePreview (pencil, on: off draws a thin line while the stroke is drawn, and the path's fill and stroke on release), size (pt), angle (°, −180–180; 0) and roundness (%, 1–100; 100) of the eraser's tip; OK runs tool.setOption {tool, values}), livePaintSelection → Live Paint Selection Tool Options (dialog `livePaintSelectionOptions`, fields selectFills and selectStrokes (both on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the highlight's width, 1–20 px; 4); OK runs tool.setOption {tool: \"livePaintSelection\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), livePaintBucket → Live Paint Bucket Options (dialog `livePaintBucketOptions`, fields paintFills (on) and paintStrokes (off), cursorSwatchPreview (on: the active fill or stroke shows above the pointer between the color swatches before and after it; Left and Right without modifiers step it through the color swatches whether it is on or off), highlight (on), color (the highlight color, #rrggbb or a layer color's name; #ff2a2a), width (the face highlight's width, 1–20 px; 4; an edge's highlight is half as wide); OK runs tool.setOption {tool: \"livePaintBucket\", values}; a color with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), shapeBuilder → Shape Builder Tool Options (dialog `shapeBuilderOptions`, fields gapDetection (off), gapLength (pt, 0–72; 3), openFilledAsClosed (on), clickStrokeSplitsPath (off), pickColorFrom: artwork|swatches (artwork), cursorSwatchPreview (off), selection: freeform|straightLine (freeform), highlightFill (on), highlightStroke (on), highlightColor (#rrggbb or a layer color's name; #ff4f4f); OK runs tool.setOption {tool: \"shapeBuilder\", values}; a highlightColor with an alpha part (#rrggbbaa or #rgba) is an error and OK changes nothing), objectsOnPath → Objects on Path Tool Options (dialog `objectsOnPathOptions`, fields pivot (0–8, row by row from the top left; 4, the center) and rotate (°, −360–360; 0); Reset puts 4 and 0 back; Save runs tool.setOption {tool: \"objectsOnPath\", values}: the objects attached next use them, groups attached before keep theirs)", ), ( "ui.colorGuideOptions", diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 0b5794b615..1dd2ef1d96 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -878,8 +878,23 @@ fn claimed_tool_keys(app: &mut VectorcraftApp, ctx: &egui::Context, view: ViewIn // The Smart Pen Tool's Tab while it draws a path (the key otherwise hides the panels). (Key::Tab, ToolKey::Tab), ] { - if !rows_take(k) && ctx.input(|i| i.key_pressed(k)) && app.session.tool_claims_key(tk, view) && ctx.input_mut(|i| i.consume_key(m, k)) { - let r = app.session.tool_key(tk, crate::canvas::mods(m, false), view); + // The modifiers held when the key went down, which can differ from the frame's at its end. + let pm = ctx.input(|i| { + i.events + .iter() + .rev() + .find_map(|e| match e { + egui::Event::Key { key, pressed: true, modifiers, .. } if *key == k => Some(*modifiers), + _ => None, + }) + .unwrap_or(m) + }); + if !rows_take(k) + && ctx.input(|i| i.key_pressed(k)) + && app.session.tool_claims_key_with(tk, crate::canvas::mods(pm, false), view) + && ctx.input_mut(|i| i.consume_key(pm, k)) + { + let r = app.session.tool_key(tk, crate::canvas::mods(pm, false), view); // Keyboard actions can change the Artboard tool's active board too. Keep the // navigator aligned with its valid survivor, as pointer dispatch does. if r.is_ok() diff --git a/crates/ui-egui/src/shortcuts/tests.rs b/crates/ui-egui/src/shortcuts/tests.rs index 60b8c5eacd..ae1f966696 100644 --- a/crates/ui-egui/src/shortcuts/tests.rs +++ b/crates/ui-egui/src/shortcuts/tests.rs @@ -2245,6 +2245,37 @@ fn alt_arrows_step_selected_type_object() { assert_ne!(selected[0], vectorcraft_doc::NodeId(rect), "a non-type object is still copied by Alt+arrow"); } +/// With the Live Paint Bucket, a plain Right picks the next color swatch, while Alt+Right keeps +/// copying and nudging the selection, and a Shift+Right released within the same frame is still a +/// Shift+Right that picks no swatch. +#[test] +fn live_paint_bucket_takes_only_plain_arrows_for_swatches() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap(); + let rect = app.session.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()["id"].as_u64().unwrap(); + app.session.execute("select.set", &json!({"ids": [rect]})).unwrap(); + let first = vectorcraft_tools::builder::color_swatches(&app.session.active().unwrap().doc)[0].name.clone(); + app.session.execute("paint.setFill", &json!({"swatch": first, "ids": []})).unwrap(); + app.select_tool("livePaintBucket"); + let fill = app.session.paint.fill.clone(); + + frame(&mut app, press(Key::ArrowRight, Modifiers::ALT)); + assert_eq!(app.session.paint.fill, fill, "Alt+Right picks no swatch"); + let selected = &app.session.active().unwrap().selection.objects; + assert_ne!(selected.as_slice(), [vectorcraft_doc::NodeId(rect)], "Alt+Right copies and nudges the selection"); + + let shift_then_released = vec![ + egui::Event::ModifiersChanged(Modifiers::SHIFT), + egui::Event::Key { key: Key::ArrowRight, physical_key: Some(Key::ArrowRight), pressed: true, repeat: false, modifiers: Modifiers::SHIFT }, + egui::Event::ModifiersChanged(Modifiers::NONE), + ]; + frame(&mut app, shift_then_released); + assert_eq!(app.session.paint.fill, fill, "Shift+Right picks no swatch"); + + frame(&mut app, press(Key::ArrowRight, Modifiers::NONE)); + assert_ne!(app.session.paint.fill, fill, "a plain Right picks the next swatch"); +} + /// #793: with a layout without Latin letters the one-key shortcuts go by the key's position: /// the P key typing Persian `ح` chooses the Pen, the V key typing Russian `м` the Selection /// tool. A Latin layout keeps the letter typed: AZERTY's A (the Q key's place) is A. diff --git a/docs/mcp.md b/docs/mcp.md index cb5c40501c..4f843939a8 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3437,7 +3437,8 @@ already walks Image Trace. Short agent entry points: at the pointer selects under these rules: the arrow with a filled square for a face, with a short line for an edge, and with an x for nothing. It keeps the look it had at the press until the release. The Live Paint Bucket's options (`tool.setOption {tool: "livePaintBucket", values}`; double-clicking the tool opens - them, dialog `livePaintBucketOptions`) are `paintFills` (on), `paintStrokes` (off), `highlight` (on), `color` (the + them, dialog `livePaintBucketOptions`) are `paintFills` (on), `paintStrokes` (off), `cursorSwatchPreview` (on), + `highlight` (on), `color` (the highlight color, `#rrggbb` or a layer color's name; `#ff2a2a`) and `width` (the face highlight's width, 1 to 20 screen pixels; 4; an edge's highlight is half as wide). The dialog's OK gives an error for a `color` with an alpha part (`#rrggbbaa` or `#rgba`) and changes nothing. With `paintFills` alone on, a click runs `livePaint.fill` and Shift-click runs `livePaint.strokeEdge`. @@ -3446,6 +3447,14 @@ already walks Image Trace. Short agent entry points: `livePaint.strokeEdge` when an edge is within 4 screen pixels of the pointer and `livePaint.fill` otherwise, and Shift-click runs `livePaint.fill`. With both off, a click runs nothing. With `highlight` off, the face or edge under the pointer is not outlined. + With the Live Paint Bucket selected and no drag in progress, Left and Right without Shift, Alt or Cmd/Ctrl (which + keep their shortcuts) run `paint.setFill {swatch, ids: []}` (or + `paint.setStroke` when the Stroke proxy is in front) with the color swatch before or after the active paint in the + Swatches panel, wrapping around at the ends; from a color that is no swatch, Right picks the first swatch and Left + the last, and with no color swatch the tool does not take the keys. The selected art keeps its paint. With + `cursorSwatchPreview` on, the active paint shows as a ringed chip above the pointer between the swatches before and + after it, except during a fill drag or with Alt held; a solid color that is no swatch shows alone, and a paint that is not a solid color shows no chip. Left and + Right step the swatches with `cursorSwatchPreview` off too. `livePaint.merge` keeps the bottom Live Paint group's id, name, lock, visibility, transparency, appearance, opacity mask and graphic style link. With a Live Paint group among the objects, `livePaint.make` adds the others to it as `livePaint.merge` does, and so does `livePaint.fill {ids}` when `point` is outside that group's faces. @@ -9703,8 +9712,8 @@ The Shape Builder tool's options are its Shape Builder Tool Options (dialog `sha `openFilledAsClosed` (on: a filled open path bounds a region as if an invisible edge closed it; off: it cuts regions as an unfilled path does), `pickColorFrom` (`artwork`, the default: a merged shape takes the appearance, opacity and blending mode of the front-most selected object under the drag's first point, else of the one under its last point, -else of the front-most selected object; `swatches`: the current fill, and the Left and Right arrow keys step the -current fill through the document's color swatches), `cursorSwatchPreview` (off; with `swatches`, chips above the +else of the front-most selected object; `swatches`: the current fill, and the Left and Right arrow keys without +Shift, Alt or Cmd/Ctrl step the current fill through the document's color swatches), `cursorSwatchPreview` (off; with `swatches`, chips above the pointer show the current fill and the swatches before and after it), `selection` (`freeform`, the default, or `straightLine`: a drag touches what the line from its first point to the pointer crosses), `highlightFill` (on by default; when it is off, merge mode shades no regions), `gapDetection` (off) with `gapLength` (pt, 0 to 72; 3), `clickStrokeSplitsPath` (In Merge Mode, diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e309f72409..e0b61377f0 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1401,6 +1401,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Object › Live Paint › Merge adds the other selected paths and Live Paint groups to the bottom selected Live Paint group ([Live Paint groups](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/live-paint-groups.html)), which keeps its id, the paint of its faces and edges, and its name, lock, visibility, transparency, appearance, opacity mask and graphic style link. Object › Live Paint › Make with a Live Paint group selected, and a Live Paint Bucket click on a selected path beside a selected Live Paint group, add the paths to that group the same way. Make, Merge and the Live Paint Bucket take paths, compound paths and groups of them; with type, an image or another object among the selection they report that it has to be converted to paths first and change nothing, as type and raster images can't go into a Live Paint group directly ([Create Live Paint groups](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/create-live-paint-groups.html)); before, such an object went among the group's hidden sources and vanished from the canvas. Evidence: `tests_build::live_paint_refuses_type_and_images_and_changes_nothing`. With the Live Paint Bucket selected, Alt-click on vector art samples its fill, stroke and stroke weight into the paint defaults ([Paint with the Live Paint Bucket tool](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html)). On a placed image it samples the image pixel as a solid fill with no stroke. Alt+Shift-click samples only the color under the pointer, including an intermediate gradient color or image pixels, into the active fill or stroke ([Default keyboard shortcuts](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)). Neither click paints or changes the selection or selected art, and the next Bucket click paints with the sampled paint. Evidence: `tests_build::live_paint_bucket_alt_sample_paints_next_face_without_changing_selection`. Double-clicking the Live Paint Bucket opens Live Paint Bucket Options ([Paint with the Live Paint Bucket tool](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html)): Paint Fills (on) and Paint Strokes (off) set whether a click paints faces, edges or both, Highlight turns the outline of the face or edge under the pointer on or off, and Color and Width set it (red and 4 px by default). Faces are outlined at the Width and edges at half of it, as the page says faces are highlighted with a thick line and edges with a thin line. With both on, a click paints the edge within 4 px of the pointer, else the face, and Shift switches to Paint Fills only; with one on, Shift switches to the other. The options last across tool switches and are saved with the preferences (`tool.setOption {tool: "livePaintBucket"}`). Evidence: `builder::tests::{bucket_options_choose_what_a_click_paints, bucket_options_set_the_highlight, bucket_highlights_an_edge_thinner_than_a_face}`, `dialogs::live_paint_bucket::tests::double_clicking_the_tool_opens_its_options_and_ok_sets_them`. + Live Paint Bucket Options has Cursor Swatch Preview, as the [help page](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/paint-with-the-live-paint-bucket-tool.html) lists: with the Bucket selected, the active fill or stroke shows above the pointer between the color swatches before and after it in the Swatches panel, and the Left and Right arrow keys pick the swatch before or after it, as the page describes. The selected art keeps its paint. As VectorCraft's own rules, the option is on at first, the arrows step the stroke when the Stroke proxy is in front, they wrap around at the ends and still step with the preview off, a color that is no swatch shows alone until Right picks the first swatch or Left the last, and the arrows do nothing during a fill drag or with no color swatch. Evidence: `tests::build::{live_paint_bucket_arrow_keys_step_the_active_paint_through_the_swatches, live_paint_bucket_arrow_keys_handle_unmatched_paint_few_swatches_and_drags, live_paint_bucket_arrow_keys_pass_swatches_of_the_same_color_for_fill_and_stroke, swatch_arrow_keys_leave_modified_arrows_to_the_shortcuts}`. Live Paint Make preserves the front-most covering source's fill when compound paths share a curved boundary, and Expand keeps the face fills. Regression evidence: `planar::tests::coincident_curves_keep_their_source_faces` checks source order, winding, fill rules and nearby boundaries; `tests::build::live_paint_make_and_expand_keep_abutting_compound_colors` checks native compound paths, Make, Expand and Undo/Redo. This preserves VectorCraft's source-fill rule without changing arrangement precision. Live Paint Gap Options (Object › Live Paint › Gap Options…, `livePaint.gapOptions`): with Gap Detection on, paint stops at the gaps that run from a loose end of a path to the nearest path, up to Small (3 pt), Medium (6 pt), Large (12 pt) or a custom size; Close Gaps With Paths adds an unpainted path across each gap; Preview applies the options live and draws the gaps in the Gap Preview Color. The options are saved, copied and undone with the group, and Merge keeps the bottom group's options when its Gap Detection is on, or else turns Gap Detection on with Small when another merged group has it on. When Gap Detection is off in every merged group, it stays off. View › Show Live Paint Gaps (`view.livePaintGaps`) draws the gaps of the selected Live Paint groups up to their Paint Stops At size, each in its group's Gap Preview Color, until Hide Live Paint Gaps or Hide Edges hides them ([Live Paint gaps](https://web.archive.org/web/20260105111201/https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/find-and-close-gaps-in-live-paint-groups.html)). From 8d0e45ef5291893ca87a98f61456d0c8005e47e8 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 12:15:49 -0800 Subject: [PATCH 05/33] Actions: keep empty path values in ui.inspect's copy of recorded steps Under a read root, an empty or blank value under a path param, or in a path param's list, names no path. ui.inspect now keeps it, so a recorded prefs.set step that clears a folder preference shows the reset. --- crates/ui-egui/src/control.rs | 7 ++++--- crates/ui-egui/src/control/tests.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index cc5ed91cb6..a840ae19e2 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -1088,13 +1088,14 @@ fn leave_out_unreadable_pref_steps(command: &str, params: &mut serde_json::Map) { use vectorcraft_engine::file_access::is_path_key; + let shown = |p: &str| p.trim().is_empty() || readable_value(p); params.retain(|key, v| { !unreadable_path_like(key) && match v.as_str() { - Some(p) => !unreadable_path_like(p) && !(is_path_key(key) && !readable_value(p)), + Some(p) => !unreadable_path_like(p) && !(is_path_key(key) && !shown(p)), None => true, } }); @@ -1102,7 +1103,7 @@ fn leave_out_unreadable_params(params: &mut serde_json::Map) { if let Value::Array(paths) = v && is_path_key(key) { - paths.retain(|p| p.as_str().is_none_or(readable_value)); + paths.retain(|p| p.as_str().is_none_or(shown)); } leave_out_unreadable_values(v); } diff --git a/crates/ui-egui/src/control/tests.rs b/crates/ui-egui/src/control/tests.rs index 4d43339a85..009998a8fd 100644 --- a/crates/ui-egui/src/control/tests.rs +++ b/crates/ui-egui/src/control/tests.rs @@ -311,6 +311,24 @@ fn a_spaced_absolute_path_outside_the_read_root_is_left_out() { assert_eq!(params, json!({"keep": inside, "list": []})); } +/// Under a read root, an empty or blank value names no path, so a recorded step keeps it under a path +/// param and in a path param's list, a folder preference reset in a `prefs.set` step's `values` +/// included; a path outside the root is still left out. +#[test] +fn an_empty_path_param_stays_under_a_read_root() { + let base = crate::tests_folder::Folder::new("unreadable-empty"); + std::fs::create_dir_all(base.join("in")).unwrap(); + let roots = vectorcraft_engine::file_access::AutomationRoots::new(&[&base.join("in")], &[]).unwrap(); + let mut params = json!({ + "values": {"fontsFolder": "", "templatesFolder": " ", "recoveryFolder": "/outside/root/x"}, + "path": "", "destinationFolder": " ", "files": ["", "/outside/root/y"], + }); + vectorcraft_engine::file_access::confine(roots.as_ref(), || { + super::leave_out_unreadable_step("prefs.set", params.as_object_mut().unwrap()); + }); + assert_eq!(params, json!({"values": {"fontsFolder": "", "templatesFolder": " "}, "path": "", "destinationFolder": " ", "files": [""]})); +} + /// Without a read root, a `file:` URL that names another host may be read, and a recorded step keeps /// it. #[test] From 3a10022dcf56b3c1ef2325a8953f765439c9d332 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 13:00:44 -0800 Subject: [PATCH 06/33] Contextual Task Bar: actions wrap onto more rows on a narrow canvas On a canvas narrower than one row of the bar, the action buttons ran past its right edge: only the font controls moved to a second row, and keep_in moved the bar without shrinking it. - The actions now wrap, in order, onto rows under the row with the handle, the chips and More Options. Their widths are measured with the button's font, and the bar's place on the canvas uses the same layout from the first frame and after a resize. - An action wider than a row, or one that would need a row past the canvas's height, is listed in More Options. - When the handle, the chips and More Options don't fit one row, the chips take rows after the actions. Without a row to spare, the font controls are left out. A canvas too small for the handle and More Options shows no bar. Docs: docs/target-app-parity.md. --- crates/ui-egui/src/canvas/task_bar.rs | 426 +++++++++++++++++++------- crates/ui-egui/src/tests/taskbar.rs | 94 ++++++ docs/target-app-parity.md | 1 + 3 files changed, 410 insertions(+), 111 deletions(-) diff --git a/crates/ui-egui/src/canvas/task_bar.rs b/crates/ui-egui/src/canvas/task_bar.rs index e908640c25..d606f630d5 100644 --- a/crates/ui-egui/src/canvas/task_bar.rs +++ b/crates/ui-egui/src/canvas/task_bar.rs @@ -73,8 +73,8 @@ pub(crate) fn task_bar_shows_type(app: &VectorcraftApp) -> bool { /// A basic shape selected with the Selection tool shows a Shape Properties button in place of Lock. /// Selected type also shows the font family, font style and Font Size controls before its Fill. /// While a Type tool edits type, or has type selected, the bar shows only those controls and the -/// Fill. The font controls take a second row when one row would be wider than the canvas, and -/// narrow there to fit it. +/// Fill. When one row would be wider than the canvas, the font controls take a row of their own, +/// narrowed to fit it, and the actions wrap onto rows under the handle's ([`bar_layout`]). /// Its handle drags it anywhere on the canvas; unpinned it keeps that offset as it follows the /// selection, pinned it stays put ([`crate::state::TaskBarPlace`]). pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { @@ -231,7 +231,7 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { } items.push((tl!("Duplicate"), "copy", "edit.duplicate", none())); } - let (fill, stroke, fill_mixed, stroke_mixed) = chip_paints(app, ui.ctx()); + let paints = chip_paints(app, ui.ctx()); let colored = has_colors(&st.doc, &st.selection.objects); let doc = st.uid; // Under the selection's lowest corner on screen, centered on the selection (the view may be @@ -239,23 +239,47 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { let lowest = xf.quad(b).iter().map(|c| c.y).fold(f32::NEG_INFINITY, f32::max); let anchor = pos2(xf.to_screen(b.center()).x, lowest); let bounds = xf.rect.shrink(8.0); + // The canvas's width less the frame's margins and stroke. + let room = bounds.width() - BAR_FRAME; // On a canvas too narrow for the type controls at their narrowest, the bar leaves them out. - let fonts = fonts && bounds.width() - 18.0 >= crate::panels::character::task_bar_min_room(); + let fonts = fonts && room >= crate::panels::character::task_bar_min_room(); + // The selected objects' Fill, Stroke, Recolor Artwork (when they have colours to recolour) and + // Lock (a basic shape's properties instead), left out with the Artboard tool. A Type tool's bar + // has the Fill alone. + let chips: Vec = [ + (!on_artboards, Chip::Fill), + (!on_artboards && !typing, Chip::Stroke), + (!on_artboards && !typing && colored, Chip::Recolor), + (!on_artboards && !typing, Chip::Lock), + ] + .into_iter() + .filter_map(|(shown, chip)| shown.then_some(chip)) + .collect(); + let widths: Vec = items.iter().map(|(label, ..)| action_width(ui, label)).collect(); + let font_w = fonts.then(|| { + let w = crate::panels::character::TASK_BAR_WIDTHS; + w.iter().sum::() + BAR_GAP * (w.len() - 1) as f32 + }); + let spacing = ui.spacing().item_spacing.y; + let chip_widths: Vec = chips.iter().map(|c| c.width()).collect(); + let layout = bar_layout(&widths, &chip_widths, font_w, room, bar_max_rows(bounds.height(), spacing)); + // On a canvas too small for its handle and More Options: no bar, rather than one that runs off + // the canvas. + let fonts = layout.as_ref().is_some_and(|l| l.fonts); ui.ctx().data_mut(|d| d.insert_temp(task_bar_id().with("fonts"), fonts)); - // The width of the bar's frame (margins and stroke), its handle, Fill and Stroke chips, - // Recolor Artwork, Lock or Shape Properties, and More Options, each after a 6-point gap. - let chips: f32 = - [(!on_artboards, 26.0), (!on_artboards && !typing, 26.0), (!on_artboards && !typing && colored, 30.0), (!on_artboards && !typing, 30.0)] - .iter() - .filter(|(shown, _)| *shown) - .map(|(_, w)| w + 6.0) - .sum(); - let (font_row, est) = size_estimate(&items, fonts, 18.0 + 4.0 + chips + 36.0, bounds.width()); + let Some(layout) = layout else { return }; + let est = layout.size(spacing); // Its place under the selection, then where the handle moved it, kept on the canvas (with the - // bar's size last frame, the estimate before it first shows). + // bar's size last frame, the layout's size before it first shows and whenever the layout + // changed since, as after the canvas or the selection changed). let under = pos2(anchor.x - est.x / 2.0, anchor.y + 28.0); let id = task_bar_id(); - let size = egui::AreaState::load(ui.ctx(), id).and_then(|s| s.size).unwrap_or(est); + let unchanged = ui.ctx().data_mut(|d| { + let last = d.get_temp::(id.with("layout")); + d.insert_temp(id.with("layout"), est); + last == Some(est) + }); + let size = egui::AreaState::load(ui.ctx(), id).and_then(|s| s.size).filter(|_| unchanged).unwrap_or(est); let keep_in = |p: Pos2| p.clamp(bounds.min, (bounds.max - size).max(bounds.min)); let place = &mut app.ui.task_bar_place; // Just unpinned: follow the selection from the pinned spot. @@ -285,95 +309,88 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { .inner_margin(egui::Margin::symmetric(8, 6)) .shadow(egui::epaint::Shadow { offset: [0, 3], blur: 10, spread: 0, color: Color32::from_black_alpha(70) }) .show(ui, |ui| { - ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 6.0; - let (g, grip) = ui.allocate_exact_size(vec2(4.0, 28.0), Sense::drag()); - let active = grip.hovered() || grip.dragged(); - ui.painter().rect_filled(g.shrink2(vec2(0.5, 4.0)), CornerRadius::same(2), if active { t.text_dim } else { t.button_border }); - if grip.dragged() { - ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing); - moved = grip.drag_delta(); - } else if grip.hovered() { - ui.ctx().set_cursor_icon(egui::CursorIcon::Grab); - } - for (label, icon, cmd, params) in &items { - let galley = ui.painter().layout_no_wrap(label.to_string(), egui::FontId::proportional(13.0), t.text_strong); - let (r, resp) = ui.allocate_exact_size(vec2(galley.size().x + 38.0, 30.0), Sense::click()); - resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, *label)); - if resp.hovered() { - ui.painter().rect_filled(r, CornerRadius::same(3), t.hover); - } - ui.painter().rect_stroke(r, CornerRadius::same(3), Stroke::new(1.0, t.button_border), StrokeKind::Inside); - crate::icons::paint(ui, icon, egui::Rect::from_min_size(r.min + vec2(8.0, 7.0), vec2(16.0, 16.0)), t.icon); - ui.painter().galley(pos2(r.left() + 30.0, r.center().y - galley.size().y / 2.0), galley, t.text_strong); - if resp.clicked() { - run = Some((*cmd, params.clone())); - } - } - // Type's font family, font style and Font Size, which act as the Character - // panel's do: on the characters a Type tool selects, else on the selected type. - if fonts && !font_row { - crate::panels::character::task_bar(app, ui, f32::INFINITY); - } - // The selected objects' Fill, Stroke, Recolor Artwork and Lock (a basic shape's - // properties instead), left out with the Artboard tool. A Type tool's bar has - // the Fill alone. - if !on_artboards && paint_chip(ui, &t, &fill, fill_mixed, None, tl!("Fill")) { - app.session.fill_active = true; - show_swatches = true; - } - if !on_artboards && !typing { - // The Stroke chip, drawn as a ring as the Control bar's is. - if paint_chip(ui, &t, &stroke, stroke_mixed, Some(4.0), tl!("Stroke")) { - app.session.fill_active = false; - show_swatches = true; - } - // Recolor Artwork, when the selection has colours to recolour. - if colored && widgets::icon_button(ui, "palette", tl!("Recolor Artwork"), false, 30.0).clicked() { - run = Some(("ui.recolorDialog", json!({}))); - } - if let Some(shape) = &shape - && let NodeKind::Path { live: Some(live), .. } = &shape.kind - { - // The shape's properties in a popover that stays open while its fields and - // its Corner dropdown are used. - let props = widgets::icon_button(ui, "shapes", tl!("Shape Properties"), false, 30.0); - widgets::popover(&props, props.clicked(), |ui| { - ui.set_min_width(250.0); - crate::panels::transform::live_shape_rows(app, ui, shape, live, "task-bar-shape", true); - }); - } else { - // The Lock button is off in isolation mode, as Object › Lock › Selection is. - let lockable = crate::menus::enabled(app, "object.lock"); - if widgets::icon_button_enabled(ui, "lock", tl!("Lock (⌘2)"), false, lockable, 30.0).clicked() { - run = Some(("object.lock", json!({}))); - } - } - } - let more = widgets::icon_button(ui, "ellipsis", tl!("More Options"), false, 30.0); - egui::Popup::menu(&more).show(|ui| { - ui.set_min_width(170.0); - let bar = [ - (tl!("Hide Bar"), "window.taskBar", false), - (tl!("Pin Bar Position"), "window.taskBar.pin", pinned), - (tl!("Reset Bar Position"), "window.taskBar.reset", false), - ]; - for (label, cmd, checked) in bar { - if widgets::menu_item(ui, label, true, checked) { - run = Some((cmd, json!({}))); - } - } - ui.separator(); - if widgets::menu_item(ui, tl!("Show Properties Panel"), true, false) { - run = Some(("window.panel", json!({"panel": "properties"}))); - } - }); - }); - if font_row { + // The first row holds the handle and More Options; actions that don't fit beside + // them go on the rows after it, in order. + for row in 0..layout.rows { ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x = 6.0; - // The canvas's width less the frame's margins and stroke. - crate::panels::character::task_bar(app, ui, bounds.width() - 18.0); + ui.spacing_mut().item_spacing.x = BAR_GAP; + if row == 0 { + let (g, grip) = ui.allocate_exact_size(vec2(BAR_HANDLE, 28.0), Sense::drag()); + let active = grip.hovered() || grip.dragged(); + ui.painter().rect_filled( + g.shrink2(vec2(0.5, 4.0)), + CornerRadius::same(2), + if active { t.text_dim } else { t.button_border }, + ); + if grip.dragged() { + ui.ctx().set_cursor_icon(egui::CursorIcon::Grabbing); + moved = grip.drag_delta(); + } else if grip.hovered() { + ui.ctx().set_cursor_icon(egui::CursorIcon::Grab); + } + } + for ((label, icon, cmd, params), at) in items.iter().zip(&layout.actions) { + if *at == Some(row) && action_button(ui, &t, label, icon) { + run = Some((*cmd, params.clone())); + } + } + // Type's font family, font style and Font Size, which act as the Character + // panel's do: on the characters a Type tool selects, else on the selected type. + if row == 0 && fonts && !layout.font_row { + crate::panels::character::task_bar(app, ui, f32::INFINITY); + } + for (chip, at) in chips.iter().zip(&layout.chips) { + if *at != row { + continue; + } + match paint_or_object_chip(app, ui, &t, *chip, &paints, shape.as_ref()) { + Some(ChipClick::Fill(fill)) => { + app.session.fill_active = fill; + show_swatches = true; + } + Some(ChipClick::Run(cmd)) => run = Some((cmd, json!({}))), + None => {} + } + } + if row != 0 { + return; + } + let more = widgets::icon_button(ui, "ellipsis", tl!("More Options"), false, BAR_MORE); + egui::Popup::menu(&more).show(|ui| { + ui.set_min_width(170.0); + // The actions too wide for any row of the bar. + let listed = items.iter().zip(&layout.actions).filter(|(_, at)| at.is_none()); + let mut any = false; + for ((label, _, cmd, params), _) in listed { + any = true; + if widgets::menu_item_name(ui, label, true, false) { + run = Some((*cmd, params.clone())); + } + } + if any { + ui.separator(); + } + let bar = [ + (tl!("Hide Bar"), "window.taskBar", false), + (tl!("Pin Bar Position"), "window.taskBar.pin", pinned), + (tl!("Reset Bar Position"), "window.taskBar.reset", false), + ]; + for (label, cmd, checked) in bar { + if widgets::menu_item(ui, label, true, checked) { + run = Some((cmd, json!({}))); + } + } + ui.separator(); + if widgets::menu_item(ui, tl!("Show Properties Panel"), true, false) { + run = Some(("window.panel", json!({"panel": "properties"}))); + } + }); + }); + } + if layout.font_row { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = BAR_GAP; + crate::panels::character::task_bar(app, ui, room); }); } }); @@ -413,14 +430,201 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { } } -/// Whether the type controls go on a second row, and the bar's estimated size before it first -/// shows: `items` and, when `fonts`, the font family, font style and Font Size controls, which take -/// a second row when one row, with the bar's other controls `fixed` points wide, would be wider than -/// `room` (and narrow to fit it there). -fn size_estimate(items: &[(&str, &str, &str, serde_json::Value)], fonts: bool, fixed: f32, room: f32) -> (bool, Vec2) { - let font_w: f32 = crate::panels::character::TASK_BAR_WIDTHS.iter().filter(|_| fonts).map(|w| w + 6.0).sum(); - let items_w = if fonts { fixed } else { 150.0 } + items.iter().map(|(l, _, _, _)| l.len() as f32 * 7.2 + 44.0).sum::(); - if fonts && items_w + font_w > room { (true, vec2(items_w.max((font_w + 16.0).min(room)), 80.0)) } else { (false, vec2(items_w + font_w, 44.0)) } +/// The width of the Contextual Task Bar's frame: its margins and stroke on both sides. +const BAR_FRAME: f32 = 18.0; +/// The gap between the bar's controls. +const BAR_GAP: f32 = 6.0; +/// The width of the bar's handle. +const BAR_HANDLE: f32 = 4.0; +/// The width of the bar's More Options button. +const BAR_MORE: f32 = 30.0; +/// The height of a row of the bar's controls. +const BAR_ROW: f32 = 30.0; + +/// The controls the Contextual Task Bar shows after its actions: the Fill and Stroke chips, +/// Recolor Artwork, and Lock or a basic shape's Shape Properties. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Chip { + Fill, + Stroke, + Recolor, + Lock, +} + +impl Chip { + fn width(self) -> f32 { + match self { + Chip::Fill | Chip::Stroke => 26.0, + Chip::Recolor | Chip::Lock => 30.0, + } + } +} + +/// What a click on one of the bar's [`Chip`]s does: make the Fill (`true`) or the Stroke active and +/// bring up Swatches, or run a command. +enum ChipClick { + Fill(bool), + Run(&'static str), +} + +/// Where each of the Contextual Task Bar's controls goes. +#[derive(Debug, PartialEq)] +struct BarLayout { + /// Each action's row (0 is the row with the handle and More Options), or `None` for an action + /// wider than a row or past the rows the canvas's height allows, which More Options lists + /// instead. + actions: Vec>, + /// Each chip's row. + chips: Vec, + /// Whether the bar shows the font controls it was given. + fonts: bool, + /// Whether the font controls take a row of their own, after the others. + font_row: bool, + /// The number of rows before the font controls' row. + rows: usize, + /// The bar's width, its frame included. + width: f32, +} + +impl BarLayout { + /// The bar's size, with `spacing` points between its rows. + fn size(&self, spacing: f32) -> Vec2 { + let rows = (self.rows + usize::from(self.font_row)) as f32; + vec2(self.width, rows * BAR_ROW + (rows - 1.0) * spacing + 14.0) + } +} + +/// How many rows of the Contextual Task Bar fit in `height` points, with `spacing` points between +/// them. +fn bar_max_rows(height: f32, spacing: f32) -> usize { + ((height - 14.0 + spacing) / (BAR_ROW + spacing)).floor().max(0.0) as usize +} + +/// The Contextual Task Bar's layout in `room` points (the canvas's width less the frame) and at +/// most `max_rows` rows, for actions and chips the given widths wide, and the font controls `fonts` +/// wide when shown. One row holds everything when it fits. Otherwise the font controls take a row +/// of their own, narrowed to the room, and the actions wrap: the first row keeps the handle, the +/// chips and More Options, and each action goes on the last row when it fits there, else on a new +/// row. When the handle, the chips and More Options don't fit in one row, the chips take rows of +/// their own after the actions. An action wider than a row, or one that would need a row past +/// `max_rows`, goes in More Options; without a row to spare the font controls are left out. `None` +/// when not even the handle and More Options, and the chips' rows, fit. +fn bar_layout(actions: &[f32], chips: &[f32], fonts: Option, room: f32, max_rows: usize) -> Option { + // Puts a control `w` wide on the last row when it fits there, else on a new row. + fn place(rows: &mut Vec, w: f32, room: f32) -> usize { + match rows.last_mut() { + Some(last) if *last + BAR_GAP + w <= room => *last += BAR_GAP + w, + _ => rows.push(w), + } + rows.len() - 1 + } + let ends = BAR_HANDLE + BAR_GAP + BAR_MORE; + if room < ends || max_rows == 0 { + return None; + } + let chips_w: f32 = chips.iter().map(|w| w + BAR_GAP).sum(); + let actions_w: f32 = actions.iter().map(|w| w + BAR_GAP).sum(); + let one_row = ends + chips_w + actions_w + fonts.map_or(0.0, |w| w + BAR_GAP); + if one_row <= room { + return Some(BarLayout { + actions: vec![Some(0); actions.len()], + chips: vec![0; chips.len()], + fonts: fonts.is_some(), + font_row: false, + rows: 1, + width: one_row + BAR_FRAME, + }); + } + let chips_first = ends + chips_w <= room; + let mut chip_rows = vec![]; + let chips: Vec = chips.iter().map(|w| if chips_first { 0 } else { place(&mut chip_rows, *w, room) }).collect(); + if 1 + chip_rows.len() > max_rows { + return None; + } + let font_row = fonts.is_some() && 1 + chip_rows.len() < max_rows; + let action_rows = max_rows - chip_rows.len() - usize::from(font_row); + let mut rows = vec![if chips_first { ends + chips_w } else { ends }]; + // Once an action needs a row past the last one, it and the actions after it go in More Options, + // keeping their order. + let mut full = false; + let actions: Vec> = actions + .iter() + .map(|w| { + if *w > room || full { + return None; + } + let mut next = rows.clone(); + let row = place(&mut next, *w, room); + full = row >= action_rows; + (!full).then(|| { + rows = next; + row + }) + }) + .collect(); + let chips = chips.into_iter().map(|row| if chips_first { 0 } else { rows.len() + row }).collect(); + let font_w = if font_row { fonts.map_or(0.0, |w| w.min(room)) } else { 0.0 }; + let width = rows.iter().chain(&chip_rows).fold(font_w, |a, b| a.max(*b)) + BAR_FRAME; + Some(BarLayout { actions, chips, fonts: font_row, font_row, rows: rows.len() + chip_rows.len(), width }) +} + +/// The width of the Contextual Task Bar's action button for `label`. +fn action_width(ui: &Ui, label: &str) -> f32 { + ui.painter().layout_no_wrap(label.to_string(), egui::FontId::proportional(13.0), Color32::PLACEHOLDER).size().x + 38.0 +} + +/// One of the Contextual Task Bar's action buttons: `icon` and `label`, named for screen readers. +/// Returns whether it was clicked. +fn action_button(ui: &mut Ui, t: &Tokens, label: &str, icon: &str) -> bool { + let galley = ui.painter().layout_no_wrap(label.to_string(), egui::FontId::proportional(13.0), t.text_strong); + let (r, resp) = ui.allocate_exact_size(vec2(galley.size().x + 38.0, BAR_ROW), Sense::click()); + resp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, true, label)); + if resp.hovered() { + ui.painter().rect_filled(r, CornerRadius::same(3), t.hover); + } + ui.painter().rect_stroke(r, CornerRadius::same(3), Stroke::new(1.0, t.button_border), StrokeKind::Inside); + crate::icons::paint(ui, icon, egui::Rect::from_min_size(r.min + vec2(8.0, 7.0), vec2(16.0, 16.0)), t.icon); + ui.painter().galley(pos2(r.left() + 30.0, r.center().y - galley.size().y / 2.0), galley, t.text_strong); + resp.clicked() +} + +/// One of the Contextual Task Bar's chips, with the Fill and Stroke `paints` ([`chip_paints`]) and +/// the basic `shape` whose properties take Lock's place. Returns what a click on it does. +fn paint_or_object_chip( + app: &mut VectorcraftApp, + ui: &mut Ui, + t: &Tokens, + chip: Chip, + paints: &(vectorcraft_color::Paint, vectorcraft_color::Paint, bool, bool), + shape: Option<&vectorcraft_doc::Node>, +) -> Option { + let (fill, stroke, fill_mixed, stroke_mixed) = paints; + match chip { + Chip::Fill => paint_chip(ui, t, fill, *fill_mixed, None, tl!("Fill")).then_some(ChipClick::Fill(true)), + // The Stroke chip, drawn as a ring as the Control bar's is. + Chip::Stroke => paint_chip(ui, t, stroke, *stroke_mixed, Some(4.0), tl!("Stroke")).then_some(ChipClick::Fill(false)), + Chip::Recolor => { + widgets::icon_button(ui, "palette", tl!("Recolor Artwork"), false, 30.0).clicked().then_some(ChipClick::Run("ui.recolorDialog")) + } + Chip::Lock => { + if let Some(shape) = shape + && let NodeKind::Path { live: Some(live), .. } = &shape.kind + { + // The shape's properties in a popover that stays open while its fields and its + // Corner dropdown are used. + let props = widgets::icon_button(ui, "shapes", tl!("Shape Properties"), false, 30.0); + widgets::popover(&props, props.clicked(), |ui| { + ui.set_min_width(250.0); + crate::panels::transform::live_shape_rows(app, ui, shape, live, "task-bar-shape", true); + }); + None + } else { + // The Lock button is off in isolation mode, as Object › Lock › Selection is. + let lockable = crate::menus::enabled(app, "object.lock"); + widgets::icon_button_enabled(ui, "lock", tl!("Lock (⌘2)"), false, lockable, 30.0).clicked().then_some(ChipClick::Run("object.lock")) + } + } + } } /// The paints the Fill and Stroke chips show, and whether each is mixed ("?"): the proxies', or diff --git a/crates/ui-egui/src/tests/taskbar.rs b/crates/ui-egui/src/tests/taskbar.rs index 68ef8a3f15..9768813a70 100644 --- a/crates/ui-egui/src/tests/taskbar.rs +++ b/crates/ui-egui/src/tests/taskbar.rs @@ -767,6 +767,100 @@ fn selected_types_font_controls_take_a_second_row_on_a_narrow_canvas() { assert!(canvas.contains_rect(r) && r.height() < 60.0, "no font controls on a 160 px screen: {r:?} in {canvas:?}"); } +/// The labels drawn inside the task bar after `frames` frames on a screen `size` points in `ctx`, +/// with the bar's rect and the canvas's. +fn bar_texts_on_screen(app: &mut VectorcraftApp, ctx: &egui::Context, size: egui::Vec2, frames: usize) -> (Vec<(String, Rect)>, Rect, Rect) { + let screen = Rect::from_min_size(Pos2::ZERO, size); + let mut texts = vec![]; + for _ in 0..frames { + let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| canvas::show(app, ui)); + out.textures_delta.clear(); + texts = shapes_text(&out.shapes.iter().map(|c| c.shape.clone()).collect::>()); + } + let r = canvas::task_bar_rect(ctx).expect("the task bar shows"); + (texts.into_iter().filter(|(_, t)| r.contains(t.center())).collect(), r, app.canvas_rect.expect("the canvas is laid out")) +} + +/// On a canvas narrower than one row of the bar's actions, the actions wrap onto more rows, in +/// order, and the bar stays whole on the canvas, also on the first frame after the canvas narrows. +#[test] +fn a_task_bars_actions_wrap_on_a_narrow_canvas() { + let mut app = app(); + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + let circle = id(app.run("path.create", json!({"d": "M100 300 A100 100 0 1 1 300 300 A100 100 0 1 1 100 300 Z"})).unwrap()); + let t = id(app.run("text.createInPath", json!({"path": circle, "mode": "onPath", "text": "Round", "at": [90, 300]})).unwrap()); + select(&mut app, &[t]); + let actions = ["Create Outlines", "Type on a Path Options", "Center on Top", "Center on Bottom", "Dynamic Text", "Duplicate"]; + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let (texts, r, canvas) = bar_texts_on_screen(&mut app, &ctx, vec2(1800.0, 700.0), 3); + assert!(canvas.contains_rect(r) && r.height() < 60.0, "one row where it fits: {r:?} in {canvas:?}"); + // The first frame after the canvas narrows, then once the bar settles. + for frames in [1, 3] { + let (texts, r, canvas) = bar_texts_on_screen(&mut app, &ctx, vec2(760.0, 700.0), frames); + assert!(canvas.contains_rect(r), "{frames} frames: {r:?} in {canvas:?}"); + let ys: Vec = actions.iter().map(|label| at(&texts, label).y).collect(); + assert!(ys.windows(2).all(|w| w[0] <= w[1]), "{frames} frames: the actions keep their order: {ys:?}"); + assert!(ys[0] < ys[ys.len() - 1], "{frames} frames: the actions take more than one row: {texts:?}"); + } + assert!(actions.iter().all(|label| has(&texts, label)), "{texts:?}"); +} + +/// On a canvas too narrow for an action's button, or too short for the rows the actions would +/// wrap onto, More Options lists those actions, and the chips take rows after the actions when they +/// don't fit beside the handle and More Options. Narrower than the handle and More Options, the bar +/// doesn't show. +#[test] +fn a_task_bar_action_wider_than_the_canvas_goes_in_more_options() { + let mut app = app(); + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + let a = id(app.run("shape.rectangle", json!({"x": 40, "y": 40, "width": 120, "height": 100})).unwrap()); + let b = id(app.run("shape.ellipse", json!({"x": 70, "y": 60, "width": 50, "height": 50})).unwrap()); + select(&mut app, &[a, b]); + app.run("object.compoundPath.make", json!({})).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let (texts, r, canvas) = bar_texts_on_screen(&mut app, &ctx, vec2(160.0, 700.0), 3); + assert!(canvas.contains_rect(r), "{r:?} in {canvas:?}"); + assert!(has(&texts, "Edit Path") && has(&texts, "Duplicate") && !has(&texts, "Release Compound Path"), "{texts:?}"); + // More Options, after the frame's margin and the handle on the first row. + let more = r.min + vec2(9.0 + 4.0 + 6.0 + 15.0, 7.0 + 15.0); + let click = |app: &mut VectorcraftApp, p: Pos2| { + let press = |pressed| Event::PointerButton { pos: p, button: PointerButton::Primary, pressed, modifiers: Default::default() }; + let screen = Rect::from_min_size(Pos2::ZERO, vec2(160.0, 700.0)); + let mut texts = vec![]; + for events in [vec![Event::PointerMoved(p)], vec![press(true)], vec![press(false)], vec![]] { + let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), events, ..Default::default() }, |ui| canvas::show(app, ui)); + out.textures_delta.clear(); + texts = shapes_text(&out.shapes.iter().map(|c| c.shape.clone()).collect::>()); + } + texts + }; + let texts = click(&mut app, more); + // Menu items are drawn after a space for the check mark. + let item = |label: &str| texts.iter().find(|(t, _)| t.trim() == label).map(|(_, r)| r.center()); + assert!(item("Hide Bar").is_some(), "More Options opens: {texts:?}"); + let release = item("Release Compound Path").unwrap_or_else(|| panic!("More Options lists the action: {texts:?}")); + click(&mut app, release); + let st = app.session.active().unwrap(); + assert_eq!(st.selection.objects.len(), 2, "the paths come apart, selected"); + assert!(st.selection.objects.iter().all(|i| matches!(st.doc.node(*i).unwrap().kind, NodeKind::Path { .. }))); + + // On a canvas too short for the rows the actions would wrap onto, More Options lists them, in + // order: the bar keeps at most the first. + let (texts, r, canvas) = bar_texts_on_screen(&mut app, &ctx, vec2(160.0, 150.0), 3); + assert!(canvas.contains_rect(r), "{r:?} in {canvas:?}"); + let shown = ["Group", "Unite", "Duplicate"].map(|label| has(&texts, label)); + assert!(!shown[1] && !shown[2], "{texts:?}"); + + let screen = Rect::from_min_size(Pos2::ZERO, vec2(60.0, 700.0)); + for _ in 0..3 { + let mut out = ctx.run_ui(egui::RawInput { screen_rect: Some(screen), ..Default::default() }, |ui| canvas::show(&mut app, ui)); + out.textures_delta.clear(); + } + assert!(canvas::task_bar_rect(&ctx).is_none(), "no room for the handle and More Options: no bar"); +} + /// When the canvas narrows so far that the bar leaves its font controls out, the font family /// field it drew before no longer takes the mouse wheel. #[test] diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 9a9b0d06ed..494d0465bc 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -845,6 +845,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - With the Artboard tool, the contextual task bar sits under the selected artboards and offers Resize (Artboard Options for the active artboard), Duplicate (each selected artboard, in one undo step, the copies then selected), Rearrange All (with two or more artboards) and Export (Export for Screens with the selected artboards checked). This follows Illustrator's bar: while the Artboard tool is in use, it has options for resizing, duplicating and exporting artboards (the [Contextual Task Bar announcement](https://community.adobe.com/questions-652/contextual-task-bar-in-illustrator-2025-quick-actions-when-you-need-them-1546580); Rearrange and Export on the bar: [Managing Artboards in Illustrator](https://creativepro.com/managing-artboards-in-illustrator/)). - The contextual task bar offers more next steps by selection (): an open path Join and Edit Path (the Direct Selection tool), a closed path Edit Path, a compound path Edit Path and Release Compound Path, a clipping group Edit Clipping Path and Release Clipping Mask instead of Ungroup and Isolate, an image selected with vector art Make Clipping Mask instead of Unite, point or area type Convert To Area Type or Convert To Point Type beside Create Outlines, and type on a path, for which the overview names an item to add an effect and rearrange the text, Type on a Path Options (`type.pathOptions`; its options: ) beside Create Outlines. Evidence: `crates/ui-egui/src/tests/taskbar.rs`. - While a Type tool (Type, Area Type, Type on a Path and their vertical versions) adds or edits type, the contextual task bar shows the font family menu, the font style menu, the Font Size combo and the Fill chip (); the Touch Type tool shows no bar, as the same page describes. Type selected with a selection tool shows the same three font controls beside its Create Outlines, conversion and Type on a Path items (). The controls act as the Character panel's do: on the characters a Type tool selects, else on every selected type object, with the size left blank where the sizes differ. VectorCraft's own rules: a Type tool's bar leaves out the object actions, the Stroke chip, Recolor Artwork and Lock; a Type tool shows no bar before it adds type or with other art selected; type selected with other art shows no font controls; the font controls go on a second row when one row would be wider than the canvas, and narrow to fit a canvas narrower than they are; while a Type tool edits type, the Fill chip shows the edited characters' fill ("?" where it differs). Evidence: `the_type_tools_show_font_and_fill_controls`, `the_type_tools_show_no_bar_without_type`, `selected_type_shows_font_controls_beside_its_actions`, `the_task_bars_font_size_sets_what_the_character_panel_would`, `the_type_tools_fill_chip_activates_the_fill`, `the_type_tools_bar_is_named_and_stays_on_the_canvas`, `selected_types_font_controls_take_a_second_row_on_a_narrow_canvas` and `the_type_tools_fill_chip_shows_the_edited_characters_fill` in `crates/ui-egui/src/tests/taskbar.rs`. + - The help page describes the contextual task bar as one horizontal row () and gives no rule for a canvas narrower than that row. VectorCraft's own rule: the bar stays whole on the canvas. When its actions don't fit in one row, they wrap, in order, onto rows under the row with the handle, the chips and More Options; an action wider than any row, or one that would need a row past the canvas's height, is listed in More Options instead; when the chips don't fit beside the handle and More Options, they take rows after the actions; with no row to spare for the font controls, the bar leaves them out; a canvas too small for the handle and More Options shows no bar. Evidence: `a_task_bars_actions_wrap_on_a_narrow_canvas` and `a_task_bar_action_wider_than_the_canvas_goes_in_more_options` in `crates/ui-egui/src/tests/taskbar.rs`. - With a live rectangle, rounded rectangle, ellipse or polygon selected with the Selection tool, the contextual task bar shows a Shape Properties button in place of Lock. It opens a popup with the shape's rows from the Transform panel (corner radius, pie angles, or sides, angle, corner and radius), which edit it through `object.setLiveShape`. The overview describes this: when a shape drawn with a shape tool (Rectangle, Star) is selected, the bar offers to change its properties where Lock Object would be ([Contextual Task Bar overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html)). Object › Lock and ⌘2 still lock the shape. Not done: stars, which the app draws as plain paths. Evidence: `a_basic_shape_shows_its_properties_in_place_of_lock`, `the_shape_properties_popup_edits_the_shape` and `other_selections_keep_lock` in `crates/ui-egui/src/tests/taskbar.rs`. - The contextual task bar has a Stroke chip beside the Fill chip, drawn with the paint the Stroke proxy shows (a "?" when the selected objects' strokes differ, as on the Fill chip); a click brings the Stroke proxy forward and shows the Swatches panel wherever it is docked, floating or closed, as a click on the Fill chip does for the Fill proxy. The reference app's bar has Fill and Stroke controls for changing an object's colors ([Contextual Task Bar overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html)). Evidence: `the_task_bar_shows_a_stroke_chip_beside_fill` and `the_task_bar_chips_follow_the_proxies_and_show_swatches_anywhere` in `crates/ui-egui/src/canvas/tests.rs`. - With anchor points selected with Direct Selection, the contextual task bar offers Remove Anchor Points, Connect (`path.join`), Cut Path, Corner and Smooth (`path.convertAnchors` with `to` set to `corner` or `smooth`), the commands of the Control bar's anchor buttons. As in Illustrator, whose bar has items that delete the selected anchors, join selected end points, split the path at the selected anchors, and make the selected anchors corner or smooth points ([Contextual Task Bar overview](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html)). Not done: Simplify, which the bar offers only while no anchors are selected, and the bar's Smooth item that adjusts a path's curvature, a command the app doesn't have (Object › Path › Smooth). From 2fc344cb8c438fbb8afa3f70db6e215925dd40d9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 14:15:03 -0800 Subject: [PATCH 07/33] PDF/.ai import: graphs keep their data and settings when their records read whole A legacy .ai graph (%_Gs ... %_GS) opened as plain art, with its table, type, axes and settings left out. A grouped column, stacked column, line or area graph now keeps them on its group when every record has the shape and values the version 7 specification's appendix A gives, the table holds at least one number, the drawn art strokes the plot's left and bottom edges, and each series group is tagged once as a whole column, holds only paths and paints them with one fill and one stroke. The saved art stays as it is on open; Graph Data redraws the graph from its data afterward, with each series' fill and stroke kept. Any other graph (pie and scatter graphs, designs, customizations, record shapes of later versions, tables past 10,000 categories, 256 series or 2,600,000 cells) keeps its art as plain objects with the existing note. Saving a graph to .ai, in either form, now warns that its data and settings are not written. --- crates/engine/src/cmd/graph/tests.rs | 63 ++ crates/engine/tests/import_fuzz.rs | 30 + crates/eps/src/ai_editing/writer.rs | 4 + crates/eps/src/import/ai/read.rs | 9 +- crates/eps/src/import/ai/read/document.rs | 4 +- crates/eps/src/import/ai/read/graph.rs | 681 ++++++++++++++++++++++ crates/eps/src/import/ai/read/ops.rs | 45 +- crates/eps/src/import/ai/read/types.rs | 16 +- crates/eps/src/import/ai/tests.rs | 218 +++++++ crates/eps/src/legacy_ai.rs | 1 + crates/eps/src/legacy_ai/writer.rs | 5 +- docs/file-format-parity.md | 9 +- docs/target-app-parity.md | 1 + 13 files changed, 1030 insertions(+), 56 deletions(-) create mode 100644 crates/eps/src/import/ai/read/graph.rs diff --git a/crates/engine/src/cmd/graph/tests.rs b/crates/engine/src/cmd/graph/tests.rs index b1a491424e..c8580549aa 100644 --- a/crates/engine/src/cmd/graph/tests.rs +++ b/crates/engine/src/cmd/graph/tests.rs @@ -5280,3 +5280,66 @@ fn graph_commands_refuse_params_of_the_wrong_type() { s.execute("graph.setData", &json!({"id": id.0, "rows": [[1, null], [2, 3]], "series": null, "transpose": null})).unwrap(); assert_eq!(spec(&s).cells(), vec![vec![Some(1.0), None], vec![Some(2.0), Some(3.0)]]); } + +/// A `.ai` in PostScript form whose graph's records read whole (version 7 specification, appendix A) +/// opens as a graph whose data can be edited: Graph Data redraws it on its plot. Saving it as `.ai` +/// says its data and settings aren't written, in either form. +#[test] +fn a_graph_opened_from_ai_keeps_its_data_for_editing() { + let graph = "%_Gs\n%_100 200 300 100 Gb\n%_5 0 0 14 0 1 0 0 6 44 Gy\n%_10 2 0 0 90 80 0 Gd\n\ + %_1 () Ga\n%_0 13 0 0 0 0 0 () GA\n%_2 () Ga\n%_0 14 0 0 0 0 0 () GA\n%_4 () Ga\n%_0 14 0 0 0 0 0 () GA\n\ + %_2 2 1 1 Gz\n%_() Gc (East) Gc (Q1) Gc 5 Gc GC\n\ + u\nu\n0.6 0 1 0 k\n110 100 m\n110 150 L\n130 150 L\n130 100 L\nf\nU\n%_2 1 0 0 Go\n\ + u\n0 0 0 1 K\n100 200 m\n100 100 L\n300 100 L\nS\nU\nU\n%_GS\n"; + let text = format!( + "%!PS-Adobe-3.0\n%%Creator: a script\n%%BoundingBox: 0 0 400 400\n%AI5_FileFormat 3\n%AI3_Cropmarks: 0 0 400 400\n%%EndComments\n\ + %%BeginProlog\n%%EndProlog\n%%BeginSetup\n%%EndSetup\n%AI5_BeginLayer\n1 1 1 1 0 0 0 79 128 255 Lb\n(Graph) Ln\n0 A\n{graph}LB\n\ + %AI5_EndLayer--\n%%PageTrailer\n%%Trailer\n%%EOF\n" + ); + let dir = vectorcraft_testkit::temp_dir("graph-ai-open"); + let path = dir.join("graph.ai"); + std::fs::write(&path, text).unwrap(); + let mut s = Session::new(); + s.execute("document.open", &json!({"path": path})).unwrap(); + let mut found = vec![]; + s.doc().unwrap().doc.walk(|n| { + if n.extras().graph.is_some() { + found.push(n.id); + } + }); + assert_eq!(found.len(), 1); + let id = found[0]; + let before = spec(&s, id); + assert_eq!( + (before.series.as_slice(), before.categories.as_slice(), before.rows.as_slice()), + (&["East".to_string()][..], &["Q1".to_string()][..], &[vec![5.0]][..]) + ); + // The fill of the series' first mark. + let series_fill = |s: &Session| { + let mut fill = None; + s.doc().unwrap().doc.walk(|n| { + if n.extras().series_index == Some(0) && fill.is_none() { + let first = n.children().and_then(|c| c.first().cloned()); + fill = first.and_then(|m| m.appearance.items.iter().find(|i| i.is_fill()).map(|i| i.paint().clone())); + } + }); + fill + }; + let painted = series_fill(&s); + assert!(painted.is_some()); + s.execute("select.set", &json!({"ids": [id.0]})).unwrap(); + s.execute("graph.setData", &json!({"id": id.0, "csv": ",East\nQ1,8\nQ2,2"})).unwrap(); + let after = spec(&s, id); + assert_eq!(after.rows, [[8.0], [2.0]]); + assert_eq!(after.rect, before.rect, "the graph is redrawn on its plot"); + assert_eq!(series_fill(&s), painted, "the series keeps the file's paint"); + for (name, version) in [("v7.ai", Some("7")), ("pdf.ai", None)] { + let mut p = json!({"path": dir.join(name), "format": "ai"}); + if let Some(v) = version { + p["aiVersion"] = json!(v); + } + let r = s.execute("file.saveAs", &p).unwrap(); + let notes = r["warnings"].as_array().cloned().unwrap_or_default(); + assert!(notes.iter().any(|w| w.as_str().is_some_and(|w| w.starts_with("graphs are written as their art"))), "{name}: {r}"); + } +} diff --git a/crates/engine/tests/import_fuzz.rs b/crates/engine/tests/import_fuzz.rs index 04e7094f9e..79178d6233 100644 --- a/crates/engine/tests/import_fuzz.rs +++ b/crates/engine/tests/import_fuzz.rs @@ -7488,3 +7488,33 @@ proptest! { prop_assert!(!notes.iter().any(|n| n.contains("art this can't read")), "the records aren't art: {:?}", notes); } } + +/// A graph in `.ai` editing data whose records read whole (version 7 specification, appendix A), of +/// a table `rows` × `cols` (its label row and column included): a series group tagged by its +/// column, and the axes on the plot's edges. +fn live_graph_text(rows: &str, cols: &str) -> String { + format!( + "%_Gs\n%_10 90 110 10 Gb\n%_5 0 0 14 0 1 0 0 6 44 Gy\n%_10 2 0 0 90 80 0 Gd\n\ + %_1 () Ga\n%_0 13 0 0 0 0 0 () GA\n%_2 () Ga\n%_0 14 0 0 0 0 0 () GA\n%_4 () Ga\n%_0 14 0 0 0 0 0 () GA\n\ + %_{rows} {cols} 1 1 Gz\n%_() Gc (East) Gc (Q1) Gc 5 Gc GC\n\ + u\nu\n0 0 0 1 k\n20 10 m\n20 50 L\n40 50 L\n40 10 L\nf\nU\n%_2 1 0 0 Go\nu\n0 0 0 1 K\n10 90 m\n10 10 L\n110 10 L\nS\nU\nU\n%_GS\n" + ) +} + +proptest! { + #![proptest_config(config())] + + /// A `.ai` graph that reads whole, then cut or damaged, or with a table of any size (zero, past + /// the limits, past what memory holds), never panics, and its drawn art always comes in. + #[test] + fn ai_live_graph_never_panics(cut in 0usize..800, edits in prop::collection::vec((0usize..800, prop::sample::select(vec!['0', '9', '-', '.', ' ', '\n', '(', ')', '/', '%', '_', 'G', 'c', 'C', 'o', 's', 'S', 'u', 'U'])), 0..12), rows in prop_oneof![Just(2u64), 0u64..20_000, Just(u64::MAX)], cols in prop_oneof![Just(2u64), 0u64..400, Just(4_000_000_000u64)]) { + let text = mutate_text(&live_graph_text("2", "2"), cut, &edits); + survive("mutated live .ai graph", || Some(vectorcraft_eps::layered_ai(&ai_private(&text), Document::new(100.0, 100.0), vec![], false, true, 1.0).0))?; + let sized = live_graph_text(&rows.to_string(), &cols.to_string()); + let (doc, notes, read) = vectorcraft_eps::layered_ai(&ai_private(&sized), Document::new(4000.0, 200.0), vec![], false, true, 1.0); + prop_assert!(read, "{:?}", notes); + let mut paths = 0; + doc.walk(|n| paths += usize::from(matches!(n.kind, vectorcraft_doc::NodeKind::Path { .. }))); + prop_assert_eq!(paths, 2, "{:?}", notes); + } +} diff --git a/crates/eps/src/ai_editing/writer.rs b/crates/eps/src/ai_editing/writer.rs index 7db5d11896..967d1f7ed5 100644 --- a/crates/eps/src/ai_editing/writer.rs +++ b/crates/eps/src/ai_editing/writer.rs @@ -56,6 +56,7 @@ pub(super) const CLIPPING: &str = "clipping groups and clipping layers whose cli pub(super) const CLIP_PAINT: &str = "the fills and strokes of clipping paths that a plain fill and line can't draw are written as paths of their own inside the clipping (fills under the clipped art, strokes over it) in the editing data for other apps"; pub(super) const CLIP_PAINT_MASKS: &str = "the opacity masks of clipping paths whose fills and strokes are written as paths of their own stay on the clipping path, not on those paths, in the editing data for other apps"; pub(super) const LIVE: &str = "blends, meshes, repeats, placed documents and intertwines are written as their art in the editing data for other apps"; +pub(super) const GRAPHS: &str = "graphs are written as their art, without their data or settings, in the editing data for other apps"; pub(super) const STROKES: &str = "strokes a plain line can't draw (inside or outside the path, variable width, dots, dashes fitted to corners, gradients, patterns, arrowheads) are written as filled outlines, and brushed strokes as their brush's art, in the editing data for other apps"; pub(super) const ITEM_TRANSPARENCY: &str = "the opacity and blending modes of single fills and strokes are left out of the editing data for other apps"; @@ -613,6 +614,9 @@ impl<'a> Writer<'a> { if self.in_symbol { self.wrote_fixed |= symbols::redrawn(n); } + if n.extras().graph.is_some() && !self.in_look { + self.warn(GRAPHS); + } // On the layers, outside art cut to a clipping object: with its art style, when it has one. if !self.in_look && self.hidden == 0 diff --git a/crates/eps/src/import/ai/read.rs b/crates/eps/src/import/ai/read.rs index 4b2936f56e..b77bc32199 100644 --- a/crates/eps/src/import/ai/read.rs +++ b/crates/eps/src/import/ai/read.rs @@ -78,7 +78,8 @@ //! //! - Graphs (version 7 specification §17): `%_Gs`, the graph's data and settings on `%_` lines, //! its drawn art as ordinary groups, paths and type (each object followed by `%_… Go`), `%_GS`. -//! The drawn art is read; the data and settings are left out, with a note. +//! The drawn art is read as it is. Its group keeps the data and settings when they read whole +//! ([`graph`]); otherwise they are left out, with a note. //! //! - Swatches: the palette (`%AI5_BeginPalette`), read by [`super::palette`], replaces the //! default swatches. @@ -133,6 +134,7 @@ mod art; mod containers; mod directives; mod document; +mod graph; mod masks; mod on_path; mod ops; @@ -146,12 +148,13 @@ pub use document::{ symbols, variables, }; pub(crate) use document::{slot_frame, slot_of}; +use graph::Graph; pub(crate) use ops::{read_art_style, read_style_list, style_moves_with}; #[cfg(test)] pub(crate) use raster::with_image_limits; pub(crate) use types::MAX_NODES; pub use types::PlacedPage; -use types::{BLEND_BUDGET, BrushArtColors, FLIP, Frame, GState, Graph, Kind, PatternHead, PendingEnvelope, Place, PlacedTail, Raster, SymbolHead}; +use types::{BLEND_BUDGET, BrushArtColors, FLIP, Frame, GState, Kind, PatternHead, PendingEnvelope, Place, PlacedTail, Raster, SymbolHead}; #[cfg(test)] pub use types::{GRAPH_PLAIN, GRAPH_UNENDED, LINKED_PLACEHOLDERS, NON_NATIVE_ART, NON_NATIVE_ART_UNREAD, VERSIONED_UNENDED}; pub(super) use types::{blend_mode, blend_name}; @@ -670,7 +673,7 @@ struct Reader<'a> { brushes_kept: BrushesKept, /// Brush objects that weren't read. brushes_unread: usize, - /// The graph being read (see [`Reader::graph_record`]). + /// The graph being read (see [`graph`]). graph: Option, /// The multi-layer mask being read (`Mb` … `MB`), which goes on past a layer's end. multi_mask: Option, diff --git a/crates/eps/src/import/ai/read/document.rs b/crates/eps/src/import/ai/read/document.rs index 0bcae74b32..ded114bc67 100644 --- a/crates/eps/src/import/ai/read/document.rs +++ b/crates/eps/src/import/ai/read/document.rs @@ -1165,7 +1165,9 @@ impl<'a> Reader<'a> { } pub(super) fn finish(mut self) -> Result { - if self.graph.take().is_some() { + if self.graph.is_some() { + self.graph_plain(); + self.graph = None; self.warn(super::types::GRAPH_UNENDED); } // Envelopes whose plugin never closed them keep their drawn art. diff --git a/crates/eps/src/import/ai/read/graph.rs b/crates/eps/src/import/ai/read/graph.rs new file mode 100644 index 0000000000..759eb4e1cd --- /dev/null +++ b/crates/eps/src/import/ai/read/graph.rs @@ -0,0 +1,681 @@ +//! A graph's records (version 7 specification §17 and appendix A) and the live graph they give. +//! +//! A graph is `%_Gs`, its functional records on `%_` lines (`Gb`, `Gy`, `Gd`, any `Gj`, three +//! `Ga` … `GA` axes, any `Gw`, `Gz`, `Gc` cells and `GC`), any `version Gt` … `GT` customizations, its +//! drawn art in one group (objects followed by `%_… Go`), then `%_GS`. The drawn art is always +//! read as it is. The group also keeps the graph's data and settings ([`GraphSpec`]) when every +//! record has the shape and values appendix A gives and the model holds each setting, the table +//! has data, the art draws the plot's left and bottom edges, and each series' group is tagged, is +//! one of the graph's own groups and paints its paths with one fill and one stroke, which the +//! graph keeps as the series' paint. +//! Any other graph stays plain art, with a note. + +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +use kurbo::{PathSeg, Point}; +use vectorcraft_doc::{Appearance, GraphKind, GraphSpec, MAX_GRAPH_CATEGORIES, MAX_GRAPH_SERIES, SeriesPaint, TickLength}; + +use super::types::{GRAPH_OPS, GRAPH_PLAIN, GRAPH_UNENDED, Kind}; +use super::{Affine, AppearanceItem, BlendMode, Node, NodeId, NodeKind, Paint, Reader, Rect, V, obj}; + +/// Most cells of a graph's table, its label row and column included. +const MAX_CELLS: usize = 2_600_000; + +/// Longest cell string (appendix A, `Gc`). +const MAX_CELL_TEXT: usize = 255; + +/// Most characters of an axis label's text before or after its numbers (appendix A, `Ga`, `GA`). +const MAX_AFFIX: usize = 9; + +/// Most tick marks per division a graph draws. +const MAX_TICK_MARKS: usize = 20; + +/// Most divisions of a value axis with set values. +const MAX_DIVISIONS: f64 = 100.0; + +/// How far an axis line may be from the plot's edge, in points. +const EDGE: f64 = 0.01; + +/// The axes in the order the records give them: bottom, left, right. +const AXES: [i64; 3] = [1, 2, 4]; + +/// The `Go` targets appendix A lists. +const TARGETS: [i64; 11] = [1, 2, 4, 5, 6, 8, 9, 10, 15, 20, 22]; + +/// The version of a graph's customizations (appendix A, `Gt`). +const CUSTOM_VERSION: i64 = 2; + +/// `Go`'s target for one series, including its legend. +const SERIES: i64 = 2; + +/// A graph being read (`%_Gs` … `%_GS`). +#[derive(Debug)] +pub(super) struct Graph { + /// Between `Gt` and `GT`, where the graph's settings may use paint and type operators on `%_` + /// lines too. + pub(super) settings: bool, + /// How many frames were open at its `Gs`: a layer that ends with fewer ends the graph. + pub(super) frames: usize, + /// How many objects the innermost frame held at `Gs`: the drawn art is the one after them. + siblings: usize, + /// The records read so far, while they can still give a live graph. + live: Option>, +} + +/// Where the records are, in the order appendix A gives. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +enum Stage { + #[default] + Bounds, + Style, + Dialog, + /// Before the `Ga` of axis `n`. + Axis(usize), + /// Before the `GA` of axis `n`. + AxisEnd(usize), + Widths, + Cells, + /// After `GC`: customizations, then the drawn art. + Done, + /// Between `Gt` and `GT`. + Custom, +} + +/// One axis: `whichAxis beforeString Ga` and `useManualValues tickMarkType minimumValue maximumValue +/// betweenValue smallTicksPerValue drawMarksBetweenLabels afterString GA`. +#[derive(Debug, Default)] +struct Axis { + before: Vec, + manual: bool, + tick: TickLength, + min: f64, + max: f64, + between: f64, + small: usize, + between_labels: bool, + after: Vec, +} + +/// A cell of the table (`Gc`). +#[derive(Debug)] +enum Cell { + Num(f64), + Text(Vec), +} + +/// The records of a graph read so far. +#[derive(Debug, Default)] +struct Records { + stage: Stage, + spec: GraphSpec, + axes: [Axis; 3], + /// `Gw`: the first column and how many widths. + widths: Vec<(usize, usize)>, + /// `Gz`: rows, columns, first data row and first data column. + size: Option<[usize; 4]>, + cells: Vec, + /// The groups `Go` tags as one series, and the column each names. + series: Vec<(NodeId, usize)>, +} + +/// A whole number. +fn int(v: &V) -> Option { + v.num().filter(|x| x.is_finite() && x.fract() == 0.0 && x.abs() < 1e15).map(|x| x as i64) +} + +/// 0 or 1. +fn flag(v: &V) -> Option { + match int(v)? { + 0 => Some(false), + 1 => Some(true), + _ => None, + } +} + +fn finite(v: &V) -> Option { + v.num().filter(|x| x.is_finite()) +} + +/// A count from 0 to `max`. +fn count(v: &V, max: usize) -> Option { + usize::try_from(int(v)?).ok().filter(|n| *n <= max) +} + +/// `tickMarkType`: 13 none, 14 short, 15 the length of the plot. +fn tick(v: &V) -> Option { + match int(v)? { + 13 => Some(TickLength::None), + 14 => Some(TickLength::Short), + 15 => Some(TickLength::Full), + _ => None, + } +} + +/// `graphType`: 5 grouped column, 6 stacked column, 7 line, 10 area. Pie (8) and scatter (9) graphs +/// keep settings the records don't give the model, and other values are not graph types. +fn kind(v: &V) -> Option { + match int(v)? { + 5 => Some(GraphKind::Column), + 6 => Some(GraphKind::StackedColumn), + 7 => Some(GraphKind::Line), + 10 => Some(GraphKind::Area), + _ => None, + } +} + +/// A percentage of the available width, 1 to 1000. +fn percent(v: &V) -> Option { + finite(v).filter(|p| (1.0..=1000.0).contains(p)) +} + +/// The tick count and values of an axis: with set values (`useManualValues` 1), the divisions +/// between its lowest and highest value, a whole number of `betweenValue` steps from the lowest up. +fn values(a: &Axis) -> Option<(usize, Option, Option)> { + if !a.manual { + return Some((0, None, None)); + } + let n = (a.max - a.min) / a.between; + let whole = n.round(); + let exact = (n - whole).abs() <= 1e-6 * whole.max(1.0); + (a.between > 0.0 && a.max > a.min && exact && (1.0..=MAX_DIVISIONS).contains(&whole)).then_some((whole as usize, Some(a.min), Some(a.max))) +} + +impl Records { + /// Take the record `w` with its operands `vals`; `None` when its shape, values or place aren't + /// those appendix A gives, or the model doesn't hold a value it sets. `to_doc` takes art space + /// to the document. + fn step(&mut self, w: &str, vals: &[V], to_doc: Affine) -> Option<()> { + match (w, self.stage) { + ("Gb", Stage::Bounds) => { + let [l, t, r, b] = vals else { return None }; + let [l, t, r, b] = [finite(l)?, finite(t)?, finite(r)?, finite(b)?]; + let plot = to_doc.transform_rect_bbox(Rect::new(l, b, r, t)); + let sound = r > l && t > b && plot.width() > 0.0 && plot.height() > 0.0 && plot.width().is_finite() && plot.height().is_finite(); + sound.then_some(())?; + self.spec.rect = plot; + self.stage = Stage::Style; + } + ("Gy", Stage::Style) => { + self.style(vals)?; + self.stage = Stage::Dialog; + } + ("Gd", Stage::Dialog) => { + let [col_width, precision, series_order, both_axes, bar, group, edge] = vals else { return None }; + count(col_width, 20).filter(|w| *w >= 3)?; + count(precision, 10)?; + // `useBothAxis` copies one axis' values to the other, which the model doesn't do. + (!flag(both_axes)?).then_some(())?; + self.spec.first_column_in_front = flag(series_order)?; + self.spec.column_width = percent(bar)?; + self.spec.cluster_width = percent(group)?; + self.spec.edge_to_edge = flag(edge)?; + self.stage = Stage::Axis(0); + } + ("Ga", Stage::Axis(n)) if n < AXES.len() => { + let [axis, V::Str(before)] = vals else { return None }; + (int(axis)? == *AXES.get(n)?).then_some(())?; + self.axes.get_mut(n)?.before = before.clone(); + self.stage = Stage::AxisEnd(n); + } + ("GA", Stage::AxisEnd(n)) => { + let [manual, ticks, min, max, between, small, between_labels, V::Str(after)] = vals else { return None }; + let a = self.axes.get_mut(n)?; + (a.manual, a.tick) = (flag(manual)?, tick(ticks)?); + (a.min, a.max, a.between) = (finite(min)?, finite(max)?, finite(between)?); + a.small = count(small, 1000)?; + a.between_labels = flag(between_labels)?; + a.after = after.clone(); + self.stage = Stage::Axis(n + 1); + } + ("Gw", Stage::Axis(3) | Stage::Widths) => { + // `col width1 … widthX numParams Gw`: numParams counts `col` and the 1 to 14 widths. + let (params, rest) = vals.split_last()?; + let params = count(params, 15).filter(|p| *p >= 2 && *p == rest.len())?; + let (col, widths) = rest.split_first()?; + let col = count(col, MAX_CELLS)?; + widths.iter().all(|w| int(w).is_some_and(|w| w == -1 || (3..=20).contains(&w))).then_some(())?; + self.widths.push((col, params - 1)); + self.stage = Stage::Widths; + } + ("Gz", Stage::Axis(3) | Stage::Widths) => { + let [rows, cols, first_row, first_col] = vals else { return None }; + let (rows, cols) = (count(rows, MAX_CELLS)?, count(cols, MAX_CELLS)?); + let (first_row, first_col) = (usize::from(flag(first_row)?), usize::from(flag(first_col)?)); + rows.checked_sub(first_row).filter(|c| (1..=MAX_GRAPH_CATEGORIES).contains(c))?; + cols.checked_sub(first_col).filter(|s| (1..=MAX_GRAPH_SERIES).contains(s))?; + rows.checked_mul(cols).filter(|n| *n <= MAX_CELLS)?; + self.widths.iter().all(|(col, n)| col.checked_add(*n).is_some_and(|end| end <= cols)).then_some(())?; + self.size = Some([rows, cols, first_row, first_col]); + self.stage = Stage::Cells; + } + ("Gc", Stage::Cells) => { + // One to fifteen values, at most one of them a string. + let strings = vals.iter().filter(|v| matches!(v, V::Str(_))).count(); + ((1..=15).contains(&vals.len()) && strings <= 1).then_some(())?; + let [rows, cols, ..] = self.size?; + (self.cells.len() + vals.len() <= rows * cols).then_some(())?; + for v in vals { + self.cells.push(match v { + V::Str(s) if s.len() <= MAX_CELL_TEXT => Cell::Text(s.clone()), + _ => Cell::Num(finite(v)?), + }); + } + } + ("GC", Stage::Cells) if vals.is_empty() => self.stage = Stage::Done, + // `version Gt`, of version 2. Customizations are edits made after the graph was built, which + // the model can't replay: only an empty list keeps the graph live. + ("Gt", Stage::Done) => { + let [version] = vals else { return None }; + (int(version)? == CUSTOM_VERSION).then_some(())?; + self.stage = Stage::Custom; + } + ("GT", Stage::Custom) if vals.is_empty() => self.stage = Stage::Done, + _ => return None, + } + Some(()) + } + + /// `graphType shadow dataPointOrder pieLegendStyle drawMarks drawLines drawLinesAsShapes + /// drawLegendsAcrossTop lineShapeWidth whichAxis [piePercentage piePctDigits] Gy`. + fn style(&mut self, vals: &[V]) -> Option<()> { + let (base, pie) = match vals.len() { + 10 => (vals, None), + 12 => vals.split_at_checked(10).map(|(b, p)| (b, Some(p)))?, + _ => return None, + }; + let [ty, shadow, order, legend, marks, lines, shapes, across, width, axis] = base else { return None }; + self.spec.kind = kind(ty)?; + self.spec.drop_shadow = flag(shadow)?; + self.spec.first_row_in_front = flag(order)?; + // 14: legends as in bar and line graphs; 15 (in wedges) and 16 (none) are pie styles. + (int(legend)? == 14).then_some(())?; + self.spec.legend = true; + self.spec.mark_points = flag(marks)?; + self.spec.connect_points = flag(lines)?; + // Lines drawn as shapes have a width the model doesn't keep. + (!flag(shapes)?).then_some(())?; + self.spec.filled_lines = false; + self.spec.legend_across_top = flag(across)?; + finite(width).filter(|w| (0.0..=100.0).contains(w))?; + // 44: the series are measured against the left axis; 45 (right) isn't kept per series. + (int(axis)? == 44).then_some(())?; + if let Some([percentage, digits]) = pie { + // Pie percentages, which only pie graphs show; shown (1) isn't kept. + (!flag(percentage)?).then_some(())?; + int(digits).filter(|d| *d >= 0)?; + } + Some(()) + } + + /// The graph's data and settings, its strings read as names in `names`; `None` when the table + /// or an axis holds what the model can't. + fn finish(&self, names: &mut obj::Names) -> Option { + let [rows, cols, first_row, first_col] = self.size?; + let mut spec = self.spec.clone(); + let text = |names: &mut obj::Names, b: &[u8], max: usize| { + let t = names.quiet(b); + (t.chars().count() <= max && !t.chars().any(char::is_control)).then(|| names.bytes(b)) + }; + // Axes: the bottom one is the category axis, the left one the value axis; the right one keeps + // its settings for when it is shown. + let [bottom, left, right] = &self.axes; + (bottom.before.is_empty() && bottom.after.is_empty() && !bottom.manual).then_some(())?; + (!left.between_labels && !right.between_labels).then_some(())?; + (spec.category_tick_length, spec.category_tick_marks) = (bottom.tick, bottom.small.max(1)); + spec.ticks_between_labels = bottom.between_labels; + (spec.tick_length, spec.tick_marks) = (left.tick, left.small.max(1)); + (spec.right_tick_length, spec.right_tick_marks) = (right.tick, right.small.max(1)); + (spec.category_tick_marks <= MAX_TICK_MARKS && spec.tick_marks <= MAX_TICK_MARKS && spec.right_tick_marks <= MAX_TICK_MARKS).then_some(())?; + (spec.ticks, spec.axis_min, spec.axis_max) = values(left)?; + (spec.right_ticks, spec.right_axis_min, spec.right_axis_max) = values(right)?; + spec.prefix = text(names, &left.before, MAX_AFFIX)?; + spec.suffix = text(names, &left.after, MAX_AFFIX)?; + spec.right_prefix = text(names, &right.before, MAX_AFFIX)?; + spec.right_suffix = text(names, &right.after, MAX_AFFIX)?; + // The table, row by row; cells left out at its end are empty. + let cell = |r: usize, c: usize| self.cells.get(r * cols + c); + let label = |names: &mut obj::Names, r: usize, c: usize| match cell(r, c) { + None => Some(String::new()), + Some(Cell::Text(t)) => text(names, t, MAX_CELL_TEXT), + Some(Cell::Num(_)) => None, + }; + // The corner of a table with both label row and label column holds nothing. + let corner_empty = match cell(0, 0) { + None => true, + Some(Cell::Text(t)) => t.is_empty(), + Some(Cell::Num(_)) => false, + }; + if first_row == 1 && first_col == 1 && !corner_empty { + return None; + } + spec.series = if first_row == 1 { (first_col..cols).map(|c| label(names, 0, c)).collect::>()? } else { vec![] }; + spec.categories = if first_col == 1 { (first_row..rows).map(|r| label(names, r, 0)).collect::>()? } else { vec![] }; + let mut data = Vec::with_capacity(rows - first_row); + for r in first_row..rows { + let row = (first_col..cols) + .map(|c| match cell(r, c) { + None => Some(None), + Some(Cell::Text(t)) if t.is_empty() => Some(None), + Some(Cell::Text(_)) => None, + Some(Cell::Num(v)) => Some(Some(*v)), + }) + .collect::>>()?; + data.push(row); + } + data.iter().flatten().any(Option::is_some).then_some(())?; + spec.set_cells(data); + Some(spec) + } + + /// The series index of each group `Go` tagged as one series, by id: every series once, its + /// columns counted in the table or among the series alike. `None` when the tags are missing, + /// repeated or out of range: the series' paint is read from its group. + fn series_tags(&self) -> Option> { + let [_, cols, _, first_col] = self.size?; + let n = cols - first_col; + let ids: HashSet = self.series.iter().map(|(id, _)| *id).collect(); + (ids.len() == self.series.len() && self.series.len() == n).then_some(())?; + let indexes = |offset: usize| -> Option> { + let mut seen = HashSet::new(); + let tags: HashMap = self + .series + .iter() + .map(|(id, col)| { + let s = col.checked_sub(offset).filter(|s| *s < n && seen.insert(*s))?; + Some((*id, u32::try_from(s).ok()?)) + }) + .collect::>()?; + Some(tags) + }; + // With a label column, the columns read as the table's or as the series' are told apart by the + // set they make; without one, they are the same. + match (indexes(0), indexes(first_col)) { + (Some(a), Some(b)) if a == b => Some(a), + (Some(a), None) | (None, Some(a)) => Some(a), + _ => None, + } + } +} + +/// Whether `art` draws the plot `plot`'s left edge and its bottom edge (the document's y runs down) +/// as straight stroked lines: the value axis and the category axis, drawn on the plot's edges. +fn draws_axes(art: &Node, plot: Rect) -> bool { + let near = |a: Point, b: Point| (a - b).hypot() <= EDGE; + let on = |s: &PathSeg, a: Point, b: Point| match s { + PathSeg::Line(l) => (near(l.p0, a) && near(l.p1, b)) || (near(l.p0, b) && near(l.p1, a)), + _ => false, + }; + let (top_left, bottom_left, bottom_right) = (Point::new(plot.x0, plot.y0), Point::new(plot.x0, plot.y1), Point::new(plot.x1, plot.y1)); + let (mut left, mut bottom) = (false, false); + let mut stack = vec![art]; + while let Some(n) = stack.pop() { + // Only shown objects draw the axes, and a path only with a stroke. + if !n.visible || n.opacity <= 0.0 || (left && bottom) { + continue; + } + let stroked = n.appearance.items.iter().any(|it| !it.is_fill() && it.visible() && it.opacity() > 0.0 && !it.paint().is_none()); + if let NodeKind::Path { path, .. } = &n.kind + && stroked + { + for s in path.to_bezpath().segments() { + left |= on(&s, top_left, bottom_left); + bottom |= on(&s, bottom_left, bottom_right); + } + } + stack.extend(n.children().into_iter().flatten().map(|c| &**c)); + } + left && bottom +} + +/// Give the groups among `n`'s children that `tags` holds their series index. +fn tag_series(n: &mut Node, tags: &HashMap) { + for c in n.children_mut().into_iter().flatten() { + if let Some(i) = tags.get(&c.id) { + Arc::make_mut(c).extras_mut().series_index = Some(*i); + } + } +} + +/// Whether every node `tags` holds is a group among `n`'s children: the graph draws its series +/// groups again as its own children. +fn tags_groups(n: &Node, tags: &HashMap) -> bool { + let found = n.children().into_iter().flatten().filter(|c| tags.contains_key(&c.id) && matches!(c.kind, NodeKind::Group { .. })).count(); + found == tags.len() +} + +/// The paint of the shown fill (`fill`) or stroke of `p`, with a stroke's weight; `None` when it +/// has none. `Err` when it has more than one, or that fill or stroke has an opacity, a blend mode +/// or effects of its own, which a series' paint doesn't hold. +fn shown(p: &Node, fill: bool) -> Result, ()> { + let mut items = p.appearance.items.iter().filter(|it| it.is_fill() == fill && it.visible()); + let Some(item) = items.next() else { return Ok(None) }; + if items.next().is_some() || item.opacity() != 1.0 || item.blend() != BlendMode::Normal || !item.effects().is_empty() { + return Err(()); + } + if item.paint().is_none() { + return Ok(None); + } + let width = match item { + AppearanceItem::Stroke(s) => s.width, + AppearanceItem::Fill(_) => 0.0, + }; + Ok(Some((item.paint().clone(), width))) +} + +/// Whether `n` is shown, fully opaque, in Normal mode, not isolated and without an opacity mask: +/// the graph draws its series groups and their marks so. +fn as_drawn(n: &Node) -> bool { + n.visible && n.opacity == 1.0 && n.blend == BlendMode::Normal && !n.isolate && n.extras().mask.is_none() +} + +/// Each series' fill and stroke, read from its group (`tags`), which holds paths only, one at +/// least: the paths with a fill share it, and every path has the same stroke or none. Only a line +/// graph (`line`) has paths without a fill: its lines. `None` when they differ, or the group, its +/// own appearance or a path is drawn otherwise than [`as_drawn`]. +fn series_paints(art: &Node, tags: &HashMap, line: bool) -> Option> { + let mut paints = vec![SeriesPaint::default(); tags.len()]; + for group in art.children().into_iter().flatten() { + let Some(slot) = tags.get(&group.id).and_then(|i| paints.get_mut(usize::try_from(*i).ok()?)) else { continue }; + (as_drawn(group) && *group.appearance == Appearance::default()).then_some(())?; + let paths: Vec<&Arc> = group.children().into_iter().flatten().collect(); + (!paths.is_empty() && paths.iter().all(|p| matches!(p.kind, NodeKind::Path { .. }))).then_some(())?; + let (mut fills, mut strokes) = (vec![], vec![]); + for p in &paths { + as_drawn(p).then_some(())?; + fills.extend(shown(p, true).ok()?); + strokes.push(shown(p, false).ok()?); + } + let fill = fills.first().map(|(paint, _)| paint.clone()); + fills.iter().all(|(paint, _)| Some(paint) == fill.as_ref()).then_some(())?; + (line || fills.len() == paths.len()).then_some(())?; + let stroke = strokes.first().cloned().flatten(); + strokes.iter().all(|s| *s == stroke).then_some(())?; + slot.fill = fill; + (slot.stroke, slot.stroke_width) = match stroke { + Some((paint, width)) => (Some(paint), Some(width)), + None => (Some(Paint::None), None), + }; + } + Some(paints) +} + +impl<'a> Reader<'a> { + /// A graph's records: `%_Gs` starts the graph on a layer, `GS` ends it, and its data and settings + /// (the operators in [`GRAPH_OPS`], and any operator on a `%_` line between `Gt` and `GT`) are + /// taken with their operands `vals`. Its drawn art, and anything else, is read as usual. `true` + /// when `w` was one of them. `ended`: whether a group and a mesh had just ended before `w`, which + /// a `Go` tag after them leaves so. + pub(super) fn graph_record(&mut self, w: &str, vals: &[V], hidden: bool, ended: (bool, bool)) -> bool { + let depth = self.frames.len(); + let Some(g) = self.graph.as_mut() else { + // Outside a graph, its operators stay ones the reader doesn't know. + if w == "Gs" && hidden && !self.frames.is_empty() { + let siblings = self.frames.last().map_or(0, |f| f.children.len()); + // A graph is live only where its art is in the document's space, in a layer or a group. + let placed = self.pattern.is_none() + && self.symbol.is_none() + && vals.is_empty() + && matches!(self.frames.last().map(|f| &f.kind), Some(Kind::Layer(_) | Kind::Group | Kind::Clip)); + self.graph = Some(Graph { settings: false, frames: depth, siblings, live: placed.then(Box::default) }); + return true; + } + return false; + }; + // The frame the graph started in closed: its art isn't where it was. + if depth < g.frames { + self.graph_plain(); + } + let Some(g) = self.graph.as_mut() else { return false }; + let stage = g.live.as_ref().map(|r| r.stage); + match w { + // A graph doesn't start inside another: the one being read goes on. + "Gs" => { + self.graph_plain(); + self.warn(GRAPH_UNENDED); + } + "GS" => self.graph_end(vals.is_empty() && hidden), + "Gt" | "GT" => { + g.settings = w == "Gt"; + self.graph_step(w, vals, hidden); + } + "Go" => { + self.graph_tag(vals, hidden); + (self.group_ended, self.mesh_ended) = ended; + } + _ if GRAPH_OPS.contains(&w) => self.graph_step(w, vals, hidden), + _ if hidden && g.settings => self.graph_plain(), + _ => { + // Before its table ends, a graph has only its records. + if !matches!(stage, None | Some(Stage::Done)) { + self.graph_plain(); + } + return false; + } + } + true + } + + /// The graph being read can't be live: its art stays plain, with a note. + pub(super) fn graph_plain(&mut self) { + if let Some(g) = self.graph.as_mut() { + g.live = None; + } + self.warn(GRAPH_PLAIN); + } + + /// Take a functional record or customization on a `%_` line. + fn graph_step(&mut self, w: &str, vals: &[V], hidden: bool) { + if self.graph.as_ref().is_none_or(|g| g.live.is_none()) { + return; + } + let to_doc = if w == "Gb" { self.space() } else { Affine::IDENTITY }; + let ok = hidden && self.graph.as_mut().and_then(|g| g.live.as_mut()).and_then(|r| r.step(w, vals, to_doc)).is_some(); + if !ok { + self.graph_plain(); + } + } + + /// `target column row whichAxis Go` after an object of the drawn art: a group tagged as one + /// series is kept with the column it names. + fn graph_tag(&mut self, vals: &[V], hidden: bool) { + let depth = self.frames.len(); + let last = self + .after_object + .then(|| self.frames.last().and_then(|f| f.children.last())) + .flatten() + .map(|n| (n.id, matches!(n.kind, NodeKind::Group { .. }))); + let Some(g) = self.graph.as_mut() else { return }; + let inside = depth > g.frames; + let Some(r) = g.live.as_mut() else { return }; + let tag = match vals { + [target, column, row, axis] if hidden && r.stage == Stage::Done => { + let (row, axis) = (int(row), int(axis)); + // A series is the whole column, measured on the left axis (2) or with no axis given (0): + // the model keeps no series on a row or on the right axis. + let whole_column = row == Some(0) && matches!(axis, Some(0 | 2)); + int(target).filter(|t| TARGETS.contains(t) && row.is_some() && axis.is_some()).zip(int(column)).map(|(t, c)| (t, c, whole_column)) + } + _ => None, + }; + let ok = match (tag, last) { + (Some((SERIES, column, true)), Some((id, true))) if inside => usize::try_from(column).ok().map(|c| r.series.push((id, c))).is_some(), + (Some((SERIES, ..)), _) => false, + (Some(_), Some(_)) => true, + _ => false, + }; + if !ok { + self.graph_plain(); + } + } + + /// `GS`: the graph ends. Its drawn art is the one object its frame gained, a group; it keeps the + /// graph's data and settings when they read whole (`whole`) and the art is registered on its plot. + fn graph_end(&mut self, whole: bool) { + let Some(g) = self.graph.take() else { return }; + let depth = self.frames.len(); + let live = g.live.filter(|r| whole && r.stage == Stage::Done && depth == g.frames); + let spec = live.as_ref().and_then(|r| r.finish(&mut self.names).zip(r.series_tags())); + let attached = spec.is_some_and(|(mut spec, tags)| { + let Some(art) = self.frames.last_mut().filter(|f| f.children.len() == g.siblings + 1).and_then(|f| f.children.last_mut()) else { + return false; + }; + if !matches!(art.kind, NodeKind::Group { clip: false, .. }) || !tags_groups(art, &tags) || !draws_axes(art, spec.rect) { + return false; + } + let Some(placed) = art.geometric_bounds().filter(|b| b.width() > 0.0 && b.height() > 0.0) else { return false }; + let r = spec.rect; + if r.x0 < placed.x0 - EDGE || r.y0 < placed.y0 - EDGE || r.x1 > placed.x1 + EDGE || r.y1 > placed.y1 + EDGE { + return false; + } + let Some(paints) = series_paints(art, &tags, spec.kind == GraphKind::Line) else { return false }; + spec.series_paints = paints; + spec.placed = Some(placed); + tag_series(art, &tags); + art.extras_mut().graph = Some(Box::new(spec)); + true + }); + if !attached { + self.warn(GRAPH_PLAIN); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use vectorcraft_color::Color; + use vectorcraft_geom::shapes; + + /// A graph's art: one series group (id 2) holding one mark painted `ap`, the group changed by + /// `group`. + fn art(ap: Appearance, group: impl Fn(&mut Node)) -> Node { + let mark = Arc::new(Node::path(NodeId(3), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), ap)); + let mut series = Node::group(NodeId(2), vec![mark]); + group(&mut series); + Node::group(NodeId(1), vec![Arc::new(series)]) + } + + /// A series whose mark has two fills has no one paint, so the graph can't draw it again. + #[test] + fn a_series_mark_with_two_fills_has_no_series_paint() { + let red = Paint::solid(Color::rgb(1.0, 0.0, 0.0)); + let one = Appearance::basic(red.clone(), Paint::None, 0.0); + let mut two = one.clone(); + two.items.extend(one.items.iter().find(|i| i.is_fill()).cloned()); + let tags = HashMap::from([(NodeId(2), 0)]); + let kept = series_paints(&art(one, |_| {}), &tags, false).expect("one fill"); + assert_eq!((kept[0].fill.as_ref(), kept[0].stroke.as_ref()), (Some(&red), Some(&Paint::None))); + assert!(series_paints(&art(two, |_| {}), &tags, false).is_none()); + } + + /// A series group that is hidden, see-through or in another blending mode is not drawn again so. + #[test] + fn a_series_group_drawn_otherwise_has_no_series_paint() { + let ap = || Appearance::basic(Paint::solid(Color::rgb(1.0, 0.0, 0.0)), Paint::None, 0.0); + let tags = HashMap::from([(NodeId(2), 0)]); + assert!(series_paints(&art(ap(), |g| g.visible = false), &tags, false).is_none(), "hidden"); + assert!(series_paints(&art(ap(), |g| g.opacity = 0.5), &tags, false).is_none(), "50% opaque"); + assert!(series_paints(&art(ap(), |g| g.blend = BlendMode::Multiply), &tags, false).is_none(), "Multiply"); + } +} diff --git a/crates/eps/src/import/ai/read/ops.rs b/crates/eps/src/import/ai/read/ops.rs index 83d0f71d40..3c642c5a02 100644 --- a/crates/eps/src/import/ai/read/ops.rs +++ b/crates/eps/src/import/ai/read/ops.rs @@ -1,12 +1,9 @@ use super::containers::close; use super::document::slot_of; -use super::types::{ - BLENDS, FLIP, Frame, GRAPH_OPS, GRAPH_PLAIN, GRAPH_UNENDED, IGNORED, Kind, MAX_DEPTH, MAX_NODES, MAX_TAG_TEXT, MAX_TAGS, Place, URL_TAG, - line_width, -}; +use super::types::{BLENDS, FLIP, Frame, GRAPH_UNENDED, IGNORED, Kind, MAX_DEPTH, MAX_NODES, MAX_TAG_TEXT, MAX_TAGS, Place, URL_TAG, line_width}; use super::{ - Affine, Appearance, AppearanceItem, Arc, BlendMode, BrushesKept, Dash, FillLayer, FillRule, Global, Gradient, GradientDef, Graph, HashMap, - HashSet, Instance, Knockout, LayerColor, LineCap, LineJoin, Node, NodeId, NodeKind, Paint, Reader, Rect, ShownNames, StrokeLayer, V, obj, paint, + Affine, Appearance, AppearanceItem, Arc, BlendMode, BrushesKept, Dash, FillLayer, FillRule, Global, Gradient, GradientDef, HashMap, HashSet, + Instance, Knockout, LayerColor, LineCap, LineJoin, Node, NodeId, NodeKind, Paint, Reader, Rect, ShownNames, StrokeLayer, V, obj, paint, }; impl<'a> Reader<'a> { @@ -69,6 +66,7 @@ impl<'a> Reader<'a> { /// `LB`: a graph started in this layer that hasn't ended doesn't go on past it; the layer ends. fn layer_end(&mut self) -> Result<(), String> { if self.graph.as_ref().is_some_and(|g| g.frames >= self.frames.len()) { + self.graph_plain(); self.graph = None; self.warn(GRAPH_UNENDED); } @@ -158,7 +156,7 @@ impl<'a> Reader<'a> { let mesh_ended = std::mem::take(&mut self.mesh_ended); let vals = self.operands(); let vals = self.take_head(vals); - if self.graph_record(w, hidden) { + if self.graph_record(w, &vals, hidden, (group_ended, mesh_ended)) { return Ok(()); } // Only `Bh` reads an undefined number. Anywhere else it would reach a point or a size, so @@ -425,33 +423,6 @@ impl<'a> Reader<'a> { Ok(()) } - /// A graph's records (version 7 specification §17 and appendix A): `%_Gs` starts the graph on - /// a layer, `GS` ends it, and its data and settings (the operators in [`GRAPH_OPS`], and any - /// operator on a `%_` line between `Gt` and `GT`) are left out with their operands. Its drawn - /// art, and anything else, is read as usual. `true` when `w` was one of them. - fn graph_record(&mut self, w: &str, hidden: bool) -> bool { - let Some(g) = self.graph.as_mut() else { - // Outside a graph, its operators stay ones the reader doesn't know. - if w == "Gs" && hidden && !self.frames.is_empty() { - self.graph = Some(Graph { settings: false, frames: self.frames.len() }); - self.warn(GRAPH_PLAIN); - return true; - } - return false; - }; - match w { - // A graph doesn't start inside another: the one being read goes on. - "Gs" => self.warn(GRAPH_UNENDED), - "GS" => self.graph = None, - "Gt" => g.settings = true, - "GT" => g.settings = false, - _ if GRAPH_OPS.contains(&w) => {} - _ if hidden && g.settings => {} - _ => return false, - } - true - } - /// `… Lb`: a layer's options, `visible preview unlocked printing dimmed`, then the colour index /// and its RGB. The colour index is at 5 with fewer than ten operands, 6 with ten to twelve /// (`masks` before it) and 7 with thirteen or more (one more flag before it). Four places after @@ -585,9 +556,9 @@ impl<'a> Reader<'a> { return; } self.image_tail = false; - // An object's tags (`XT`) follow it one after another, and an image's follow the `XH` that - // ends its data. - if matches!(w, "Xy" | "Xd" | "XW" | "BB" | "LB" | "XT" | "XH") { + // An object's tags (`XT`, and a graph's `Go`) follow it one after another, and an image's + // follow the `XH` that ends its data. + if matches!(w, "Xy" | "Xd" | "XW" | "BB" | "LB" | "XT" | "XH" | "Go") { return; } self.after_object = false; diff --git a/crates/eps/src/import/ai/read/types.rs b/crates/eps/src/import/ai/read/types.rs index 6457c1cde1..83a838a6cb 100644 --- a/crates/eps/src/import/ai/read/types.rs +++ b/crates/eps/src/import/ai/read/types.rs @@ -436,8 +436,8 @@ pub const NON_NATIVE_ART_UNREAD: &str = "non-native art (a placed PDF's content) /// The name of the group a `/ForeignObject`'s art comes in as. pub const NON_NATIVE_ART: &str = "Non-native art"; -/// The note for a file with a graph (`%_Gs` … `%_GS`): its drawn art comes in, its data and -/// settings don't. +/// The note for a file with a graph (`%_Gs` … `%_GS`) whose data and settings can't be kept: its +/// drawn art comes in as plain art. pub const GRAPH_PLAIN: &str = "graphs come in as plain art, without their data or settings"; /// The note for a graph that starts inside another or doesn't end before its layer does: the art @@ -445,18 +445,8 @@ pub const GRAPH_PLAIN: &str = "graphs come in as plain art, without their data o pub const GRAPH_UNENDED: &str = "a graph's records don't end where they should: its art was read as it is"; /// A graph's records (the version 7 specification's chapter 17 and appendix A): two-letter -/// operators starting with `G`, read and left out with their operands. +/// operators starting with `G`, read with their operands. pub(super) const GRAPH_OPS: &[&str] = &[ "G1", "G+", "Ga", "GA", "Gb", "Gc", "GC", "Gd", "GD", "Ge", "Gf", "Gg", "Gi", "Gj", "Gl", "Gm", "Go", "Gp", "Gr", "Gs", "GS", "Gt", "GT", "Gv", "Gw", "GX", "Gx", "Gy", "Gz", ]; - -/// A graph being read (`%_Gs` … `%_GS`). -#[derive(Debug)] -pub(super) struct Graph { - /// Between `Gt` and `GT`, where the graph's settings may use paint and type operators on `%_` - /// lines too. - pub(super) settings: bool, - /// How many frames were open at its `Gs`: a layer that ends with fewer ends the graph. - pub(super) frames: usize, -} diff --git a/crates/eps/src/import/ai/tests.rs b/crates/eps/src/import/ai/tests.rs index 23d2536adc..346f31210e 100644 --- a/crates/eps/src/import/ai/tests.rs +++ b/crates/eps/src/import/ai/tests.rs @@ -10434,6 +10434,7 @@ fn type_with_an_art_brush_keeps_its_drawn_look() { } use read::{GRAPH_PLAIN, GRAPH_UNENDED}; +use vectorcraft_doc::{GraphKind, TickLength}; /// Editing data in the legacy format (version 7 specification, chapter 2): `%AI5_FileFormat 2.0`, /// the art size and the template box in the header, then `body`. @@ -10575,6 +10576,223 @@ fn a_graphs_records_take_their_operands() { assert_eq!(r.warnings, [GRAPH_PLAIN]); } +/// A graph's style record (`Gy`) for a grouped column graph: no shadow, legends as in column +/// graphs, lines without marks, measured against the left axis. +const LIVE_STYLE: &str = "%_5 0 0 14 0 1 0 0 6 44 Gy\n"; + +/// A graph's dialog record (`Gd`): columns 90% wide in clusters 80% wide. +const LIVE_DIALOG: &str = "%_10 2 0 0 90 80 0 Gd\n"; + +/// A graph's axes, bottom, left and right: the bottom one with no tick marks, between its labels; +/// the left one labeled `$` … ` units`, with two tick marks a division. +const LIVE_AXES: &str = "%_1 () Ga\n%_0 13 0 0 0 0 1 () GA\n%_2 ($) Ga\n%_0 14 0 0 0 2 0 ( units) GA\n%_4 () Ga\n%_0 14 0 0 0 0 0 () GA\n"; + +/// A graph's table: series East and West, categories Q1 to Q3, and an empty cell (West in Q2). +const LIVE_TABLE: &str = "%_4 3 1 1 Gz\n%_() Gc (East) Gc (West) Gc\n%_(Q1) Gc 5 3 (Q2) Gc 7 () Gc (Q3) Gc 4 6 Gc GC\n"; + +/// A column of a graph's series at `x`, tagged as one data bar. +fn series_bar(x: f64) -> String { + format!("0 0 0 1 k\n{}f\n%_4 1 0 0 Go\n", rect_path(x, 100.0, x + 20.0, 150.0)) +} + +/// The first series' mark in [`live_art`]. +fn series_mark() -> String { + format!("0 0 0 1 k\n{}f\n", rect_path(110.0, 100.0, 130.0, 150.0)) +} + +/// [`series_mark`] filled and stroked, then an outline stroked alike with no fill. +fn series_mark_and_outline() -> String { + format!("0 0 0 1 k\n0 0 0 1 K\n{}B\n{}S\n", rect_path(110.0, 100.0, 130.0, 150.0), rect_path(112.0, 100.0, 118.0, 120.0)) +} + +/// The drawn art of the graph [`live_graph`] gives: two series groups, each tagged as one series by +/// its table column, and the axes on the plot's left and bottom edges. +fn live_art() -> String { + format!( + "u\n{}U\n%_2 1 0 0 Go\nu\n{}U\n%_2 2 0 0 Go\nu\n0 0 0 1 K\n100 200 m\n100 100 L\n300 100 L\nS\nU\n%_9 0 0 2 Go\n", + series_bar(110.0), + series_bar(140.0) + ) +} + +/// A graph whose records read whole, written from the version 7 specification's appendix A, on the +/// plot 100 100 300 200 (art space). +fn live_graph() -> String { + format!("%_Gs\n%_100 200 300 100 Gb\n{LIVE_STYLE}{LIVE_DIALOG}{LIVE_AXES}{LIVE_TABLE}u\n{}U\n%_GS\n", live_art()) +} + +/// The graphs in `doc`'s art, and the series groups tagged in them. +fn live_graphs(doc: &Document) -> (Vec, usize) { + let (mut graphs, mut tagged) = (vec![], 0); + doc.walk(|n| { + if n.extras().graph.is_some() { + graphs.push(n.clone()); + } + tagged += usize::from(n.extras().series_index.is_some()); + }); + (graphs, tagged) +} + +fn same_rect(a: Rect, b: Rect) -> bool { + (a.x0 - b.x0).abs() + (a.y0 - b.y0).abs() + (a.x1 - b.x1).abs() + (a.y1 - b.y1).abs() < 1e-9 +} + +/// A graph whose records read whole keeps its data and settings on its group, which holds its +/// drawn art as it is, each series group tagged with its index. +#[test] +fn a_graph_whose_records_read_whole_keeps_its_data_and_settings() { + let r = read(stream7(&layer7("Graph", &live_graph())).as_bytes()).unwrap(); + assert!(r.warnings.is_empty(), "{:?}", r.warnings); + let a = art(&r.doc); + assert_eq!(a.len(), 1); + let g = a[0].extras().graph.clone().expect("a live graph"); + assert_eq!(g.kind, GraphKind::Column); + assert_eq!(g.series, ["East", "West"]); + assert_eq!(g.categories, ["Q1", "Q2", "Q3"]); + assert_eq!(g.cells(), [[Some(5.0), Some(3.0)], [Some(7.0), None], [Some(4.0), Some(6.0)]]); + assert_eq!((g.column_width, g.cluster_width), (90.0, 80.0)); + assert_eq!((g.prefix.as_str(), g.suffix.as_str(), g.tick_length, g.tick_marks), ("$", " units", TickLength::Short, 2)); + assert_eq!((g.category_tick_length, g.ticks_between_labels), (TickLength::None, true)); + assert_eq!((g.ticks, g.axis_min, g.axis_max), (0, None, None)); + assert!(g.legend && !g.legend_across_top && !g.mark_points && g.connect_points && !g.drop_shadow && !g.edge_to_edge, "{g:?}"); + // The drawn art: two series groups and the axes, as written. + let kids = a[0].children().unwrap(); + assert_eq!(kids.len(), 3); + let index: Vec<_> = kids.iter().map(|k| k.extras().series_index).collect(); + assert_eq!(index, [Some(0), Some(1), None]); + assert!(kids[..2].iter().all(|k| k.children().is_some_and(|c| c.len() == 1))); + // Each series keeps its marks' fill, and no stroke, for when the graph is drawn again. + assert_eq!(g.series_paints.len(), 2); + let fill = kids[0].children().unwrap()[0].appearance.items.iter().find(|i| i.is_fill()).map(|i| i.paint().clone()); + assert!(g.series_paints.iter().all(|p| p.fill == fill && p.stroke.as_ref().is_some_and(|s| s.is_none())), "{:?}", g.series_paints); + let axes = kids[2].children().unwrap()[0].geometric_bounds().unwrap(); + assert!(same_rect(g.rect, axes), "the plot is where the axes are: {:?} {axes:?}", g.rect); + assert_eq!(g.placed, a[0].geometric_bounds()); +} + +/// The graph types the records give: 5 grouped column, 6 stacked column, 7 line, 10 area; and the +/// style and dialog settings, each kept as the graph's. +#[test] +fn a_graphs_type_and_settings_come_from_its_records() { + let live = |style: &str, dialog: &str, axes: &str| { + let text = live_graph().replace(LIVE_STYLE, style).replace(LIVE_DIALOG, dialog).replace(LIVE_AXES, axes); + let r = read(stream7(&layer7("Graph", &text)).as_bytes()).unwrap(); + assert!(r.warnings.is_empty(), "{style}{dialog}: {:?}", r.warnings); + art(&r.doc)[0].extras().graph.clone().expect("a live graph") + }; + for (ty, kind) in [(5, GraphKind::Column), (6, GraphKind::StackedColumn), (7, GraphKind::Line), (10, GraphKind::Area)] { + let g = live(&format!("%_{ty} 0 0 14 0 1 0 0 6 44 Gy\n"), LIVE_DIALOG, LIVE_AXES); + assert_eq!(g.kind, kind, "{ty}"); + } + let axes = LIVE_AXES.replace("%_0 14 0 0 0 2 0 ( units) GA", "%_1 15 0 100 25 0 0 () GA"); + let g = live("%_7 1 1 14 1 0 0 1 6 44 0 2 Gy\n", "%_10 2 1 0 120 50 1 Gd\n", &axes); + assert!(g.drop_shadow && g.first_row_in_front && g.mark_points && !g.connect_points && g.legend_across_top, "{g:?}"); + assert!(g.first_column_in_front && g.edge_to_edge, "{g:?}"); + assert_eq!((g.column_width, g.cluster_width), (120.0, 50.0)); + assert_eq!((g.ticks, g.axis_min, g.axis_max, g.tick_length, g.tick_marks), (4, Some(0.0), Some(100.0), TickLength::Full, 1)); + // A line graph's series draws its line unfilled beside its filled marks. + let text = live_graph().replace(LIVE_STYLE, "%_7 0 0 14 0 1 0 0 6 44 Gy\n").replacen(&series_mark(), &series_mark_and_outline(), 1); + let r = read(stream7(&layer7("Graph", &text)).as_bytes()).unwrap(); + assert!(r.warnings.is_empty(), "{:?}", r.warnings); + assert!(art(&r.doc)[0].extras().graph.is_some()); + // An empty list of customizations, of the version appendix A gives (2), sets nothing. + let text = live_graph().replace("GC\n", "GC\n%_2 Gt\n%_GT\n"); + let r = read(stream7(&layer7("Graph", &text)).as_bytes()).unwrap(); + assert!(r.warnings.is_empty(), "{:?}", r.warnings); + assert!(art(&r.doc)[0].extras().graph.is_some()); +} + +/// A graph keeps its drawn art and stays plain, with one note, when a record has a shape or value +/// appendix A doesn't give, sets what the model doesn't keep, or the table, the series tags or the +/// art don't fit the graph. +#[test] +fn graphs_the_model_cant_keep_stay_plain_art() { + let cases: Vec<(&str, String)> = vec![ + ("an eleventh style value", live_graph().replace(LIVE_STYLE, "%_5 0 0 14 0 1 0 0 6 44 0 Gy\n")), + ("eleven dialog values", live_graph().replace(LIVE_DIALOG, "%_10 2 0 0 90 80 0 0 0 0 0 Gd\n")), + ("a fourth axis", live_graph().replace(LIVE_AXES, &format!("{LIVE_AXES}%_8 () Ga\n%_0 14 0 0 0 0 0 () GA\n"))), + ("ten axis values", live_graph().replace("%_0 14 0 0 0 0 0 () GA", "%_0 14 0 0 0 0 0 () 0 0 GA")), + ("a pie graph", live_graph().replace("%_5 0 0 14", "%_8 0 0 14")), + ("a scatter graph", live_graph().replace("%_5 0 0 14", "%_9 0 0 14")), + ("type 11", live_graph().replace("%_5 0 0 14", "%_11 0 0 14")), + ("legends in wedges", live_graph().replace("%_5 0 0 14", "%_5 0 0 15")), + ("the right axis", live_graph().replace("6 44 Gy", "6 45 Gy")), + ("lines as shapes", live_graph().replace("%_5 0 0 14 0 1 0", "%_5 0 0 14 0 1 1")), + ("both axes alike", live_graph().replace(LIVE_DIALOG, "%_10 2 0 1 90 80 0 Gd\n")), + ("a subscription", live_graph().replace(LIVE_DIALOG, &format!("{LIVE_DIALOG}%_0 0 1 1 1 Gj\n"))), + ("a customization", live_graph().replace("GC\n", "GC\n%_2 Gt\n%_(Bars) GD\n%_GT\n")), + ("customizations without their version", live_graph().replace("GC\n", "GC\n%_Gt\n%_GT\n")), + ("customizations of another version", live_graph().replace("GC\n", "GC\n%_3 Gt\n%_GT\n")), + ("bounds upside down", live_graph().replace("%_100 200 300 100 Gb", "%_100 100 300 200 Gb")), + ("a plot the axes aren't on", live_graph().replace("%_100 200 300 100 Gb", "%_50 200 300 100 Gb")), + ("a text data cell", live_graph().replace("7 () Gc", "(seven) Gc () Gc")), + ("a table of empty cells", live_graph().replace(LIVE_TABLE, "%_4 3 1 1 Gz\n%_() Gc (East) Gc (West) Gc\n%_GC\n")), + ("a cell past the table", live_graph().replace("4 6 Gc GC", "4 6 1 Gc GC")), + ("too many categories", live_graph().replace("%_4 3 1 1 Gz", "%_10002 3 1 1 Gz")), + ("a table too large", live_graph().replace("%_4 3 1 1 Gz", "%_4000000000 4000000000 1 1 Gz")), + ("a number as a label", live_graph().replace("(West) Gc", "2025 Gc")), + ("a series untagged", live_graph().replace("%_2 2 0 0 Go", "%_4 2 0 0 Go")), + ("a series tagged twice", live_graph().replace("%_2 2 0 0 Go", "%_2 1 0 0 Go")), + ("a path tagged as a series", live_graph().replacen("%_4 1 0 0 Go", "%_2 1 0 0 Go", 1)), + ("a target the specification doesn't list", live_graph().replace("%_9 0 0 2 Go", "%_3 0 0 2 Go")), + ("art before its group", live_graph().replace("GC\nu\n", &format!("GC\n{}f\nu\n", rect_path(0.0, 0.0, 5.0, 5.0)))), + ("a record on a line that shows", live_graph().replace("%_10 2 0 0 90 80 0 Gd", "10 2 0 0 90 80 0 Gd")), + ("series groups inside another group", live_graph().replace("GC\nu\n", "GC\nu\nu\n").replace("U\n%_GS", "U\nU\n%_GS")), + ( + "two fills in one series", + live_graph().replacen( + "f\n%_4 1 0 0 Go\nU\n", + &format!("f\n%_4 1 0 0 Go\n0 0 0 0.5 k\n{}f\nU\n", rect_path(112.0, 100.0, 118.0, 120.0)), + 1, + ), + ), + ("axes filled, not stroked", live_graph().replace("300 100 L\nS\n", "300 100 L\nf\n")), + ("a series tagged on a row", live_graph().replace("%_2 2 0 0 Go", "%_2 2 1 0 Go")), + ("a series tagged on the right axis", live_graph().replace("%_2 2 0 0 Go", "%_2 2 0 4 Go")), + ("an unfilled mark in a column series", live_graph().replacen(&series_mark(), &series_mark_and_outline(), 1)), + ("a series' mark in a group of its own", live_graph().replacen(&series_mark(), &format!("u\n{}U\n", series_mark()), 1)), + ( + "an untagged graph of one series", + live_graph() + .replace(LIVE_TABLE, "%_2 2 1 1 Gz\n%_() Gc (East) Gc (Q1) Gc 5 Gc GC\n") + .replace("%_2 2 0 0 Go", "%_4 1 0 0 Go") + .replace("%_2 1 0 0 Go", "%_4 1 0 0 Go"), + ), + ]; + for (what, text) in cases { + let r = read(stream7(&layer7("Graph", &text)).as_bytes()).unwrap_or_else(|e| panic!("{what}: {e}")); + assert_eq!(r.warnings, [GRAPH_PLAIN], "{what}"); + assert_eq!(live_graphs(&r.doc), (vec![], 0), "{what}"); + let mut kept = false; + r.doc.walk(|n| kept |= n.children().is_some_and(|c| c.len() == 3)); + assert!(kept, "{what}: the drawn art is kept"); + } +} + +/// A graph in a group or a clipping group keeps its data on its own group, not the container's; of +/// two graphs in one layer, each is kept or left plain on its own, and the note is given once. +#[test] +fn graphs_are_kept_one_by_one_wherever_they_are() { + let clip = format!("q\n{}W n\n{}Q\n", rect_path(0.0, 0.0, 400.0, 400.0), live_graph()); + for (what, text) in [("in a group", format!("u\n{}U\n", live_graph())), ("in a clipping group", clip)] { + let r = read(stream7(&layer7("Graph", &text)).as_bytes()).unwrap(); + assert!(r.warnings.is_empty(), "{what}: {:?}", r.warnings); + let outer = &art(&r.doc)[0]; + assert!(outer.extras().graph.is_none(), "{what}"); + let (graphs, tagged) = live_graphs(&r.doc); + assert_eq!((graphs.len(), tagged), (1, 2), "{what}"); + assert!(outer.children().unwrap().iter().any(|c| c.id == graphs[0].id), "{what}: the graph's own group"); + } + let pie = live_graph().replace("%_5 0 0 14", "%_8 0 0 14"); + let body = format!("{}{pie}{}", live_graph(), live_graph()); + let r = read(stream7(&layer7("Graph", &body)).as_bytes()).unwrap(); + assert_eq!(r.warnings, [GRAPH_PLAIN]); + let a = art(&r.doc); + assert_eq!(a.len(), 3); + let live: Vec = a.iter().map(|n| n.extras().graph.is_some()).collect(); + assert_eq!(live, [true, false, true]); +} + /// Editing data in the legacy format (`%AI5_FileFormat 2.0`, ten `Lb` operands), then `body`. fn mask_stream7(body: &str) -> String { format!( diff --git a/crates/eps/src/legacy_ai.rs b/crates/eps/src/legacy_ai.rs index 00bb29d12f..71648f955a 100644 --- a/crates/eps/src/legacy_ai.rs +++ b/crates/eps/src/legacy_ai.rs @@ -122,6 +122,7 @@ const STROKES: &str = "strokes a plain line can't draw (inside or outside the path, variable width, gradients, brushes, arrowheads) are written as filled outlines"; const IMAGE_ALPHA: &str = "transparent image pixels are written over white"; const LIVE: &str = "symbols, blends, envelopes, meshes, repeats, placed documents and intertwines are written as their art"; +const GRAPHS: &str = "graphs are written as their art, without their data or settings"; /// The warning for an image left out because it can't be decoded: the note raster exports give /// (`vectorcraft-engine`'s `rasterfx::undecoded_warning`). const UNDECODED: &str = "an image couldn't be decoded and is missing from the picture"; diff --git a/crates/eps/src/legacy_ai/writer.rs b/crates/eps/src/legacy_ai/writer.rs index 20f8b32b96..c6f99eee79 100644 --- a/crates/eps/src/legacy_ai/writer.rs +++ b/crates/eps/src/legacy_ai/writer.rs @@ -9,7 +9,7 @@ use super::{ ROTATED_TYPE, Rect, STROKES, SUBLAYERS, Shape, StrokeLayer, SubPath, TITLE_AND_COLOR_NAMES, TRANSPARENCY, TYPE, TYPE_ATTRIBUTES, TextKind, TextObject, UNDECODED, Vec2, Write, ai_ops, latin, ps, stroke, }; -use super::{CONIC, CONIC_WEDGES, DIAMOND, HUE_STOPS, SPREAD, SPREAD_CAPPED}; +use super::{CONIC, CONIC_WEDGES, DIAMOND, GRAPHS, HUE_STOPS, SPREAD, SPREAD_CAPPED}; pub(super) struct Writer<'a> { doc: &'a Document, @@ -254,6 +254,9 @@ impl<'a> Writer<'a> { if n.name.as_deref().is_some_and(|s| !s.is_empty()) && !n.is_layer() { self.warn(NAMES); } + if n.extras().graph.is_some() { + self.warn(GRAPHS); + } let locked = locked || n.locked; match &n.kind { NodeKind::Layer { children, clip, .. } => { diff --git a/docs/file-format-parity.md b/docs/file-format-parity.md index c5cb2a78c1..26a9cbc90b 100644 --- a/docs/file-format-parity.md +++ b/docs/file-format-parity.md @@ -232,7 +232,14 @@ private editing data. VectorCraft: from 0 to 9 leaves the mode as it was. Legacy `.ai` placed EPS files in the version 7 form (`` ` `` … `~` with no `%AI5_BeginPlace`) are read like other placed files without a preview: as the page's art in their box. Graphs (`%_Gs` … `%_GS`) come in as their - drawn art, as plain groups, paths and type, without their data or settings, with a note. + drawn art. A grouped column, stacked column, line or area graph also keeps its table, type, axes and settings + when every record has the shape and values the version 7 specification's appendix A gives + ([the specification](https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf), printed pp. 109–112 + and 146–158), its table holds at least one number, its art draws the plot's left and bottom edges, and each series' + group is tagged once and paints its marks with one fill and one stroke, which the graph keeps as that series' + paint; Graph Data then edits it. Other graphs (pie and scatter graphs, graphs with designs or + customizations, records of later versions) come in as plain groups, paths and type, with a note. Saving a graph to + `.ai`, in either form, writes its art without its data or settings, with a warning. Legacy `.ai` patterns in the version 7 form (a list of `@` and `&` layers, `_ &` for the tile's background) open as pattern swatches, with a fill's offset, scale, rotation, reflection and shear applied before its matrix. Legacy `.ai` linked images in diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e7c1b28726..2d22d74e36 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1619,6 +1619,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur A `.ai` opened from its editing data opens its Objects on Path groups live, with their path, their objects in order along it, Pivot and Rotate, and their art where the file draws it; a group whose spacing has been dragged, or with options an Objects on Path group doesn't hold, opens as a plain group (`crates/eps/src/import/ai/tests.rs`). `.ai` symbol definitions and placed instances open as live symbols, in the Symbols panel's order; an instance of a symbol that isn't read, or whose appearance differs from its symbol's, comes in as the page draws it (`crates/eps/src/import/ai/tests.rs`). In a `.ai` opened from its editing data, an object in a pattern's art or a brush's art with an art style VectorCraft reads opens as itself with its effects live, as an object on a layer does, and an object in a pattern's art keeps the id of a graphic style in the file's list (`crates/eps/src/import/ai/tests.rs`). + A graph in a `.ai` whose records have the shape and values the published version 7 specification gives ([the specification](https://13thmonkey.org/documentation/fonts/AI7FileFormat.pdf), printed pp. 109–112 and 146–158) opens as a live grouped column, stacked column, line or area graph with its saved art, table, axes and settings, and Graph Data edits it ([the help page](https://helpx.adobe.com/illustrator/desktop/automate-visualize-data/visualize-data/add-graph-data.html)); other graphs keep their art as plain objects with a note, and saving a graph to `.ai` warns that its data and settings aren't written (`crates/eps/src/import/ai/tests.rs`, `crates/engine/src/cmd/graph/tests.rs`). Blends and envelopes in the definitions of a `.ai`'s symbols, patterns and brushes' art count against the same per-file limits on blend layout and envelope checks as the layers' art, so a file with many definitions does not get those limits again for each one (`crates/eps/src/import/ai/read/directives.rs`). A `.ai` or EPS whose editing data has brushes opens with the file's Calligraphic, Scatter, Art and Bristle brushes in the Brushes panel in place of the default ones, in the file's order: the Calligraphic size, roundness and angle with their variation, the Scatter size, spacing, scatter and rotation, the Bristle settings, and the Scatter and Art brushes' art (Art brushes in the older and newer forms of their objects). A brush library `.ai` (brushes and no layers) opens from its PDF part with its brushes, so Other Library lists them. An Art brush in Tints keeps that colorization. Pattern brushes and brushes that can't be read are left out with a note. Paths with a Calligraphic brush, and paths with an Art brush in the newer form at 100% in Tints that stretches art of solid-filled paths unflipped, open as the path with the brush on its stroke. Paths with an Art brush in the older form, and other brushed paths, still come in as their drawn look (part of #372 and #637; `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`). In a `.ai` opened from its editing data, a path or type object with Offset Path, Outline Object or Outline Stroke on the whole object opens as itself with those effects live, in their order and shown or hidden; on a fill or stroke of type they stay on that fill or stroke. Offset Path keeps its Offset, Joins and Miter Limit, and Outline Stroke takes its width from the stroke. An Offset Path with a setting missing, out of range or not read keeps the drawn art, as does a path with a Path effect on one fill or stroke (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`). From 193ed34794c86e419ac9f5b07e5e1a0fb4d4fd2e Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 14:16:51 -0800 Subject: [PATCH 08/33] i18n: German has a row for every message the untranslated report lists The German catalog lacked a row for 1,842 of the status and error messages that the untranslated report finds in the sources, so the status bar showed those messages in English. - de.tsv gains 1,842 `@msg` rows: the errors of commands and their parameter checks, scripts, file import and export, printing and plug-ins. The translations are VectorCraft's own, written from the English meaning with the catalog's conventions and its existing feature names. Placeholders, parameter names, command ids and literal values stay as the English writes them. - The untranslated report now lists no German messages. Menu strings, `tl!` literals, undo labels and swatch names are unchanged. - Two of the messages have two placeholders in a row. The catalog does not load such a row, so those two are still shown in English. - The German row of docs/localization-parity.md gives the catalog's current message counts. Docs: docs/target-app-parity.md, docs/localization-parity.md. --- crates/ui-egui/src/i18n/de.tsv | 1843 ++++++++++++++++++++++++++++++++ docs/localization-parity.md | 2 +- docs/target-app-parity.md | 1 + 3 files changed, 1845 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 8fe1734165..bca5343ac8 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -7047,3 +7047,1846 @@ breakText Glyphs Glyphen Margin Rand Box Rahmen Swatch Farbfeld +# Status and error messages of commands, scripts, file import and export, and plug-ins. +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions 'caller', 'callee' und 'arguments' können in Funktionen im Strict-Modus nicht verwendet werden +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} wurde für einen Wert aufgerufen, der kein Boolean ist +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB CMYK-Bilder, deren Samples nicht in CMYK gelesen werden konnten, wurden in RGB importiert +@msg Cannot write to the location of “{name}”; choose where to save it Am Speicherort von „{name}“ kann nicht geschrieben werden; wählen Sie aus, wo die Datei gespeichert werden soll +@msg DCS files can only be placed linked: use File › Place with Link on DCS-Dateien können nur verknüpft platziert werden: Verwenden Sie Datei › Platzieren mit aktivierter Option „Verknüpfen“ +@msg Date.prototype method called on a value that is not a Date object Date.prototype-Methode wurde für einen Wert aufgerufen, der kein Date-Objekt ist +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString wurde für einen Wert aufgerufen, der kein Objekt ist +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString wurde für einen Wert aufgerufen, der keine Funktion ist +@msg Import SVG Filter reads SVG files, not SVGZ SVG-Filter importieren liest SVG-Dateien, keine SVGZ-Dateien +@msg JSON.parse: {why} (at offset {at}) JSON.parse: {why} (bei Offset {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify kann keine Struktur schreiben, die sich selbst enthält +@msg Namespace.prototype.toString needs a Namespace Namespace.prototype.toString braucht einen Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have Profile für neue Dokumente brauchen den Voreinstellungsordner, den diese Sitzung nicht hat +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} wurde für einen Wert aufgerufen, der keine Zahl ist +@msg Object.create's prototype must be an object or null der Prototyp für Object.create muss ein Objekt oder null sein +@msg Object.{function} was given a value that is not an object Object.{function} hat einen Wert erhalten, der kein Objekt ist +@msg QName.prototype.toString needs a QName QName.prototype.toString braucht einen QName +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} wurde für einen Wert aufgerufen, der kein RegExp-Objekt ist +@msg Stencil Bridges: the selected art is too large or too detailed to bridge Schablonenstege: Das ausgewählte Bildmaterial ist zu groß oder zu detailliert für Stege +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString wurde für einen Wert aufgerufen, der kein String ist +@msg The {name} profile could not be read, so the new document has the default settings: {reason} Das Profil {name} konnte nicht gelesen werden, daher hat das neue Dokument die Standardeinstellungen: {reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time Überfüllen: Mehr als {_1} Objektpaare liegen nah genug zum Überfüllen, oder zu viele Objekte teilen sich einen horizontalen Bereich; überfüllen Sie weniger Objekte auf einmal +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off Entgitterrahmen und -linien: Keine Entgitterlinie passt außerhalb des Bildmaterials, und der Entgitterrahmen ist deaktiviert +@msg \\ at end of pattern \\ am Ende des Musters +@msg `alignOn` cannot be empty `alignOn` darf nicht leer sein +@msg `alignOn` must be a string `alignOn` muss ein String sein +@msg `align` must be a string `align` muss ein String sein +@msg `answer` for this {kind} dialog must be one of: {_1} `answer` für dieses {kind}-Dialogfeld muss eines der folgenden sein: {_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` hat mehr als {MAX_ANSWERS} Antworten +@msg `answers` is larger than 1 MiB `answers` ist größer als 1 MiB +@msg `answers` must be a list of answers `answers` muss eine Liste von Antworten sein +@msg `artboard` must be the index of an artboard of the document `artboard` muss der Index einer Zeichenfläche des Dokuments sein +@msg `artboardsAt` must be [x, y] `artboardsAt` muss [x, y] sein +@msg `artboards` names an artboard the document does not have `artboards` nennt eine Zeichenfläche, die das Dokument nicht hat +@msg `artboards` names an artboard twice `artboards` nennt eine Zeichenfläche zweimal +@msg `at` must be [x, y] (got {at}) `at` muss [x, y] sein (erhalten: {at}) +@msg `beside` needs `side` `beside` braucht `side` +@msg `code` must be a whole number `code` muss eine ganze Zahl sein +@msg `colorMode` must be one of rgb, cmyk `colorMode` muss rgb oder cmyk sein +@msg `colors` does not apply to the {name} reduction `colors` gilt nicht für die Reduzierung {name} +@msg `colors` must be a whole number from 2 to 256 `colors` muss eine ganze Zahl von 2 bis 256 sein +@msg `colors` must be at least 9 for the uniform reduction `colors` muss für die einheitliche Reduzierung mindestens 9 sein +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` muss zwischen 1 und {MAX_THEME_COLORS} liegen (erhalten: {n}) +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` muss eine ganze Zahl von 0 bis {MAX_COUNT} sein +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` muss eine Zahl von 0 bis 100 sein +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` muss zwischen {lo} und {hi} pt liegen +@msg `focal` and `radius` cannot be used together `focal` und `radius` können nicht zusammen verwendet werden +@msg `focal` must be an array of [subpath, anchor] pairs `focal` muss ein Array aus Paaren [subpath, anchor] sein +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` nennt [{si}, {ai}], das kein Ankerpunkt des Pfads ist +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` nimmt nur null, was eine Glyphenauswahl entfernt (text.setGlyphAlternate legt eine fest) +@msg `group` must be one of {_1} `group` muss eines der folgenden sein: {_1} +@msg `group` must be the Objects on Path group of `member` and `target` `group` muss die Gruppe „Objekte auf Pfad“ von `member` und `target` sein +@msg `id` is an object id, not {v} `id` ist eine Objekt-ID, nicht {v} +@msg `item` needs `index` `item` braucht `index` +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` gehört zum ersten Zeichen einer Zeile, nicht zu einem Zeichenformat (text.setRangeStyle legt es fest) +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` muss „pushInFirst“, „pushOutFirst“ oder „pushOutOnly“ sein +@msg `leader` must be a string `leader` muss ein String sein +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` muss zwischen 1 und {MAX_SUGGESTIONS} liegen +@msg `name` must be a name that is not empty `name` muss ein nicht leerer Name sein +@msg `name` must not be empty `name` darf nicht leer sein +@msg `origin` must be [x, y] in points `origin` muss [x, y] in Punkt sein +@msg `picks` must be an array (got {v}) `picks` muss ein Array sein (erhalten: {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` muss 1 bis {MAX_PICKS} Auswahlen enthalten (erhalten: {_1}) +@msg `radius` must be 0 or more `radius` muss 0 oder größer sein +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` muss [x0, y0, x1, y1] sein (erhalten: {r}) +@msg `replace` must be a string `replace` muss ein String sein +@msg `rows` must be lists of numbers or nulls, not {r} `rows` müssen Listen aus Zahlen oder null-Werten sein, nicht {r} +@msg `scale` must be a number from 0.001 to 1000 `scale` muss eine Zahl von 0,001 bis 1000 sein +@msg `scope.{key}` must be a non-empty array `scope.{key}` muss ein nicht leeres Array sein +@msg `scope` must be "document", {artboards} or {layers} `scope` muss „document“, {artboards} oder {layers} sein +@msg `side` must be "left", "right", "top" or "bottom" `side` muss „left“, „right“, „top“ oder „bottom“ sein +@msg `side` places the artboard: leave out x, y, width and height `side` platziert die Zeichenfläche: Lassen Sie x, y, width und height weg +@msg `source` is the only path in series group `target` `source` ist der einzige Pfad in der Reihengruppe `target` +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` ergibt mehr als {MAX_COUNT} Linien über den Rahmen +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` muss größer als 0 und höchstens {MAX_DISTANCE} pt sein +@msg `target` must be an object of the same Objects on Path group as `member` `target` muss ein Objekt derselben Gruppe „Objekte auf Pfad“ wie `member` sein +@msg `version` must be 0 or {_1} `version` muss 0 oder {_1} sein +@msg `version` must be 0 or {newest} `version` muss 0 oder {newest} sein +@msg `weight` must be from 0.01 to 1000 pt `weight` muss zwischen 0,01 und 1000 pt liegen +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` muss eine Liste aus {_1} Zahlen sein, eine pro Zeile (erhalten: {v}) +@msg `xml` must be a string `xml` muss ein String sein +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it `{KEY}`-Eintrag {n} gibt dem Absatz bei Byte {start} von {_1} andere Einzüge als ein Eintrag davor +@msg `{KEY}` entry {n} must be an object `{KEY}`-Eintrag {n} muss ein Objekt sein +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` `{KEY}`-Eintrag {n} legt weder `leftIndent` noch `firstLineIndent` fest +@msg `{KEY}` entry {n}: `id` must be an object id `{KEY}`-Eintrag {n}: `id` muss eine Objekt-ID sein +@msg `{KEY}` entry {n}: `start` must be a byte offset `{KEY}`-Eintrag {n}: `start` muss ein Byte-Offset sein +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt `{KEY}`-Eintrag {n}: `{k}` muss eine Zahl von -{MAX_PARA} bis {MAX_PARA} pt sein +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} `{KEY}`-Eintrag {n}: Kein Absatz von {_1} beginnt bei Byte {start} +@msg `{KEY}` entry {n}: unknown key `{k}` `{KEY}`-Eintrag {n}: unbekannter Schlüssel `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object `{KEY}`-Eintrag {n}: {_1} ist kein Textobjekt +@msg `{KEY}` is empty `{KEY}` ist leer +@msg `{KEY}` must be a list `{KEY}` muss eine Liste sein +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` nimmt höchstens {MAX_PARAGRAPH_INDENTS} Einträge (erhalten: {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` nimmt kein `{other}` daneben +@msg `{_1}` can't name a composite font `{_1}` kann keine zusammengesetzte Schrift benennen +@msg `{_1}` is the name of an installed font `{_1}` ist der Name einer installierten Schrift +@msg `{_1}`: {_2} is set in `{font}`, a composite font `{_1}`: {_2} ist in `{font}` gesetzt, einer zusammengesetzten Schrift +@msg `{base}` is larger than {_1} MB `{base}` ist größer als {_1} MB +@msg `{base}` is not in the DCS file's folder `{base}` ist nicht im Ordner der DCS-Datei +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` enthält mehr als {MAX_EDIT_WORDS} Wörter +@msg `{key}` must be 0 or more `{key}` muss 0 oder größer sein +@msg `{key}` must be [x, y] numbers `{key}` muss aus den Zahlen [x, y] bestehen +@msg `{key}` must be a list of words `{key}` muss eine Liste von Wörtern sein +@msg `{key}` must be a list, not {v} `{key}` muss eine Liste sein, nicht {v} +@msg `{key}` must be a number or null, not {v} `{key}` muss eine Zahl oder null sein, nicht {v} +@msg `{key}` must be a whole number of 0 or more `{key}` muss eine ganze Zahl von 0 oder größer sein +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` muss zwischen 0 und {MAX_DISTANCE} pt liegen +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` muss zwischen {lo} und {hi} liegen, nicht {v} +@msg `{key}` must be one of auto, always, never `{key}` muss auto, always oder never sein +@msg `{key}` must be one of miter, round, bevel `{key}` muss miter, round oder bevel sein +@msg `{key}` must be one word of at most {_1} characters `{key}` muss ein Wort mit höchstens {_1} Zeichen sein +@msg `{key}` must hold single words of at most {_1} characters `{key}` muss einzelne Wörter mit höchstens {_1} Zeichen enthalten +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` muss ein Objekt nennen, das an den Pfad einer Gruppe „Objekte auf Pfad“ angehängt ist +@msg `{key}` names an object that is hidden or locked `{key}` nennt ein Objekt, das ausgeblendet oder gesperrt ist +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` ist eine eigene Datei, die nur neben dem Pfad der DCS-Datei gefunden werden kann +@msg `{name}`: give the kanji part `{name}`: Geben Sie den Kanji-Teil an +@msg `{name}`: parts must be an object `{name}`: parts muss ein Objekt sein +@msg `{name}`: unknown class `{k}`; the classes are kanji, kana, punctuation, symbols, roman and numerals `{name}`: unbekannte Klasse `{k}`; die Klassen sind kanji, kana, punctuation, symbols, roman und numerals +@msg `{name}`: unknown key `{k}` in {_1} `{name}`: unbekannter Schlüssel `{k}` in {_1} +@msg `{name}`: {_1} must be an object `{name}`: {_1} muss ein Objekt sein +@msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} +@msg `{raw}` can't name a composite font (1 to {COMPOSITE_NAME_MAX} characters, no control characters) `{raw}` kann keine zusammengesetzte Schrift benennen (1 bis {COMPOSITE_NAME_MAX} Zeichen, keine Steuerzeichen) +@msg `{u}` is no Unicode character `{u}` ist kein Unicode-Zeichen +@msg `{word}` isn't in the custom dictionary `{word}` ist nicht im Benutzerwörterbuch +@msg a CMap has too many mappings eine CMap hat zu viele Zuordnungen +@msg a \\u escape needs four hexadecimal digits ein \\u-Escape braucht vier Hexadezimalziffern +@msg a comma in an XPath expression must separate function arguments ein Komma in einem XPath-Ausdruck muss Funktionsargumente trennen +@msg a document holds at most {_1} SVG filters ein Dokument enthält höchstens {_1} SVG-Filter +@msg a document keeps at most {COMPOSITE_FONTS_MAX} composite fonts ein Dokument enthält höchstens {COMPOSITE_FONTS_MAX} zusammengesetzte Schriften +@msg a document with unsaved changes has no recovery copy ein Dokument mit nicht gespeicherten Änderungen hat keine Wiederherstellungskopie +@msg a font's data is too large die Daten einer Schrift sind zu groß +@msg a font's subroutines are too large die Subroutinen einer Schrift sind zu groß +@msg a function declaration without a name eine Funktionsdeklaration ohne Namen +@msg a host value has too many entries ein Host-Wert hat zu viele Einträge +@msg a host value is nested too deep ein Host-Wert ist zu tief verschachtelt +@msg a namespace declaration uses a reserved prefix or URI eine Namespace-Deklaration verwendet ein reserviertes Präfix oder einen reservierten URI +@msg a namespace qualifier is undefined ein Namespace-Qualifizierer ist nicht definiert +@msg a namespace with an empty URI cannot have a prefix ein Namespace mit leerem URI kann kein Präfix haben +@msg a number has no digits eine Zahl hat keine Ziffern +@msg a number's exponent has no digits der Exponent einer Zahl hat keine Ziffern +@msg a number's fraction has no digits der Nachkommateil einer Zahl hat keine Ziffern +@msg a property descriptor cannot have both a value or writable and a get or set ein Eigenschaftsdeskriptor kann nicht zugleich value oder writable und get oder set haben +@msg a property descriptor must be an object ein Eigenschaftsdeskriptor muss ein Objekt sein +@msg a property's {name} must be a function or undefined {name} einer Eigenschaft muss eine Funktion oder undefined sein +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied ein Datenstrom darin würde zu mehr als 256 MB decodiert, oder seine Decodierparameter können nicht angewendet werden +@msg a string holds a control character ein String enthält ein Steuerzeichen +@msg a string holds an escape sequence JSON does not have ein String enthält eine Escape-Sequenz, die JSON nicht kennt +@msg a string is not closed ein String ist nicht geschlossen +@msg an SVG filter is named by its id: give `xml` with `name` ein SVG-Filter wird über seine ID benannt: Geben Sie `xml` mit `name` an +@msg an XML element has two attributes with the same name ein XML-Element hat zwei Attribute mit demselben Namen +@msg an XML value cannot be inserted into itself or its descendants ein XML-Wert kann nicht in sich selbst oder seine Nachfahren eingefügt werden +@msg an XML value's children cannot be assigned by position die Kinder eines XML-Werts können nicht über ihre Position zugewiesen werden +@msg an XML value's children cannot be deleted by position die Kinder eines XML-Werts können nicht über ihre Position gelöscht werden +@msg an XMLList converts to XML only when it has one item eine XMLList wird nur in XML umgewandelt, wenn sie genau ein Element hat +@msg an XPath node test is missing ein XPath-Knotentest fehlt +@msg an XPath operand is missing ein XPath-Operand fehlt +@msg an XPath operator is missing ein XPath-Operator fehlt +@msg an XPath predicate or step cannot follow here ein XPath-Prädikat oder -Schritt kann hier nicht folgen +@msg an XPath predicate or step follows a value that is not a node-set ein XPath-Prädikat oder -Schritt folgt auf einen Wert, der keine Knotenmenge ist +@msg an XPath step is missing ein XPath-Schritt fehlt +@msg an image couldn't be decoded and is missing from the picture ein Bild konnte nicht decodiert werden und fehlt in der Darstellung +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) ein Bild mit {w} × {h} Pixeln ist zu groß zum Konvertieren (höchstens {_1} Pixel) +@msg apply was called on a value that is not a function apply wurde für einen Wert aufgerufen, der keine Funktion ist +@msg area type in a thread cannot use Dynamic Text: {frames} Flächentext in verkettetem Text kann keinen dynamischen Text verwenden: {frames} +@msg array literal too long Array-Literal zu lang +@msg assignment to a constant binding Zuweisung an eine konstante Bindung +@msg at least one row that isn't excluded needs a weight above 0 mindestens eine nicht ausgeschlossene Zeile braucht eine Gewichtung über 0 +@msg blend must be a mode name blend muss der Name eines Modus sein +@msg both sides of an XPath | must select nodes beide Seiten eines XPath-| müssen Knoten auswählen +@msg canceled abgebrochen +@msg cannot convert the object to a primitive value das Objekt kann nicht in einen primitiven Wert umgewandelt werden +@msg cannot convert undefined or null to an object undefined oder null kann nicht in ein Objekt umgewandelt werden +@msg character dimensions cannot be represented with the source text transform Zeichenmaße können mit der Texttransformation der Quelle nicht dargestellt werden +@msg character dimensions cannot be represented with the target text transform Zeichenmaße können mit der Texttransformation des Ziels nicht dargestellt werden +@msg continuation must be an array of path chunks continuation muss ein Array aus Pfadstücken sein +@msg continuation requires points continuation erfordert Punkte +@msg dataBase64 is not base64 dataBase64 ist kein Base64 +@msg each font must be {name, parts} jede Schrift muss {name, parts} sein +@msg each font needs a name jede Schrift braucht einen Namen +@msg each path chunk must contain 1 to 4096 points jedes Pfadstück muss 1 bis 4096 Punkte enthalten +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) jede Auswahl ist {{at: [x, y]}} oder {{rect: [x0, y0, x1, y1]}} (erhalten: {v}) +@msg each weight must be a number of 0 or more (got {v}) jede Gewichtung muss eine Zahl von 0 oder größer sein (erhalten: {v}) +@msg finally table {t} out of range finally-Tabelle {t} außerhalb des gültigen Bereichs +@msg flags cannot be given with a RegExp object mit einem RegExp-Objekt können keine Flags angegeben werden +@msg fonts must be a list of composite fonts fonts muss eine Liste zusammengesetzter Schriften sein +@msg function declaration index out of range Index der Funktionsdeklaration außerhalb des gültigen Bereichs +@msg give `char`, `text`, `code` or `unicode` geben Sie `char`, `text`, `code` oder `unicode` an +@msg give `count` or `spacing`, not both geben Sie `count` oder `spacing` an, nicht beides +@msg give `name`, an SVG filter's name geben Sie `name` an, den Namen eines SVG-Filters +@msg give `path`, an SVG file, or `svg`, SVG text geben Sie `path` an, eine SVG-Datei, oder `svg`, SVG-Text +@msg give `xml`, one SVG element geben Sie `xml` an, ein SVG-Element +@msg give exactly one of `path`, `data` and `dataBase64` geben Sie genau eines von `path`, `data` und `dataBase64` an +@msg give fonts: [{name, parts}…] geben Sie fonts: [{name, parts}…] an +@msg give index or indexes, not both geben Sie index oder indexes an, nicht beides +@msg give one element geben Sie ein einzelnes Element an +@msg give one of path, data or dataBase64 geben Sie eines von path, data oder dataBase64 an +@msg give opacity or blend geben Sie opacity oder blend an +@msg handler at {_1} expects {_2} values but its range starts with {d} der Handler bei {_1} erwartet {_2} Werte, aber sein Bereich beginnt mit {d} +@msg handler depth too large Handler-Tiefe zu groß +@msg handler range {_1}..{_2} out of range Handler-Bereich {_1}..{_2} außerhalb des gültigen Bereichs +@msg hiddenLayers must be true or false hiddenLayers muss true oder false sein +@msg hvcC hvcC +@msg images in a spot ink whose samples couldn't be read were imported in RGB Bilder in einer Volltonfarbe, deren Samples nicht gelesen werden konnten, wurden in RGB importiert +@msg images past the most pixels one file may hold were left out Bilder über der Höchstzahl an Pixeln, die eine Datei enthalten darf, wurden ausgelassen +@msg images too large to read were left out Bilder, die zum Lesen zu groß sind, wurden ausgelassen +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes darf höchstens {MAX_GUIDE_INDEXES} Einträge enthalten +@msg indexes must be an array of guide indexes indexes muss ein Array aus Hilfslinienindizes sein +@msg instruction out of range Anweisung außerhalb des gültigen Bereichs +@msg instruction {pc} ({op}) pops below the stack Anweisung {pc} ({op}) entnimmt mehr Werte, als der Stapel enthält +@msg instruction {pc} reached with stack depths {seen} and {d} Anweisung {pc} wird mit den Stapeltiefen {seen} und {d} erreicht +@msg invalid array length ungültige Array-Länge +@msg invalid group ungültige Gruppe +@msg invalid object id {v}: expected a non-negative integer ungültige Objekt-ID {v}: erwartet wird eine nicht negative ganze Zahl +@msg invalid regular expression flag '{_1}' ungültiges Flag '{_1}' für regulären Ausdruck +@msg invalid regular expression: {m} ungültiger regulärer Ausdruck: {m} +@msg invalid time value ungültiger Zeitwert +@msg is a confirm's answer, whose `value` must be true or false ist die Antwort auf eine Bestätigung, deren `value` true oder false sein muss +@msg is a prompt's answer, whose `value` must be a string or null ist die Antwort auf eine Eingabeaufforderung, deren `value` ein String oder null sein muss +@msg is an alert's answer, whose `value` must be "ok" ist die Antwort auf eine Warnung, deren `value` „ok“ sein muss +@msg is longer than 16 KiB ist länger als 16 KiB +@msg it draws its image through a clipping path es zeichnet sein Bild durch einen Beschneidungspfad +@msg it draws no image es zeichnet kein Bild +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes sie ist größer als {MAX_DICTIONARY_FILE_BYTES} Byte +@msg it isn't a file es ist keine Datei +@msg it names {_1} plates, and at most {MAX_PLATES} are read sie nennt {_1} Platten, und höchstens {MAX_PLATES} werden gelesen +@msg its art is too long or too detailed for a head seine Grafik ist zu lang oder zu detailliert für eine Spitze +@msg its art paints nothing seine Grafik malt nichts +@msg its image is too large to read sein Bild ist zu groß zum Lesen +@msg its plates are too large: more than {MAX_SAMPLES} pixels together seine Platten sind zu groß: zusammen mehr als {MAX_SAMPLES} Pixel +@msg its plates are too large: {_1} plates of {w} × {h} pixels seine Platten sind zu groß: {_1} Platten mit {w} × {h} Pixeln +@msg its plates can't be kept together seine Platten können nicht zusammengehalten werden +@msg its shapes could not be combined into one seine Formen konnten nicht zu einer zusammengefasst werden +@msg jump to {pc}, past the end Sprung zu {pc}, hinter das Ende +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding muss eines der folgenden sein: {_1} +@msg localize: the first argument must be a localization object or a string localize: Das erste Argument muss ein Lokalisierungsobjekt oder ein String sein +@msg missing `picks` `picks` fehlt +@msg missing `word` `word` fehlt +@msg must be an object with `kind` and `value` muss ein Objekt mit `kind` und `value` sein +@msg must have `kind` alert, confirm or prompt muss `kind` alert, confirm oder prompt haben +@msg must have exactly `kind` and `value` muss genau `kind` und `value` haben +@msg names must be a list of names names muss eine Liste von Namen sein +@msg names must be strings names müssen Strings sein +@msg needs a closing quote after the name braucht ein schließendes Anführungszeichen nach dem Namen +@msg needs a name braucht einen Namen +@msg needs a name of at most 64 bytes braucht einen Namen mit höchstens 64 Byte +@msg needs a name of letters, digits and _, or a quoted name braucht einen Namen aus Buchstaben, Ziffern und _ oder einen Namen in Anführungszeichen +@msg needs a name without control characters braucht einen Namen ohne Steuerzeichen +@msg nesting too deep zu tief verschachtelt +@msg no HP-GL format kein HP-GL-Format +@msg no New Document profile called `{shown}` ({_1}) kein Profil für neue Dokumente namens `{shown}` ({_1}) +@msg no SVG filter at index {k} kein SVG-Filter bei Index {k} +@msg no arrowheads file here hier ist keine Pfeilspitzendatei +@msg no character has code {code} kein Zeichen hat den Code {code} +@msg no composite font keine zusammengesetzte Schrift +@msg no composite font named `{n}` keine zusammengesetzte Schrift namens `{n}` +@msg no composite fonts to export keine zusammengesetzten Schriften zum Exportieren +@msg no custom arrowhead {name} (see stroke.arrowheads.list) keine eigene Pfeilspitze {name} (siehe stroke.arrowheads.list) +@msg no file to open keine Datei zum Öffnen +@msg no instructions keine Anweisungen +@msg no layer to rename keine Ebene zum Umbenennen +@msg no random numbers ({e}) keine Zufallszahlen ({e}) +@msg no selected or given object has a visible 3D effect kein ausgewähltes oder angegebenes Objekt hat einen sichtbaren 3D-Effekt +@msg no such artboard {v} keine solche Zeichenfläche {v} +@msg no symbol named `{_1}` kein Symbol namens `{_1}` +@msg node {_1} already has the text variable `{_2}` Knoten {_1} hat bereits die Textvariable `{_2}` +@msg not a DCS file keine DCS-Datei +@msg not a RegExp object kein RegExp-Objekt +@msg not a composite fonts file: no compositeFonts list keine Datei mit zusammengesetzten Schriften: keine compositeFonts-Liste +@msg not a composite fonts file: {e} keine Datei mit zusammengesetzten Schriften: {e} +@msg not a regular expression kein regulärer Ausdruck +@msg not an action set: the text doesn't start with /version and a number kein Aktionssatz: Der Text beginnt nicht mit /version und einer Zahl +@msg nothing to repeat nichts zu wiederholen +@msg numbers out of order in {_1} quantifier Zahlen in falscher Reihenfolge im Quantifizierer {_1} +@msg object {_1} needs a 3D effect Objekt {_1} braucht einen 3D-Effekt +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic Objekt {_1}: Ein hohles „Extrudieren und abgeflachte Kante“ hat keine Deckflächen: Aktivieren Sie „Deckflächen“, um eine Grafik zuzuweisen +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front Objekt {_1}: Bei einer Tiefe von 0 hat das Objekt nur eine Fläche: Weisen Sie seine Grafiken der Vorderseite zu +@msg object {_1}: {_2} Objekt {_1}: {_2} +@msg object {_1}: {e} Objekt {_1}: {e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams Objekt {_1}: {name} bei Index {index} hat keine Beleuchtungsvorgaben; legen Sie seine Lichter mit effect.setParams fest +@msg opacity must be a number opacity muss eine Zahl sein +@msg operand too large Operand zu groß +@msg order is a whole number from 0 to {others}, not `{v}` order ist eine ganze Zahl von 0 bis {others}, nicht `{v}` +@msg path chunks must share their boundary point Pfadstücke müssen ihren Grenzpunkt teilen +@msg path is empty der Pfad ist leer +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false braucht die Zeichenflächen, um die bearbeitbare Grafik zu behalten, wenn useArtboards false ist +@msg point x must be a number Punkt x muss eine Zahl sein +@msg point y must be a number Punkt y muss eine Zahl sein +@msg points must be [x,y] pairs Punkte müssen [x,y]-Paare sein +@msg points must be finite canvas points Punkte müssen endliche Punkte auf der Arbeitsfläche sein +@msg range out of order in character class Bereich in falscher Reihenfolge in der Zeichenklasse +@msg reduce of an empty array with no initial value reduce eines leeren Arrays ohne Startwert +@msg regular expression flag '{_1}' given twice Flag '{_1}' des regulären Ausdrucks zweimal angegeben +@msg sampled text would exceed the canvas (coordinates out of range) der Mustertext würde über die Arbeitsfläche hinausgehen (Koordinaten außerhalb des gültigen Bereichs) +@msg select an SVG filter wählen Sie einen SVG-Filter aus +@msg select an object with a 3D effect wählen Sie ein Objekt mit einem 3D-Effekt aus +@msg select an object with a 3D effect, or give its id in ids wählen Sie ein Objekt mit einem 3D-Effekt aus, oder geben Sie seine ID in ids an +@msg select one layer, group or object to rename wählen Sie eine Ebene, Gruppe oder ein Objekt zum Umbenennen aus +@msg select the art to weed wählen Sie das zu entgitternde Bildmaterial aus +@msg select type on a path or area type wählen Sie Pfadtext oder Flächentext aus +@msg side is left or right, not `{v}` side ist left oder right, nicht `{v}` +@msg source text transform is collapsed or unrepresentable die Texttransformation der Quelle ist zusammengefallen oder nicht darstellbar +@msg table must be true or false table muss true oder false sein +@msg takes one name nimmt einen Namen +@msg target text transform is collapsed or unrepresentable die Texttransformation des Ziels ist zusammengefallen oder nicht darstellbar +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer das ergibt zu viele Teile (höchstens 100000) oder Strichschritte: Vereinfachen Sie das Strichmuster oder verlängern Sie die Striche +@msg the Layers panel is not shown das Bedienfeld „Ebenen“ wird nicht angezeigt +@msg the Layers panel's search, filter or Show Layers Only hides its row die Suche, der Filter oder „Nur Ebenen einblenden“ im Bedienfeld „Ebenen“ blendet seine Zeile aus +@msg the Objects on Path group is hidden or locked die Gruppe „Objekte auf Pfad“ ist ausgeblendet oder gesperrt +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes die SVG-Datei ist größer als {MAX_IMPORT_BYTES} Byte +@msg the XML operation needs an XML or XMLList value die XML-Operation braucht einen XML- oder XMLList-Wert +@msg the XPath expression does not evaluate to a node-set der XPath-Ausdruck ergibt keine Knotenmenge +@msg the XPath expression ends early der XPath-Ausdruck endet zu früh +@msg the XPath expression has an unmatched ) der XPath-Ausdruck hat eine ) ohne Gegenstück +@msg the XPath expression has an unmatched ] der XPath-Ausdruck hat eine ] ohne Gegenstück +@msg the XPath expression has an unmatched closing bracket der XPath-Ausdruck hat eine schließende Klammer ohne Gegenstück +@msg the XPath expression selects a namespace node, which is not an XML value der XPath-Ausdruck wählt einen Namespace-Knoten aus, der kein XML-Wert ist +@msg the XPath expression selects the root node, which is not an XML value der XPath-Ausdruck wählt den Wurzelknoten aus, der kein XML-Wert ist +@msg the XPath function {name} needs a node-set die XPath-Funktion {name} braucht eine Knotenmenge +@msg the XPath function {name} takes the wrong number of arguments die XPath-Funktion {name} erhält die falsche Anzahl an Argumenten +@msg the XPath name functions need a node-set die XPath-Namensfunktionen brauchen eine Knotenmenge +@msg the XPath prefix {shown} is not bound to a namespace das XPath-Präfix {shown} ist an keinen Namespace gebunden +@msg the XPath variable ${shown} is not bound die XPath-Variable ${shown} ist nicht gebunden +@msg the argument list is not an object die Argumentliste ist kein Objekt +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out die Grafik ist zu groß für EPS: Was nach 256 MB PostScript folgt, wurde ausgelassen +@msg the bitmaps hold more pixels than one file may die Bitmaps enthalten mehr Pixel, als eine Datei enthalten darf +@msg the code runs past its last instruction der Code läuft über seine letzte Anweisung hinaus +@msg the curve tolerance must be from 0.01 to 10 points die Kurventoleranz muss zwischen 0,01 und 10 Punkt liegen +@msg the custom dictionary couldn't be saved to {_1}: {e} das Benutzerwörterbuch konnte nicht unter {_1} gespeichert werden: {e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is das Benutzerwörterbuch {_1} kann nicht gelesen werden ({why}), daher bleibt es unverändert +@msg the document already has an SVG filter named {name} das Dokument hat bereits einen SVG-Filter namens {name} +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it das Dokument hat einen anderen SVG-Filter namens {name}: Verwenden Sie svgFilter.apply oder svgFilter.define mit `replace`, um ihn zu bearbeiten +@msg the document has no SVG filter named {name} das Dokument hat keinen SVG-Filter namens {name} +@msg the document has no SVG filter named {r} das Dokument hat keinen SVG-Filter namens {r} +@msg the document has no color swatches das Dokument hat keine Farbfelder +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 die Farbfelder des Dokuments enthalten {_1} Farben; die Reduzierung auf Dokumentfarben nimmt höchstens 255 +@msg the file holds more than {COMPOSITE_FONTS_MAX} composite fonts die Datei enthält mehr als {COMPOSITE_FONTS_MAX} zusammengesetzte Schriften +@msg the file holds no composite fonts die Datei enthält keine zusammengesetzten Schriften +@msg the file holds two composite fonts named `{_1}` die Datei enthält zwei zusammengesetzte Schriften namens `{_1}` +@msg the file is larger than 4 MB die Datei ist größer als 4 MB +@msg the file isn't valid SVG: {e} die Datei ist kein gültiges SVG: {e} +@msg the file names no plate die Datei nennt keine Platte +@msg the function's prototype is not an object der Prototyp der Funktion ist kein Objekt +@msg the guide is hidden or locked die Hilfslinie ist ausgeblendet oder gesperrt +@msg the library's file is outside the read roots die Datei der Bibliothek liegt außerhalb der Lesestammordner +@msg the method needs an XML value die Methode braucht einen XML-Wert +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) die Farbe ist kein Freiformverlauf (wenden Sie einen mit paint.editGradient {kind} an) +@msg the parameters or body do not form one function die Parameter oder der Rumpf bilden keine einzelne Funktion +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it der eingefügte SVG-Filter {name} zeichnet anders als der gleichnamige Filter des Dokuments, und kein freier Name passt zu ihm +@msg the pen table holds more than 255 pens die Stifttabelle enthält mehr als 255 Stifte +@msg the plates can't be kept die Platten können nicht behalten werden +@msg the position table does not match the instructions die Positionstabelle passt nicht zu den Anweisungen +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes der Text der Eingabeaufforderung ist länger als {MAX_PROMPT_BYTES} Byte +@msg the radix must be between 2 and 36 die Basis muss zwischen 2 und 36 liegen +@msg the right side of 'in' is not an object die rechte Seite von 'in' ist kein Objekt +@msg the right side of 'instanceof' is not a function die rechte Seite von 'instanceof' ist keine Funktion +@msg the selected art is nested more than {MAX_DEPTH} levels deep das ausgewählte Bildmaterial ist mehr als {MAX_DEPTH} Ebenen tief verschachtelt +@msg the selected art is too large to measure das ausgewählte Bildmaterial ist zu groß zum Messen +@msg the selection has no outlines to weed around die Auswahl hat keine Umrisse, um die herum entgittert werden kann +@msg the value bind was called on is not a function der Wert, für den bind aufgerufen wurde, ist keine Funktion +@msg the value is not a constructor der Wert ist kein Konstruktor +@msg the value is not a function der Wert ist keine Funktion +@msg the value is not a valid XML name der Wert ist kein gültiger XML-Name +@msg the value is not an XML attribute name der Wert ist kein XML-Attributname +@msg the value is not an XML name der Wert ist kein XML-Name +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines die Entgitterlinien kreuzen die Umrisse des Bildmaterials zu oft zum Messen; verwenden Sie weniger Linien +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap die Entgitterlinien verlaufen zu nah an zu vielen Umrisssegmenten zum Messen; verwenden Sie weniger Linien oder einen kleineren Abstand +@msg the weeding lines would be more than {max_pieces} pieces die Entgitterlinien würden mehr als {max_pieces} Teile ergeben +@msg the {_1} plate lies outside the file die Platte {_1} liegt außerhalb der Datei +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that dies ist eine binäre DXF-Datei, die nicht gelesen werden kann: Speichern Sie sie in der CAD-Anwendung als ASCII-DXF und öffnen Sie diese +@msg this warp's points can't be edited: use Reset with Mesh first die Punkte dieser Verkrümmung können nicht bearbeitet werden: Verwenden Sie zuerst „Mit Gitter zurücksetzen“ +@msg toExponential() digits must be between 0 and 20 die Stellen für toExponential() müssen zwischen 0 und 20 liegen +@msg toFixed() digits must be between 0 and 20 die Stellen für toFixed() müssen zwischen 0 und 20 liegen +@msg toISOString is not a function toISOString ist keine Funktion +@msg toPrecision() argument must be between 1 and 21 das Argument von toPrecision() muss zwischen 1 und 21 liegen +@msg toSource: a getter cannot be written as source toSource: Ein Getter kann nicht als Quelltext geschrieben werden +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource: Ein Array mit Eigenschaften neben seinen Elementen, einer nicht aufzählbaren Eigenschaft oder einem anderen Prototyp kann nicht als Quelltext geschrieben werden +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource: Ein Objekt mit einer nicht aufzählbaren Eigenschaft oder mit einem anderen Prototyp als Object.prototype kann nicht als Quelltext geschrieben werden +@msg toSource: the source is too long toSource: Der Quelltext ist zu lang +@msg toSource: the value contains itself toSource: Der Wert enthält sich selbst +@msg toSource: the value has too many entries toSource: Der Wert hat zu viele Einträge +@msg toSource: the value is nested too deep toSource: Der Wert ist zu tief verschachtelt +@msg toString is not a function toString ist keine Funktion +@msg too many arguments zu viele Argumente +@msg too many quantifiers zu viele Quantifizierer +@msg two composite fonts are named `{_1}` zwei zusammengesetzte Schriften heißen `{_1}` +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) unbekannte OpenType-Funktion `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; mit vorangestelltem - ausschalten; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom und aalt nehmen die Nummer einer Alternative als `tag=N` mit N von 0 bis {_1}, etwa `salt=2`) +@msg unknown dither `{d}` (diffusion, pattern, noise or none) unbekanntes Dithering `{d}` (diffusion, pattern, noise oder none) +@msg unknown key `{k}`; a font takes name and parts unbekannter Schlüssel `{k}`; eine Schrift nimmt name und parts +@msg unknown panel `{panel}` unbekanntes Bedienfeld `{panel}` +@msg unknown parameter `{key}` unbekannter Parameter `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} unbekannter Parameter `{within}settings.{format}`; {why} +@msg unknown parameter `{within}{key}`; {why} unbekannter Parameter `{within}{key}`; {why} +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) unbekannte Reduzierung `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform oder document) +@msg unmatched ')' ')' ohne Gegenstück +@msg unterminated character class nicht abgeschlossene Zeichenklasse +@msg unterminated group nicht abgeschlossene Gruppe +@msg wordGap and lineGap need area type in the selection wordGap und lineGap brauchen Flächentext in der Auswahl +@msg xpath needs its variables as an object xpath braucht seine Variablen als Objekt +@msg xpath needs the expression as a string xpath braucht den Ausdruck als String +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED}, und seine Einstellungen und sein Cache können nicht von {_1} nach {_2} zurückverschoben werden: {e} +@msg {_1} is not a folder: {e} {_1} ist kein Ordner: {e} +@msg {_1}: {_2} and {_3} are in the way {_1}: {_2} und {_3} sind im Weg +@msg {code}: {0} {code}: {0} +@msg {dir}: {e} {dir}: {e} +@msg {file} names more than one linked JavaScript file {file} nennt mehr als eine verknüpfte JavaScript-Datei +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: Der Pfad einer eigenen Pfeilspitze braucht eine zu füllende Fläche und höchstens {most} Ankerpunkte und {most} Teilpfade, jeder Ankerpunkt höchstens {far} Spitzeneinheiten von ihrer Spitze entfernt +@msg {k}: bad custom arrowhead path: {e} {k}: ungültiger Pfad einer eigenen Pfeilspitze: {e} +@msg {k}: bad custom arrowhead: {e} {k}: ungültige eigene Pfeilspitze: {e} +@msg {k}: baseline must be {_1} to {_2}% {k}: baseline muss {_1} bis {_2} % sein +@msg {k}: custom must be a name {k}: custom muss ein Name sein +@msg {k}: give a font {k}: Geben Sie eine Schrift an +@msg {k}: only kana can be scaled at the center {k}: nur Kana können an der Mitte skaliert werden +@msg {k}: only kana, roman and numerals can be scaled {k}: nur Kana, lateinische Zeichen und Ziffern können skaliert werden +@msg {k}: scale must be {_1} to {_2}% {k}: scale muss {_1} bis {_2} % sein +@msg {k}: size must be {_1} to {_2}% {k}: size muss {_1} bis {_2} % sein +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: Kopieren nimmt eine DCS-Datei nur, wenn ihre Platten darin enthalten sind; verknüpfen Sie sie dort neu, wo ihre Plattendateien liegen +@msg {path}: larger than {_1} MB {path}: größer als {_1} MB +@msg {shown} is not an XPath 1.0 function {shown} ist keine XPath-1.0-Funktion +@msg {v} names no layer or sublayer {v} nennt keine Ebene oder Unterebene +@msg {what} is not a function {what} ist keine Funktion +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what} würde mehr als {MAX_COPIES_PER_SHEET} Kopien auf einem Bogen drucken: Vergrößern Sie es +@msg {what} {i} out of range {what} {i} außerhalb des gültigen Bereichs +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. Ein Profil unterstützt einen Kreiseln-Effekt. Bearbeiten Sie stattdessen die Optionen des vorhandenen Effekts. +@msg Add Points at Crossings: a path went missing Punkte an Kreuzungen hinzufügen: Ein Pfad ist verloren gegangen +@msg CSS options: {e} CSS-Optionen: {e} +@msg CSV has a header but no data rows die CSV-Datei hat eine Kopfzeile, aber keine Datenzeilen +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark die CSV-Datei enthält NUL-Bytes: Speichern Sie sie als UTF-8 oder als UTF-16 mit Byte-Order-Mark +@msg CSV is empty die CSV-Datei ist leer +@msg CSV is larger than {MAX_CSV_BYTES} bytes die CSV-Datei ist größer als {MAX_CSV_BYTES} Byte +@msg CSV is not UTF-16 die CSV-Datei ist nicht UTF-16 +@msg CSV is not UTF-8 die CSV-Datei ist nicht UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes die CSV-Datei wäre größer als {MAX_CSV_BYTES} Byte +@msg Choose original artwork, not a dimension's generated paths Wählen Sie die ursprüngliche Grafik, nicht die erzeugten Pfade einer Bemaßung +@msg Contour: select paths or shapes Umriss: Wählen Sie Pfade oder Formen aus +@msg Contour: the Offset Path effect is not available Umriss: Der Effekt „Pfad verschieben“ ist nicht verfügbar +@msg Create needs fills, strokes or both Erstellen braucht Flächen, Konturen oder beides +@msg DXF options: {e} DXF-Optionen: {e} +@msg DXF scale must be a positive number up to {_1}, not {scale} der DXF-Maßstab muss eine positive Zahl bis {_1} sein, nicht {scale} +@msg DXF {field} `{s}`: {allowed} DXF {field} `{s}`: {allowed} +@msg Dimension is locked or hidden Die Bemaßung ist gesperrt oder ausgeblendet +@msg Dimension mode does not match its source Der Bemaßungsmodus passt nicht zu seiner Quelle +@msg Draw Inside needs exactly one selected path, compound path or text „Innen zeichnen“ braucht genau einen ausgewählten Pfad, zusammengesetzten Pfad oder Text +@msg EPS Save writes all artwork: use document.export for selected-only EPS Speichern als EPS schreibt die gesamte Grafik: Verwenden Sie document.export für ein EPS nur mit der Auswahl +@msg EPS flattener: {e} EPS-Reduzierung: {e} +@msg EPS options: {e} EPS-Optionen: {e} +@msg EPS {field} `{s}`: {allowed} EPS {field} `{s}`: {allowed} +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft Für Bildschirme exportieren schreibt png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai oder vectorcraft +@msg Flare click coordinates must be finite numbers Die Klickkoordinaten für Blendenflecke müssen endliche Zahlen sein +@msg HTML Text applies to object slices of type only HTML-Text gilt nur für Objekt-Slices aus Text +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) „Objekte identifizieren nach“ ist XML-ID: Der Name muss eine gültige XML-ID sein (beginnt mit einem Buchstaben, Unterstrich oder Doppelpunkt; keine Leerzeichen) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) Bildnachzeichner fehlgeschlagen: interner Fehler: Sein Thread wurde beendet (bitte melden Sie diesen Fehler) +@msg Image Trace failed: {e} Bildnachzeichner fehlgeschlagen: {e} +@msg Image Trace finished, but its document was closed Bildnachzeichner abgeschlossen, aber sein Dokument wurde geschlossen +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again Bildnachzeichner abgeschlossen, aber das Bild oder seine Einstellungen haben sich inzwischen geändert: Zeichnen Sie es erneut nach +@msg Invalid dimension offset Ungültiger Bemaßungsversatz +@msg Invalid point Ungültiger Punkt +@msg Join: a subpath went missing Zusammenfügen: Ein Teilpfad ist verloren gegangen +@msg Lock Workspace is not available in VectorCraft Arbeitsbereich sperren ist in VectorCraft nicht verfügbar +@msg Move Panel to New Window is not available: floating panels stay in the main window Bedienfeld in neues Fenster verschieben ist nicht verfügbar: Schwebende Bedienfelder bleiben im Hauptfenster +@msg Offset Path: the path is too complex to offset Pfad verschieben: Der Pfad ist zu komplex zum Verschieben +@msg Offset Path: {e} Pfad verschieben: {e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported der PDF-Inhalt ist unvollständig oder überschreitet die Grenzen des Lesers; es wurde kein Dokument importiert +@msg PDF flattener: {e} PDF-Reduzierung: {e} +@msg PDF options: {e} PDF-Optionen: {e} +@msg Recolor Artwork is no longer open „Grafik neu färben“ ist nicht mehr geöffnet +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 SVG ist nicht UTF-8, UTF-16 oder ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export Speichern schreibt eine Zeichenfläche: Nennen Sie eine, oder exportieren Sie mehrere mit document.export +@msg Select a dimension first Wählen Sie zuerst eine Bemaßung aus +@msg Select dimensions to update Wählen Sie die zu aktualisierenden Bemaßungen aus +@msg Select only dimensions Wählen Sie nur Bemaßungen aus +@msg Select two or more objects, or a group Wählen Sie zwei oder mehr Objekte oder eine Gruppe aus +@msg Shaper needs 1–64 source paths Shaper braucht 1–64 Quellpfade +@msg Switch x/y is for scatter graphs x/y vertauschen gilt für Streudiagramme +@msg Text options: {e} Textoptionen: {e} +@msg `align` is start, center or end `align` ist start, center oder end +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` muss ein Array aus Paaren [subpath, anchor] sein +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` muss Ankerpunktindizes (0, 1, 2…) auflisten, nicht {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` muss Ankerpunktindizes (0, 1, 2…) auflisten, nicht {v} +@msg `artboards` must be a list of artboard indices `artboards` muss eine Liste von Zeichenflächenindizes sein +@msg `asset` is the asset id (see assets.list) `asset` ist die Asset-ID (siehe assets.list) +@msg `at` must be a byte offset `at` muss ein Byte-Offset sein +@msg `autoLeading` must be a number `autoLeading` muss eine Zahl sein +@msg `background` must be "", "matte" or #rrggbb `background` muss "", „matte“ oder #rrggbb sein +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` muss „none“, „standard“ oder „forced“ sein +@msg `center` must be [x, y] `center` muss [x, y] sein +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` muss „romanBaseline“, „emBoxTop“, „emBoxCenter“, „emBoxBottom“, „icfTop“ oder „icfBottom“ sein +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` muss „default“, „leftToRight“ oder „rightToLeft“ sein +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` `clearStroke` erfordert `stroke: false`, `ids: []` und kein `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` muss eine Liste von Textobjekt-IDs sein, nicht {v} +@msg `clear` must list type objects; {_1} is not one `clear` muss Textobjekte auflisten; {_1} ist keines +@msg `clear` must not list `id` `clear` darf `id` nicht auflisten +@msg `color` needs a `rule` to make the theme from `color` braucht eine `rule`, aus der das Farbthema erstellt wird +@msg `colors` is empty `colors` ist leer +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` ist vertical, uniform, repeating oder sliding +@msg `columns` must be a whole number 1..50 `columns` muss eine ganze Zahl von 1..50 sein +@msg `commands` must not be empty `commands` darf nicht leer sein +@msg `contents` goes with `to` `contents` gehört zu `to` +@msg `corners` must be sharp, round or square, not `{other}` `corners` muss sharp, round oder square sein, nicht `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` muss Ankerpunktindizes (0, 1, 2…) auflisten, nicht {v} +@msg `count` must be a whole number from 1 to 100 `count` muss eine ganze Zahl von 1 bis 100 sein +@msg `dataBase64` is larger than the module limit `dataBase64` ist größer als die Modulgrenze +@msg `dataBase64` is not valid base64 `dataBase64` ist kein gültiges Base64 +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` kann nicht mit `ids`, `reverse` oder `append` kombiniert werden +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` und `childLimit` begrenzen die Zusammenfassung: Übergeben Sie `summary: true` +@msg `design` is a design name, or null for plain columns `design` ist ein Designname oder null für einfache Säulen +@msg `design` is a design name, or null for the default marker `design` ist ein Designname oder null für die Standardmarkierung +@msg `destinationFolder` is required when destination is `folder` `destinationFolder` ist erforderlich, wenn destination `folder` ist +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` muss `none`, `saveAndClose` oder `folder` sein +@msg `diameter` must be from 0.1 to 2500 pt `diameter` muss zwischen 0,1 und 2500 pt liegen +@msg `digits` must be one of {_1} `digits` muss eines der folgenden sein: {_1} +@msg `dimImages` is a percentage or false `dimImages` ist ein Prozentwert oder false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` muss „auto“, „leftToRight“ oder „rightToLeft“ sein +@msg `each` is the value one design stands for, more than 0 `each` ist der Wert, für den ein Design steht, größer als 0 +@msg `end` must be "start" or "end" `end` muss „start“ oder „end“ sein +@msg `ends` applies to joining exactly two paths `ends` gilt für das Zusammenfügen von genau zwei Pfaden +@msg `event` must be one of {_1} `event` muss eines der folgenden sein: {_1} +@msg `every` must be "all", "odd" or "even", not {v} `every` muss „all“, „odd“ oder „even“ sein, nicht {v} +@msg `expand` must be true or false `expand` muss true oder false sein +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` muss 1 bis 1000 Pfade von Schriftdateien sein, die die letzte Suche mit text.findFontFiles gefunden hat +@msg `folder` must be an absolute folder path `folder` muss ein absoluter Ordnerpfad sein +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` muss ein Objekt aus Achsen-Tags und Werten sein, etwa {"wght"} +@msg `format` must be a string `format` muss ein String sein +@msg `format` must be one of {_1} `format` muss eines der folgenden sein: {_1} +@msg `fractions` is chop or scale `fractions` ist chop oder scale +@msg `from` and `to` must differ `from` und `to` müssen sich unterscheiden +@msg `from` has an empty frame `from` hat einen leeren Rahmen +@msg `from` must be a non-negative integer `from` muss eine nicht negative ganze Zahl sein +@msg `from` must be area type or type on a path `from` muss Flächentext oder Pfadtext sein +@msg `gid` must be a glyph id, not {v} `gid` muss eine Glyphen-ID sein, nicht {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` gehört zu einem einzelnen Zeichen, nicht zu einem Zeichenformat (text.setGlyphAlternate legt es fest) +@msg `guide` must be a guide index (guide.list) `guide` muss ein Hilfslinienindex sein (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` muss default, left, center oder right sein +@msg `id` must be a node id `id` muss eine Knoten-ID sein +@msg `id` must be an object id, not {v} `id` muss eine Objekt-ID sein, nicht {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` muss Gruppen „Objekte auf Pfad“ oder Objekte darin nennen +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` nimmt höchstens {MAX_INSPECT_IDS} IDs +@msg `imageMap` must be none, rectangle or polygon `imageMap` muss none, rectangle oder polygon sein +@msg `includeArtboardFill` must be true or false `includeArtboardFill` muss true oder false sein +@msg `includeSubdirs` must be true or false `includeSubdirs` muss true oder false sein +@msg `index` must be a point index `index` muss ein Punktindex sein +@msg `index` must be a whole number or null `index` muss eine ganze Zahl oder null sein +@msg `indices` must be a non-empty array of item indices `indices` muss ein nicht leeres Array aus Elementindizes sein +@msg `items` is a list of {kind, item} `items` ist eine Liste aus {kind, item} +@msg `items` is empty `items` ist leer +@msg `items` must be a list of {id, points} `items` muss eine Liste aus {id, points} sein +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` muss eine Liste aus {{id, anchors?}} sein, nicht {v} +@msg `items` must be an array of {id, anchors} `items` muss ein Array aus {id, anchors} sein +@msg `items` must be an array of {{id, corners?}}, not {v} `items` muss ein Array aus {{id, corners?}} sein, nicht {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` muss „none“, „short“, „medium“, „long“ oder „stylistic“ sein +@msg `kind` is one of {_1} `kind` ist eines der folgenden: {_1} +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` muss „round“, „invertedRound“ oder „chamfer“ sein, nicht {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` muss `text`, `visibility`, `image` oder `graph` sein +@msg `kind` must be a string `kind` muss ein String sein +@msg `kind` must be image, noImage or htmlText `kind` muss image, noImage oder htmlText sein +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` muss „none“, „hard“ oder „soft“ sein +@msg `label` must be a non-empty name of at most 200 characters `label` muss ein nicht leerer Name mit höchstens 200 Zeichen sein +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` muss ein Sprachname oder -Tag sein, etwa „Turkish“ oder „tr“ +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` muss „romanBaseline“ oder „emBoxTop“ sein +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` muss zwischen 0,1 und 5000 liegen oder null für Automatisch sein +@msg `length` must be a number of at most 1000000 pt either way `length` muss eine Zahl von höchstens 1000000 pt in beide Richtungen sein +@msg `line` must be true or false `line` muss true oder false sein +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) darf nicht größer als `maxAngle` ({max_angle}°) sein +@msg `mode` must be "handles", "straight" or "smooth" `mode` muss „handles“, „straight“ oder „smooth“ sein +@msg `mode` must be "straight", "bezier" or "arc" `mode` muss „straight“, „bezier“ oder „arc“ sein +@msg `mode` must be one of {_1} `mode` muss eines der folgenden sein: {_1} +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` muss „none“, „yakumonoHankaku“, „gyomatsuYakumonoHankaku“, „gyomatsuYakumonoZenkaku“, „yakumonoZenkaku“ oder „lineEndHalf“ sein +@msg `names` is a list of preset names `names` ist eine Liste von Vorgabennamen +@msg `names` lists preset names `names` listet Vorgabennamen auf +@msg `names` must be a list of names `names` muss eine Liste von Namen sein +@msg `names` must be a list of swatch names `names` muss eine Liste von Farbfeldnamen sein +@msg `names` must be an array of style names `names` muss ein Array aus Formatnamen sein +@msg `new` is a design name, or true `new` ist ein Designname oder true +@msg `path` must be a folder `path` muss ein Ordner sein +@msg `path` must be a path object `path` muss ein Pfadobjekt sein +@msg `pivot` must be a whole number from 0 to 8 `pivot` muss eine ganze Zahl von 0 bis 8 sein +@msg `points` must be a list of [x, y] points `points` muss eine Liste von Punkten [x, y] sein +@msg `points` must be a list of point indexes `points` muss eine Liste von Punktindizes sein +@msg `points` must be an array of point indices `points` muss ein Array aus Punktindizes sein +@msg `points` must be point indexes `points` müssen Punktindizes sein +@msg `port` must be "in" or "out" `port` muss „in“ oder „out“ sein +@msg `position` is above or below `position` ist above oder below +@msg `preset` must be a string `preset` muss ein String sein +@msg `ratio` must be from 0.1 to 0.5 `ratio` muss zwischen 0,1 und 0,5 liegen +@msg `rect` must be [x, y, width, height] with a positive size `rect` muss [x, y, width, height] mit einer positiven Größe sein +@msg `relation` must be overlap, inside or outside `relation` muss overlap, inside oder outside sein +@msg `reversed` must be true or false `reversed` muss true oder false sein +@msg `rotate` must be a finite number `rotate` muss eine endliche Zahl sein +@msg `rule` must be nonZero or evenOdd `rule` muss nonZero oder evenOdd sein +@msg `scale` must be a number in (0, 100] `scale` muss eine Zahl in (0, 100] sein +@msg `scope` must be "selection" or "document", not {other} `scope` muss „selection“ oder „document“ sein, nicht {other} +@msg `script` must be a string `script` muss ein String sein +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` ist eine nicht leere Liste von Reihenindizes +@msg `seriesIndexes` needs a `type` or a `valueAxis` `seriesIndexes` braucht einen `type` oder eine `valueAxis` +@msg `shift` must be a number of points `shift` muss eine Anzahl Punkt sein +@msg `showCenter` must be true or false `showCenter` muss true oder false sein +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` muss „fullJustify“, „alignLeft“, „alignCenter“ oder „alignRight“ sein +@msg `size` is a percentage of the base text's size, 1 to 100 `size` ist ein Prozentwert der Größe des Grundtexts, 1 bis 100 +@msg `source` and `target` are the same object `source` und `target` sind dasselbe Objekt +@msg `source` must be `folder` or `datasets` `source` muss `folder` oder `datasets` sein +@msg `spacing` must be a number `spacing` muss eine Zahl sein +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` muss eine Anzahl Punkt sein, 0 oder größer, oder „auto“ +@msg `start` must be a finite number `start` muss eine endliche Zahl sein +@msg `starts` must be an array of anchor indices (or nulls) `starts` muss ein Array aus Ankerpunktindizes (oder null-Werten) sein +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` muss linked, embedded, missing oder modified oder eine Liste davon sein +@msg `stop` must be a stop index `stop` muss ein Farbstoppindex sein +@msg `story` styles the whole story: give no `start` or `end` `story` formatiert den gesamten Textabschnitt: Geben Sie kein `start` oder `end` an +@msg `story` takes the whole story: give no `start` or `end` `story` nimmt den gesamten Textabschnitt: Geben Sie kein `start` oder `end` an +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` gilt für Konturen (stroke: true) +@msg `style` must be roman or japanese `style` muss roman oder japanese sein +@msg `subpaths` must be two integer indices `subpaths` müssen zwei ganzzahlige Indizes sein +@msg `symbol` must be a symbol name `symbol` muss ein Symbolname sein +@msg `target` must be "object" or "contents", not {v} `target` muss „object“ oder „contents“ sein, nicht {v} +@msg `tint` applies to global and spot colours `tint` gilt für globale Farben und Volltonfarben +@msg `tint` goes with a global or spot `swatch` `tint` gehört zu einem globalen oder Volltonfarben-`swatch` +@msg `tint` must be a number (%) `tint` muss eine Zahl sein (%) +@msg `tint` needs a global or spot colour `tint` braucht eine globale Farbe oder Volltonfarbe +@msg `to` is already threaded `to` ist bereits verkettet +@msg `to` is the design's new name `to` ist der neue Name des Designs +@msg `to` must be [x, y] `to` muss [x, y] sein +@msg `to` must be a closed path, area type or type on a path other than `from` `to` muss ein geschlossener Pfad, Flächentext oder Pfadtext außer `from` sein +@msg `to` must be a whole number `to` muss eine ganze Zahl sein +@msg `to` takes the rows of one object's stack, not aligned rows `to` nimmt die Zeilen des Stapels eines Objekts, keine ausgerichteten Zeilen +@msg `useLab` must be true or false `useLab` muss true oder false sein +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` muss default, top, middle, baseline oder bottom sein +@msg `values` must be a list `values` muss eine Liste sein +@msg `values` must be an object `values` muss ein Objekt sein +@msg `values` must name hex, rgb, cmyk, lab or gray `values` muss hex, rgb, cmyk, lab oder gray nennen +@msg `values` names a kind twice `values` nennt eine Art zweimal +@msg `{_1}` can't be edited or removed `{_1}` kann nicht bearbeitet oder entfernt werden +@msg `{_1}` is built in and always listed `{_1}` ist integriert und wird immer aufgeführt +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` ist keine Schriftdatei, die die letzte Suche mit text.findFontFiles gefunden hat +@msg `{_1}` would be overwritten by the package: choose a different folder or name `{_1}` würde durch das Paket überschrieben: Wählen Sie einen anderen Ordner oder Namen +@msg `{file}` has no art to place `{file}` hat keine Grafik zum Platzieren +@msg `{file}` is over {_1} MB `{file}` ist größer als {_1} MB +@msg `{f}` belongs to a VectorCraft that is running `{f}` gehört zu einem laufenden VectorCraft +@msg `{f}` is the copy of a document open now `{f}` ist die Kopie eines gerade geöffneten Dokuments +@msg `{id}` is no writable format (see document.formats) `{id}` ist kein beschreibbares Format (siehe document.formats) +@msg `{id}` isn't a loaded library `{id}` ist keine geladene Bibliothek +@msg `{key}` has no colours `{key}` hat keine Farben +@msg `{key}` is a design name `{key}` ist ein Designname +@msg `{key}` is a design name or a list of them `{key}` ist ein Designname oder eine Liste davon +@msg `{key}` is longer than {MAX_NAME} characters `{key}` ist länger als {MAX_NAME} Zeichen +@msg `{key}` is out of range `{key}` liegt außerhalb des gültigen Bereichs +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` muss [min, desired, max] oder {{min?, desired?, max?}} in Prozent sein +@msg `{key}` must be a non-negative integer `{key}` muss eine nicht negative ganze Zahl sein +@msg `{key}` must be a number {_1}..{_2} `{key}` muss eine Zahl von {_1}..{_2} sein +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` muss eine ganze Zahl von mindestens 0 sein, nicht {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` muss ein Index eines Aussehenselements (Malreihenfolge) oder null sein +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` muss ein Array aus Asset-IDs sein (siehe assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` muss zwischen 0 und 180 Grad liegen, nicht {a} +@msg `{key}` must be from {_1} to {shift_max} `{key}` muss zwischen {_1} und {shift_max} liegen +@msg `{key}`: not a colour: {v} `{key}`: keine Farbe: {v} +@msg `{k}.family` is required `{k}.family` ist erforderlich +@msg `{k}` is a number 0..1 `{k}` ist eine Zahl von 0..1 +@msg `{k}` is true or false `{k}` ist true oder false +@msg `{k}` isn't an export setting: {_1} `{k}` ist keine Exporteinstellung: {_1} +@msg `{k}` must be a byte offset `{k}` muss ein Byte-Offset sein +@msg `{k}` must be a number `{k}` muss eine Zahl sein +@msg `{k}` must be a number of points `{k}` muss eine Anzahl Punkt sein +@msg `{k}` must be a string `{k}` muss ein String sein +@msg `{k}` must be a whole number `{k}` muss eine ganze Zahl sein +@msg `{k}` must be from {lo} to {hi} for {key} `{k}` muss für {key} zwischen {lo} und {hi} liegen +@msg `{k}` must be {what}, not {v} `{k}` muss {what} sein, nicht {v} +@msg `{l}` isn't a global or spot colour `{l}` ist keine globale Farbe und keine Volltonfarbe +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` ist keine Bitmap: Verwenden Sie clipboard.importSvg, importPdf oder importText +@msg `{name}` can't be deleted `{name}` kann nicht gelöscht werden +@msg `{name}` can't be duplicated `{name}` kann nicht dupliziert werden +@msg `{name}` can't be edited `{name}` kann nicht bearbeitet werden +@msg `{name}` can't be edited this way `{name}` kann nicht auf diese Weise bearbeitet werden +@msg `{name}` can't be renamed `{name}` kann nicht umbenannt werden +@msg `{name}` can't name a folder `{name}` kann keinen Ordner benennen +@msg `{name}` has no art to place `{name}` hat keine Grafik zum Platzieren +@msg `{name}` has no text to place `{name}` hat keinen Text zum Platzieren +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text `{name}` im Word-Dokument ist kein UTF-8- oder UTF-16-Text +@msg `{name}` in the Word document is not well-formed XML: {e} `{name}` im Word-Dokument ist kein wohlgeformtes XML: {e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep `{name}` im Word-Dokument verschachtelt Elemente mehr als {MAX_NESTING} Ebenen tief +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` ist eine integrierte Vorgabe und schreibgeschützt: Speichern Sie sie unter einem anderen Namen +@msg `{name}` is a built-in preset: save under another name `{name}` ist eine integrierte Vorgabe: Speichern Sie unter einem anderen Namen +@msg `{name}` is a built-in profile and stays `{name}` ist ein integriertes Profil und bleibt erhalten +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` ist ein Tonwert von `{base}`: Bearbeiten Sie `{base}` (ein Tonwertfeld nimmt nur einen neuen Namen an) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` ist ein Arbeitsfarbraum: Wählen Sie zuerst mit edit.colorSettings einen anderen +@msg `{name}` is built in and stays `{name}` ist integriert und bleibt erhalten +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` nimmt eine Farbe seiner eigenen Art (Farbe, Verlauf oder Muster) +@msg `{name}`: Minimum must not exceed Desired, nor Desired Maximum `{name}`: Minimum darf Optimal nicht übersteigen und Optimal nicht Maximum +@msg `{n}` can't be deleted `{n}` kann nicht gelöscht werden +@msg `{n}` can't be merged `{n}` kann nicht zusammengeführt werden +@msg `{n}` can't be moved `{n}` kann nicht verschoben werden +@msg `{n}` is not a colour swatch `{n}` ist kein Farbfeld +@msg `{n}` isn't a solid colour `{n}` ist keine Vollfarbe +@msg `{n}`: colour groups hold solid colours only `{n}`: Farbgruppen enthalten nur Vollfarben +@msg `{other}` doesn't trace an image `{other}` zeichnet kein Bild nach +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`: Die Verweise des Schritts kopieren mehr als {_1} MB an Werten +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` ist kein Achsen-Tag: Ein Tag hat vier Zeichen, etwa „wght“ +@msg `{to}` is empty or already used `{to}` ist leer oder wird bereits verwendet +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` ist länger als {MAX_TEXT} Byte +@msg `{what}` must be text `{what}` muss Text sein +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard eine DXF-Zeichnung hat einen Ursprung: Übergeben Sie useArtboards: true für eine Zeichnung pro Zeichenfläche +@msg a PDF presets file with over {_1} presets eine PDF-Vorgabendatei mit über {_1} Vorgaben +@msg a batch preview cannot switch documents eine Stapelvorschau kann nicht zwischen Dokumenten wechseln +@msg a brush named `{nn}` exists ein Pinsel namens `{nn}` existiert bereits +@msg a container of the listed objects is gone ein Container der aufgelisteten Objekte ist nicht mehr vorhanden +@msg a content stream can't be read ein Inhaltsdatenstrom kann nicht gelesen werden +@msg a content stream isn't a stream ein Inhaltsdatenstrom ist kein Datenstrom +@msg a design holds at most {_1} objects ein Design enthält höchstens {_1} Objekte +@msg a design needs a name ein Design braucht einen Namen +@msg a dictionary grew too large ein Wörterbuch ist zu groß geworden +@msg a document keeps at most {_1} graph designs ein Dokument enthält höchstens {_1} Diagrammdesigns +@msg a document keeps at most {_1} saved selections ein Dokument enthält höchstens {_1} gespeicherte Auswahlen +@msg a document keeps at most {_1} views ein Dokument enthält höchstens {_1} Ansichten +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files ein Dokument verknüpft höchstens {MAX_SVG_SCRIPTS} JavaScript-Dateien +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes die Ordnung eines feConvolveMatrix-Filters ist {x}×{y}, was bei der Multiplikation der Kernelgröße in usvg überläuft +@msg a gradient preset is already called {n} eine Verlaufsvorgabe heißt bereits {n} +@msg a graph has at most {_1} series, so it transposes with at most that many categories ein Diagramm hat höchstens {_1} Reihen, daher lässt es sich nur mit höchstens so vielen Kategorien transponieren +@msg a layer can't be merged into its own sublayer eine Ebene kann nicht mit ihrer eigenen Unterebene zusammengeführt werden +@msg a layer's form box isn't an array der Formularrahmen einer Ebene ist kein Array +@msg a layer's form can't be read das Formular einer Ebene kann nicht gelesen werden +@msg a layer's form has no box das Formular einer Ebene hat keinen Rahmen +@msg a layer's form has no dictionary das Formular einer Ebene hat kein Wörterbuch +@msg a legend draws at most {MAX_CHIPS} swatches eine Legende zeichnet höchstens {MAX_CHIPS} Farbfelder +@msg a name names one asset: give multiple: false, or one object ein Name benennt ein einzelnes Asset: Geben Sie multiple: false oder ein Objekt an +@msg a page can't be read eine Seite kann nicht gelesen werden +@msg a page object is used by something else ein Seitenobjekt wird von etwas anderem verwendet +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) eine Seitenminiatur muss aus RGB-Pixeln bestehen, 1 bis {THUMBNAIL_SIZE} pro Seite (erhalten: {w} × {h}) +@msg a pattern layer doesn't end what it starts eine Musterebene beendet nicht, was sie beginnt +@msg a pattern layer's colors hold something else die Farben einer Musterebene enthalten etwas anderes +@msg a pattern named `{n}` exists ein Muster namens `{n}` existiert bereits +@msg a plug-in with id {_1} is already installed ein Plug-in mit der ID {_1} ist bereits installiert +@msg a preset named `{n}` exists eine Vorgabe namens `{n}` existiert bereits +@msg a print presets file with over {_1} presets eine Druckvorgabendatei mit über {_1} Vorgaben +@msg a profile is already called {n} ein Profil heißt bereits {n} +@msg a row can't go into itself eine Zeile kann nicht in sich selbst eingefügt werden +@msg a series goes on the left or the right value axis eine Reihe gehört auf die linke oder die rechte Werteachse +@msg a start must be an anchor index ≥ 0 or null ein Anfang muss ein Ankerpunktindex ≥ 0 oder null sein +@msg a style named `{name}` already exists ein Format namens `{name}` existiert bereits +@msg a style named `{to}` already exists ein Format namens `{to}` existiert bereits +@msg a swatch needs a colour, gradient or pattern ein Farbfeld braucht eine Farbe, einen Verlauf oder ein Muster +@msg a symbol named `{n}` already exists ein Symbol namens `{n}` existiert bereits +@msg a theme has 1 to {THEME_SIZE} colours ein Farbthema hat 1 bis {THEME_SIZE} Farben +@msg a theme is already called {name} (replace: "{name}" overwrites it) ein Farbthema heißt bereits {name} (replace: „{name}“ überschreibt es) +@msg a toolbar name has at most {MAX_NAME} characters ein Werkzeugleistenname hat höchstens {MAX_NAME} Zeichen +@msg a view named `{_1}` exists eine Ansicht namens `{_1}` existiert bereits +@msg a view named `{name}` exists eine Ansicht namens `{name}` existiert bereits +@msg a workspace keeps at most {MAX} toolbars ein Arbeitsbereich enthält höchstens {MAX} Werkzeugleisten +@msg a {_1} series can't go in a {_2} graph eine Reihe vom Typ {_1} kann nicht in ein Diagramm vom Typ {_2} +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}: „pdf“ (PDF-kompatibel, Standard) oder „7“ (älteres .ai, Version 7) +@msg already editing an opacity mask eine Deckkraftmaske wird bereits bearbeitet +@msg already editing another object's opacity mask die Deckkraftmaske eines anderen Objekts wird bereits bearbeitet +@msg already in pattern editing mode bereits im Musterbearbeitungsmodus +@msg an artboard is listed twice eine Zeichenfläche ist zweimal aufgeführt +@msg an empty artboard has no width or height to scale to eine leere Zeichenfläche hat keine Breite oder Höhe, auf die skaliert werden kann +@msg an image is too large for DXF and was left out ein Bild ist zu groß für DXF und wurde ausgelassen +@msg an image of {_1} MB, at most {_2} MB ein Bild mit {_1} MB, höchstens {_2} MB +@msg an image of {declared_w} × {declared_h} pixels is too large to draw ein Bild mit {declared_w} × {declared_h} Pixeln ist zu groß zum Zeichnen +@msg an image of {width} × {height} pixels is too large to draw ein Bild mit {width} × {height} Pixeln ist zu groß zum Zeichnen +@msg an index must be a whole number ein Index muss eine ganze Zahl sein +@msg an inline graphic has no glyphs eine eingebundene Grafik hat keine Glyphen +@msg an invisible text form can't be read ein unsichtbares Textformular kann nicht gelesen werden +@msg an object is missing ein Objekt fehlt +@msg an object's header is missing der Kopf eines Objekts fehlt +@msg an overprinting form can't be read ein überdruckendes Formular kann nicht gelesen werden +@msg an unread object has no readable bounds ein nicht gelesenes Objekt hat keine lesbaren Begrenzungen +@msg an unread object holds a frame of threaded type ein nicht gelesenes Objekt enthält einen Rahmen mit verkettetem Text +@msg an unread object is missing from its layer ein nicht gelesenes Objekt fehlt in seiner Ebene +@msg anchor `smooth` must be true or false Ankerpunkt-`smooth` muss true oder false sein +@msg anchor `{key}` must be [x, y] Ankerpunkt-`{key}` muss [x, y] sein +@msg anchor `{key}` must be a number Ankerpunkt-`{key}` muss eine Zahl sein +@msg angle and roundness must be finite angle und roundness müssen endlich sein +@msg angles must be a list as long as pins (degrees or null) angles muss eine Liste so lang wie pins sein (Grad oder null) +@msg appearance must be flat|center|edge appearance muss flat|center|edge sein +@msg apply is "fill" or "stroke", not `{other}` apply ist „fill“ oder „stroke“, nicht `{other}` +@msg area must be a closed polygon of 3 to 100000 [x, y] points area muss ein geschlossenes Polygon aus 3 bis 100000 Punkten [x, y] sein +@msg area type needs a path that encloses an area Flächentext braucht einen Pfad, der eine Fläche umschließt +@msg area type {_1} is named twice Flächentext {_1} ist zweimal genannt +@msg arrowAlign must be extend|tip, got {o} arrowAlign muss extend|tip sein, erhalten: {o} +@msg arrowScale must be [start, end] arrowScale muss [start, end] sein +@msg artboard is a 0-based index artboard ist ein nullbasierter Index +@msg artboard must be a 0-based index artboard muss ein nullbasierter Index sein +@msg artboard {i} doesn't print Zeichenfläche {i} wird nicht gedruckt +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout muss {layout?, columns?, spacing?, rightToLeft?} sein +@msg artboardLayout.columns must be 1 or more artboardLayout.columns muss 1 oder größer sein +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout muss gridByRow, gridByColumn, row oder column sein +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft muss true oder false sein +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing muss 0–{MAX_SPACING} pt sein +@msg artboards must be a non-empty list artboards muss eine nicht leere Liste sein +@msg artboards must be a number artboards muss eine Zahl sein +@msg asset `{_1}` has no art to export Asset `{_1}` hat keine Grafik zum Exportieren +@msg at most {MAX_KEYWORDS} keywords höchstens {MAX_KEYWORDS} Stichwörter +@msg at most {MAX_MATCHES} matches are kept höchstens {MAX_MATCHES} Treffer werden behalten +@msg at most {MAX_POINTS} points höchstens {MAX_POINTS} Punkte +@msg at most {MAX_QUEUE} files höchstens {MAX_QUEUE} Dateien +@msg at must be [x, y] at muss [x, y] sein +@msg at/rect lie outside the canvas at/rect liegen außerhalb der Arbeitsfläche +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute muss size|leading|tracking|kerning|baselineShift sein +@msg attrs must be an object attrs muss ein Objekt sein +@msg automation path rejected: the folder's name isn't valid text Automatisierungspfad abgelehnt: Der Name des Ordners ist kein gültiger Text +@msg automation path rejected: {_1} isn't valid text Automatisierungspfad abgelehnt: {_1} ist kein gültiger Text +@msg automation path rejected: {why}: {path} Automatisierungspfad abgelehnt: {why}: {path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis muss „vertical“, „horizontal“ oder ein Winkel in Grad sein, nicht `{given}` +@msg backgroundContents must be transparent or white backgroundContents muss transparent oder white sein +@msg bad AES key ungültiger AES-Schlüssel +@msg bad `runs`: {e} ungültige `runs`: {e} +@msg bad arrowScale ungültige arrowScale +@msg bad base64 ungültiges Base64 +@msg bad color {c} ungültige Farbe {c} +@msg bad colour ungültige Farbe +@msg bad colour in `colors` ungültige Farbe in `colors` +@msg bad colour {v} ungültige Farbe {v} +@msg bad colour {v} in groupColors ungültige Farbe {v} in groupColors +@msg bad cross-reference entry ungültiger Querverweiseintrag +@msg bad cross-reference table ungültige Querverweistabelle +@msg bad current colour {v} ungültige aktuelle Farbe {v} +@msg bad dataBase64 ungültiges dataBase64 +@msg bad edit ungültige Bearbeitung +@msg bad fill {v} ungültige Fläche {v} +@msg bad fit: {e} ungültige Einpassung: {e} +@msg bad model {other} (keep, new or document) ungültiges Modell {other} (keep, new oder document) +@msg bad new colour {v} ungültige neue Farbe {v} +@msg bad object ungültiges Objekt +@msg bad object number ungültige Objektnummer +@msg bad offset ungültiger Offset +@msg bad path data: {e} ungültige Pfaddaten: {e} +@msg bad point {v}: expected [x, y] or [x, y, pressure] ungültiger Punkt {v}: erwartet wird [x, y] oder [x, y, pressure] +@msg bad reference ungültiger Verweis +@msg bad trailer size ungültige Trailer-Größe +@msg bad {k} colour ungültige {k}-Farbe +@msg batches cannot nest Stapel können nicht verschachtelt werden +@msg bind something to a variable first binden Sie zuerst etwas an eine Variable +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed muss 0–{MAX_BLEED} pt sein: eine Zahl, [top, bottom, left, right] oder {{top, bottom, left, right}} +@msg brush must be a name or null brush muss ein Name oder null sein +@msg by must be a number by muss eine Zahl sein +@msg can't add page thumbnails: {why} Seitenminiaturen können nicht hinzugefügt werden: {why} +@msg can't add the editing data for other apps: {why} die Bearbeitungsdaten für andere Anwendungen können nicht hinzugefügt werden: {why} +@msg can't edit the written PDF: {why} das geschriebene PDF kann nicht bearbeitet werden: {why} +@msg can't encrypt the PDF: {why} das PDF kann nicht verschlüsselt werden: {why} +@msg can't linearise the PDF: {why} das PDF kann nicht linearisiert werden: {why} +@msg can't open `{_1}`: {_2} `{_1}` kann nicht geöffnet werden: {_2} +@msg can't open `{_1}`: {e} `{_1}` kann nicht geöffnet werden: {e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) `{name}` kann nicht geöffnet werden: kein Format, das VectorCraft liest (siehe document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) `{name}` kann nicht platziert werden: kein Format, das VectorCraft liest (siehe document.formats) +@msg can't read back the written PDF: {e} das geschriebene PDF kann nicht zurückgelesen werden: {e} +@msg can't read this Word document: {why} dieses Word-Dokument kann nicht gelesen werden: {why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp .{e}-Bilder können nicht geschrieben werden: Verwenden Sie .png, .jpg, .gif, .webp, .tif oder .bmp +@msg can't write PDF layers, overprint or invisible text: {why} PDF-Ebenen, Überdrucken oder unsichtbarer Text können nicht geschrieben werden: {why} +@msg can't write the file's copyright: {why} das Copyright der Datei kann nicht geschrieben werden: {why} +@msg can't write the file's modification date: {why} das Änderungsdatum der Datei kann nicht geschrieben werden: {why} +@msg can't write the pages' user units: {why} die Benutzereinheiten der Seiten können nicht geschrieben werden: {why} +@msg cannot distort a zero-size or non-finite bounding box ein Begrenzungsrahmen der Größe null oder mit nicht endlichen Werten kann nicht verzerrt werden +@msg cannot move an object into itself ein Objekt kann nicht in sich selbst verschoben werden +@msg case must be upper|lower|title|sentence case muss upper|lower|title|sentence sein +@msg category `{c}`: one of {_1} Kategorie `{c}`: eines von {_1} +@msg center must be [x, y] center muss [x, y] sein +@msg choose at least one artboard wählen Sie mindestens eine Zeichenfläche +@msg choose at least one status wählen Sie mindestens einen Status +@msg clip must be true or false clip muss true oder false sein +@msg clippingMask must be true or false clippingMask muss true oder false sein +@msg closed must be a boolean closed muss ein boolescher Wert sein +@msg collapsed must be true or false collapsed muss true oder false sein +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: Kein RGB- oder CMYK-Profil heißt „{profile}“ +@msg colorMode must be rgb or cmyk, not `{m}` colorMode muss rgb oder cmyk sein, nicht `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel muss {own} (der Modus des Dokuments), grayscale oder bitmap sein +@msg colour groups can't go into colour groups Farbgruppen können nicht in Farbgruppen eingefügt werden +@msg colour, mode and spot apply to solid-colour swatches only colour, mode und spot gelten nur für Vollfarbfelder +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read Spalte {column}: `{cell}` ist eine `%`-Spalte von Diagrammdatendateien, die nicht gelesen werden +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable Spalte {column}: `{cell}` nennt `{name}`, eine Variable vom Typ {_1} +@msg column {column}: `{cell}` names no variable Spalte {column}: `{cell}` nennt keine Variable +@msg column {column}: the variable name is longer than {MAX_NAME} characters Spalte {column}: Der Variablenname ist länger als {MAX_NAME} Zeichen +@msg column: true floats the whole column and takes no onto, above or below column: true macht die ganze Spalte schwebend und nimmt kein onto, above oder below +@msg command must be a shape.* command command muss ein shape.*-Befehl sein +@msg commands[{i}]: missing `command` commands[{i}]: `command` fehlt +@msg composer must be "singleLine" or "everyLine" composer muss „singleLine“ oder „everyLine“ sein +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes zusammengesetzte Formen nehmen Pfade, zusammengesetzte Pfade, Gruppen, zusammengesetzte Formen, Text, Angleichungen und Verzerrungshüllen +@msg compound {_1} must contain exactly one open path to join zusammengesetzter Pfad {_1} muss genau einen offenen Pfad zum Zusammenfügen enthalten +@msg connect end must be "start" or "end" connect end muss „start“ oder „end“ sein +@msg connect needs a path other than the one extended connect braucht einen anderen Pfad als den verlängerten +@msg connect needs id connect braucht id +@msg connect path has no open end der connect-Pfad hat kein offenes Ende +@msg copy needs path: the file to copy into the document's folder copy braucht path: die Datei, die in den Ordner des Dokuments kopiert werden soll +@msg copy takes images only: relink placed documents where their files are copy nimmt nur Bilder: Verknüpfen Sie platzierte Dokumente dort neu, wo ihre Dateien liegen +@msg copying a file isn't available on the web das Kopieren einer Datei ist im Web nicht verfügbar +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus muss unknown, copyrighted oder publicDomain sein +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl muss eine URL sein (keine Leerzeichen, höchstens 2048 Zeichen) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners müssen 4 Punkte [x, y] sein (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`: item oder file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`: Nur PDF-Seiten haben Art-, Trim-, Bleed- und Media-Boxen; verwenden Sie „crop“ oder „bounding“ +@msg crop rectangle doesn't overlap the image das Freistellungsrechteck überlappt das Bild nicht +@msg cross-reference and object streams aren't supported Querverweis- und Objektdatenströme werden nicht unterstützt +@msg damaged compressed data: {e} beschädigte komprimierte Daten: {e} +@msg dash lengths must be numbers, got {v} Strichlängen müssen Zahlen sein, erhalten: {v} +@msg dash must be a list of lengths or null, got {v} dash muss eine Liste von Längen oder null sein, erhalten: {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) Datenschlüssel `{k}`: Buchstaben, Ziffern, -, _ und . (nicht name oder vc-…) +@msg data must be an object of key: value, not {v} data muss ein Objekt aus key: value sein, nicht {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare Datensatz `{name}` enthält einen Wert für `{var}`, den die Bibliothek nicht deklariert +@msg data set `{name}` holds two values for `{var}` Datensatz `{name}` enthält zwei Werte für `{var}` +@msg data set `{name}` is declared twice Datensatz `{name}` ist zweimal deklariert +@msg data takes at most {MAX_DATA} keys at once data nimmt höchstens {MAX_DATA} Schlüssel auf einmal +@msg dataset `{name}` already exists Datensatz `{name}` existiert bereits +@msg dataset `{to}` already exists Datensatz `{to}` existiert bereits +@msg date `{d}`: YYYY-MM-DD Datum `{d}`: YYYY-MM-DD +@msg decoded data is too large die decodierten Daten sind zu groß +@msg define a variable first definieren Sie zuerst eine Variable +@msg detect must be true or false detect muss true oder false sein +@msg direct-select anchor points of a path wählen Sie Ankerpunkte eines Pfads direkt aus +@msg distance must be > 0 distance muss > 0 sein +@msg dx and dy give the arrow's direction dx und dy geben die Richtung des Pfeils an +@msg dx and dy must be numbers dx und dy müssen Zahlen sein +@msg dxf fitTo must be [width, height], positive dxf fitTo muss [width, height] sein, positiv +@msg dxf must be an object of DXF import options dxf muss ein Objekt aus DXF-Importoptionen sein +@msg dxf scale must be a positive number up to {_1}, not {s} dxf scale muss eine positive Zahl bis {_1} sein, nicht {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt dxf unit `{u}`: eine Einheit wie mm, cm, in oder pt +@msg dxf: {e} dxf: {e} +@msg each format is an object {format, scale?, suffix?} jedes Format ist ein Objekt {format, scale?, suffix?} +@msg each item needs an id jedes Element braucht eine id +@msg each match needs `kind` jeder Treffer braucht `kind` +@msg each match needs a `name` jeder Treffer braucht einen `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them jedes Element von `fonts` ist {family, style?}; 1 bis 1000 davon +@msg each of `items` is {kind, item} jedes Element von `items` ist {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} jedes Element von `items` ist {{id, corners?}} mit einer Objekt-ID, nicht {it} +@msg each row needs `from` jede Zeile braucht `from` +@msg each stop needs a position jeder Farbstopp braucht eine Position +@msg each subpath needs an anchors array jeder Teilpfad braucht ein anchors-Array +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType muss easeIn, easeOut, easeInOut oder easeOutIn sein +@msg editingData must be true or false editingData muss true oder false sein +@msg editingData must be true, false or "always" editingData muss true, false oder „always“ sein +@msg edits overlap Bearbeitungen überlappen sich +@msg effects of objects whose appearances differ can't be moved together Effekte von Objekten mit unterschiedlichem Aussehen können nicht zusammen verschoben werden +@msg empty header in column {_1} leere Kopfzeile in Spalte {_1} +@msg encoding `{_1}`: utf8 or utf16 Codierung `{_1}`: utf8 oder utf16 +@msg ends must be two of "first" and "last" ends muss zwei von „first“ und „last“ sein +@msg every numbered name for the result is taken jeder nummerierte Name für das Ergebnis ist bereits vergeben +@msg expanded SVG styles exceed the import size limit die erweiterten SVG-Stile überschreiten die Importgrößengrenze +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes die Exportvorgabendatei ist größer als {MAX_PRESET_FILE_BYTES} Byte +@msg exportText must be editable or appearance exportText muss editable oder appearance sein +@msg exportType must be movieClip or graphic, not `{s}` exportType muss movieClip oder graphic sein, nicht `{s}` +@msg extend needs id extend braucht id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges listet höchstens {MAX_FEATURE_CHANGES} Tags auf +@msg featureChanges must be a list of tags featureChanges muss eine Liste von Tags sein +@msg features must be a list of tags features muss eine Liste von Tags sein +@msg fidelity must be 0..100 fidelity muss 0..100 sein +@msg files must be {name, dataBase64} files muss {name, dataBase64} sein +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit muss none|autoHeight|shrinkText|block sein, erhalten: {id} +@msg flattener: {e} Reduzierung: {e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`: high, medium, low oder eine gespeicherte (siehe flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile muss along|across sein, erhalten: {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`: none, rows, format, item oder itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels Ordner tiefer als {MAX_BATCH_DEPTH} Ebenen +@msg folders deeper than {MAX_DEPTH} levels Ordner tiefer als {MAX_DEPTH} Ebenen +@msg font family `{family}` is not available die Schriftfamilie `{family}` ist nicht verfügbar +@msg font family `{tf}` is not available die Schriftfamilie `{tf}` ist nicht verfügbar +@msg format `{f}`: pdf or postscript Format `{f}`: pdf oder postscript +@msg format `{f}`: text or json Format `{f}`: text oder json +@msg format is `{format}` das Format ist `{format}` +@msg formats must be an array of rows, not {v} formats muss ein Array aus Zeilen sein, nicht {v} +@msg frequency must be between 1 and 300 lines per inch frequency muss zwischen 1 und 300 Linien pro Zoll liegen +@msg from and to must be finite from und to müssen endlich sein +@msg from must be [x0, y0, x1, y1] from muss [x0, y0, x1, y1] sein +@msg give `before` or `after`, not both geben Sie `before` oder `after` an, nicht beides +@msg give `colors`, or `color` and `rule` geben Sie `colors` an, oder `color` und `rule` +@msg give `event`, such as onclick geben Sie `event` an, etwa onclick +@msg give `file`, the JavaScript file's path or URL geben Sie `file` an, den Pfad oder die URL der JavaScript-Datei +@msg give `fill` and/or `stroke` geben Sie `fill` und/oder `stroke` an +@msg give `fill`, `stroke` or both geben Sie `fill`, `stroke` oder beides an +@msg give `folder` geben Sie `folder` an +@msg give `fonts`, or open a document geben Sie `fonts` an, oder öffnen Sie ein Dokument +@msg give `from` or `indices`, not both geben Sie `from` oder `indices` an, nicht beides +@msg give `id` (no text is being edited) geben Sie `id` an (es wird kein Text bearbeitet) +@msg give `id` and `anchors`, or `items`, not both geben Sie `id` und `anchors` oder `items` an, nicht beides +@msg give `id` or `guide`, not both geben Sie `id` oder `guide` an, nicht beides +@msg give `length` or `to` geben Sie `length` oder `to` an +@msg give `matches`: [{kind, name, target}] geben Sie `matches` an: [{kind, name, target}] +@msg give `name` (or edit a pattern) geben Sie `name` an (oder bearbeiten Sie ein Muster) +@msg give `name` or `names` geben Sie `name` oder `names` an +@msg give `path` (a .wasm file) or `dataBase64` (the module) geben Sie `path` (eine .wasm-Datei) oder `dataBase64` (das Modul) an +@msg give `path`, `data` or `dataBase64` geben Sie `path`, `data` oder `dataBase64` an +@msg give `path`, `data`, `dataBase64` or `csv` geben Sie `path`, `data`, `dataBase64` oder `csv` an +@msg give `path`, `xml` or `dataBase64` geben Sie `path`, `xml` oder `dataBase64` an +@msg give `script`, the JavaScript the event runs ("" removes the event) geben Sie `script` an, das JavaScript, das das Ereignis ausführt ("" entfernt das Ereignis) +@msg give `text` (the reading) or `remove` geben Sie `text` (die Lesung) oder `remove` an +@msg give `x` and `y` (and a `tolerance` of 0 or more) geben Sie `x` und `y` an (und eine `tolerance` von 0 oder größer) +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments geben Sie einen Modus oder Anpassungen für r/g/b, c/m/y/k, gray oder tint an +@msg give a name, or paint the active proxy with a saved preset geben Sie einen Namen an, oder färben Sie den aktiven Proxy mit einer gespeicherten Vorgabe +@msg give a name, or select a stroke with a saved profile geben Sie einen Namen an, oder wählen Sie eine Kontur mit einem gespeicherten Profil aus +@msg give a pattern name or select an object painted with a pattern geben Sie einen Musternamen an, oder wählen Sie ein mit einem Muster gefärbtes Objekt aus +@msg give at least one of clip, invert, disabled, linked geben Sie mindestens eines von clip, invert, disabled, linked an +@msg give color, none, swatch or gradient geben Sie color, none, swatch oder gradient an +@msg give dataBase64 geben Sie dataBase64 an +@msg give either at (a click) or area (a path drawn around the overlaps) geben Sie entweder at (einen Klick) oder area (einen um die Überlappungen gezeichneten Pfad) an +@msg give formats or a preset, not both geben Sie formats oder eine Vorgabe an, nicht beides +@msg give newName or delete geben Sie newName oder delete an +@msg give offset or by geben Sie offset oder by an +@msg give one of `to`, `rect` or `at` geben Sie eines von `to`, `rect` oder `at` an +@msg give one of assets, layers, selection or fullDocument geben Sie eines von assets, layers, selection oder fullDocument an +@msg give only one of onto, above, below, icon and a column place geben Sie nur eines von onto, above, below, icon und einem Spaltenplatz an +@msg give origin: [x, y], or reset: true geben Sie origin: [x, y] oder reset: true an +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note geben Sie overprintFill, overprintStroke, showCenter, imageMap, url oder note an +@msg give path (a file) or folder geben Sie path (eine Datei) oder folder an +@msg give path, or name and dataBase64 geben Sie path an, oder name und dataBase64 +@msg give paths or files geben Sie paths oder files an +@msg give rows, rowHeight, columns or columnWidth geben Sie rows, rowHeight, columns oder columnWidth an +@msg give svg, or dataBase64 of an SVG or SVGZ file geben Sie svg an, oder dataBase64 einer SVG- oder SVGZ-Datei +@msg give text geben Sie text an +@msg give the asset a name geben Sie dem Asset einen Namen +@msg give the preset a `name` geben Sie der Vorgabe einen `name` +@msg give the preset a name geben Sie der Vorgabe einen Namen +@msg give the preset a name (1–64 characters) geben Sie der Vorgabe einen Namen (1–64 Zeichen) +@msg give the settings to change, e.g. {"formats"}]} geben Sie die zu ändernden Einstellungen an, z. B. {"formats"}]} +@msg give the style a name geben Sie dem Format einen Namen +@msg give two or more style `names` geben Sie zwei oder mehr Formatnamen in `names` an +@msg give two or more swatches geben Sie zwei oder mehr Farbfelder an +@msg give x, y, or a handle's length or angle geben Sie x, y oder die Länge oder den Winkel eines Griffs an +@msg give {key, value} or {values} geben Sie {key, value} oder {values} an +@msg global must be true or false global muss true oder false sein +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) Glyphe {_1} wird für {ch} mit dem Format des Zeichens nicht gezeichnet (etwa Großbuchstaben, Kapitälchen oder ein Zahlensystem) +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} Glyphe {g} ist keine Alternative zu {ch} in {_1} {_2} +@msg gradient must be "objects" or "mesh" gradient muss „objects“ oder „mesh“ sein +@msg grays must be "profile" or "black", not `{g}` grays muss „profile“ oder „black“ sein, nicht `{g}` +@msg gridColors must be a preset name or two colours gridColors muss ein Vorgabenname oder zwei Farben sein +@msg gridSize must be small, medium or large gridSize muss small, medium oder large sein +@msg guide {g} is not selected Hilfslinie {g} ist nicht ausgewählt +@msg handle must be 0..3 handle muss 0..3 sein +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}: „layer“ (Standard), „omit“ oder „visible“ +@msg highlight a layer to merge into markieren Sie eine Ebene, in die zusammengeführt werden soll +@msg highlight two or more rows in one layer or group markieren Sie zwei oder mehr Zeilen in einer Ebene oder Gruppe +@msg highlight two or more rows to merge markieren Sie zwei oder mehr Zeilen zum Zusammenführen +@msg icon docks a panel in the icon column: use window.panel.dock icon dockt ein Bedienfeld in der Symbolspalte an: Verwenden Sie window.panel.dock +@msg id must be a dimension id id muss eine Bemaßungs-ID sein +@msg image has no bounds das Bild hat keine Begrenzungen +@msg image {_1} is linked already Bild {_1} ist bereits verknüpft +@msg imageMap `{m}`: none, client or server imageMap `{m}`: none, client oder server +@msg index out of range Index außerhalb des gültigen Bereichs +@msg instances must be 1..1000 instances muss 1..1000 sein +@msg invalid `color` ungültige `color` +@msg invalid anchor index {v}: expected a non-negative integer ungültiger Ankerpunktindex {v}: erwartet wird eine nicht negative ganze Zahl +@msg invalid anchor reference {v}: expected [subpath, anchor] ungültiger Ankerpunktverweis {v}: erwartet wird [subpath, anchor] +@msg invalid angle ungültiger Winkel +@msg invalid brush: {e} ungültiger Pinsel: {e} +@msg invalid color ungültige Farbe +@msg invalid colour {c} ungültige Farbe {c} +@msg invalid delta ungültiges Delta +@msg invalid point ungültiger Punkt +@msg invalid size ungültige Größe +@msg invalid t ungültiges t +@msg invalid tolerance ungültige Toleranz +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) seine Zeichenflächen liegen alle außerhalb der Arbeitsfläche (mehr als {_1} pt vom Ursprung entfernt) +@msg its clipping masks are nested too deeply seine Schnittmasken sind zu tief verschachtelt +@msg its metadata has no description seine Metadaten haben keine Beschreibung +@msg its metadata has no direct length seine Metadaten haben keine direkte Länge +@msg join two different paths fügen Sie zwei verschiedene Pfade zusammen +@msg justify must be auto|left|center|right|justifyAll justify muss auto|left|center|right|justifyAll sein +@msg kbps must be a positive number, not {kbps} kbps muss eine positive Zahl sein, nicht {kbps} +@msg keywords must be a list of words or a comma-separated string keywords muss eine Liste von Wörtern oder ein durch Kommas getrennter String sein +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters keywords darf höchstens {MAX_KEYWORD} Zeichen ohne Steuerzeichen haben +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind muss 1, 2 oder 3 sein, oder geben Sie einen Vorgaben-`name` an (siehe perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind muss graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle oder paraStyle sein +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout muss „on“, „off“, „neutral“ oder ein bool sein, nicht {v} +@msg layer `{name}` has no art to export Ebene `{name}` hat keine Grafik zum Exportieren +@msg layer must name a layer or sublayer layer muss eine Ebene oder Unterebene nennen +@msg layers can't be intertwined: select the objects in them Ebenen können nicht verflochten werden: Wählen Sie die Objekte darin aus +@msg layers go only at the top level or in layers, objects in layers and groups Ebenen gehören nur auf die oberste Stufe oder in Ebenen, Objekte in Ebenen und Gruppen +@msg layers must be true or false layers muss true oder false sein +@msg layout must be gridByRow, gridByColumn, row or column layout muss gridByRow, gridByColumn, row oder column sein +@msg leading must be a number or "auto" leading muss eine Zahl oder „auto“ sein +@msg left/right must be non-negative widths left/right müssen nicht negative Breiten sein +@msg level `{t}`: 2 or 3 Stufe `{t}`: 2 oder 3 +@msg line {line}: more than {MAX_CSV_COLS} columns Zeile {line}: mehr als {MAX_CSV_COLS} Spalten +@msg line {line}: unclosed quote Zeile {line}: nicht geschlossenes Anführungszeichen +@msg line {line}: value for `{var}` must match its variable kind Zeile {line}: Der Wert für `{var}` muss zur Art seiner Variable passen +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`: lf oder crlf +@msg loading profiles from disk isn't available in the browser das Laden von Profilen vom Datenträger ist im Browser nicht verfügbar +@msg margin: a {m} pt margin leaves no imageable area on the paper margin: Ein Rand von {m} pt lässt keinen druckbaren Bereich auf dem Papier +@msg matrix must be finite and invertible, depth finite matrix muss endlich und invertierbar sein, depth endlich +@msg matte: {e} matte: {e} +@msg member is for order front, forward, backward or back member gilt für order front, forward, backward oder back +@msg missing `artboards` `artboards` fehlt +@msg missing `at` [x, y] `at` [x, y] fehlt +@msg missing `attrs` `attrs` fehlt +@msg missing `color` or `opacity` `color` oder `opacity` fehlt +@msg missing `colors` `colors` fehlt +@msg missing `commands` `commands` fehlt +@msg missing `destinationFolder` `destinationFolder` fehlt +@msg missing `effect` id `effect`-ID fehlt +@msg missing `family` `family` fehlt +@msg missing `find` `find` fehlt +@msg missing `gid` (a glyph id, or null for the default glyph) `gid` fehlt (eine Glyphen-ID oder null für die Standardglyphe) +@msg missing `group` `group` fehlt +@msg missing `id` `id` fehlt +@msg missing `id`: give one, or select one type object `id` fehlt: Geben Sie eine an, oder wählen Sie ein Textobjekt aus +@msg missing `index` (a point index, or null to clear) `index` fehlt (ein Punktindex oder null zum Löschen) +@msg missing `index` (a stop index, or null to clear) `index` fehlt (ein Farbstoppindex oder null zum Löschen) +@msg missing `index` (an item index or null) `index` fehlt (ein Elementindex oder null) +@msg missing `item` `item` fehlt +@msg missing `library` `library` fehlt +@msg missing `library` (see graphicStyle.libraries) `library` fehlt (siehe graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) `library` fehlt (siehe swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) `map` fehlt (Zeilen [{from, to}] oder {current}) +@msg missing `matrix` [a,b,c,d,e,f] `matrix` [a,b,c,d,e,f] fehlt +@msg missing `name` (see gradient.presets.list) `name` fehlt (siehe gradient.presets.list) +@msg missing `offset` (a distance in points) `offset` fehlt (ein Abstand in Punkt) +@msg missing `path` `path` fehlt +@msg missing `path` (a path object id) `path` fehlt (eine Pfadobjekt-ID) +@msg missing `rule` `rule` fehlt +@msg missing `source` id `source`-ID fehlt +@msg missing `start` (the byte offset of a character) `start` fehlt (der Byte-Offset eines Zeichens) +@msg missing `status`: linked, embedded, missing or modified `status` fehlt: linked, embedded, missing oder modified +@msg missing `target` id `target`-ID fehlt +@msg missing `text` `text` fehlt +@msg missing `to` `to` fehlt +@msg missing `to` (an index) `to` fehlt (ein Index) +@msg missing `to`: a non-negative integer `to` fehlt: eine nicht negative ganze Zahl +@msg missing `type` `type` fehlt +@msg missing `value` `value` fehlt +@msg missing `{key}` `{key}` fehlt +@msg missing `{k}` `{k}` fehlt +@msg missing anchor Ankerpunkt fehlt +@msg missing anchors Ankerpunkte fehlen +@msg missing anchors [[subpath, anchor]…] Ankerpunkte fehlen [[subpath, anchor]…] +@msg missing array `{key}` Array `{key}` fehlt +@msg missing case case fehlt +@msg missing color Farbe fehlt +@msg missing commands Befehle fehlen +@msg missing destination folder Zielordner fehlt +@msg missing from from fehlt +@msg missing group Gruppe fehlt +@msg missing id or ids id oder ids fehlt +@msg missing ids ids fehlt +@msg missing indexes Indizes fehlen +@msg missing matrix [a,b,c,d,e,f] Matrix [a,b,c,d,e,f] fehlt +@msg missing name Name fehlt +@msg missing op op fehlt +@msg missing or invalid `color` `color` fehlt oder ist ungültig +@msg missing or unknown `tool` `tool` fehlt oder ist unbekannt +@msg missing point Punkt fehlt +@msg missing point [x,y] Punkt [x,y] fehlt +@msg missing point `{key}` ([x, y]) Punkt `{key}` fehlt ([x, y]) +@msg missing points Punkte fehlen +@msg missing points [[x,y]…] Punkte fehlen [[x,y]…] +@msg missing segment Segment fehlt +@msg missing stops Farbstopps fehlen +@msg missing subpaths Teilpfade fehlen +@msg missing target Ziel fehlt +@msg missing to to fehlt +@msg missing x, y x, y fehlen +@msg mode is set, add or toggle, not `{other}` mode ist set, add oder toggle, nicht `{other}` +@msg mode must be "cmyk" or "rgb" mode muss „cmyk“ oder „rgb“ sein +@msg mode must be area|onPath, got {o} mode muss area|onPath sein, erhalten: {o} +@msg more than 10000 linked files: package dependencies in smaller parts mehr als 10000 verknüpfte Dateien: Verpacken Sie Abhängigkeiten in kleineren Teilen +@msg more than 256 commands mehr als 256 Befehle +@msg more than {MAX_BATCH_FILES} data sets mehr als {MAX_BATCH_FILES} Datensätze +@msg more than {MAX_BATCH_FILES} files mehr als {MAX_BATCH_FILES} Dateien +@msg more than {MAX_CSV_ROWS} data rows mehr als {MAX_CSV_ROWS} Datenzeilen +@msg more than {MAX_FILES} images mehr als {MAX_FILES} Bilder +@msg more than {MAX_IMPORT} presets mehr als {MAX_IMPORT} Vorgaben +@msg moved must be [[x,y]…] moved muss [[x,y]…] sein +@msg name (a toolbar's name) is required name (der Name einer Werkzeugleiste) ist erforderlich +@msg name (a toolbar's name) must be a string name (der Name einer Werkzeugleiste) muss ein String sein +@msg name must be a toolbar's name name muss der Name einer Werkzeugleiste sein +@msg names is empty names ist leer +@msg names must be an array of symbol names names muss ein Array aus Symbolnamen sein +@msg need `anchor` or `segment` + `t` `anchor` oder `segment` + `t` erforderlich +@msg need at least one anchor mindestens ein Ankerpunkt erforderlich +@msg need at least one point mindestens ein Punkt erforderlich +@msg need at least two points mindestens zwei Punkte erforderlich +@msg needs points braucht Punkte +@msg nineSlice must be true or false nineSlice muss true oder false sein +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides muss [left, top, right, bottom] sein, jeweils -16..16, mit left + right < 1 und top + bottom < 1 +@msg no PDF preset named `{n}` keine PDF-Vorgabe namens `{n}` +@msg no action set is current kein Aktionssatz ist aktuell +@msg no active data set kein aktiver Datensatz +@msg no appearance item {index} kein Aussehenselement {index} +@msg no artboard {_1} keine Zeichenfläche {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) keine Zeichenfläche {artboard} (nullbasiert; das Dokument hat {_1}) +@msg no asset {id} kein Asset {id} +@msg no asset {id} (see assets.list) kein Asset {id} (siehe assets.list) +@msg no assets to export keine Assets zum Exportieren +@msg no assets yet: collect art for export first (assets.add) noch keine Assets: Sammeln Sie zuerst Grafiken für den Export (assets.add) +@msg no brush named `{n}` kein Pinsel namens `{n}` +@msg no character at byte {start} kein Zeichen bei Byte {start} +@msg no colour keine Farbe +@msg no colour group `{group}` keine Farbgruppe `{group}` +@msg no colour group `{name}` keine Farbgruppe `{name}` +@msg no data sets keine Datensätze +@msg no dataset named `{from}` kein Datensatz namens `{from}` +@msg no dataset named `{name}` kein Datensatz namens `{name}` +@msg no datasets keine Datensätze +@msg no design named `{name}` kein Design namens `{name}` +@msg no design named `{n}` kein Design namens `{n}` +@msg no design named `{old}` kein Design namens `{old}` +@msg no encoder for {_1} yet noch kein Encoder für {_1} +@msg no export preset `{name}` (see document.exportPresets.list) keine Exportvorgabe `{name}` (siehe document.exportPresets.list) +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder kein Ordner: Übergeben Sie `path` oder legen Sie Voreinstellungen › Plug-ins & Arbeitsvolumes › Zusätzlicher Plug-in-Ordner fest +@msg no fonts to look for: they are all available keine Schriften zu suchen: Alle sind verfügbar +@msg no formats to export keine Formate zum Exportieren +@msg no gradient preset `{name}` (see gradient.presets.list) keine Verlaufsvorgabe `{name}` (siehe gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` kein Grafikstil `{n}` in `{_1}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) keine Grafikstil-Bibliothek `{key}` (siehe graphicStyle.libraries) +@msg no guide selected keine Hilfslinie ausgewählt +@msg no guide selected (or give an index) keine Hilfslinie ausgewählt (oder geben Sie einen Index an) +@msg no guide {i} keine Hilfslinie {i} +@msg no images in the folder keine Bilder im Ordner +@msg no item at index {_1} kein Element bei Index {_1} +@msg no item at index {from} kein Element bei Index {from} +@msg no item at one of the indices {idx} kein Element bei einem der Indizes {idx} +@msg no layer keine Ebene +@msg no layer {id} (see layer.list) keine Ebene {id} (siehe layer.list) +@msg no layers to export keine Ebenen zum Exportieren +@msg no library `{key}` keine Bibliothek `{key}` +@msg no metafile format kein Metadateiformat +@msg no object {_1} kein Objekt {_1} +@msg no objects keine Objekte +@msg no parent layer keine übergeordnete Ebene +@msg no perspective grid preset named `{b}` (see perspective.presets.list) keine Perspektivenrastervorgabe namens `{b}` (siehe perspective.presets.list) +@msg no perspective grid preset named `{n}` keine Perspektivenrastervorgabe namens `{n}` +@msg no point selected (give `index`) kein Punkt ausgewählt (geben Sie `index` an) +@msg no preset called `{from}` (see file.newPresets) keine Vorgabe namens `{from}` (siehe file.newPresets) +@msg no preset called `{name}` (see file.newPresets) keine Vorgabe namens `{name}` (siehe file.newPresets) +@msg no preset named `{n}` keine Vorgabe namens `{n}` +@msg no print preset named `{b}` (see print.presets.list) keine Druckvorgabe namens `{b}` (siehe print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) keine Druckvorgabe namens `{name}` (Vorgaben: {_1}) +@msg no print preset named `{n}` keine Druckvorgabe namens `{n}` +@msg no profile named `{name}` kein Profil namens `{name}` +@msg no readable files in the folder keine lesbaren Dateien im Ordner +@msg no recovery copy `{f}` (see file.recovery.list) keine Wiederherstellungskopie `{f}` (siehe file.recovery.list) +@msg no rows keine Zeilen +@msg no saved PDF preset named `{name}` keine gespeicherte PDF-Vorgabe namens `{name}` +@msg no saved export preset `{name}` keine gespeicherte Exportvorgabe `{name}` +@msg no saved gradient preset called {name} keine gespeicherte Verlaufsvorgabe namens {name} +@msg no saved gradient presets keine gespeicherten Verlaufsvorgaben +@msg no saved preset called `{name}` keine gespeicherte Vorgabe namens `{name}` +@msg no saved presets to export (name [Default] in `names` to export it) keine gespeicherten Vorgaben zum Exportieren (nennen Sie [Default] in `names`, um sie zu exportieren) +@msg no saved presets to export (name built-in ones in `names`) keine gespeicherten Vorgaben zum Exportieren (nennen Sie integrierte in `names`) +@msg no saved print preset named `{name}` keine gespeicherte Druckvorgabe namens `{name}` +@msg no saved profile called {name} kein gespeichertes Profil namens {name} +@msg no saved selection `{name}` keine gespeicherte Auswahl `{name}` +@msg no saved theme called {name} (see colorTheme.list) kein gespeichertes Farbthema namens {name} (siehe colorTheme.list) +@msg no saved views keine gespeicherten Ansichten +@msg no series {i} keine Reihe {i} +@msg no slice {n} in the image (see slice.list) kein Slice {n} im Bild (siehe slice.list) +@msg no source art keine Quellgrafik +@msg no stop {i} (the gradient has {n}) kein Farbstopp {i} (der Verlauf hat {n}) +@msg no style `{name}` kein Format `{name}` +@msg no such assets (see assets.list) keine solchen Assets (siehe assets.list) +@msg no such frame anchor, or the frame would reach past the canvas kein solcher Rahmenankerpunkt, oder der Rahmen würde über die Arbeitsfläche hinausreichen +@msg no such objects keine solchen Objekte +@msg no such path anchor, or the path would reach past the canvas kein solcher Pfadankerpunkt, oder der Pfad würde über die Arbeitsfläche hinausreichen +@msg no such rows keine solchen Zeilen +@msg no swatch `{keep}` kein Farbfeld `{keep}` +@msg no swatch `{n}` kein Farbfeld `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) kein Farbfeld `{n}` (Farbfelder und Farbgruppen werden getrennt verschoben) +@msg no swatch library `{key}` (see swatch.library.list) keine Farbfeldbibliothek `{key}` (siehe swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) keine Farbfeldbibliothek `{key}` (siehe swatch.library.list; „document“: die Farbfelder des Dokuments) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) keine Farbfeldbibliothek `{key}` (siehe swatch.library.list; „{DOCUMENT_SWATCHES}“: die Farbfelder des Dokuments) +@msg no swatch named `{n}` kein Farbfeld namens `{n}` +@msg no swatch or colour group `{n}` kein Farbfeld und keine Farbgruppe `{n}` +@msg no swatch or colour group `{n}` in `{_1}` kein Farbfeld und keine Farbgruppe `{n}` in `{_1}` +@msg no symbol named `{name}` kein Symbol namens `{name}` +@msg no text objects selected keine Textobjekte ausgewählt +@msg no toolbar named `{_1}` keine Werkzeugleiste namens `{_1}` +@msg no toolbar named `{name}` keine Werkzeugleiste namens `{name}` +@msg no tracing preset `{preset}` (see imageTrace.presets) keine Nachzeichnervorgabe `{preset}` (siehe imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) hier ist kein Benutzerbibliotheksordner (speichern Sie mit einem Pfad oder ohne Pfad, um die Daten zu erhalten) +@msg no variable named `{from}` keine Variable namens `{from}` +@msg no variable named `{name}` keine Variable namens `{name}` +@msg no variable named `{var}` keine Variable namens `{var}` +@msg no variable named `{v}` keine Variable namens `{v}` +@msg no view named `{name}` keine Ansicht namens `{name}` +@msg node {_1} is not a graph Knoten {_1} ist kein Diagramm +@msg node {_1} is not an image Knoten {_1} ist kein Bild +@msg node {_1} is not type Knoten {_1} ist kein Text +@msg not a PDF presets file (format `{_1}`) keine PDF-Vorgabendatei (Format `{_1}`) +@msg not a PDF presets file: {e} keine PDF-Vorgabendatei: {e} +@msg not a Word document: its WordDocument stream doesn't start with a file information block kein Word-Dokument: Sein WordDocument-Datenstrom beginnt nicht mit einem File Information Block +@msg not a Word document: its compound file has no WordDocument stream kein Word-Dokument: Seine Verbunddatei hat keinen WordDocument-Datenstrom +@msg not a Word document: its main part is not a WordprocessingML document kein Word-Dokument: Sein Hauptteil ist kein WordprocessingML-Dokument +@msg not a Word document: its package has no main document part kein Word-Dokument: Sein Paket hat keinen Hauptdokumentteil +@msg not a closed path kein geschlossener Pfad +@msg not a flattener presets file (format `{_1}`) keine Reduzierungsvorgabendatei (Format `{_1}`) +@msg not a flattener presets file: {e} keine Reduzierungsvorgabendatei: {e} +@msg not a layer or group keine Ebene oder Gruppe +@msg not a library file: {e} keine Bibliotheksdatei: {e} +@msg not a mesh envelope point kein Punkt eines Hüllengitters +@msg not a perspective grid presets file (format `{_1}`) keine Perspektivenrastervorgabendatei (Format `{_1}`) +@msg not a perspective grid presets file: {e} keine Perspektivenrastervorgabendatei: {e} +@msg not a print presets file (format `{_1}`) keine Druckvorgabendatei (Format `{_1}`) +@msg not a print presets file: {e} keine Druckvorgabendatei: {e} +@msg not a stored or deflated central record kein gespeicherter oder Deflate-komprimierter zentraler Eintrag +@msg not a valid SVGZ file: {e} keine gültige SVGZ-Datei: {e} +@msg not an RTF file: it doesn't start with {\\rtf keine RTF-Datei: Sie beginnt nicht mit {\\rtf +@msg not an image VectorCraft reads (see document.formats) kein Bild, das VectorCraft liest (siehe document.formats) +@msg not area type (text in a frame) outside perspective kein Flächentext (Text in einem Rahmen) außerhalb der Perspektive +@msg not editing an opacity mask es wird keine Deckkraftmaske bearbeitet +@msg not type on a path outside perspective kein Pfadtext außerhalb der Perspektive +@msg nothing prints nichts wird gedruckt +@msg nothing to bind nichts zu binden +@msg nothing to change nichts zu ändern +@msg nothing to change: give dx, dy, hScale, vScale or rotation nichts zu ändern: Geben Sie dx, dy, hScale, vScale oder rotation an +@msg nothing to crop nichts zuzuschneiden +@msg nothing to export nichts zu exportieren +@msg nothing to export: the document has no visible art nichts zu exportieren: Das Dokument hat keine sichtbare Grafik +@msg nothing to mark nichts zu markieren +@msg nothing to print: every artboard picked is blank nichts zu drucken: Jede gewählte Zeichenfläche ist leer +@msg nothing to print: the document has no art that prints nichts zu drucken: Das Dokument hat keine Grafik, die gedruckt wird +@msg object is not a path das Objekt ist kein Pfad +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object Objekttransparenz kann nicht auf markierte Zeichen übertragen werden; deaktivieren Sie „Transparenz“ in den Pipettenoptionen oder wählen Sie das ganze Objekt aus +@msg object {_1} can't be set as point type Objekt {_1} kann nicht als Punkttext festgelegt werden +@msg object {_1} has lost its file's contents: relink it Objekt {_1} hat den Inhalt seiner Datei verloren: Verknüpfen Sie es neu +@msg object {_1} has no path Objekt {_1} hat keinen Pfad +@msg object {_1} has no size Objekt {_1} hat keine Größe +@msg object {_1} is hidden or locked, or on a locked artboard Objekt {_1} ist ausgeblendet oder gesperrt oder liegt auf einer gesperrten Zeichenfläche +@msg object {_1} is not a placed document Objekt {_1} ist kein platziertes Dokument +@msg object {_1} is not an Intertwine group Objekt {_1} ist keine Verflechtungsgruppe +@msg object {_1} is not an image Objekt {_1} ist kein Bild +@msg object {_1} is not an image or a linked file Objekt {_1} ist kein Bild und keine verknüpfte Datei +@msg object {_1} is not an image or a placed document Objekt {_1} ist kein Bild und kein platziertes Dokument +@msg object {_1} is not in this Intertwine group Objekt {_1} ist nicht in dieser Verflechtungsgruppe +@msg object {_1} needs a 3D effect at index {index} Objekt {_1} braucht einen 3D-Effekt bei Index {index} +@msg object {id} has anchors 0–{_1} for `corners`, not {past} Objekt {id} hat die Ankerpunkte 0–{_1} für `corners`, nicht {past} +@msg object {id} has {n} anchors, so no anchor {past} Objekt {id} hat {n} Ankerpunkte, also keinen Ankerpunkt {past} +@msg objects must be none, move or copy objects muss none, move oder copy sein +@msg objects were left out Objekte wurden ausgelassen +@msg offset out of range Offset außerhalb des gültigen Bereichs +@msg on must be true or false on muss true oder false sein +@msg only groups, layers, type and images have a Contents (Characters) row nur Gruppen, Ebenen, Text und Bilder haben eine Zeile „Inhalt (Zeichen)“ +@msg only interior mesh lines can be deleted nur innere Gitterlinien können gelöscht werden +@msg only solid colours can be added (not gradients or patterns) nur Vollfarben können hinzugefügt werden (keine Verläufe oder Muster) +@msg only solid-colour swatches can be spot colours nur Vollfarbfelder können Volltonfarben sein +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first nur die eingebettete Vorschau dieser Affinity-Datei konnte gelesen werden: Verwenden Sie Datei › Öffnen, um sie mit ihrer Warnung zu sehen, oder exportieren Sie zuerst SVG oder PDF aus Affinity +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning nur die eingebettete Vorschau dieser Affinity-Datei konnte gelesen werden; verwenden Sie Datei › Öffnen, um sie mit ihrer Warnung zu sehen +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon und ein Spaltenplatz gehören zu window.panel.float und window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto, above, below, icon und ein Spaltenplatz nehmen kein anchor, side, zone oder before +@msg onto, above, below, icon and a column place take no x, y, width or height onto, above, below, icon und ein Spaltenplatz nehmen kein x, y, width oder height +@msg open a document to look for its linked files öffnen Sie ein Dokument, um nach seinen verknüpften Dateien zu suchen +@msg options must be an object (see file.formatOptions) options muss ein Objekt sein (siehe file.formatOptions) +@msg order must be leftToRight or rightToLeft order muss leftToRight oder rightToLeft sein +@msg orientation must be portrait or landscape, not `{o}` orientation muss portrait oder landscape sein, nicht `{o}` +@msg outlineText {v}: true or false outlineText {v}: true oder false +@msg output intent profile: {e} Profil der Ausgabemethode: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: Kein RGB- oder CMYK-Profil heißt „{name}“ +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: {_1}-Dateien sind CMYK, und „{name}“ ist ein RGB-Profil +@msg output.destination: {e} output.destination: {e} +@msg output.inks: no ink is set to print output.inks: Keine Druckfarbe ist zum Drucken eingestellt +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: Kein Profil heißt „{name}“, und eine {std}-Ausgabemethode bettet ihr Profil ein +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent: Kein Profil heißt „{name}“: Eine {std}-Ausgabemethode bettet ein Profil ein oder nennt eine registrierte Druckbedingung (outputConditionId und registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: {std}-Dateien drucken in CMYK, und „{name}“ ist ein RGB-Profil +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: „{name}“ ist ein Graustufenprofil, das keine {std}-Ausgabemethode sein kann +@msg overlap: {o} pt is half a tile or more overlap: {o} pt ist eine halbe Kachel oder mehr +@msg overlapping Shaper paths must be in the same layer or group überlappende Shaper-Pfade müssen in derselben Ebene oder Gruppe liegen +@msg overlapping edits überlappende Bearbeitungen +@msg page art conflicts with image data for {key} die Seitengrafik widerspricht den Bilddaten für {key} +@msg page art conflicts with pattern {name} die Seitengrafik widerspricht dem Muster {name} +@msg page art conflicts with swatch {name} die Seitengrafik widerspricht dem Farbfeld {name} +@msg page art is missing image data for {key} der Seitengrafik fehlen die Bilddaten für {key} +@msg page art is missing pattern {name} der Seitengrafik fehlt das Muster {name} +@msg page art is missing swatch {name} der Seitengrafik fehlt das Farbfeld {name} +@msg page must be a whole number from 1 page muss eine ganze Zahl ab 1 sein +@msg page thumbnail: {e} Seitenminiatur: {e} +@msg page {page} of the PDF has no objects Seite {page} des PDF hat keine Objekte +@msg page {page}: `{file}` has {n} {artboards} Seite {page}: `{file}` hat {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} Seite {page}: `{name}` hat {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages muss ein Bereich wie „2-3“ oder eine Seitenzahl sein, nicht {v} +@msg pages: {e} pages: {e} +@msg panel position must be finite die Bedienfeldposition muss endlich sein +@msg parameters must be an object parameters muss ein Objekt sein +@msg params must be an object params muss ein Objekt sein +@msg passwords can have up to {MAX_PASSWORD} bytes Kennwörter können bis zu {MAX_PASSWORD} Byte haben +@msg path has no open end der Pfad hat kein offenes Ende +@msg paths must be strings paths müssen Strings sein +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false braucht jede Zeichenfläche, um die bearbeitbare Grafik zu behalten +@msg pdfs `{m}`: single or multiple pdfs `{m}`: single oder multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom ist „artwork“ oder „swatches“, nicht {v} +@msg pins and moved must be non-empty lists of the same length pins und moved müssen nicht leere Listen gleicher Länge sein +@msg pins must be [[x,y]…] pins muss [[x,y]…] sein +@msg pins must be finite pins müssen endlich sein +@msg place is above, below or inside, not `{other}` place ist above, below oder inside, nicht `{other}` +@msg plane must be left|right|ground plane muss left|right|ground sein +@msg plane must be left|right|ground|none plane muss left|right|ground|none sein +@msg point is outside the mesh der Punkt liegt außerhalb des Gitters +@msg points must be [t, left, right] with non-negative widths points muss [t, left, right] mit nicht negativen Breiten sein +@msg points must be a non-empty [[x,y,pressure?]…] list points muss eine nicht leere Liste [[x,y,pressure?]…] sein +@msg points must be a non-empty list of finite [x,y] points muss eine nicht leere Liste endlicher [x,y] sein +@msg points must be an array or null points muss ein Array oder null sein +@msg points must be finite points müssen endlich sein +@msg points must be finite [x,y] pairs points müssen endliche [x,y]-Paare sein +@msg points must contain 1–4096 coordinates points muss 1–4096 Koordinaten enthalten +@msg position must be normal, superscript or subscript position muss normal, superscript oder subscript sein +@msg position must be topLeft, topRight, bottomLeft or bottomRight position muss topLeft, topRight, bottomLeft oder bottomRight sein +@msg positions must be between 0 and 100000 pt positions müssen zwischen 0 und 100000 pt liegen +@msg ppi must be a positive number, not {ppi} ppi muss eine positive Zahl sein, nicht {ppi} +@msg ppi must be between 1 and 2400 ppi muss zwischen 1 und 2400 liegen +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` ist nicht transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape muss clippingMask|transparency sein +@msg preset `{id}`: mobile or density Vorgabe `{id}`: mobile oder density +@msg preset must be standard, diffuse, topLeft or right preset muss standard, diffuse, topLeft oder right sein +@msg preset must be the name of a print preset preset muss der Name einer Druckvorgabe sein +@msg previewMode must be default, pixel or overprint previewMode muss default, pixel oder overprint sein +@msg print settings: {e} Druckeinstellungen: {e} +@msg procedures nest too deeply Prozeduren sind zu tief verschachtelt +@msg procedures nested too deeply Prozeduren zu tief verschachtelt +@msg quotes must be {double?, single?} quotes muss {double?, single?} sein +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} muss ein öffnendes und ein schließendes Anführungszeichen sein, z. B. "“”" +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi muss 1–2400 sein (72 Bildschirm, 150 mittel, 300 hoch) +@msg rating must be a whole number from 0 to {MAX_RATING} rating muss eine ganze Zahl von 0 bis {MAX_RATING} sein +@msg rect and {k} contradict each other: give the area or the artboards rect und {k} widersprechen sich: Geben Sie den Bereich oder die Zeichenflächen an +@msg rect applies to raster formats, not {_1} rect gilt für Rasterformate, nicht für {_1} +@msg rect is [x, y, width, height] in points rect ist [x, y, width, height] in Punkt +@msg rect must be [x, y, width, height] rect muss [x, y, width, height] sein +@msg rect must be [x, y, width, height] with a positive width and height rect muss [x, y, width, height] mit positiver Breite und Höhe sein +@msg rect needs finite numbers and a positive width and height rect braucht endliche Zahlen und eine positive Breite und Höhe +@msg redraw needs id redraw braucht id +@msg redraw within must be a finite distance of 0 or more redraw within muss ein endlicher Abstand von 0 oder größer sein +@msg registration must be 0..8 registration muss 0..8 sein +@msg release a layer or a group wandeln Sie eine Ebene oder eine Gruppe zurück +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace behält Platz und Transformation des ersetzten Objekts: Lassen Sie template, at und rect weg +@msg replace: select the one object to replace replace: Wählen Sie das eine zu ersetzende Objekt aus +@msg resolution must be a number or screen|medium|high resolution muss eine Zahl oder screen|medium|high sein +@msg resolution must be between 1 and 2400 ppi resolution muss zwischen 1 und 2400 ppi liegen +@msg rotation must be a number of degrees rotation muss eine Gradzahl sein +@msg rotation must be finite rotation muss endlich sein +@msg rows and cols must be 1..50 rows und cols müssen 1..50 sein +@msg rows and columns must be 1..500 rows und columns müssen 1..500 sein +@msg rows of objects whose appearances differ can't be reordered together Zeilen von Objekten mit unterschiedlichem Aussehen können nicht zusammen neu angeordnet werden +@msg save the document first: Package collects a saved document speichern Sie zuerst das Dokument: Verpacken sammelt ein gespeichertes Dokument +@msg save the document first: copy puts the file in its folder speichern Sie zuerst das Dokument: copy legt die Datei in seinen Ordner +@msg savedPreset: {e} savedPreset: {e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`: ein Faktor wie 2x, eine Breite (100w), eine Höhe (100h) oder eine Auflösung (72ppi) +@msg scale must be a number above 0, at most 64 scale muss eine Zahl über 0 sein, höchstens 64 +@msg scale must be non-zero scale darf nicht null sein +@msg scope must be selection or all (got {other}) scope muss selection oder all sein (erhalten: {other}) +@msg scribble is too long die Kritzelei ist zu lang +@msg segment does not exist das Segment existiert nicht +@msg select a Shaper group wählen Sie eine Shaper-Gruppe aus +@msg select a blend to set easing or color shift wählen Sie eine Angleichung aus, um Beschleunigung oder Farbverschiebung festzulegen +@msg select a compound shape wählen Sie eine zusammengesetzte Form aus +@msg select a graph wählen Sie ein Diagramm aus +@msg select a group or layer with visible, unlocked members wählen Sie eine Gruppe oder Ebene mit sichtbaren, nicht gesperrten Elementen aus +@msg select a layer wählen Sie eine Ebene aus +@msg select a path wählen Sie einen Pfad aus +@msg select a text object wählen Sie ein Textobjekt aus +@msg select an image (or an Image Trace group) wählen Sie ein Bild (oder eine Bildnachzeichner-Gruppe) aus +@msg select an image, or pass id wählen Sie ein Bild aus, oder übergeben Sie id +@msg select an object (or pass `id`) wählen Sie ein Objekt aus (oder übergeben Sie `id`) +@msg select an object with a freeform gradient wählen Sie ein Objekt mit einem Freiformverlauf aus +@msg select an object with an opacity mask wählen Sie ein Objekt mit einer Deckkraftmaske aus +@msg select area type wählen Sie Flächentext aus +@msg select area type (text in a frame) wählen Sie Flächentext (Text in einem Rahmen) aus +@msg select area type and closed paths (at least one area type object) wählen Sie Flächentext und geschlossene Pfade aus (mindestens ein Flächentextobjekt) +@msg select area type whose first line can fit its frame wählen Sie Flächentext aus, dessen erste Zeile in seinen Rahmen passt +@msg select artwork to add its colours wählen Sie Grafik aus, um ihre Farben hinzuzufügen +@msg select artwork to make a group of its colours wählen Sie Grafik aus, um aus ihren Farben eine Gruppe zu erstellen +@msg select artwork to recolour, or give a colour `group` wählen Sie die neu zu färbende Grafik aus, oder geben Sie eine Farbgruppe in `group` an +@msg select exactly two path ends to join wählen Sie genau zwei Pfadenden zum Zusammenfügen aus +@msg select members of a compound shape wählen Sie Elemente einer zusammengesetzten Form aus +@msg select objects or give ids wählen Sie Objekte aus, oder geben Sie ids an +@msg select objects to align to the guide wählen Sie Objekte aus, die an der Hilfslinie ausgerichtet werden sollen +@msg select one Intertwine group wählen Sie eine Verflechtungsgruppe aus +@msg select one image or pass id wählen Sie ein Bild aus, oder übergeben Sie id +@msg select paths wählen Sie Pfade aus +@msg select paths or compound paths wählen Sie Pfade oder zusammengesetzte Pfade aus +@msg select rows or objects to collect wählen Sie Zeilen oder Objekte zum Sammeln aus +@msg select something to export wählen Sie etwas zum Exportieren aus +@msg select something to export (or turn off selectedOnly) wählen Sie etwas zum Exportieren aus (oder deaktivieren Sie selectedOnly) +@msg select text to redefine the style from wählen Sie den Text aus, aus dem das Format neu definiert werden soll +@msg select the art and, on top of it, the mask object wählen Sie das Bildmaterial und darüber das Maskenobjekt aus +@msg select the art for the design wählen Sie das Bildmaterial für das Design aus +@msg select the art for the pattern tile wählen Sie das Bildmaterial für die Musterkachel aus +@msg select the art to add first wählen Sie zuerst das hinzuzufügende Bildmaterial aus +@msg select the art to collect (or give its ids) wählen Sie das zu sammelnde Bildmaterial aus (oder geben Sie seine IDs an) +@msg select the content and a path on top wählen Sie den Inhalt und einen darüberliegenden Pfad aus +@msg select the images to relink, or pass ids wählen Sie die neu zu verknüpfenden Bilder aus, oder übergeben Sie ids +@msg select the images, or pass ids wählen Sie die Bilder aus, oder übergeben Sie ids +@msg select the linked images or placed documents, or pass ids wählen Sie die verknüpften Bilder oder platzierten Dokumente aus, oder übergeben Sie ids +@msg select the object to copy from wählen Sie das Objekt aus, aus dem kopiert werden soll +@msg select the objects (or give their ids) wählen Sie die Objekte aus (oder geben Sie ihre IDs an) +@msg select the objects to attach to the path wählen Sie die Objekte aus, die an den Pfad angehängt werden sollen +@msg select the objects to bind wählen Sie die zu bindenden Objekte aus +@msg select two or more objects to intertwine wählen Sie zwei oder mehr Objekte zum Verflechten aus +@msg select two or more slices to combine wählen Sie zwei oder mehr Slices zum Kombinieren aus +@msg select type on a path wählen Sie Pfadtext aus +@msg selection `{m}`: one or each selection `{m}`: one oder each +@msg settings is an object of document.exportForScreens params settings ist ein Objekt aus Parametern von document.exportForScreens +@msg settings must be an object settings muss ein Objekt sein +@msg show `{show}`: all, missing, modified or embedded show `{show}`: all, missing, modified oder embedded +@msg show must be true or false show muss true oder false sein +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size muss „small“, „medium“, „large“ oder eine Anzahl Punkt über 0 bis {_1} sein +@msg size must be positive size muss positiv sein +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode ist „fixed“ oder „pressure“, nicht `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode ist „fixed“ oder „pressure“, nicht {other} +@msg sizeVariation must be 0 or more sizeVariation muss 0 oder größer sein +@msg smallCaps must be true or false smallCaps muss true oder false sein +@msg sort `{x}`: name, kind or status sort `{x}`: name, kind oder status +@msg source paths are too large for Shaper die Quellpfade sind zu groß für Shaper +@msg spot, mode and group apply to solid colours only spot, mode und group gelten nur für Vollfarben +@msg status `{s}`: linked, embedded, missing or modified status `{s}`: linked, embedded, missing oder modified +@msg steps must be 1..1000 steps muss 1..1000 sein +@msg steps must be 1..{MAX_STEPS} steps muss 1..{MAX_STEPS} sein +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) Farbstopp {i}: `plates` muss aus vier Zahlen von 0 bis 1 bestehen (C, M, Y, K) +@msg stopped before the archive was finished angehalten, bevor das Archiv fertig war +@msg strokeMode must be within|along|across, got {m} strokeMode muss within|along|across sein, erhalten: {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions muss ein Objekt aus Optionen von stroke.set sein +@msg strokeWidth must be a positive number of pixels strokeWidth muss eine positive Anzahl Pixel sein +@msg subpath {si} is not an open subpath Teilpfad {si} ist kein offener Teilpfad +@msg subpaths required when joining the same path subpaths ist beim Zusammenfügen desselben Pfads erforderlich +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict muss „merge“ oder „add“ sein, nicht {v} +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text muss {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} sein +@msg text transform is collapsed or unrepresentable die Texttransformation ist zusammengefallen oder nicht darstellbar +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces muss eine ganze Zahl von 2 bis 100 sein +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`: Verwenden Sie „{_1}“ oder „{_2}“ +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: Geben Sie {path} oder {name, dataBase64} einer Bilddatei an, oder eine Textur, wie effect.list sie angibt +@msg textureImage: {e} textureImage: {e} +@msg that makes more than {MAX_PIECES} slices das ergibt mehr als {MAX_PIECES} Slices +@msg that object is not in the overlap dieses Objekt liegt nicht in der Überlappung +@msg the Affinity document has no page with a size das Affinity-Dokument hat keine Seite mit einer Größe +@msg the CMYK image couldn't be written das CMYK-Bild konnte nicht geschrieben werden +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected die PDF-Sicherheitseinstellungen wurden ohne ihre Kennwörter gespeichert: Geben Sie openPassword oder permissionsPassword erneut an, oder security ohne passwordRequired, um ungeschützt zu exportieren +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) die RTF-Datei hat zu viele Absätze oder Formatierungswechsel, um als Text platziert zu werden (höchstens {MAX_PARAGRAPHS} Absätze und {MAX_RUNS} Textläufe) +@msg the RTF file is too large to place as type (at most {_1} MB) die RTF-Datei ist zu groß, um als Text platziert zu werden (höchstens {_1} MB) +@msg the SVG expands to more than {0} nested SVG images das SVG wird zu mehr als {0} verschachtelten SVG-Bildern erweitert +@msg the SVG expands to more than {0} objects das SVG wird zu mehr als {0} Objekten erweitert +@msg the SVG has no drawable objects das SVG hat keine zeichenbaren Objekte +@msg the SVGZ file unpacks to more than {_1} MB die SVGZ-Datei wird auf mehr als {_1} MB entpackt +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place das Word-Dokument hat mehr als {MAX_PAGES} Seiten an Eigenschaften, zu viele zum Platzieren +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place das Word-Dokument hat mehr als {MAX_RUNS} Läufe mit Zeichen- oder Absatzeigenschaften, zu viele zum Platzieren +@msg the Word document has more than {_1} MB of formatting properties, too many to place das Word-Dokument hat mehr als {_1} MB an Formatierungseigenschaften, zu viele zum Platzieren +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) das Word-Dokument hat zu viel Text oder Formatierung, um als Text platziert zu werden (höchstens {MAX_PARAGRAPHS} Absätze, {MAX_RUNS} Textläufe und {_1} MB Text) +@msg the Word document has too much text to place as type (at most {_1} MB) das Word-Dokument hat zu viel Text, um als Text platziert zu werden (höchstens {_1} MB) +@msg the Word document is encrypted or protected with a password; save it without one to place it das Word-Dokument ist verschlüsselt oder mit einem Kennwort geschützt; speichern Sie es ohne Kennwort, um es zu platzieren +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) das Word-Dokument ist zu groß zum Platzieren (sein Datenstrom {name} enthält mehr als {_1} MB) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off das Word-Dokument wurde zuletzt mit Schnellspeicherung gespeichert, die VectorCraft nicht liest; speichern Sie es erneut mit deaktivierter Schnellspeicherung +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document das Word-Dokument wurde mit Word 6.0 oder Word 95 gespeichert, das VectorCraft nicht liest; speichern Sie es als Word-97-2003-Dokument +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place der Text des Word-Dokuments besteht aus mehr als {MAX_PIECES} Teilen, zu viele zum Platzieren +@msg the action set ids are not a list die IDs der Aktionssätze sind keine Liste +@msg the active paint is not a gradient die aktive Farbe ist kein Verlauf +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size die Zeichenflächen passen nicht auf die Arbeitsfläche: weniger Zeichenflächen, weniger Abstand oder eine kleinere Größe +@msg the character's size can't be measured die Größe des Zeichens kann nicht gemessen werden +@msg the compound shape is too complex to expand die zusammengesetzte Form ist zu komplex zum Umwandeln +@msg the container of the listed objects is gone der Container der aufgelisteten Objekte ist nicht mehr vorhanden +@msg the copy `{user_id}` can't be read as a library die Kopie `{user_id}` kann nicht als Bibliothek gelesen werden +@msg the copy would leave the canvas die Kopie würde die Arbeitsfläche verlassen +@msg the cut cannot be made on this art: {e} der Schnitt kann an diesem Bildmaterial nicht ausgeführt werden: {e} +@msg the data isn't an EMF or WMF picture die Daten sind kein EMF- oder WMF-Bild +@msg the data isn't an EPS or PostScript picture die Daten sind kein EPS- oder PostScript-Bild +@msg the deferred paste is no longer available das verzögerte Einfügen ist nicht mehr verfügbar +@msg the design is too big to draw at every data point of this graph das Design ist zu groß, um an jedem Datenpunkt dieses Diagramms gezeichnet zu werden +@msg the design is too big to draw in every column of this graph das Design ist zu groß, um in jeder Säule dieses Diagramms gezeichnet zu werden +@msg the dialog it was opened over is no longer open das Dialogfeld, über dem es geöffnet wurde, ist nicht mehr geöffnet +@msg the dictionary stack overflowed der Wörterbuchstapel ist übergelaufen +@msg the editing data's hash has no place der Hash der Bearbeitungsdaten hat keinen Platz +@msg the entities the document declares expand to too much text die vom Dokument deklarierten Entitäten werden zu zu viel Text erweitert +@msg the envelope's content has no bounds der Inhalt der Verzerrungshülle hat keine Begrenzungen +@msg the envelope's mesh is malformed das Gitter der Verzerrungshülle ist fehlerhaft +@msg the file holds no `variableSets` element die Datei enthält kein `variableSets`-Element +@msg the file is larger than {_1} MB die Datei ist größer als {_1} MB +@msg the file is too large die Datei ist zu groß +@msg the file was updated or encrypted already die Datei wurde bereits aktualisiert oder verschlüsselt +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) das erste Zeichen einer Zeile hat kein Zeichen davor zum Unterschneiden: Es bewegt sich nur nach oben und unten (dy) +@msg the font {_1} is not available die Schrift {_1} ist nicht verfügbar +@msg the format has no file extension das Format hat keine Dateierweiterung +@msg the graphic can't be read: {e} die Grafik kann nicht gelesen werden: {e} +@msg the graphic is empty die Grafik ist leer +@msg the graphic's data is damaged die Daten der Grafik sind beschädigt +@msg the graphic's document has no artboard das Dokument der Grafik hat keine Zeichenfläche +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them die Kopfzeile `{cell}` für `{name}` würde als Variable `{other}` zurückgelesen: Benennen Sie eine von ihnen um +@msg the image `{path}` isn't followed: {r} dem Bild `{path}` wird nicht gefolgt: {r} +@msg the image has no embedded pixels das Bild hat keine eingebetteten Pixel +@msg the image is empty das Bild ist leer +@msg the image is fully transparent das Bild ist vollständig transparent +@msg the image is not on an artboard; pass rect das Bild liegt auf keiner Zeichenfläche; übergeben Sie rect +@msg the image's pixels are missing die Pixel des Bilds fehlen +@msg the image's pixels aren't in the document die Pixel des Bilds sind nicht im Dokument +@msg the image's pixels can't be read: {e} die Pixel des Bilds können nicht gelesen werden: {e} +@msg the image's transparency doesn't fit it die Transparenz des Bilds passt nicht zu ihm +@msg the job would print more than {MAX_PAGES} pages der Auftrag würde mehr als {MAX_PAGES} Seiten drucken +@msg the job would print more than {_1} pages der Auftrag würde mehr als {_1} Seiten drucken +@msg the layers hold no art to mask die Ebenen enthalten keine Grafik zum Maskieren +@msg the layout moved das Layout hat sich verschoben +@msg the library has a group `{name}` already die Bibliothek hat bereits eine Gruppe `{name}` +@msg the library has no group `{name}` die Bibliothek hat keine Gruppe `{name}` +@msg the library has no item `{item}` die Bibliothek hat kein Element `{item}` +@msg the library has no item `{other}` die Bibliothek hat kein Element `{other}` +@msg the library holds {MAX_GROUPS} groups already die Bibliothek enthält bereits {MAX_GROUPS} Gruppen +@msg the library holds {MAX_ITEMS} items of this kind already die Bibliothek enthält bereits {MAX_ITEMS} Elemente dieser Art +@msg the name is empty der Name ist leer +@msg the object has no fill, stroke or effect das Objekt hat keine Fläche, Kontur oder Effekt +@msg the object has no graphic style (pass `name`) das Objekt hat keinen Grafikstil (übergeben Sie `name`) +@msg the objects don't match the plan die Objekte passen nicht zum Plan +@msg the open and permissions passwords must differ das Kennwort zum Öffnen und das Berechtigungskennwort müssen sich unterscheiden +@msg the operand stack overflowed der Operandenstapel ist übergelaufen +@msg the page has no area die Seite hat keine Fläche +@msg the page's type in an unread object's box isn't in its page art der Text der Seite im Rahmen eines nicht gelesenen Objekts ist nicht in seiner Seitengrafik +@msg the paint behind the proxy is not a gradient die Farbe hinter dem Proxy ist kein Verlauf +@msg the parent must be a layer or sublayer das übergeordnete Element muss eine Ebene oder Unterebene sein +@msg the path can't be extended der Pfad kann nicht verlängert werden +@msg the path can't be inside one of the objects to attach der Pfad darf nicht in einem der anzuhängenden Objekte liegen +@msg the path has no segments to measure der Pfad hat keine Segmente zum Messen +@msg the path is empty der Pfad ist leer +@msg the path is too short to shorten that much der Pfad ist zu kurz, um ihn so stark zu kürzen +@msg the picture has no objects das Bild hat keine Objekte +@msg the program runs too long das Programm läuft zu lange +@msg the program uses too much memory das Programm verwendet zu viel Speicher +@msg the rectangle must have a width and a height das Rechteck muss eine Breite und eine Höhe haben +@msg the rectangle needs a size and must lie on the canvas das Rechteck braucht eine Größe und muss auf der Arbeitsfläche liegen +@msg the selected art has no width das ausgewählte Bildmaterial hat keine Breite +@msg the selection draws nothing die Auswahl zeichnet nichts +@msg the selection has no area die Auswahl hat keine Fläche +@msg the selection has no art to export die Auswahl hat keine Grafik zum Exportieren +@msg the selection has no bounds die Auswahl hat keine Begrenzungen +@msg the selection has no graphic style die Auswahl hat keinen Grafikstil +@msg the slice would leave the canvas der Slice würde die Arbeitsfläche verlassen +@msg the spot colour couldn't be added die Volltonfarbe konnte nicht hinzugefügt werden +@msg the subpath has no ends to extend (it is closed or a single point) der Teilpfad hat keine Enden zum Verlängern (er ist geschlossen oder ein einzelner Punkt) +@msg the symbols are nested too deeply to expand their instances die Symbole sind zu tief verschachtelt, um ihre Instanzen umzuwandeln +@msg the text file is too large to place as type (at most {_1} MB) die Textdatei ist zu groß, um als Text platziert zu werden (höchstens {_1} MB) +@msg the text is empty der Text ist leer +@msg the text is over {_1} MB der Text ist größer als {_1} MB +@msg the top object must be a path das oberste Objekt muss ein Pfad sein +@msg the trace was cancelled das Nachzeichnen wurde abgebrochen +@msg the trailer has no size der Trailer hat keine Größe +@msg the type object is flattened to nothing das Textobjekt wird zu nichts reduziert +@msg the value of axis `{tag}` must be a number or null der Wert der Achse `{tag}` muss eine Zahl oder null sein +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets die Variablenbibliothek deklariert mehr als {MAX_LIBRARY_DATASETS} Datensätze +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities die Variablenbibliothek deklariert mehr als {MAX_LIBRARY_ENTITIES} Entitäten +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables die Variablenbibliothek deklariert mehr als {MAX_LIBRARY_VARIABLES} Variablen +@msg the variable library declares no variables die Variablenbibliothek deklariert keine Variablen +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes die Variablenbibliothek ist größer als {MAX_LIBRARY_BYTES} Byte +@msg the variable library is not UTF-8 die Variablenbibliothek ist nicht UTF-8 +@msg the variable library is not well-formed XML: {e} die Variablenbibliothek ist kein wohlgeformtes XML: {e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep die Variablenbibliothek verschachtelt Elemente mehr als {MAX_LIBRARY_NESTING} Ebenen tief +@msg the variable library's document type declaration holds something other than entities of plain text die Dokumenttypdeklaration der Variablenbibliothek enthält etwas anderes als Entitäten aus reinem Text +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes die Entitäten der Variablenbibliothek werden zu mehr als {MAX_LIBRARY_BYTES} Byte erweitert +@msg the window has no dock das Fenster hat kein Dock +@msg the written PDF has no catalog das geschriebene PDF hat keinen Katalog +@msg the written PDF has no cross-reference table das geschriebene PDF hat keine Querverweistabelle +@msg the written PDF's metadata can't be edited die Metadaten des geschriebenen PDF können nicht bearbeitet werden +@msg there are no colour swatches to draw es gibt keine Farbfelder zum Zeichnen +@msg there is already a design named `{name}` es gibt bereits ein Design namens `{name}` +@msg there is already a design named `{to}` es gibt bereits ein Design namens `{to}` +@msg there is already a toolbar named `{name}` es gibt bereits eine Werkzeugleiste namens `{name}` +@msg there is already a toolbar named `{to}` es gibt bereits eine Werkzeugleiste namens `{to}` +@msg there is no library: create one first es gibt keine Bibliothek: Erstellen Sie zuerst eine +@msg these paths are too degenerate for Shaper diese Pfade sind zu entartet für Shaper +@msg thumbnail: no page {v} (the {what} has {count}) Miniatur: keine Seite {v} ({what} hat {count}) +@msg tileRange of {what}: {e} tileRange von {what}: {e} +@msg title can't be empty title darf nicht leer sein +@msg to (the new name) is required to (der neue Name) ist erforderlich +@msg to must be fill or stroke to muss fill oder stroke sein +@msg to must be smooth or corner to muss smooth oder corner sein +@msg too large for a zip archive: package to a folder zu groß für ein ZIP-Archiv: Verpacken Sie in einen Ordner +@msg too many Shaper edits zu viele Shaper-Bearbeitungen +@msg too many `points` (at most 100000) zu viele `points` (höchstens 100000) +@msg too many characters: at most {MAX_CHARS} zu viele Zeichen: höchstens {MAX_CHARS} +@msg too many dots for this size: lower the frequency zu viele Punkte für diese Größe: Verringern Sie die Frequenz +@msg too many filters over one file zu viele Filter über einer Datei +@msg too many overlapping paths for Shaper zu viele überlappende Pfade für Shaper +@msg too many saved clips zu viele gespeicherte Beschneidungen +@msg too many saved graphics states zu viele gespeicherte Grafikzustände +@msg toolbar (a toolbar's name) must be a string toolbar (der Name einer Werkzeugleiste) muss ein String sein +@msg toolbar name can't be empty der Werkzeugleistenname darf nicht leer sein +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}: „flatten“ (Standard) oder „omit“ +@msg two saved selections would be named `{_1}` zwei gespeicherte Auswahlen würden `{_1}` heißen +@msg type dimension cannot be represented with this text transform das Textmaß kann mit dieser Texttransformation nicht dargestellt werden +@msg type dimension must be finite das Textmaß muss endlich sein +@msg unexpected resources unerwartete Ressourcen +@msg unknown CMYK profile `{name}` unbekanntes CMYK-Profil `{name}` +@msg unknown File Info field `{k}` unbekanntes Dateiinformationen-Feld `{k}` +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) unbekannte OpenType-Funktion `{t}` in featureChanges (die Tags, die `features` nimmt) +@msg unknown PDF preset `{name}` (presets: {_1}) unbekannte PDF-Vorgabe `{name}` (Vorgaben: {_1}) +@msg unknown Warp style unbekannter Verkrümmungsstil +@msg unknown alignToPath `{a}` unbekanntes alignToPath `{a}` +@msg unknown arrowhead {v} unbekannte Pfeilspitze {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) unbekannter artboardLayout-Schlüssel `{k}` (layout, columns, spacing, rightToLeft) +@msg unknown attribute `{attribute}` unbekanntes Attribut `{attribute}` +@msg unknown attribute `{k}` unbekanntes Attribut `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) unbekannte Achse `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` unbekannte Füllmethode `{b}` +@msg unknown blend mode {b} unbekannte Füllmethode {b} +@msg unknown brush type `{o}` unbekannter Pinseltyp `{o}` +@msg unknown category `{c}` unbekannte Kategorie `{c}` +@msg unknown category `{c}` ({_1}) unbekannte Kategorie `{c}` ({_1}) +@msg unknown character `{c}` unbekanntes Zeichen `{c}` +@msg unknown character style number field unbekanntes Zahlenfeld im Zeichenformat +@msg unknown effect `{e}` unbekannter Effekt `{e}` +@msg unknown end {e} unbekanntes Ende {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) unbekanntes flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution oder eine gespeicherte: siehe flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) unbekanntes Format `{f}` (vcswatches, gpl, ase oder css) +@msg unknown from {f} unbekanntes from {f} +@msg unknown graph type `{t}` unbekannter Diagrammtyp `{t}` +@msg unknown grid colours `{name}` (one of {_1}) unbekannte Rasterfarben `{name}` (eines von {_1}) +@msg unknown gridType `{t}` unbekannter gridType `{t}` +@msg unknown highlight `{v}` unbekannte Hervorhebung `{v}` +@msg unknown intent `{i}` unbekannte Renderpriorität `{i}` +@msg unknown issue kind {id} unbekannte Problemart {id} +@msg unknown issue kind {v} unbekannte Problemart {v} +@msg unknown knockout state {k} unbekannter Aussparungszustand {k} +@msg unknown language `{language}` (one of {_1}) unbekannte Sprache `{language}` (eine von {_1}) +@msg unknown language `{l}` (one of {_1}) unbekannte Sprache `{l}` (eine von {_1}) +@msg unknown method `{m}` ({_1}) unbekannte Methode `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) unbekannter Modus `{other}` (gray, rgb, hsb, lab, cmyk oder web) +@msg unknown mode `{other}` (mono|cmyk) unbekannter Modus `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) unbekannter Modus `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) unbekannter Modus `{other}` (set, add, subtract) +@msg unknown mode `{o}` unbekannter Modus `{o}` +@msg unknown mode {m} unbekannter Modus {m} +@msg unknown model `{other}` unbekanntes Modell `{other}` +@msg unknown model `{other}` (keep, new or document) unbekanntes Modell `{other}` (keep, new oder document) +@msg unknown op {o} unbekannte Operation {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back unbekannte Reihenfolge `{o}`: toggle, front, forward, backward oder back +@msg unknown overprints `{v}` (preserve, simulate or discard) unbekanntes Überdrucken `{v}` (preserve, simulate oder discard) +@msg unknown panel unbekanntes Bedienfeld +@msg unknown pieLegend `{v}` (none, standard or wedges) unbekannte pieLegend `{v}` (none, standard oder wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) unbekannte piePosition `{v}` (even, ratio oder stacked) +@msg unknown pieSort `{v}` (none, all or first) unbekannte pieSort `{v}` (none, all oder first) +@msg unknown plate `{n}` unbekannte Platte `{n}` +@msg unknown preference `{k}` unbekannte Voreinstellung `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) unbekanntes Profil {id} (siehe stroke.widthProfile.list) +@msg unknown proof target `{t}` unbekanntes Proof-Ziel `{t}` +@msg unknown quotes key `{k}` (double, single) unbekannter quotes-Schlüssel `{k}` (double, single) +@msg unknown resolution `{v}` unbekannte Auflösung `{v}` +@msg unknown rule `{rule}` ({_1}) unbekannte Regel `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) unbekannte Regel `{r}` (siehe color.harmony) +@msg unknown setting `{k}` unbekannte Einstellung `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) unbekannte Form `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) unbekannter Abstand `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) unbekannter tileType `{t}` ({_1}) +@msg unknown units unbekannte Einheiten +@msg unknown units `{u}` unbekannte Einheiten `{u}` +@msg unknown value axis `{v}` (left, right or both) unbekannte Werteachse `{v}` (left, right oder both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) unbekannte Variante `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` unbekannter Verkrümmungsstil `{s}` +@msg unknown {key} `{v}` (none, short or full) unbekanntes {key} `{v}` (none, short oder full) +@msg unknown {key} profile `{n}` unbekanntes {key}-Profil `{n}` +@msg unknown {k} key `{key}` (size, position) unbekannter {k}-Schlüssel `{key}` (size, position) +@msg unreadable object nicht lesbares Objekt +@msg unsafe destination file name unsicherer Zieldateiname +@msg use paths or compound paths verwenden Sie Pfade oder zusammengesetzte Pfade +@msg useArtboards must be true or false, not {v} useArtboards muss true oder false sein, nicht {v} +@msg value for `{var}` must match its variable kind der Wert für `{var}` muss zur Art seiner Variable passen +@msg variable `{_1}` is already defined Variable `{_1}` ist bereits definiert +@msg variable `{name}` has no `trait` Variable `{name}` hat kein `trait` +@msg variable `{name}` is already defined Variable `{name}` ist bereits definiert +@msg variable `{name}` is declared twice Variable `{name}` ist zweimal deklariert +@msg variable `{to}` already exists Variable `{to}` existiert bereits +@msg view is required view ist erforderlich +@msg view must be one of {_1} view muss eines der folgenden sein: {_1} +@msg warpStyle can only edit an applied Warp effect warpStyle kann nur einen angewendeten Verkrümmungseffekt bearbeiten +@msg width and height must be more than 0 and at most {limit} width und height müssen größer als 0 und höchstens {limit} sein +@msg width factors must be finite Breitenfaktoren müssen endlich sein +@msg width must be positive width muss positiv sein +@msg width out of range Breite außerhalb des gültigen Bereichs +@msg width/height must be > 0 width/height müssen > 0 sein +@msg x and y must be finite numbers x und y müssen endliche Zahlen sein +@msg x and y must be given together x und y müssen zusammen angegeben werden +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone braucht einen Anker: Docken Sie mit anchor neben einem Bedienfeld an oder mit side (Start oder End) in einer neuen Dockspalte +@msg zoom must be positive zoom muss positiv sein +@msg {C}: no effect at index {list} {C}: kein Effekt bei Index {list} +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} ist geschützt und bleibt erhalten +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} ist geschützt: Speichern Sie die Einstellungen unter einem anderen Namen +@msg {LABEL} produced an empty result {LABEL} hat ein leeres Ergebnis erzeugt +@msg {_1} MB of document, at most {_2} MB {_1} MB Dokument, höchstens {_2} MB +@msg {_1} MB of images, at most {_2} MB {_1} MB Bilder, höchstens {_2} MB +@msg {_1} bytes of RGBA data for a {width} × {height} image {_1} Byte RGBA-Daten für ein Bild mit {width} × {height} +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} kann nicht gelesen werden oder hat sich seit dem Lesen geändert: Verknüpfen Sie es zuerst neu oder aktualisieren Sie es +@msg {_1} can't go into {into} {_1} kann nicht in {into} eingefügt werden +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} kann das Drucken nicht auf niedrige Auflösung beschränken: Wählen Sie PDF 1.4 oder höher, oder erlauben Sie das Drucken in hoher Auflösung +@msg {_1} files can't be opened yet {_1}-Dateien können noch nicht geöffnet werden +@msg {_1} files can't be password-protected: remove the passwords or choose no standard {_1}-Dateien können nicht mit einem Kennwort geschützt werden: Entfernen Sie die Kennwörter oder wählen Sie keinen Standard +@msg {_1} files can't be {_2} {_1}-Dateien können nicht {_2} sein +@msg {_1} files can't carry editing data: turn off preserveEditing {_1}-Dateien können keine Bearbeitungsdaten enthalten: Deaktivieren Sie preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers {_1}-Dateien können keine PDF-Ebenen haben: Deaktivieren Sie createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one {_1}-Dateien brauchen ein Erstellungsdatum, und diese Plattform hat keine Uhr, die eines liefert +@msg {_1} isn't a path {_1} ist kein Pfad +@msg {_1} options: {e} {_1}-Optionen: {e} +@msg {_1} paints nothing to rasterize {_1} malt nichts, das gerastert werden kann +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) {_1}-Kennwörter können bis zu {MAX_LEGACY_PASSWORD} ASCII-Zeichen haben (PDF 1.7 und höher nehmen beliebige) +@msg {_1} with linked images or an image map writes several files: use document.export {_1} mit verknüpften Bildern oder einer Imagemap schreibt mehrere Dateien: Verwenden Sie document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} schreibt hier eine Datei pro Zeichenfläche: Verwenden Sie document.export, oder nennen Sie eine Zeichenfläche +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} Pixel ist zu groß für {_3} (höchstens {max} Pixel pro Seite): Verringern Sie die Skalierung +@msg {_1}: belongs to another account {_1}: gehört einem anderen Konto +@msg {_1}: not a folder of its own {_1}: kein eigener Ordner +@msg {_1}: not a plain file {_1}: keine normale Datei +@msg {dest}: {e} {dest}: {e} +@msg {doc} has no folder {doc} hat keinen Ordner +@msg {file} isn't linked {file} ist nicht verknüpft +@msg {folder} is not a package folder {folder} ist kein Paketordner +@msg {folder}: more than {max} files and folders {folder}: mehr als {max} Dateien und Ordner +@msg {folder}: {e} {folder}: {e} +@msg {g} is not a Live Paint group {g} ist keine Gruppe „Interaktiv malen“ +@msg {id} is not a slice {id} ist kein Slice +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} muss 0–{MAX_ADD_AROUND} pt sein +@msg {key} must be [x,y] {key} muss [x,y] sein +@msg {key} must be a percentage {min}..100 {key} muss ein Prozentwert von {min}..100 sein +@msg {key} must be a positive number, not {n} {key} muss eine positive Zahl sein, nicht {n} +@msg {key} must be a positive number, not {v} {key} muss eine positive Zahl sein, nicht {v} +@msg {key} must be one of {_1} {key} muss eines der folgenden sein: {_1} +@msg {key} must be {_1}, got {s} {key} muss {_1} sein, erhalten: {s} +@msg {key} must name a floating panel {key} muss ein schwebendes Bedienfeld nennen +@msg {key} must name a panel in another group {key} muss ein Bedienfeld in einer anderen Gruppe nennen +@msg {key} must name a panel that shows {key} muss ein angezeigtes Bedienfeld nennen +@msg {key} must name a registered panel {key} muss ein registriertes Bedienfeld nennen +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) {kind}-Dateien haben keine Transparenz, und die Datei hätte {what}: Reduzieren Sie sie zuerst (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) {kind}-Bilder können nicht geöffnet werden (siehe document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` ist keine der Auswahlmöglichkeiten +@msg {k} has control characters{_1} {k} enthält Steuerzeichen{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} ist zu lang (höchstens {MAX_TEXT} Zeichen) +@msg {k} must be > 0 {k} muss > 0 sein +@msg {k} must be a number (pt) or a length such as "210 mm" {k} muss eine Zahl (pt) oder eine Länge wie „210 mm“ sein +@msg {k} must be a number from {min} to {max} {k} muss eine Zahl von {min} bis {max} sein +@msg {k} must be a number or "auto" {k} muss eine Zahl oder „auto“ sein +@msg {k} must be a string {k} muss ein String sein +@msg {k} must be text {k} muss Text sein +@msg {k} must be true or false {k} muss true oder false sein +@msg {k} must be {{size?, position?}} {k} muss {{size?, position?}} sein +@msg {k}: can't read `{s}` as a length {k}: `{s}` kann nicht als Länge gelesen werden +@msg {label} must be a number at most {max} {label} muss eine Zahl von höchstens {max} sein +@msg {label} must be a number from -1296 to 1296 {label} muss eine Zahl von -1296 bis 1296 sein +@msg {label} must be a number from 1 to 10000 {label} muss eine Zahl von 1 bis 10000 sein +@msg {name} `{s}`: {ids} {name} `{s}`: {ids} +@msg {name} is built in and can't be deleted {name} ist integriert und kann nicht gelöscht werden +@msg {name} must be {lo}–{hi}{unit} (got {v}) {name} muss {lo}–{hi}{unit} sein (erhalten: {v}) +@msg {name}: CRC or size mismatch {name}: CRC oder Größe stimmt nicht überein +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}: nur die eingebettete Vorschau dieser Affinity-Datei konnte gelesen werden; verwenden Sie Datei › Öffnen, um sie mit ihrer Warnung zu sehen, oder exportieren Sie vor dem Platzieren SVG oder PDF aus Affinity +@msg {name}: {e} {name}: {e} +@msg {native}; its embedded preview can't be read either ({e}) {native}; seine eingebettete Vorschau kann ebenfalls nicht gelesen werden ({e}) +@msg {std} files are CMYK, and the file would have colours in {_1} {std}-Dateien sind CMYK, und die Datei hätte Farben in {_1} +@msg {std} files embed their fonts, and the file would have one that isn't {std}-Dateien betten ihre Schriften ein, und die Datei hätte eine, die nicht eingebettet ist +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} ist die Datei, aus der dieses Dokument gelesen wurde, und beim Lesen wurde etwas ausgelassen ({what}): Ein Überschreiben würde es endgültig verlieren. Schreiben Sie stattdessen eine andere Datei, oder übergeben Sie acknowledgeLoss: true, um sie trotzdem zu ersetzen +@msg {ty} brushes need selected art (or params.{k}) {ty}-Pinsel brauchen ausgewähltes Bildmaterial (oder params.{k}) +@msg {v} is not an asset id {v} ist keine Asset-ID +@msg {what} can't be intertwined {what} kann nicht verflochten werden +@msg {what} doesn't fit the paper with its bleed and marks {what} passt mit Anschnitt und Marken nicht auf das Papier +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what} würde auf mehr als {MAX_TILES} Kacheln gedruckt: Verkleinern Sie es +@msg Alignment must be one of the choices Ausrichtung muss eine der Auswahlmöglichkeiten sein +@msg Line Gap must be a number Zeilenabstand muss eine Zahl sein +@msg `at` must be a point [x, y] `at` muss ein Punkt [x, y] sein +@msg `points` must hold from 2 to 100000 points `points` muss 2 bis 100000 Punkte enthalten +@msg `rect` must be [x, y, width, height] `rect` muss [x, y, width, height] sein +@msg `tolerance` must be from 0 to 100 pt `tolerance` muss zwischen 0 und 100 pt liegen +@msg `warichuAlign` must be one of {_1} `warichuAlign` muss eines der folgenden sein: {_1} +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` muss eine ganze Zahl von {_1} bis {_2} sein +@msg bad point {v}: expected [x, y] ungültiger Punkt {v}: erwartet wird [x, y] +@msg give exactly one of `at`, `rect` and `points` geben Sie genau eines von `at`, `rect` und `points` an +@msg the line is too long, or the paths it passes too many, to check die Linie ist zu lang, oder sie kreuzt zu viele Pfade, um sie zu prüfen +@msg the paths are too many or too tangled to find where they cross die Pfade sind zu zahlreich oder zu verschlungen, um ihre Kreuzungen zu finden +@msg the rectangle or line reaches too many paths to check das Rechteck oder die Linie erreicht zu viele Pfade, um sie zu prüfen +@msg {name} must be a number {name} muss eine Zahl sein diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..a6078c1ba2 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -35,7 +35,7 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker | Portuguese | `pt-br` | 6,106 catalog entries (counted 2026-10-31 on `main` at `d26dd933a`): menus, `tl!` literals and messages complete when counted; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) | | Indonesian | `id` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one plural form | dialogs and tooltips all; 750 message rows (French has 796) | Latin | no | full | — (review) | | Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) | -| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (602 of 604 message rows; landed 2026-10-10) | Latin | contributor-written, not reviewed | full | — (review) | +| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (3,463 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists no missing messages for it) | Latin | contributor-written, not reviewed | full | — (review) | | Korean | `ko` | 4,870 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | all | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) | | Vietnamese | `vi` | 4,886 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form; 4,046 plain + 18 `@plural` + 28 contextual + 794 `@msg` | all | Latin with Vietnamese diacritics (stacked marks attach in precomposed, decomposed and keyboard forms, #4491; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e7c1b28726..70b73024d8 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -882,6 +882,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur The status bar's messages for documents opened or saved with notes and for discarded or restored recovered documents, and the Global Edit options' Results line, do the same (`open_save_and_recovery_messages_take_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`); the warnings for linked images and placed documents output as their previews and the errors for a page past a placed file's last page or artboard name their counts in the singular or the plural in English. - Edit › Undo and Edit › Redo, in the in-window and native menus, name the step in the interface language as the History panel lists it (`panels::history::step_name`), so when the catalog has no plain row for a step named after an effect, such as Feather, the menu shows the translation of the effect's menu row without its ellipsis (`undo_and_redo_name_the_step_as_the_history_panel_does`). - The History panel translates the undo step names the engine, the tools and the shell write as literals: the untranslated report (`untranslated_report`) lists any a complete catalog lacks, and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis. + - The German catalog (`crates/ui-egui/src/i18n/de.tsv`) has an `@msg` row for every status and error message the untranslated report (`untranslated_report`) finds in the sources, 3,463 `@msg` rows in all: the errors of commands, scripts, file import and export and plug-ins. The report's German section lists no messages. A message with two placeholders in a row (`Template::parse` in `crates/ui-egui/src/i18n/catalog.rs`) is still shown in English. - The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set. - The History panel menu's Clear History (`edit.clearHistory`) removes every step to undo and to redo and leaves the document as it is; it is not an undo step itself, and it is disabled when there is no step to clear. - The New Document from State button at the bottom of the History panel (`edit.newDocumentFromState`) makes a new document, which becomes active, from the state the panel has selected ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/undo-edits-and-manage-design-history.html)). VectorCraft's own rules: the new document takes the next Untitled-N name, is marked unsaved, has nothing selected and has no steps to undo or redo, and the source document and its steps stay as they are. From 15b75be8602fcf8a5370f3c06e60cf34effbe8f6 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 14:35:40 -0800 Subject: [PATCH 09/33] i18n: Italian translates 1,802 more status and error messages it is in COMPLETE_MESSAGES, but the untranslated report listed 1,842 status and error messages with no Italian row, so with the interface in Italian those messages showed in English. - it.tsv gains 1,802 `@msg` rows. The translations are VectorCraft's own, written from the English meaning with the catalog's terms, and await native review. Placeholders, backticked names, command ids, double-quoted values and bare true, false and null stay as the English writes them. - The untranslated report now lists 40 Italian messages. 38 of those keys get Italian rows from open pull requests 7211, 7214 and 7116, so they are left out here. Menu strings, `tl!` literals, undo labels and swatch names are unchanged. - The Italian row of docs/localization-parity.md gives the catalog's current counts, and docs/target-app-parity.md has a line for the change. --- crates/ui-egui/src/i18n/it.tsv | 1804 ++++++++++++++++++++++++++++++++ docs/localization-parity.md | 2 +- docs/target-app-parity.md | 1 + 3 files changed, 1806 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index fee7dbd594..d86c040533 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -7081,3 +7081,1807 @@ breakText Glyphs Glifi Margin Margine Box Riquadro Swatch Campione + +# Status and error messages from across the app, script engines and file readers. +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions 'caller', 'callee' e 'arguments' non possono essere usati nelle funzioni in modalità strict +@msg Alignment must be one of the choices L'allineamento deve essere una delle scelte disponibili +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} è stato chiamato su un valore che non è un Boolean +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB Le immagini CMYK i cui campioni non è stato possibile leggere in CMYK sono state importate in RGB +@msg Cannot write to the location of “{name}”; choose where to save it Impossibile scrivere nella posizione di “{name}”; scegli dove salvarlo +@msg DCS files can only be placed linked: use File › Place with Link on I file DCS possono essere inseriti solo come collegati: usa File › Inserisci con Collega attivato +@msg Date.prototype method called on a value that is not a Date object Metodo di Date.prototype chiamato su un valore che non è un oggetto Date +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString è stato chiamato su un valore che non è un oggetto +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString è stato chiamato su un valore che non è una funzione +@msg Import SVG Filter reads SVG files, not SVGZ Importa filtro SVG legge file SVG, non SVGZ +@msg JSON.parse: {why} (at offset {at}) JSON.parse: {why} (all'offset {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify non può scrivere una struttura che contiene se stessa +@msg Line Gap must be a number La distanza tra le righe deve essere un numero +@msg Namespace.prototype.toString needs a Namespace Namespace.prototype.toString richiede un Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have I profili di nuovo documento richiedono la cartella delle preferenze, che questa esecuzione non ha +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} è stato chiamato su un valore che non è un numero +@msg Object.create's prototype must be an object or null Il prototipo di Object.create deve essere un oggetto o null +@msg Object.{function} was given a value that is not an object Object.{function} ha ricevuto un valore che non è un oggetto +@msg QName.prototype.toString needs a QName QName.prototype.toString richiede un QName +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} è stato chiamato su un valore che non è un oggetto RegExp +@msg Stencil Bridges: the selected art is too large or too detailed to bridge Ponti per stencil: la grafica selezionata è troppo grande o troppo dettagliata per essere collegata +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString è stato chiamato su un valore che non è una stringa +@msg The {name} profile could not be read, so the new document has the default settings: {reason} Non è stato possibile leggere il profilo {name}, quindi il nuovo documento ha le impostazioni predefinite: {reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time Trapping: più di {_1} coppie di oggetti sono abbastanza vicine da richiedere il trapping, oppure troppi oggetti condividono un intervallo orizzontale; applica il trapping a meno oggetti alla volta +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off Riquadro e linee di spellicolatura: nessuna linea di spellicolatura entra fuori dalla grafica e il riquadro di spellicolatura è disattivato +@msg \\ at end of pattern \\ alla fine del pattern +@msg `alignOn` cannot be empty `alignOn` non può essere vuoto +@msg `alignOn` must be a string `alignOn` deve essere una stringa +@msg `align` must be a string `align` deve essere una stringa +@msg `answer` for this {kind} dialog must be one of: {_1} `answer` per questa finestra di dialogo {kind} deve essere uno tra: {_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` ha più di {MAX_ANSWERS} risposte +@msg `answers` is larger than 1 MiB `answers` supera 1 MiB +@msg `answers` must be a list of answers `answers` deve essere un elenco di risposte +@msg `artboard` must be the index of an artboard of the document `artboard` deve essere l'indice di una tavola da disegno del documento +@msg `artboardsAt` must be [x, y] `artboardsAt` deve essere [x, y] +@msg `artboards` names an artboard the document does not have `artboards` indica una tavola da disegno che il documento non ha +@msg `artboards` names an artboard twice `artboards` indica due volte la stessa tavola da disegno +@msg `at` must be [x, y] (got {at}) `at` deve essere [x, y] (ricevuto {at}) +@msg `at` must be a point [x, y] `at` deve essere un punto [x, y] +@msg `beside` needs `side` `beside` richiede `side` +@msg `code` must be a whole number `code` deve essere un numero intero +@msg `colorMode` must be one of rgb, cmyk `colorMode` deve essere uno tra rgb, cmyk +@msg `colors` does not apply to the {name} reduction `colors` non si applica alla riduzione {name} +@msg `colors` must be a whole number from 2 to 256 `colors` deve essere un numero intero compreso tra 2 e 256 +@msg `colors` must be at least 9 for the uniform reduction `colors` deve essere almeno 9 per la riduzione uniforme +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` deve essere compreso tra 1 e {MAX_THEME_COLORS} (ricevuto {n}) +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` deve essere un numero intero compreso tra 0 e {MAX_COUNT} +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` deve essere un numero compreso tra 0 e 100 +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` deve essere compreso tra {lo} e {hi} pt +@msg `focal` and `radius` cannot be used together `focal` e `radius` non possono essere usati insieme +@msg `focal` must be an array of [subpath, anchor] pairs `focal` deve essere un array di coppie [subpath, anchor] +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` indica [{si}, {ai}], che non è un punto di ancoraggio del tracciato +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` accetta solo null, che rimuove una scelta di glifo (text.setGlyphAlternate ne imposta una) +@msg `group` must be one of {_1} `group` deve essere uno tra {_1} +@msg `group` must be the Objects on Path group of `member` and `target` `group` deve essere il gruppo Oggetti su tracciato di `member` e `target` +@msg `id` is an object id, not {v} `id` è un id di oggetto, non {v} +@msg `item` needs `index` `item` richiede `index` +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` riguarda il primo carattere di una riga, non uno stile di carattere (lo imposta text.setRangeStyle) +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` deve essere "pushInFirst", "pushOutFirst" o "pushOutOnly" +@msg `leader` must be a string `leader` deve essere una stringa +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` deve essere compreso tra 1 e {MAX_SUGGESTIONS} +@msg `name` must be a name that is not empty `name` deve essere un nome non vuoto +@msg `name` must not be empty `name` non deve essere vuoto +@msg `origin` must be [x, y] in points `origin` deve essere [x, y] in punti +@msg `picks` must be an array (got {v}) `picks` deve essere un array (ricevuto {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` deve contenere da 1 a {MAX_PICKS} scelte (ricevute {_1}) +@msg `points` must hold from 2 to 100000 points `points` deve contenere da 2 a 100000 punti +@msg `radius` must be 0 or more `radius` deve essere 0 o maggiore +@msg `rect` must be [x, y, width, height] `rect` deve essere [x, y, width, height] +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` deve essere [x0, y0, x1, y1] (ricevuto {r}) +@msg `replace` must be a string `replace` deve essere una stringa +@msg `rows` must be lists of numbers or nulls, not {r} `rows` deve essere un elenco di liste di numeri o null, non {r} +@msg `scale` must be a number from 0.001 to 1000 `scale` deve essere un numero compreso tra 0.001 e 1000 +@msg `scope.{key}` must be a non-empty array `scope.{key}` deve essere un array non vuoto +@msg `scope` must be "document", {artboards} or {layers} `scope` deve essere "document", {artboards} o {layers} +@msg `side` must be "left", "right", "top" or "bottom" `side` deve essere "left", "right", "top" o "bottom" +@msg `side` places the artboard: leave out x, y, width and height `side` posiziona la tavola da disegno: ometti x, y, width e height +@msg `source` is the only path in series group `target` `source` è l'unico tracciato nel gruppo in serie `target` +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` produce più di {MAX_COUNT} linee nel riquadro +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` deve essere maggiore di 0 e al massimo {MAX_DISTANCE} pt +@msg `target` must be an object of the same Objects on Path group as `member` `target` deve essere un oggetto dello stesso gruppo Oggetti su tracciato di `member` +@msg `tolerance` must be from 0 to 100 pt `tolerance` deve essere compreso tra 0 e 100 pt +@msg `version` must be 0 or {_1} `version` deve essere 0 o {_1} +@msg `version` must be 0 or {newest} `version` deve essere 0 o {newest} +@msg `warichuAlign` must be one of {_1} `warichuAlign` deve essere uno tra {_1} +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` deve essere un numero intero compreso tra {_1} e {_2} +@msg `weight` must be from 0.01 to 1000 pt `weight` deve essere compreso tra 0.01 e 1000 pt +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` deve essere un elenco di {_1} numeri, uno per riga (ricevuto {v}) +@msg `xml` must be a string `xml` deve essere una stringa +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it la voce {n} di `{KEY}` assegna al paragrafo al byte {start} di {_1} rientri diversi da quelli di una voce precedente +@msg `{KEY}` entry {n} must be an object la voce {n} di `{KEY}` deve essere un oggetto +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` la voce {n} di `{KEY}` non imposta né `leftIndent` né `firstLineIndent` +@msg `{KEY}` entry {n}: `id` must be an object id `{KEY}` voce {n}: `id` deve essere un id di oggetto +@msg `{KEY}` entry {n}: `start` must be a byte offset `{KEY}` voce {n}: `start` deve essere un offset in byte +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt `{KEY}` voce {n}: `{k}` deve essere un numero compreso tra -{MAX_PARA} e {MAX_PARA} pt +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} `{KEY}` voce {n}: nessun paragrafo di {_1} inizia al byte {start} +@msg `{KEY}` entry {n}: unknown key `{k}` `{KEY}` voce {n}: chiave sconosciuta `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object `{KEY}` voce {n}: {_1} non è un oggetto di testo +@msg `{KEY}` is empty `{KEY}` è vuoto +@msg `{KEY}` must be a list `{KEY}` deve essere un elenco +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` accetta al massimo {MAX_PARAGRAPH_INDENTS} voci (ricevute {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` non accetta `{other}` insieme +@msg `{base}` is larger than {_1} MB `{base}` supera {_1} MB +@msg `{base}` is not in the DCS file's folder `{base}` non si trova nella cartella del file DCS +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` contiene più di {MAX_EDIT_WORDS} parole +@msg `{key}` must be 0 or more `{key}` deve essere 0 o maggiore +@msg `{key}` must be [x, y] numbers `{key}` deve essere [x, y] numerici +@msg `{key}` must be a list of words `{key}` deve essere un elenco di parole +@msg `{key}` must be a list, not {v} `{key}` deve essere un elenco, non {v} +@msg `{key}` must be a number or null, not {v} `{key}` deve essere un numero o null, non {v} +@msg `{key}` must be a whole number of 0 or more `{key}` deve essere un numero intero pari a 0 o maggiore +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` deve essere compreso tra 0 e {MAX_DISTANCE} pt +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` deve essere compreso tra {lo} e {hi}, non {v} +@msg `{key}` must be one of auto, always, never `{key}` deve essere uno tra auto, always, never +@msg `{key}` must be one of miter, round, bevel `{key}` deve essere uno tra miter, round, bevel +@msg `{key}` must be one word of at most {_1} characters `{key}` deve essere una sola parola di al massimo {_1} caratteri +@msg `{key}` must hold single words of at most {_1} characters `{key}` deve contenere singole parole di al massimo {_1} caratteri +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` deve indicare un oggetto collegato al tracciato di un gruppo Oggetti su tracciato +@msg `{key}` names an object that is hidden or locked `{key}` indica un oggetto nascosto o bloccato +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` è un file a sé, che può trovarsi solo accanto al percorso del file DCS +@msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} +@msg `{u}` is no Unicode character `{u}` non è un carattere Unicode +@msg `{word}` isn't in the custom dictionary `{word}` non è nel dizionario personalizzato +@msg a CMap has too many mappings una CMap ha troppe corrispondenze +@msg a \\u escape needs four hexadecimal digits un escape \\u richiede quattro cifre esadecimali +@msg a comma in an XPath expression must separate function arguments una virgola in un'espressione XPath deve separare gli argomenti di una funzione +@msg a document holds at most {_1} SVG filters un documento contiene al massimo {_1} filtri SVG +@msg a document with unsaved changes has no recovery copy un documento con modifiche non salvate non ha una copia di ripristino +@msg a font's data is too large i dati di un font sono troppo grandi +@msg a font's subroutines are too large le subroutine di un font sono troppo grandi +@msg a function declaration without a name una dichiarazione di funzione senza nome +@msg a host value has too many entries un valore host ha troppe voci +@msg a host value is nested too deep un valore host è annidato troppo in profondità +@msg a namespace declaration uses a reserved prefix or URI una dichiarazione di namespace usa un prefisso o un URI riservato +@msg a namespace qualifier is undefined un qualificatore di namespace non è definito +@msg a namespace with an empty URI cannot have a prefix un namespace con URI vuoto non può avere un prefisso +@msg a number has no digits un numero non ha cifre +@msg a number's exponent has no digits l'esponente di un numero non ha cifre +@msg a number's fraction has no digits la parte frazionaria di un numero non ha cifre +@msg a property descriptor cannot have both a value or writable and a get or set un descrittore di proprietà non può avere sia value o writable sia get o set +@msg a property descriptor must be an object un descrittore di proprietà deve essere un oggetto +@msg a property's {name} must be a function or undefined il {name} di una proprietà deve essere una funzione o undefined +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied uno stream al suo interno verrebbe decodificato in più di 256 MB, oppure i suoi parametri di decodifica non possono essere applicati +@msg a string holds a control character una stringa contiene un carattere di controllo +@msg a string holds an escape sequence JSON does not have una stringa contiene una sequenza di escape che JSON non prevede +@msg a string is not closed una stringa non è chiusa +@msg an SVG filter is named by its id: give `xml` with `name` un filtro SVG prende il nome dal suo id: indica `xml` con `name` +@msg an XML element has two attributes with the same name un elemento XML ha due attributi con lo stesso nome +@msg an XML value cannot be inserted into itself or its descendants un valore XML non può essere inserito in se stesso o nei suoi discendenti +@msg an XML value's children cannot be assigned by position i figli di un valore XML non possono essere assegnati per posizione +@msg an XML value's children cannot be deleted by position i figli di un valore XML non possono essere eliminati per posizione +@msg an XMLList converts to XML only when it has one item un XMLList si converte in XML solo quando ha un solo elemento +@msg an XPath node test is missing manca un test di nodo XPath +@msg an XPath operand is missing manca un operando XPath +@msg an XPath operator is missing manca un operatore XPath +@msg an XPath predicate or step cannot follow here qui non può seguire un predicato o un passo XPath +@msg an XPath predicate or step follows a value that is not a node-set un predicato o un passo XPath segue un valore che non è un node-set +@msg an XPath step is missing manca un passo XPath +@msg an image couldn't be decoded and is missing from the picture non è stato possibile decodificare un'immagine, che manca dal disegno +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) un'immagine di {w} × {h} pixel è troppo grande per essere convertita (al massimo {_1} pixel) +@msg apply was called on a value that is not a function apply è stato chiamato su un valore che non è una funzione +@msg area type in a thread cannot use Dynamic Text: {frames} il testo in area in una concatenazione non può usare il testo dinamico: {frames} +@msg array literal too long letterale array troppo lungo +@msg assignment to a constant binding assegnazione a un binding costante +@msg at least one row that isn't excluded needs a weight above 0 almeno una riga non esclusa deve avere un peso maggiore di 0 +@msg bad point {v}: expected [x, y] punto non valido {v}: previsto [x, y] +@msg blend must be a mode name blend deve essere il nome di un metodo +@msg both sides of an XPath | must select nodes entrambi i lati di un | XPath devono selezionare nodi +@msg canceled annullato +@msg cannot convert the object to a primitive value impossibile convertire l'oggetto in un valore primitivo +@msg cannot convert undefined or null to an object impossibile convertire undefined o null in un oggetto +@msg character dimensions cannot be represented with the source text transform le dimensioni dei caratteri non possono essere rappresentate con la trasformazione del testo di origine +@msg character dimensions cannot be represented with the target text transform le dimensioni dei caratteri non possono essere rappresentate con la trasformazione del testo di destinazione +@msg continuation must be an array of path chunks continuation deve essere un array di segmenti di tracciato +@msg continuation requires points continuation richiede dei punti +@msg each path chunk must contain 1 to 4096 points ogni segmento di tracciato deve contenere da 1 a 4096 punti +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) ogni scelta è {{at: [x, y]}} o {{rect: [x0, y0, x1, y1]}} (ricevuto {v}) +@msg each weight must be a number of 0 or more (got {v}) ogni peso deve essere un numero pari a 0 o maggiore (ricevuto {v}) +@msg finally table {t} out of range tabella finally {t} fuori intervallo +@msg flags cannot be given with a RegExp object i flag non possono essere indicati con un oggetto RegExp +@msg function declaration index out of range indice di dichiarazione di funzione fuori intervallo +@msg give `char`, `text`, `code` or `unicode` indica `char`, `text`, `code` o `unicode` +@msg give `count` or `spacing`, not both indica `count` o `spacing`, non entrambi +@msg give `name`, an SVG filter's name indica `name`, il nome di un filtro SVG +@msg give `path`, an SVG file, or `svg`, SVG text indica `path`, un file SVG, o `svg`, testo SVG +@msg give `xml`, one SVG element indica `xml`, un solo elemento SVG +@msg give exactly one of `at`, `rect` and `points` indica esattamente uno tra `at`, `rect` e `points` +@msg give exactly one of `path`, `data` and `dataBase64` indica esattamente uno tra `path`, `data` e `dataBase64` +@msg give index or indexes, not both indica index o indexes, non entrambi +@msg give one element indica un solo elemento +@msg give opacity or blend indica opacity o blend +@msg handler at {_1} expects {_2} values but its range starts with {d} il gestore a {_1} prevede {_2} valori, ma il suo intervallo inizia con {d} +@msg handler depth too large profondità del gestore troppo grande +@msg handler range {_1}..{_2} out of range intervallo del gestore {_1}..{_2} fuori intervallo +@msg hiddenLayers must be true or false hiddenLayers deve essere true o false +@msg hvcC hvcC +@msg images in a spot ink whose samples couldn't be read were imported in RGB le immagini in un inchiostro spot i cui campioni non è stato possibile leggere sono state importate in RGB +@msg images past the most pixels one file may hold were left out le immagini oltre il numero massimo di pixel che un file può contenere sono state escluse +@msg images too large to read were left out le immagini troppo grandi da leggere sono state escluse +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes può contenere al massimo {MAX_GUIDE_INDEXES} voci +@msg indexes must be an array of guide indexes indexes deve essere un array di indici di guide +@msg instruction out of range istruzione fuori intervallo +@msg instruction {pc} ({op}) pops below the stack l'istruzione {pc} ({op}) estrae oltre il fondo dello stack +@msg instruction {pc} reached with stack depths {seen} and {d} istruzione {pc} raggiunta con profondità dello stack {seen} e {d} +@msg invalid array length lunghezza dell'array non valida +@msg invalid group gruppo non valido +@msg invalid object id {v}: expected a non-negative integer id di oggetto non valido {v}: previsto un intero non negativo +@msg invalid regular expression flag '{_1}' flag di espressione regolare non valido '{_1}' +@msg invalid regular expression: {m} espressione regolare non valida: {m} +@msg invalid time value valore di tempo non valido +@msg is a confirm's answer, whose `value` must be true or false è la risposta a una conferma, il cui `value` deve essere true o false +@msg is a prompt's answer, whose `value` must be a string or null è la risposta a una richiesta di input, il cui `value` deve essere una stringa o null +@msg is an alert's answer, whose `value` must be "ok" è la risposta a un avviso, il cui `value` deve essere "ok" +@msg is longer than 16 KiB supera 16 KiB +@msg it draws its image through a clipping path disegna la sua immagine attraverso un tracciato di ritaglio +@msg it draws no image non disegna alcuna immagine +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes supera {MAX_DICTIONARY_FILE_BYTES} byte +@msg it isn't a file non è un file +@msg it names {_1} plates, and at most {MAX_PLATES} are read indica {_1} lastre, e ne vengono lette al massimo {MAX_PLATES} +@msg its art is too long or too detailed for a head la sua grafica è troppo lunga o troppo dettagliata per una testa +@msg its art paints nothing la sua grafica non dipinge nulla +@msg its image is too large to read la sua immagine è troppo grande da leggere +@msg its plates are too large: more than {MAX_SAMPLES} pixels together le sue lastre sono troppo grandi: più di {MAX_SAMPLES} pixel in totale +@msg its plates are too large: {_1} plates of {w} × {h} pixels le sue lastre sono troppo grandi: {_1} lastre di {w} × {h} pixel +@msg its plates can't be kept together le sue lastre non possono essere tenute insieme +@msg its shapes could not be combined into one non è stato possibile combinare le sue forme in una sola +@msg jump to {pc}, past the end salto a {pc}, oltre la fine +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding deve essere uno tra {_1} +@msg localize: the first argument must be a localization object or a string localize: il primo argomento deve essere un oggetto di localizzazione o una stringa +@msg missing `picks` `picks` mancante +@msg missing `word` `word` mancante +@msg must be an object with `kind` and `value` deve essere un oggetto con `kind` e `value` +@msg must have `kind` alert, confirm or prompt deve avere `kind` alert, confirm o prompt +@msg must have exactly `kind` and `value` deve avere esattamente `kind` e `value` +@msg needs a closing quote after the name richiede una virgoletta di chiusura dopo il nome +@msg needs a name richiede un nome +@msg needs a name of at most 64 bytes richiede un nome di al massimo 64 byte +@msg needs a name of letters, digits and _, or a quoted name richiede un nome di lettere, cifre e _, oppure un nome tra virgolette +@msg needs a name without control characters richiede un nome senza caratteri di controllo +@msg nesting too deep annidamento troppo profondo +@msg no HP-GL format nessun formato HP-GL +@msg no New Document profile called `{shown}` ({_1}) nessun profilo di nuovo documento chiamato `{shown}` ({_1}) +@msg no SVG filter at index {k} nessun filtro SVG all'indice {k} +@msg no arrowheads file here nessun file di punte di freccia qui +@msg no character has code {code} nessun carattere ha il codice {code} +@msg no custom arrowhead {name} (see stroke.arrowheads.list) nessuna punta di freccia personalizzata {name} (vedi stroke.arrowheads.list) +@msg no file to open nessun file da aprire +@msg no instructions nessuna istruzione +@msg no layer to rename nessun livello da rinominare +@msg no random numbers ({e}) nessun numero casuale ({e}) +@msg no selected or given object has a visible 3D effect nessun oggetto selezionato o indicato ha un effetto 3D visibile +@msg no such artboard {v} tavola da disegno {v} inesistente +@msg no symbol named `{_1}` nessun simbolo chiamato `{_1}` +@msg node {_1} already has the text variable `{_2}` il nodo {_1} ha già la variabile di testo `{_2}` +@msg not a DCS file non è un file DCS +@msg not a RegExp object non è un oggetto RegExp +@msg not a regular expression non è un'espressione regolare +@msg not an action set: the text doesn't start with /version and a number non è un set di azioni: il testo non inizia con /version e un numero +@msg nothing to repeat niente da ripetere +@msg numbers out of order in {_1} quantifier numeri non in ordine nel quantificatore {_1} +@msg object {_1} needs a 3D effect l'oggetto {_1} richiede un effetto 3D +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic oggetto {_1}: un'estrusione e smussatura cava non ha tappi: attiva Tappo per applicare una grafica +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front oggetto {_1}: con una profondità di 0 l'oggetto ha una sola faccia: applica le sue grafiche sulla faccia anteriore +@msg object {_1}: {_2} oggetto {_1}: {_2} +@msg object {_1}: {e} oggetto {_1}: {e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams oggetto {_1}: {name} all'indice {index} non ha predefiniti di illuminazione; imposta le sue luci con effect.setParams +@msg opacity must be a number opacity deve essere un numero +@msg operand too large operando troppo grande +@msg order is a whole number from 0 to {others}, not `{v}` order è un numero intero compreso tra 0 e {others}, non `{v}` +@msg path chunks must share their boundary point i segmenti di tracciato devono condividere il punto di confine +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false richiede le tavole da disegno per conservare la grafica modificabile quando useArtboards è false +@msg point x must be a number la x del punto deve essere un numero +@msg point y must be a number la y del punto deve essere un numero +@msg points must be [x,y] pairs points deve contenere coppie [x,y] +@msg points must be finite canvas points points deve contenere punti finiti dell'area di lavoro +@msg range out of order in character class intervallo non in ordine nella classe di caratteri +@msg reduce of an empty array with no initial value reduce di un array vuoto senza valore iniziale +@msg regular expression flag '{_1}' given twice flag di espressione regolare '{_1}' indicato due volte +@msg sampled text would exceed the canvas (coordinates out of range) il testo campionato supererebbe l'area di lavoro (coordinate fuori intervallo) +@msg select an SVG filter seleziona un filtro SVG +@msg select an object with a 3D effect seleziona un oggetto con un effetto 3D +@msg select an object with a 3D effect, or give its id in ids seleziona un oggetto con un effetto 3D, o indica il suo id in ids +@msg select one layer, group or object to rename seleziona un livello, un gruppo o un oggetto da rinominare +@msg select the art to weed seleziona la grafica da spellicolare +@msg select type on a path or area type seleziona un testo su tracciato o un testo in area +@msg side is left or right, not `{v}` side è left o right, non `{v}` +@msg source text transform is collapsed or unrepresentable la trasformazione del testo di origine è degenere o non rappresentabile +@msg table must be true or false table deve essere true o false +@msg takes one name accetta un solo nome +@msg target text transform is collapsed or unrepresentable la trasformazione del testo di destinazione è degenere o non rappresentabile +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer questo produce troppi pezzi (al massimo 100000) o passi del tratteggio: semplifica lo schema del tratteggio o allunga i trattini +@msg the Layers panel is not shown il pannello Livelli non è visualizzato +@msg the Layers panel's search, filter or Show Layers Only hides its row la ricerca, il filtro o Mostra solo livelli del pannello Livelli ne nasconde la riga +@msg the Objects on Path group is hidden or locked il gruppo Oggetti su tracciato è nascosto o bloccato +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes il file SVG supera {MAX_IMPORT_BYTES} byte +@msg the XML operation needs an XML or XMLList value l'operazione XML richiede un valore XML o XMLList +@msg the XPath expression does not evaluate to a node-set l'espressione XPath non restituisce un node-set +@msg the XPath expression ends early l'espressione XPath termina prematuramente +@msg the XPath expression has an unmatched ) l'espressione XPath ha una ) senza corrispondenza +@msg the XPath expression has an unmatched ] l'espressione XPath ha una ] senza corrispondenza +@msg the XPath expression has an unmatched closing bracket l'espressione XPath ha una parentesi di chiusura senza corrispondenza +@msg the XPath expression selects a namespace node, which is not an XML value l'espressione XPath seleziona un nodo namespace, che non è un valore XML +@msg the XPath expression selects the root node, which is not an XML value l'espressione XPath seleziona il nodo radice, che non è un valore XML +@msg the XPath function {name} needs a node-set la funzione XPath {name} richiede un node-set +@msg the XPath function {name} takes the wrong number of arguments la funzione XPath {name} riceve un numero errato di argomenti +@msg the XPath name functions need a node-set le funzioni XPath sui nomi richiedono un node-set +@msg the XPath prefix {shown} is not bound to a namespace il prefisso XPath {shown} non è associato a un namespace +@msg the XPath variable ${shown} is not bound la variabile XPath ${shown} non è associata +@msg the argument list is not an object l'elenco degli argomenti non è un oggetto +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out la grafica è troppo grande per EPS: ciò che segue i 256 MB di PostScript è stato escluso +@msg the bitmaps hold more pixels than one file may le bitmap contengono più pixel di quanti ne possa contenere un file +@msg the code runs past its last instruction il codice prosegue oltre la sua ultima istruzione +@msg the curve tolerance must be from 0.01 to 10 points la tolleranza della curva deve essere compresa tra 0.01 e 10 punti +@msg the custom dictionary couldn't be saved to {_1}: {e} impossibile salvare il dizionario personalizzato in {_1}: {e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is impossibile leggere il dizionario personalizzato {_1} ({why}), quindi viene lasciato com'è +@msg the document already has an SVG filter named {name} il documento ha già un filtro SVG chiamato {name} +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it il documento ha un altro filtro SVG chiamato {name}: usa svgFilter.apply, oppure svgFilter.define con `replace` per modificarlo +@msg the document has no SVG filter named {name} il documento non ha alcun filtro SVG chiamato {name} +@msg the document has no SVG filter named {r} il documento non ha alcun filtro SVG chiamato {r} +@msg the document has no color swatches il documento non ha campioni di colore +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 i campioni del documento contengono {_1} colori; la riduzione al documento ne accetta al massimo 255 +@msg the file isn't valid SVG: {e} il file non è un SVG valido: {e} +@msg the file names no plate il file non indica alcuna lastra +@msg the function's prototype is not an object il prototipo della funzione non è un oggetto +@msg the guide is hidden or locked la guida è nascosta o bloccata +@msg the library's file is outside the read roots il file della libreria è fuori dalle cartelle di lettura consentite +@msg the line is too long, or the paths it passes too many, to check la linea è troppo lunga, o i tracciati che attraversa sono troppi, per poterla verificare +@msg the method needs an XML value il metodo richiede un valore XML +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) la pittura non è una sfumatura a forma libera (applicane una con paint.editGradient {kind}) +@msg the parameters or body do not form one function i parametri o il corpo non formano un'unica funzione +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it il filtro SVG incollato {name} disegna in modo diverso dal filtro del documento con quel nome, e non c'è un nome libero adatto +@msg the paths are too many or too tangled to find where they cross i tracciati sono troppi o troppo intrecciati per trovare dove si incrociano +@msg the pen table holds more than 255 pens la tabella delle penne contiene più di 255 penne +@msg the plates can't be kept le lastre non possono essere conservate +@msg the position table does not match the instructions la tabella delle posizioni non corrisponde alle istruzioni +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes il testo della richiesta supera {MAX_PROMPT_BYTES} byte +@msg the radix must be between 2 and 36 la base deve essere compresa tra 2 e 36 +@msg the rectangle or line reaches too many paths to check il rettangolo o la linea raggiunge troppi tracciati da verificare +@msg the right side of 'in' is not an object il lato destro di 'in' non è un oggetto +@msg the right side of 'instanceof' is not a function il lato destro di 'instanceof' non è una funzione +@msg the selected art is nested more than {MAX_DEPTH} levels deep la grafica selezionata è annidata per più di {MAX_DEPTH} livelli +@msg the selected art is too large to measure la grafica selezionata è troppo grande da misurare +@msg the selection has no outlines to weed around la selezione non ha contorni attorno a cui spellicolare +@msg the value bind was called on is not a function il valore su cui è stato chiamato bind non è una funzione +@msg the value is not a constructor il valore non è un costruttore +@msg the value is not a function il valore non è una funzione +@msg the value is not a valid XML name il valore non è un nome XML valido +@msg the value is not an XML attribute name il valore non è un nome di attributo XML +@msg the value is not an XML name il valore non è un nome XML +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines le linee di spellicolatura incrociano i contorni della grafica troppe volte per poterle misurare; usa meno linee +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap le linee di spellicolatura passano troppo vicino a troppi segmenti di contorno per poterle misurare; usa meno linee o una distanza minore +@msg the weeding lines would be more than {max_pieces} pieces le linee di spellicolatura sarebbero più di {max_pieces} pezzi +@msg the {_1} plate lies outside the file la lastra {_1} si trova fuori dal file +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that questo è un file DXF binario, che non può essere letto: salvalo dall'app CAD come DXF ASCII e apri quello +@msg this warp's points can't be edited: use Reset with Mesh first i punti di questa distorsione non possono essere modificati: usa prima Reimposta con trama +@msg toExponential() digits must be between 0 and 20 le cifre di toExponential() devono essere comprese tra 0 e 20 +@msg toFixed() digits must be between 0 and 20 le cifre di toFixed() devono essere comprese tra 0 e 20 +@msg toISOString is not a function toISOString non è una funzione +@msg toPrecision() argument must be between 1 and 21 l'argomento di toPrecision() deve essere compreso tra 1 e 21 +@msg toSource: a getter cannot be written as source toSource: un getter non può essere scritto come sorgente +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource: un array con proprietà oltre ai suoi elementi, una proprietà non enumerabile o un altro prototipo non può essere scritto come sorgente +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource: un oggetto con una proprietà non enumerabile, o il cui prototipo non è Object.prototype, non può essere scritto come sorgente +@msg toSource: the source is too long toSource: il sorgente è troppo lungo +@msg toSource: the value contains itself toSource: il valore contiene se stesso +@msg toSource: the value has too many entries toSource: il valore ha troppe voci +@msg toSource: the value is nested too deep toSource: il valore è annidato troppo in profondità +@msg toString is not a function toString non è una funzione +@msg too many arguments troppi argomenti +@msg too many quantifiers troppi quantificatori +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) funzione OpenType sconosciuta `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; anteponi - per disattivarla; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom e aalt accettano il numero di un'alternativa come `tag=N` con N da 0 a {_1}, ad esempio `salt=2`) +@msg unknown dither `{d}` (diffusion, pattern, noise or none) dithering sconosciuto `{d}` (diffusion, pattern, noise o none) +@msg unknown panel `{panel}` pannello sconosciuto `{panel}` +@msg unknown parameter `{key}` parametro sconosciuto `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} parametro sconosciuto `{within}settings.{format}`; {why} +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) riduzione sconosciuta `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform o document) +@msg unmatched ')' ')' senza corrispondenza +@msg unterminated character class classe di caratteri non terminata +@msg unterminated group gruppo non terminato +@msg wordGap and lineGap need area type in the selection wordGap e lineGap richiedono un testo in area nella selezione +@msg xpath needs its variables as an object xpath richiede le sue variabili come oggetto +@msg xpath needs the expression as a string xpath richiede l'espressione come stringa +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED}, e le sue impostazioni e la sua cache non possono essere spostate di nuovo da {_1} a {_2}: {e} +@msg {_1} is not a folder: {e} {_1} non è una cartella: {e} +@msg {_1}: {_2} and {_3} are in the way {_1}: {_2} e {_3} sono d'intralcio +@msg {code}: {0} {code}: {0} +@msg {dir}: {e} {dir}: {e} +@msg {file} names more than one linked JavaScript file {file} indica più di un file JavaScript collegato +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: il tracciato di una punta di freccia personalizzata richiede un'area da riempire e al massimo {most} punti di ancoraggio e {most} sottotracciati, ciascun punto entro {far} unità della punta dal suo vertice +@msg {k}: bad custom arrowhead path: {e} {k}: tracciato della punta di freccia personalizzata non valido: {e} +@msg {k}: bad custom arrowhead: {e} {k}: punta di freccia personalizzata non valida: {e} +@msg {k}: custom must be a name {k}: custom deve essere un nome +@msg {name} must be a number {name} deve essere un numero +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: copy accetta un file DCS solo quando le sue lastre sono al suo interno; ricollegalo dove si trovano i file delle sue lastre +@msg {path}: larger than {_1} MB {path}: supera {_1} MB +@msg {shown} is not an XPath 1.0 function {shown} non è una funzione XPath 1.0 +@msg {v} names no layer or sublayer {v} non indica alcun livello o sottolivello +@msg {what} is not a function {what} non è una funzione +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what} stamperebbe più di {MAX_COPIES_PER_SHEET} copie su un foglio: ingrandiscilo +@msg {what} {i} out of range {what} {i} fuori intervallo +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. Un profilo supporta un solo effetto Rivoluzione. Modifica invece le opzioni dell'effetto esistente. +@msg Add Points at Crossings: a path went missing Aggiungi punti agli incroci: un tracciato è andato perso +@msg CSS options: {e} Opzioni CSS: {e} +@msg CSV has a header but no data rows Il CSV ha un'intestazione ma nessuna riga di dati +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark Il CSV contiene byte NUL: salvalo come UTF-8, o come UTF-16 con un byte-order mark +@msg CSV is empty Il CSV è vuoto +@msg CSV is larger than {MAX_CSV_BYTES} bytes Il CSV supera {MAX_CSV_BYTES} byte +@msg CSV is not UTF-16 Il CSV non è in UTF-16 +@msg CSV is not UTF-8 Il CSV non è in UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes Il CSV supererebbe {MAX_CSV_BYTES} byte +@msg Choose original artwork, not a dimension's generated paths Scegli la grafica originale, non i tracciati generati da una quota +@msg Contour: select paths or shapes Contorno: seleziona tracciati o forme +@msg Contour: the Offset Path effect is not available Contorno: l'effetto Sposta tracciato non è disponibile +@msg Create needs fills, strokes or both Crea richiede riempimenti, tracce o entrambi +@msg DXF options: {e} Opzioni DXF: {e} +@msg DXF scale must be a positive number up to {_1}, not {scale} La scala DXF deve essere un numero positivo fino a {_1}, non {scale} +@msg DXF {field} `{s}`: {allowed} DXF {field} `{s}`: {allowed} +@msg Dimension is locked or hidden La quota è bloccata o nascosta +@msg Dimension mode does not match its source La modalità della quota non corrisponde alla sua origine +@msg Draw Inside needs exactly one selected path, compound path or text Disegna all'interno richiede esattamente un tracciato, un tracciato composto o un testo selezionato +@msg EPS Save writes all artwork: use document.export for selected-only EPS Il salvataggio EPS scrive tutta la grafica: usa document.export per un EPS con la sola selezione +@msg EPS flattener: {e} Appiattimento EPS: {e} +@msg EPS options: {e} Opzioni EPS: {e} +@msg EPS {field} `{s}`: {allowed} EPS {field} `{s}`: {allowed} +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft Esporta per schermi scrive png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai o vectorcraft +@msg Flare click coordinates must be finite numbers Le coordinate del clic per Riflesso devono essere numeri finiti +@msg HTML Text applies to object slices of type only Testo HTML si applica solo alle sezioni oggetto di testo +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) Identifica oggetti per è impostato su ID XML: il nome deve essere un ID XML valido (inizia con una lettera, un trattino basso o due punti; senza spazi) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) Ricalco immagine non riuscito: errore interno: il suo thread si è fermato (segnala questo bug) +@msg Image Trace failed: {e} Ricalco immagine non riuscito: {e} +@msg Image Trace finished, but its document was closed Ricalco immagine terminato, ma il suo documento è stato chiuso +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again Ricalco immagine terminato, ma nel frattempo l'immagine o le sue impostazioni sono cambiate: ricalcala di nuovo +@msg Invalid dimension offset Distanza della quota non valida +@msg Invalid point Punto non valido +@msg Join: a subpath went missing Unisci: un sottotracciato è andato perso +@msg Lock Workspace is not available in VectorCraft Blocca area di lavoro non è disponibile in VectorCraft +@msg Move Panel to New Window is not available: floating panels stay in the main window Sposta pannello in una nuova finestra non è disponibile: i pannelli mobili restano nella finestra principale +@msg Offset Path: the path is too complex to offset Sposta tracciato: il tracciato è troppo complesso da spostare +@msg Offset Path: {e} Sposta tracciato: {e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported Il contenuto PDF è incompleto o supera i limiti del lettore; nessun documento è stato importato +@msg PDF flattener: {e} Appiattimento PDF: {e} +@msg PDF options: {e} Opzioni PDF: {e} +@msg Recolor Artwork is no longer open Ricolora grafica non è più aperto +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 L'SVG non è in UTF-8, UTF-16 o ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export Salva scrive una sola tavola da disegno: indicane una, o esportane diverse con document.export +@msg Select a dimension first Seleziona prima una quota +@msg Select dimensions to update Seleziona le quote da aggiornare +@msg Select only dimensions Seleziona solo quote +@msg Select two or more objects, or a group Seleziona due o più oggetti, o un gruppo +@msg Shaper needs 1–64 source paths Shaper richiede da 1 a 64 tracciati di origine +@msg Switch x/y is for scatter graphs Scambia x/y è per i grafici a dispersione +@msg Text options: {e} Opzioni testo: {e} +@msg `align` is start, center or end `align` è start, center o end +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` deve essere un array di coppie [subpath, anchor] +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` deve elencare indici di punti di ancoraggio (0, 1, 2…), non {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` deve elencare indici di punti di ancoraggio (0, 1, 2…), non {v} +@msg `artboards` must be a list of artboard indices `artboards` deve essere un elenco di indici di tavole da disegno +@msg `asset` is the asset id (see assets.list) `asset` è l'id della risorsa (vedi assets.list) +@msg `at` must be a byte offset `at` deve essere un offset in byte +@msg `autoLeading` must be a number `autoLeading` deve essere un numero +@msg `background` must be "", "matte" or #rrggbb `background` deve essere "", "matte" o #rrggbb +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` deve essere "none", "standard" o "forced" +@msg `center` must be [x, y] `center` deve essere [x, y] +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` deve essere "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" o "icfBottom" +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` deve essere "default", "leftToRight" o "rightToLeft" +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` `clearStroke` richiede `stroke: false`, `ids: []` e nessun `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` deve essere un elenco di id di oggetti di testo, non {v} +@msg `clear` must list type objects; {_1} is not one `clear` deve elencare oggetti di testo; {_1} non lo è +@msg `clear` must not list `id` `clear` non deve elencare `id` +@msg `color` needs a `rule` to make the theme from `color` richiede una `rule` da cui creare il tema +@msg `colors` is empty `colors` è vuoto +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` è vertical, uniform, repeating o sliding +@msg `columns` must be a whole number 1..50 `columns` deve essere un numero intero 1..50 +@msg `commands` must not be empty `commands` non deve essere vuoto +@msg `contents` goes with `to` `contents` va usato con `to` +@msg `corners` must be sharp, round or square, not `{other}` `corners` deve essere sharp, round o square, non `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` deve elencare indici di punti di ancoraggio (0, 1, 2…), non {v} +@msg `count` must be a whole number from 1 to 100 `count` deve essere un numero intero compreso tra 1 e 100 +@msg `dataBase64` is larger than the module limit `dataBase64` supera il limite del modulo +@msg `dataBase64` is not valid base64 `dataBase64` non è un base64 valido +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` non può essere combinato con `ids`, `reverse` o `append` +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` e `childLimit` riducono il riepilogo: passa `summary: true` +@msg `design` is a design name, or null for plain columns `design` è il nome di un design, o null per colonne semplici +@msg `design` is a design name, or null for the default marker `design` è il nome di un design, o null per il marcatore predefinito +@msg `destinationFolder` is required when destination is `folder` `destinationFolder` è obbligatorio quando la destinazione è `folder` +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` deve essere `none`, `saveAndClose` o `folder` +@msg `diameter` must be from 0.1 to 2500 pt `diameter` deve essere compreso tra 0.1 e 2500 pt +@msg `digits` must be one of {_1} `digits` deve essere uno tra {_1} +@msg `dimImages` is a percentage or false `dimImages` è una percentuale o false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` deve essere "auto", "leftToRight" o "rightToLeft" +@msg `each` is the value one design stands for, more than 0 `each` è il valore rappresentato da un design, maggiore di 0 +@msg `end` must be "start" or "end" `end` deve essere "start" o "end" +@msg `ends` applies to joining exactly two paths `ends` si applica all'unione di esattamente due tracciati +@msg `event` must be one of {_1} `event` deve essere uno tra {_1} +@msg `every` must be "all", "odd" or "even", not {v} `every` deve essere "all", "odd" o "even", non {v} +@msg `expand` must be true or false `expand` deve essere true o false +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` deve contenere da 1 a 1000 percorsi di file di font trovati dall'ultima ricerca text.findFontFiles +@msg `folder` must be an absolute folder path `folder` deve essere il percorso assoluto di una cartella +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` deve essere un oggetto di tag di assi e valori, ad esempio {"wght"} +@msg `format` must be a string `format` deve essere una stringa +@msg `format` must be one of {_1} `format` deve essere uno tra {_1} +@msg `fractions` is chop or scale `fractions` è chop o scale +@msg `from` and `to` must differ `from` e `to` devono essere diversi +@msg `from` has an empty frame `from` ha una cornice vuota +@msg `from` must be a non-negative integer `from` deve essere un intero non negativo +@msg `from` must be area type or type on a path `from` deve essere un testo in area o un testo su tracciato +@msg `gid` must be a glyph id, not {v} `gid` deve essere un id di glifo, non {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` riguarda un singolo carattere, non uno stile di carattere (lo imposta text.setGlyphAlternate) +@msg `guide` must be a guide index (guide.list) `guide` deve essere un indice di guida (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` deve essere default, left, center o right +@msg `id` must be a node id `id` deve essere un id di nodo +@msg `id` must be an object id, not {v} `id` deve essere un id di oggetto, non {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` deve indicare gruppi Oggetti su tracciato o oggetti al loro interno +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` accetta al massimo {MAX_INSPECT_IDS} id +@msg `imageMap` must be none, rectangle or polygon `imageMap` deve essere none, rectangle o polygon +@msg `includeArtboardFill` must be true or false `includeArtboardFill` deve essere true o false +@msg `includeSubdirs` must be true or false `includeSubdirs` deve essere true o false +@msg `index` must be a point index `index` deve essere un indice di punto +@msg `index` must be a whole number or null `index` deve essere un numero intero o null +@msg `indices` must be a non-empty array of item indices `indices` deve essere un array non vuoto di indici di elementi +@msg `items` is a list of {kind, item} `items` è un elenco di {kind, item} +@msg `items` is empty `items` è vuoto +@msg `items` must be a list of {id, points} `items` deve essere un elenco di {id, points} +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` deve essere un elenco di {{id, anchors?}}, non {v} +@msg `items` must be an array of {id, anchors} `items` deve essere un array di {id, anchors} +@msg `items` must be an array of {{id, corners?}}, not {v} `items` deve essere un array di {{id, corners?}}, non {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` deve essere "none", "short", "medium", "long" o "stylistic" +@msg `kind` is one of {_1} `kind` è uno tra {_1} +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` deve essere "round", "invertedRound" o "chamfer", non {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` deve essere `text`, `visibility`, `image` o `graph` +@msg `kind` must be a string `kind` deve essere una stringa +@msg `kind` must be image, noImage or htmlText `kind` deve essere image, noImage o htmlText +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` deve essere "none", "hard" o "soft" +@msg `label` must be a non-empty name of at most 200 characters `label` deve essere un nome non vuoto di al massimo 200 caratteri +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` deve essere il nome o il tag di una lingua, ad esempio "Turkish" o "tr" +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` deve essere "romanBaseline" o "emBoxTop" +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` deve essere compreso tra 0.1 e 5000, o null per Auto +@msg `length` must be a number of at most 1000000 pt either way `length` deve essere un numero di al massimo 1000000 pt in entrambe le direzioni +@msg `line` must be true or false `line` deve essere true o false +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) non può essere maggiore di `maxAngle` ({max_angle}°) +@msg `mode` must be "handles", "straight" or "smooth" `mode` deve essere "handles", "straight" o "smooth" +@msg `mode` must be "straight", "bezier" or "arc" `mode` deve essere "straight", "bezier" o "arc" +@msg `mode` must be one of {_1} `mode` deve essere uno tra {_1} +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` deve essere "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" o "lineEndHalf" +@msg `names` is a list of preset names `names` è un elenco di nomi di predefiniti +@msg `names` lists preset names `names` elenca nomi di predefiniti +@msg `names` must be a list of names `names` deve essere un elenco di nomi +@msg `names` must be a list of swatch names `names` deve essere un elenco di nomi di campioni +@msg `names` must be an array of style names `names` deve essere un array di nomi di stili +@msg `new` is a design name, or true `new` è il nome di un design, o true +@msg `path` must be a folder `path` deve essere una cartella +@msg `path` must be a path object `path` deve essere un oggetto tracciato +@msg `pivot` must be a whole number from 0 to 8 `pivot` deve essere un numero intero compreso tra 0 e 8 +@msg `points` must be a list of [x, y] points `points` deve essere un elenco di punti [x, y] +@msg `points` must be a list of point indexes `points` deve essere un elenco di indici di punti +@msg `points` must be an array of point indices `points` deve essere un array di indici di punti +@msg `points` must be point indexes `points` deve contenere indici di punti +@msg `port` must be "in" or "out" `port` deve essere "in" o "out" +@msg `position` is above or below `position` è above o below +@msg `preset` must be a string `preset` deve essere una stringa +@msg `ratio` must be from 0.1 to 0.5 `ratio` deve essere compreso tra 0.1 e 0.5 +@msg `rect` must be [x, y, width, height] with a positive size `rect` deve essere [x, y, width, height] con una dimensione positiva +@msg `relation` must be overlap, inside or outside `relation` deve essere overlap, inside o outside +@msg `reversed` must be true or false `reversed` deve essere true o false +@msg `rotate` must be a finite number `rotate` deve essere un numero finito +@msg `rule` must be nonZero or evenOdd `rule` deve essere nonZero o evenOdd +@msg `scale` must be a number in (0, 100] `scale` deve essere un numero in (0, 100] +@msg `scope` must be "selection" or "document", not {other} `scope` deve essere "selection" o "document", non {other} +@msg `script` must be a string `script` deve essere una stringa +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` è un elenco non vuoto di indici di serie +@msg `seriesIndexes` needs a `type` or a `valueAxis` `seriesIndexes` richiede un `type` o un `valueAxis` +@msg `shift` must be a number of points `shift` deve essere un numero di punti +@msg `showCenter` must be true or false `showCenter` deve essere true o false +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` deve essere "fullJustify", "alignLeft", "alignCenter" o "alignRight" +@msg `size` is a percentage of the base text's size, 1 to 100 `size` è una percentuale della dimensione del testo di base, da 1 a 100 +@msg `source` and `target` are the same object `source` e `target` sono lo stesso oggetto +@msg `source` must be `folder` or `datasets` `source` deve essere `folder` o `datasets` +@msg `spacing` must be a number `spacing` deve essere un numero +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` deve essere un numero di punti, pari a 0 o maggiore, o "auto" +@msg `start` must be a finite number `start` deve essere un numero finito +@msg `starts` must be an array of anchor indices (or nulls) `starts` deve essere un array di indici di punti di ancoraggio (o null) +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` deve essere linked, embedded, missing o modified, o un elenco di questi +@msg `stop` must be a stop index `stop` deve essere un indice di interruzione +@msg `story` styles the whole story: give no `start` or `end` `story` applica lo stile all'intero brano: non indicare `start` o `end` +@msg `story` takes the whole story: give no `start` or `end` `story` prende l'intero brano: non indicare `start` o `end` +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` si applica alle tracce (stroke: true) +@msg `style` must be roman or japanese `style` deve essere roman o japanese +@msg `subpaths` must be two integer indices `subpaths` deve essere due indici interi +@msg `symbol` must be a symbol name `symbol` deve essere il nome di un simbolo +@msg `target` must be "object" or "contents", not {v} `target` deve essere "object" o "contents", non {v} +@msg `tint` applies to global and spot colours `tint` si applica ai colori globali e spot +@msg `tint` goes with a global or spot `swatch` `tint` va usato con un `swatch` globale o spot +@msg `tint` must be a number (%) `tint` deve essere un numero (%) +@msg `tint` needs a global or spot colour `tint` richiede un colore globale o spot +@msg `to` is already threaded `to` è già concatenato +@msg `to` is the design's new name `to` è il nuovo nome del design +@msg `to` must be [x, y] `to` deve essere [x, y] +@msg `to` must be a closed path, area type or type on a path other than `from` `to` deve essere un tracciato chiuso, un testo in area o un testo su tracciato diverso da `from` +@msg `to` must be a whole number `to` deve essere un numero intero +@msg `to` takes the rows of one object's stack, not aligned rows `to` accetta le righe della pila di un solo oggetto, non righe allineate +@msg `useLab` must be true or false `useLab` deve essere true o false +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` deve essere default, top, middle, baseline o bottom +@msg `values` must be a list `values` deve essere un elenco +@msg `values` must be an object `values` deve essere un oggetto +@msg `values` must name hex, rgb, cmyk, lab or gray `values` deve indicare hex, rgb, cmyk, lab o gray +@msg `values` names a kind twice `values` indica due volte lo stesso tipo +@msg `{_1}` can't be edited or removed `{_1}` non può essere modificato o rimosso +@msg `{_1}` is built in and always listed `{_1}` è incorporato ed è sempre elencato +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` non è un file di font trovato dall'ultima ricerca text.findFontFiles +@msg `{_1}` would be overwritten by the package: choose a different folder or name `{_1}` verrebbe sovrascritto dal pacchetto: scegli una cartella o un nome diverso +@msg `{file}` has no art to place `{file}` non ha grafica da inserire +@msg `{file}` is over {_1} MB `{file}` supera {_1} MB +@msg `{f}` belongs to a VectorCraft that is running `{f}` appartiene a un VectorCraft in esecuzione +@msg `{f}` is the copy of a document open now `{f}` è la copia di un documento aperto in questo momento +@msg `{id}` is no writable format (see document.formats) `{id}` non è un formato scrivibile (vedi document.formats) +@msg `{id}` isn't a loaded library `{id}` non è una libreria caricata +@msg `{key}` has no colours `{key}` non ha colori +@msg `{key}` is a design name `{key}` è il nome di un design +@msg `{key}` is a design name or a list of them `{key}` è il nome di un design o un elenco di nomi +@msg `{key}` is longer than {MAX_NAME} characters `{key}` supera {MAX_NAME} caratteri +@msg `{key}` is out of range `{key}` è fuori intervallo +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` deve essere [min, desired, max] o {{min?, desired?, max?}}, in percentuale +@msg `{key}` must be a non-negative integer `{key}` deve essere un intero non negativo +@msg `{key}` must be a number {_1}..{_2} `{key}` deve essere un numero {_1}..{_2} +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` deve essere un numero intero pari almeno a 0, non {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` deve essere un indice di voce dell'aspetto (ordine di pittura) o null +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` deve essere un array di id di risorse (vedi assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` deve essere compreso tra 0 e 180 gradi, non {a} +@msg `{key}` must be from {_1} to {shift_max} `{key}` deve essere compreso tra {_1} e {shift_max} +@msg `{key}`: not a colour: {v} `{key}`: non è un colore: {v} +@msg `{k}.family` is required `{k}.family` è obbligatorio +@msg `{k}` is a number 0..1 `{k}` è un numero 0..1 +@msg `{k}` is true or false `{k}` è true o false +@msg `{k}` isn't an export setting: {_1} `{k}` non è un'impostazione di esportazione: {_1} +@msg `{k}` must be a byte offset `{k}` deve essere un offset in byte +@msg `{k}` must be a number `{k}` deve essere un numero +@msg `{k}` must be a number of points `{k}` deve essere un numero di punti +@msg `{k}` must be a string `{k}` deve essere una stringa +@msg `{k}` must be a whole number `{k}` deve essere un numero intero +@msg `{k}` must be from {lo} to {hi} for {key} `{k}` deve essere compreso tra {lo} e {hi} per {key} +@msg `{k}` must be {what}, not {v} `{k}` deve essere {what}, non {v} +@msg `{l}` isn't a global or spot colour `{l}` non è un colore globale o spot +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` non è una bitmap: usa clipboard.importSvg, importPdf o importText +@msg `{name}` can't be deleted `{name}` non può essere eliminato +@msg `{name}` can't be duplicated `{name}` non può essere duplicato +@msg `{name}` can't be edited `{name}` non può essere modificato +@msg `{name}` can't be edited this way `{name}` non può essere modificato in questo modo +@msg `{name}` can't be renamed `{name}` non può essere rinominato +@msg `{name}` can't name a folder `{name}` non può essere il nome di una cartella +@msg `{name}` has no art to place `{name}` non ha grafica da inserire +@msg `{name}` has no text to place `{name}` non ha testo da inserire +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text `{name}` nel documento Word non è testo UTF-8 o UTF-16 +@msg `{name}` in the Word document is not well-formed XML: {e} `{name}` nel documento Word non è XML ben formato: {e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep `{name}` nel documento Word annida elementi a più di {MAX_NESTING} livelli di profondità +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` è un predefinito incorporato ed è di sola lettura: salvalo con un altro nome +@msg `{name}` is a built-in preset: save under another name `{name}` è un predefinito incorporato: salva con un altro nome +@msg `{name}` is a built-in profile and stays `{name}` è un profilo incorporato e rimane +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` è una tinta di `{base}`: modifica `{base}` (un campione di tinta accetta solo un nuovo nome) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` è uno spazio di lavoro: scegline prima un altro con edit.colorSettings +@msg `{name}` is built in and stays `{name}` è incorporato e rimane +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` accetta una pittura del proprio tipo (colore, sfumatura o motivo) +@msg `{n}` can't be deleted `{n}` non può essere eliminato +@msg `{n}` can't be merged `{n}` non può essere unito +@msg `{n}` can't be moved `{n}` non può essere spostato +@msg `{n}` is not a colour swatch `{n}` non è un campione di colore +@msg `{n}` isn't a solid colour `{n}` non è un colore pieno +@msg `{n}`: colour groups hold solid colours only `{n}`: i gruppi di colori contengono solo colori pieni +@msg `{other}` doesn't trace an image `{other}` non ricalca un'immagine +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`: i riferimenti del passo copiano più di {_1} MB di valori +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` non è un tag di asse: un tag è di quattro caratteri, ad esempio "wght" +@msg `{to}` is empty or already used `{to}` è vuoto o già usato +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` supera {MAX_TEXT} byte +@msg `{what}` must be text `{what}` deve essere testo +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard un disegno DXF ha una sola origine: passa useArtboards: true per un disegno per tavola da disegno +@msg a PDF presets file with over {_1} presets un file di predefiniti PDF con più di {_1} predefiniti +@msg a batch preview cannot switch documents un'anteprima batch non può cambiare documento +@msg a brush named `{nn}` exists esiste un pennello chiamato `{nn}` +@msg a container of the listed objects is gone un contenitore degli oggetti elencati non esiste più +@msg a content stream can't be read impossibile leggere uno stream di contenuto +@msg a content stream isn't a stream uno stream di contenuto non è uno stream +@msg a design holds at most {_1} objects un design contiene al massimo {_1} oggetti +@msg a design needs a name un design richiede un nome +@msg a dictionary grew too large un dizionario è diventato troppo grande +@msg a document keeps at most {_1} graph designs un documento conserva al massimo {_1} design di grafico +@msg a document keeps at most {_1} saved selections un documento conserva al massimo {_1} selezioni salvate +@msg a document keeps at most {_1} views un documento conserva al massimo {_1} viste +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files un documento collega al massimo {MAX_SVG_SCRIPTS} file JavaScript +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes l'ordine di un filtro feConvolveMatrix è {x}×{y}, che fa traboccare la moltiplicazione della dimensione del kernel eseguita da usvg +@msg a gradient preset is already called {n} esiste già un predefinito di sfumatura chiamato {n} +@msg a graph has at most {_1} series, so it transposes with at most that many categories un grafico ha al massimo {_1} serie, quindi si traspone con al massimo altrettante categorie +@msg a layer can't be merged into its own sublayer un livello non può essere unito a un proprio sottolivello +@msg a layer's form box isn't an array il riquadro del form di un livello non è un array +@msg a layer's form can't be read impossibile leggere il form di un livello +@msg a layer's form has no box il form di un livello non ha un riquadro +@msg a layer's form has no dictionary il form di un livello non ha un dizionario +@msg a legend draws at most {MAX_CHIPS} swatches una legenda disegna al massimo {MAX_CHIPS} campioni +@msg a name names one asset: give multiple: false, or one object un nome indica una sola risorsa: indica multiple: false, o un solo oggetto +@msg a page can't be read impossibile leggere una pagina +@msg a page object is used by something else un oggetto pagina è usato da qualcos'altro +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) una miniatura di pagina deve essere in pixel RGB, da 1 a {THUMBNAIL_SIZE} per lato (ricevuto {w} × {h}) +@msg a pattern layer doesn't end what it starts un livello di motivo non chiude ciò che apre +@msg a pattern layer's colors hold something else i colori di un livello di motivo contengono qualcos'altro +@msg a pattern named `{n}` exists esiste un motivo chiamato `{n}` +@msg a plug-in with id {_1} is already installed un plug-in con id {_1} è già installato +@msg a preset named `{n}` exists esiste un predefinito chiamato `{n}` +@msg a print presets file with over {_1} presets un file di predefiniti di stampa con più di {_1} predefiniti +@msg a profile is already called {n} esiste già un profilo chiamato {n} +@msg a row can't go into itself una riga non può entrare in se stessa +@msg a series goes on the left or the right value axis una serie va sull'asse dei valori sinistro o destro +@msg a start must be an anchor index ≥ 0 or null un inizio deve essere un indice di punto di ancoraggio ≥ 0 o null +@msg a style named `{name}` already exists esiste già uno stile chiamato `{name}` +@msg a style named `{to}` already exists esiste già uno stile chiamato `{to}` +@msg a swatch needs a colour, gradient or pattern un campione richiede un colore, una sfumatura o un motivo +@msg a symbol named `{n}` already exists esiste già un simbolo chiamato `{n}` +@msg a theme has 1 to {THEME_SIZE} colours un tema ha da 1 a {THEME_SIZE} colori +@msg a theme is already called {name} (replace: "{name}" overwrites it) esiste già un tema chiamato {name} (replace: "{name}" lo sovrascrive) +@msg a toolbar name has at most {MAX_NAME} characters il nome di una barra degli strumenti ha al massimo {MAX_NAME} caratteri +@msg a view named `{_1}` exists esiste una vista chiamata `{_1}` +@msg a view named `{name}` exists esiste una vista chiamata `{name}` +@msg a workspace keeps at most {MAX} toolbars un'area di lavoro conserva al massimo {MAX} barre degli strumenti +@msg a {_1} series can't go in a {_2} graph una serie {_1} non può andare in un grafico {_2} +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}: "pdf" (compatibile PDF, il valore predefinito) o "7" (.ai legacy, versione 7) +@msg already editing an opacity mask stai già modificando una maschera di opacità +@msg already editing another object's opacity mask stai già modificando la maschera di opacità di un altro oggetto +@msg already in pattern editing mode già in modalità di modifica motivo +@msg an artboard is listed twice una tavola da disegno è elencata due volte +@msg an empty artboard has no width or height to scale to una tavola da disegno vuota non ha larghezza o altezza a cui adattarsi +@msg an image is too large for DXF and was left out un'immagine è troppo grande per DXF ed è stata esclusa +@msg an image of {_1} MB, at most {_2} MB un'immagine di {_1} MB, al massimo {_2} MB +@msg an image of {declared_w} × {declared_h} pixels is too large to draw un'immagine di {declared_w} × {declared_h} pixel è troppo grande da disegnare +@msg an image of {width} × {height} pixels is too large to draw un'immagine di {width} × {height} pixel è troppo grande da disegnare +@msg an index must be a whole number un indice deve essere un numero intero +@msg an inline graphic has no glyphs una grafica in linea non ha glifi +@msg an invisible text form can't be read impossibile leggere un form di testo invisibile +@msg an object is missing manca un oggetto +@msg an object's header is missing manca l'intestazione di un oggetto +@msg an overprinting form can't be read impossibile leggere un form in sovrastampa +@msg an unread object has no readable bounds un oggetto non letto non ha limiti leggibili +@msg an unread object holds a frame of threaded type un oggetto non letto contiene una cornice di testo concatenato +@msg an unread object is missing from its layer un oggetto non letto manca dal suo livello +@msg anchor `smooth` must be true or false `smooth` del punto di ancoraggio deve essere true o false +@msg anchor `{key}` must be [x, y] `{key}` del punto di ancoraggio deve essere [x, y] +@msg anchor `{key}` must be a number `{key}` del punto di ancoraggio deve essere un numero +@msg angle and roundness must be finite angle e roundness devono essere finiti +@msg angles must be a list as long as pins (degrees or null) angles deve essere un elenco lungo quanto pins (gradi o null) +@msg appearance must be flat|center|edge appearance deve essere flat|center|edge +@msg apply is "fill" or "stroke", not `{other}` apply è "fill" o "stroke", non `{other}` +@msg area must be a closed polygon of 3 to 100000 [x, y] points area deve essere un poligono chiuso da 3 a 100000 punti [x, y] +@msg area type needs a path that encloses an area il testo in area richiede un tracciato che racchiuda un'area +@msg area type {_1} is named twice il testo in area {_1} è indicato due volte +@msg arrowAlign must be extend|tip, got {o} arrowAlign deve essere extend|tip, ricevuto {o} +@msg arrowScale must be [start, end] arrowScale deve essere [start, end] +@msg artboard is a 0-based index artboard è un indice a partire da 0 +@msg artboard must be a 0-based index artboard deve essere un indice a partire da 0 +@msg artboard {i} doesn't print la tavola da disegno {i} non viene stampata +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout deve essere {layout?, columns?, spacing?, rightToLeft?} +@msg artboardLayout.columns must be 1 or more artboardLayout.columns deve essere 1 o maggiore +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout deve essere gridByRow, gridByColumn, row o column +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft deve essere true o false +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing deve essere 0–{MAX_SPACING} pt +@msg artboards must be a non-empty list artboards deve essere un elenco non vuoto +@msg artboards must be a number artboards deve essere un numero +@msg asset `{_1}` has no art to export la risorsa `{_1}` non ha grafica da esportare +@msg at most {MAX_KEYWORDS} keywords al massimo {MAX_KEYWORDS} parole chiave +@msg at most {MAX_MATCHES} matches are kept vengono conservate al massimo {MAX_MATCHES} corrispondenze +@msg at most {MAX_POINTS} points al massimo {MAX_POINTS} punti +@msg at most {MAX_QUEUE} files al massimo {MAX_QUEUE} file +@msg at must be [x, y] at deve essere [x, y] +@msg at/rect lie outside the canvas at/rect si trovano fuori dall'area di lavoro +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute deve essere size|leading|tracking|kerning|baselineShift +@msg attrs must be an object attrs deve essere un oggetto +@msg automation path rejected: the folder's name isn't valid text percorso di automazione rifiutato: il nome della cartella non è testo valido +@msg automation path rejected: {_1} isn't valid text percorso di automazione rifiutato: {_1} non è testo valido +@msg automation path rejected: {why}: {path} percorso di automazione rifiutato: {why}: {path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis deve essere "vertical", "horizontal" o un angolo in gradi, non `{given}` +@msg backgroundContents must be transparent or white backgroundContents deve essere transparent o white +@msg bad AES key chiave AES non valida +@msg bad `runs`: {e} `runs` non valido: {e} +@msg bad arrowScale arrowScale non valido +@msg bad base64 base64 non valido +@msg bad color {c} colore non valido {c} +@msg bad colour colore non valido +@msg bad colour in `colors` colore non valido in `colors` +@msg bad colour {v} colore non valido {v} +@msg bad colour {v} in groupColors colore non valido {v} in groupColors +@msg bad cross-reference entry voce di riferimento incrociato non valida +@msg bad cross-reference table tabella dei riferimenti incrociati non valida +@msg bad current colour {v} colore corrente non valido {v} +@msg bad dataBase64 dataBase64 non valido +@msg bad edit modifica non valida +@msg bad fill {v} riempimento non valido {v} +@msg bad fit: {e} adattamento non valido: {e} +@msg bad model {other} (keep, new or document) modello non valido {other} (keep, new o document) +@msg bad new colour {v} nuovo colore non valido {v} +@msg bad object oggetto non valido +@msg bad object number numero di oggetto non valido +@msg bad offset offset non valido +@msg bad path data: {e} dati del tracciato non validi: {e} +@msg bad point {v}: expected [x, y] or [x, y, pressure] punto non valido {v}: previsto [x, y] o [x, y, pressure] +@msg bad reference riferimento non valido +@msg bad trailer size dimensione del trailer non valida +@msg bad {k} colour colore {k} non valido +@msg batches cannot nest i batch non possono essere annidati +@msg bind something to a variable first prima collega qualcosa a una variabile +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed deve essere 0–{MAX_BLEED} pt: un numero, [top, bottom, left, right] o {{top, bottom, left, right}} +@msg brush must be a name or null brush deve essere un nome o null +@msg by must be a number by deve essere un numero +@msg can't add page thumbnails: {why} impossibile aggiungere le miniature delle pagine: {why} +@msg can't add the editing data for other apps: {why} impossibile aggiungere i dati di modifica per altre app: {why} +@msg can't edit the written PDF: {why} impossibile modificare il PDF scritto: {why} +@msg can't encrypt the PDF: {why} impossibile crittografare il PDF: {why} +@msg can't linearise the PDF: {why} impossibile linearizzare il PDF: {why} +@msg can't open `{_1}`: {_2} impossibile aprire `{_1}`: {_2} +@msg can't open `{_1}`: {e} impossibile aprire `{_1}`: {e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) impossibile aprire `{name}`: non è un formato che VectorCraft legge (vedi document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) impossibile inserire `{name}`: non è un formato che VectorCraft legge (vedi document.formats) +@msg can't read back the written PDF: {e} impossibile rileggere il PDF scritto: {e} +@msg can't read this Word document: {why} impossibile leggere questo documento Word: {why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp impossibile scrivere immagini .{e}: usa .png, .jpg, .gif, .webp, .tif o .bmp +@msg can't write PDF layers, overprint or invisible text: {why} impossibile scrivere livelli PDF, sovrastampa o testo invisibile: {why} +@msg can't write the file's copyright: {why} impossibile scrivere il copyright del file: {why} +@msg can't write the file's modification date: {why} impossibile scrivere la data di modifica del file: {why} +@msg can't write the pages' user units: {why} impossibile scrivere le unità utente delle pagine: {why} +@msg cannot distort a zero-size or non-finite bounding box impossibile distorcere un riquadro di delimitazione di dimensione zero o non finito +@msg cannot move an object into itself impossibile spostare un oggetto in se stesso +@msg case must be upper|lower|title|sentence case deve essere upper|lower|title|sentence +@msg category `{c}`: one of {_1} categoria `{c}`: una tra {_1} +@msg center must be [x, y] center deve essere [x, y] +@msg choose at least one artboard scegli almeno una tavola da disegno +@msg choose at least one status scegli almeno uno stato +@msg clip must be true or false clip deve essere true o false +@msg clippingMask must be true or false clippingMask deve essere true o false +@msg closed must be a boolean closed deve essere un booleano +@msg collapsed must be true or false collapsed deve essere true o false +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: nessun profilo RGB o CMYK si chiama “{profile}” +@msg colorMode must be rgb or cmyk, not `{m}` colorMode deve essere rgb o cmyk, non `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel deve essere {own} (il metodo del documento), grayscale o bitmap +@msg colour groups can't go into colour groups i gruppi di colori non possono entrare in altri gruppi di colori +@msg colour, mode and spot apply to solid-colour swatches only colore, metodo e spot si applicano solo ai campioni di colore pieno +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read colonna {column}: `{cell}` è una colonna `%` dei file di dati dei grafici, che non vengono letti +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable colonna {column}: `{cell}` indica `{name}`, che è una variabile {_1} +@msg column {column}: `{cell}` names no variable colonna {column}: `{cell}` non indica alcuna variabile +@msg column {column}: the variable name is longer than {MAX_NAME} characters colonna {column}: il nome della variabile supera {MAX_NAME} caratteri +@msg column: true floats the whole column and takes no onto, above or below column: true rende mobile l'intera colonna e non accetta onto, above o below +@msg command must be a shape.* command command deve essere un comando shape.* +@msg commands[{i}]: missing `command` commands[{i}]: `command` mancante +@msg composer must be "singleLine" or "everyLine" composer deve essere "singleLine" o "everyLine" +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes le forme composte accettano tracciati, tracciati composti, gruppi, forme composte, testo, fusioni e involucri +@msg compound {_1} must contain exactly one open path to join il composto {_1} deve contenere esattamente un tracciato aperto da unire +@msg connect end must be "start" or "end" end di connect deve essere "start" o "end" +@msg connect needs a path other than the one extended connect richiede un tracciato diverso da quello esteso +@msg connect needs id connect richiede id +@msg connect path has no open end il tracciato di connect non ha un'estremità aperta +@msg copy needs path: the file to copy into the document's folder copy richiede path: il file da copiare nella cartella del documento +@msg copy takes images only: relink placed documents where their files are copy accetta solo immagini: ricollega i documenti inseriti dove si trovano i loro file +@msg copying a file isn't available on the web la copia di un file non è disponibile sul web +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus deve essere unknown, copyrighted o publicDomain +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl deve essere un URL (senza spazi, al massimo 2048 caratteri) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners deve essere 4 punti [x, y] (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`: item o file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`: solo le pagine PDF hanno i riquadri art, trim, bleed e media; usa "crop" o "bounding" +@msg crop rectangle doesn't overlap the image il rettangolo di ritaglio non si sovrappone all'immagine +@msg cross-reference and object streams aren't supported i riferimenti incrociati e gli stream di oggetti non sono supportati +@msg damaged compressed data: {e} dati compressi danneggiati: {e} +@msg dash lengths must be numbers, got {v} le lunghezze dei trattini devono essere numeri, ricevuto {v} +@msg dash must be a list of lengths or null, got {v} dash deve essere un elenco di lunghezze o null, ricevuto {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) chiave di dati `{k}`: lettere, cifre, -, _ e . (non name o vc-…) +@msg data must be an object of key: value, not {v} data deve essere un oggetto chiave: valore, non {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare il set di dati `{name}` contiene un valore per `{var}`, che la libreria non dichiara +@msg data set `{name}` holds two values for `{var}` il set di dati `{name}` contiene due valori per `{var}` +@msg data set `{name}` is declared twice il set di dati `{name}` è dichiarato due volte +@msg data takes at most {MAX_DATA} keys at once data accetta al massimo {MAX_DATA} chiavi alla volta +@msg dataset `{name}` already exists il set di dati `{name}` esiste già +@msg dataset `{to}` already exists il set di dati `{to}` esiste già +@msg date `{d}`: YYYY-MM-DD data `{d}`: YYYY-MM-DD +@msg decoded data is too large i dati decodificati sono troppo grandi +@msg define a variable first definisci prima una variabile +@msg detect must be true or false detect deve essere true o false +@msg direct-select anchor points of a path seleziona con Selezione diretta i punti di ancoraggio di un tracciato +@msg distance must be > 0 distance deve essere > 0 +@msg dx and dy give the arrow's direction dx e dy indicano la direzione della freccia +@msg dx and dy must be numbers dx e dy devono essere numeri +@msg dxf fitTo must be [width, height], positive fitTo di dxf deve essere [width, height], positivi +@msg dxf must be an object of DXF import options dxf deve essere un oggetto di opzioni di importazione DXF +@msg dxf scale must be a positive number up to {_1}, not {s} la scala di dxf deve essere un numero positivo fino a {_1}, non {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt unità dxf `{u}`: un'unità come mm, cm, in o pt +@msg dxf: {e} dxf: {e} +@msg each format is an object {format, scale?, suffix?} ogni formato è un oggetto {format, scale?, suffix?} +@msg each item needs an id ogni elemento richiede un id +@msg each match needs `kind` ogni corrispondenza richiede `kind` +@msg each match needs a `name` ogni corrispondenza richiede un `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them ogni voce di `fonts` è {family, style?}; da 1 a 1000 voci +@msg each of `items` is {kind, item} ogni voce di `items` è {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} ogni voce di `items` è {{id, corners?}} con un id di oggetto, non {it} +@msg each row needs `from` ogni riga richiede `from` +@msg each stop needs a position ogni interruzione richiede una posizione +@msg each subpath needs an anchors array ogni sottotracciato richiede un array anchors +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType deve essere easeIn, easeOut, easeInOut o easeOutIn +@msg editingData must be true or false editingData deve essere true o false +@msg editingData must be true, false or "always" editingData deve essere true, false o "always" +@msg edits overlap le modifiche si sovrappongono +@msg effects of objects whose appearances differ can't be moved together gli effetti di oggetti con aspetti diversi non possono essere spostati insieme +@msg empty header in column {_1} intestazione vuota nella colonna {_1} +@msg encoding `{_1}`: utf8 or utf16 codifica `{_1}`: utf8 o utf16 +@msg ends must be two of "first" and "last" ends deve essere due tra "first" e "last" +@msg every numbered name for the result is taken tutti i nomi numerati per il risultato sono già usati +@msg expanded SVG styles exceed the import size limit gli stili SVG espansi superano il limite di dimensione dell'importazione +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes il file dei predefiniti di esportazione supera {MAX_PRESET_FILE_BYTES} byte +@msg exportText must be editable or appearance exportText deve essere editable o appearance +@msg exportType must be movieClip or graphic, not `{s}` exportType deve essere movieClip o graphic, non `{s}` +@msg extend needs id extend richiede id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges elenca al massimo {MAX_FEATURE_CHANGES} tag +@msg featureChanges must be a list of tags featureChanges deve essere un elenco di tag +@msg features must be a list of tags features deve essere un elenco di tag +@msg fidelity must be 0..100 fidelity deve essere 0..100 +@msg files must be {name, dataBase64} files deve essere {name, dataBase64} +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit deve essere none|autoHeight|shrinkText|block, ricevuto {id} +@msg flattener: {e} appiattimento: {e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`: high, medium, low o uno salvato (vedi flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile deve essere along|across, ricevuto {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`: none, rows, format, item o itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels cartelle più profonde di {MAX_BATCH_DEPTH} livelli +@msg folders deeper than {MAX_DEPTH} levels cartelle più profonde di {MAX_DEPTH} livelli +@msg font family `{family}` is not available la famiglia di font `{family}` non è disponibile +@msg font family `{tf}` is not available la famiglia di font `{tf}` non è disponibile +@msg format `{f}`: pdf or postscript formato `{f}`: pdf o postscript +@msg format `{f}`: text or json formato `{f}`: text o json +@msg format is `{format}` il formato è `{format}` +@msg formats must be an array of rows, not {v} formats deve essere un array di righe, non {v} +@msg frequency must be between 1 and 300 lines per inch frequency deve essere compreso tra 1 e 300 linee per pollice +@msg from and to must be finite from e to devono essere finiti +@msg from must be [x0, y0, x1, y1] from deve essere [x0, y0, x1, y1] +@msg give `before` or `after`, not both indica `before` o `after`, non entrambi +@msg give `colors`, or `color` and `rule` indica `colors`, oppure `color` e `rule` +@msg give `event`, such as onclick indica `event`, ad esempio onclick +@msg give `file`, the JavaScript file's path or URL indica `file`, il percorso o l'URL del file JavaScript +@msg give `fill` and/or `stroke` indica `fill` e/o `stroke` +@msg give `fill`, `stroke` or both indica `fill`, `stroke` o entrambi +@msg give `folder` indica `folder` +@msg give `fonts`, or open a document indica `fonts`, o apri un documento +@msg give `from` or `indices`, not both indica `from` o `indices`, non entrambi +@msg give `id` (no text is being edited) indica `id` (nessun testo è in modifica) +@msg give `id` and `anchors`, or `items`, not both indica `id` e `anchors`, oppure `items`, non entrambi +@msg give `id` or `guide`, not both indica `id` o `guide`, non entrambi +@msg give `length` or `to` indica `length` o `to` +@msg give `matches`: [{kind, name, target}] indica `matches`: [{kind, name, target}] +@msg give `name` (or edit a pattern) indica `name` (o modifica un motivo) +@msg give `name` or `names` indica `name` o `names` +@msg give `path` (a .wasm file) or `dataBase64` (the module) indica `path` (un file .wasm) o `dataBase64` (il modulo) +@msg give `path`, `data` or `dataBase64` indica `path`, `data` o `dataBase64` +@msg give `path`, `data`, `dataBase64` or `csv` indica `path`, `data`, `dataBase64` o `csv` +@msg give `path`, `xml` or `dataBase64` indica `path`, `xml` o `dataBase64` +@msg give `script`, the JavaScript the event runs ("" removes the event) indica `script`, il JavaScript eseguito dall'evento ("" rimuove l'evento) +@msg give `text` (the reading) or `remove` indica `text` (la lettura) o `remove` +@msg give `x` and `y` (and a `tolerance` of 0 or more) indica `x` e `y` (e una `tolerance` pari a 0 o maggiore) +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments indica un metodo o regolazioni r/g/b, c/m/y/k, gray o tint +@msg give a name, or paint the active proxy with a saved preset indica un nome, o dipingi il proxy attivo con un predefinito salvato +@msg give a name, or select a stroke with a saved profile indica un nome, o seleziona una traccia con un profilo salvato +@msg give a pattern name or select an object painted with a pattern indica il nome di un motivo o seleziona un oggetto dipinto con un motivo +@msg give at least one of clip, invert, disabled, linked indica almeno uno tra clip, invert, disabled, linked +@msg give color, none, swatch or gradient indica color, none, swatch o gradient +@msg give dataBase64 indica dataBase64 +@msg give either at (a click) or area (a path drawn around the overlaps) indica at (un clic) o area (un tracciato disegnato attorno alle sovrapposizioni) +@msg give formats or a preset, not both indica formats o un predefinito, non entrambi +@msg give newName or delete indica newName o delete +@msg give offset or by indica offset o by +@msg give one of `to`, `rect` or `at` indica uno tra `to`, `rect` o `at` +@msg give one of assets, layers, selection or fullDocument indica uno tra assets, layers, selection o fullDocument +@msg give only one of onto, above, below, icon and a column place indica solo uno tra onto, above, below, icon e una posizione in colonna +@msg give origin: [x, y], or reset: true indica origin: [x, y], o reset: true +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note indica overprintFill, overprintStroke, showCenter, imageMap, url o note +@msg give path (a file) or folder indica path (un file) o folder +@msg give path, or name and dataBase64 indica path, oppure name e dataBase64 +@msg give paths or files indica paths o files +@msg give rows, rowHeight, columns or columnWidth indica rows, rowHeight, columns o columnWidth +@msg give svg, or dataBase64 of an SVG or SVGZ file indica svg, o dataBase64 di un file SVG o SVGZ +@msg give text indica text +@msg give the asset a name assegna un nome alla risorsa +@msg give the preset a `name` assegna un `name` al predefinito +@msg give the preset a name assegna un nome al predefinito +@msg give the preset a name (1–64 characters) assegna un nome al predefinito (1–64 caratteri) +@msg give the settings to change, e.g. {"formats"}]} indica le impostazioni da modificare, ad es. {"formats"}]} +@msg give the style a name assegna un nome allo stile +@msg give two or more style `names` indica due o più `names` di stili +@msg give two or more swatches indica due o più campioni +@msg give x, y, or a handle's length or angle indica x, y, o la lunghezza o l'angolo di una maniglia +@msg give {key, value} or {values} indica {key, value} o {values} +@msg global must be true or false global deve essere true o false +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) il glifo {_1} non viene disegnato per {ch} con lo stile del carattere (ad esempio Tutto maiuscole, Maiuscoletto o un sistema numerico) +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} il glifo {g} non è un'alternativa di {ch} in {_1} {_2} +@msg gradient must be "objects" or "mesh" gradient deve essere "objects" o "mesh" +@msg grays must be "profile" or "black", not `{g}` grays deve essere "profile" o "black", non `{g}` +@msg gridColors must be a preset name or two colours gridColors deve essere il nome di un predefinito o due colori +@msg gridSize must be small, medium or large gridSize deve essere small, medium o large +@msg guide {g} is not selected la guida {g} non è selezionata +@msg handle must be 0..3 handle deve essere 0..3 +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}: "layer" (il valore predefinito), "omit" o "visible" +@msg highlight a layer to merge into evidenzia un livello in cui unire +@msg highlight two or more rows in one layer or group evidenzia due o più righe in un unico livello o gruppo +@msg highlight two or more rows to merge evidenzia due o più righe da unire +@msg icon docks a panel in the icon column: use window.panel.dock icon ancora un pannello nella colonna delle icone: usa window.panel.dock +@msg id must be a dimension id id deve essere l'id di una quota +@msg image has no bounds l'immagine non ha limiti +@msg image {_1} is linked already l'immagine {_1} è già collegata +@msg imageMap `{m}`: none, client or server imageMap `{m}`: none, client o server +@msg index out of range indice fuori intervallo +@msg instances must be 1..1000 instances deve essere 1..1000 +@msg invalid `color` `color` non valido +@msg invalid anchor index {v}: expected a non-negative integer indice di punto di ancoraggio non valido {v}: previsto un intero non negativo +@msg invalid anchor reference {v}: expected [subpath, anchor] riferimento a punto di ancoraggio non valido {v}: previsto [subpath, anchor] +@msg invalid angle angolo non valido +@msg invalid brush: {e} pennello non valido: {e} +@msg invalid color colore non valido +@msg invalid colour {c} colore non valido {c} +@msg invalid delta delta non valido +@msg invalid point punto non valido +@msg invalid size dimensione non valida +@msg invalid t t non valido +@msg invalid tolerance tolleranza non valida +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) le sue tavole da disegno si trovano tutte oltre l'area di lavoro (a più di {_1} pt dall'origine) +@msg its clipping masks are nested too deeply le sue maschere di ritaglio sono annidate troppo in profondità +@msg its metadata has no description i suoi metadati non hanno una descrizione +@msg its metadata has no direct length i suoi metadati non hanno una lunghezza diretta +@msg join two different paths unisci due tracciati diversi +@msg justify must be auto|left|center|right|justifyAll justify deve essere auto|left|center|right|justifyAll +@msg kbps must be a positive number, not {kbps} kbps deve essere un numero positivo, non {kbps} +@msg keywords must be a list of words or a comma-separated string keywords deve essere un elenco di parole o una stringa separata da virgole +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters keywords deve avere al massimo {MAX_KEYWORD} caratteri, senza caratteri di controllo +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind deve essere 1, 2 o 3, oppure indica un `name` di predefinito (vedi perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind deve essere graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle o paraStyle +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout deve essere "on", "off", "neutral" o un bool, non {v} +@msg layer `{name}` has no art to export il livello `{name}` non ha grafica da esportare +@msg layer must name a layer or sublayer layer deve indicare un livello o un sottolivello +@msg layers can't be intertwined: select the objects in them i livelli non possono essere intrecciati: seleziona gli oggetti al loro interno +@msg layers go only at the top level or in layers, objects in layers and groups i livelli vanno solo al primo livello o in altri livelli, gli oggetti in livelli e gruppi +@msg layers must be true or false layers deve essere true o false +@msg layout must be gridByRow, gridByColumn, row or column layout deve essere gridByRow, gridByColumn, row o column +@msg leading must be a number or "auto" leading deve essere un numero o "auto" +@msg left/right must be non-negative widths left/right devono essere larghezze non negative +@msg level `{t}`: 2 or 3 livello `{t}`: 2 o 3 +@msg line {line}: more than {MAX_CSV_COLS} columns riga {line}: più di {MAX_CSV_COLS} colonne +@msg line {line}: unclosed quote riga {line}: virgolette non chiuse +@msg line {line}: value for `{var}` must match its variable kind riga {line}: il valore per `{var}` deve corrispondere al tipo della sua variabile +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`: lf o crlf +@msg loading profiles from disk isn't available in the browser il caricamento dei profili dal disco non è disponibile nel browser +@msg margin: a {m} pt margin leaves no imageable area on the paper margin: un margine di {m} pt non lascia alcuna area stampabile sulla carta +@msg matrix must be finite and invertible, depth finite matrix deve essere finita e invertibile, depth finito +@msg matte: {e} matte: {e} +@msg member is for order front, forward, backward or back member è per order front, forward, backward o back +@msg missing `artboards` `artboards` mancante +@msg missing `at` [x, y] `at` [x, y] mancante +@msg missing `attrs` `attrs` mancante +@msg missing `color` or `opacity` `color` o `opacity` mancante +@msg missing `colors` `colors` mancante +@msg missing `commands` `commands` mancante +@msg missing `destinationFolder` `destinationFolder` mancante +@msg missing `effect` id id `effect` mancante +@msg missing `family` `family` mancante +@msg missing `find` `find` mancante +@msg missing `gid` (a glyph id, or null for the default glyph) `gid` mancante (un id di glifo, o null per il glifo predefinito) +@msg missing `group` `group` mancante +@msg missing `id` `id` mancante +@msg missing `id`: give one, or select one type object `id` mancante: indicane uno, o seleziona un oggetto di testo +@msg missing `index` (a point index, or null to clear) `index` mancante (un indice di punto, o null per cancellare) +@msg missing `index` (a stop index, or null to clear) `index` mancante (un indice di interruzione, o null per cancellare) +@msg missing `index` (an item index or null) `index` mancante (un indice di voce o null) +@msg missing `item` `item` mancante +@msg missing `library` `library` mancante +@msg missing `library` (see graphicStyle.libraries) `library` mancante (vedi graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) `library` mancante (vedi swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) `map` mancante (righe [{from, to}] o {current}) +@msg missing `matrix` [a,b,c,d,e,f] `matrix` [a,b,c,d,e,f] mancante +@msg missing `name` (see gradient.presets.list) `name` mancante (vedi gradient.presets.list) +@msg missing `offset` (a distance in points) `offset` mancante (una distanza in punti) +@msg missing `path` `path` mancante +@msg missing `path` (a path object id) `path` mancante (l'id di un oggetto tracciato) +@msg missing `rule` `rule` mancante +@msg missing `source` id id `source` mancante +@msg missing `start` (the byte offset of a character) `start` mancante (l'offset in byte di un carattere) +@msg missing `status`: linked, embedded, missing or modified `status` mancante: linked, embedded, missing o modified +@msg missing `target` id id `target` mancante +@msg missing `text` `text` mancante +@msg missing `to` `to` mancante +@msg missing `to` (an index) `to` mancante (un indice) +@msg missing `to`: a non-negative integer `to` mancante: un intero non negativo +@msg missing `type` `type` mancante +@msg missing `value` `value` mancante +@msg missing `{key}` `{key}` mancante +@msg missing `{k}` `{k}` mancante +@msg missing anchor punto di ancoraggio mancante +@msg missing anchors punti di ancoraggio mancanti +@msg missing anchors [[subpath, anchor]…] anchors mancanti [[subpath, anchor]…] +@msg missing array `{key}` array `{key}` mancante +@msg missing case case mancante +@msg missing color colore mancante +@msg missing commands comandi mancanti +@msg missing destination folder cartella di destinazione mancante +@msg missing from from mancante +@msg missing group gruppo mancante +@msg missing id or ids id o ids mancante +@msg missing ids ids mancante +@msg missing indexes indici mancanti +@msg missing matrix [a,b,c,d,e,f] matrice [a,b,c,d,e,f] mancante +@msg missing name nome mancante +@msg missing op op mancante +@msg missing or invalid `color` `color` mancante o non valido +@msg missing or unknown `tool` `tool` mancante o sconosciuto +@msg missing point punto mancante +@msg missing point [x,y] punto [x,y] mancante +@msg missing point `{key}` ([x, y]) punto `{key}` mancante ([x, y]) +@msg missing points punti mancanti +@msg missing points [[x,y]…] punti mancanti [[x,y]…] +@msg missing segment segmento mancante +@msg missing stops interruzioni mancanti +@msg missing subpaths sottotracciati mancanti +@msg missing target destinazione mancante +@msg missing to to mancante +@msg missing x, y x, y mancanti +@msg mode is set, add or toggle, not `{other}` mode è set, add o toggle, non `{other}` +@msg mode must be "cmyk" or "rgb" mode deve essere "cmyk" o "rgb" +@msg mode must be area|onPath, got {o} mode deve essere area|onPath, ricevuto {o} +@msg more than 10000 linked files: package dependencies in smaller parts più di 10000 file collegati: crea il pacchetto delle dipendenze in parti più piccole +@msg more than 256 commands più di 256 comandi +@msg more than {MAX_BATCH_FILES} data sets più di {MAX_BATCH_FILES} set di dati +@msg more than {MAX_BATCH_FILES} files più di {MAX_BATCH_FILES} file +@msg more than {MAX_CSV_ROWS} data rows più di {MAX_CSV_ROWS} righe di dati +@msg more than {MAX_FILES} images più di {MAX_FILES} immagini +@msg more than {MAX_IMPORT} presets più di {MAX_IMPORT} predefiniti +@msg moved must be [[x,y]…] moved deve essere [[x,y]…] +@msg name (a toolbar's name) is required name (il nome di una barra degli strumenti) è obbligatorio +@msg name (a toolbar's name) must be a string name (il nome di una barra degli strumenti) deve essere una stringa +@msg name must be a toolbar's name name deve essere il nome di una barra degli strumenti +@msg names is empty names è vuoto +@msg names must be an array of symbol names names deve essere un array di nomi di simboli +@msg need `anchor` or `segment` + `t` serve `anchor` o `segment` + `t` +@msg need at least one anchor serve almeno un punto di ancoraggio +@msg need at least one point serve almeno un punto +@msg need at least two points servono almeno due punti +@msg needs points richiede dei punti +@msg nineSlice must be true or false nineSlice deve essere true o false +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides deve essere [left, top, right, bottom], ciascuno -16..16, con left + right < 1 e top + bottom < 1 +@msg no PDF preset named `{n}` nessun predefinito PDF chiamato `{n}` +@msg no action set is current nessun set di azioni è corrente +@msg no active data set nessun set di dati attivo +@msg no appearance item {index} nessuna voce dell'aspetto {index} +@msg no artboard {_1} nessuna tavola da disegno {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) nessuna tavola da disegno {artboard} (a partire da 0; il documento ne ha {_1}) +@msg no asset {id} nessuna risorsa {id} +@msg no asset {id} (see assets.list) nessuna risorsa {id} (vedi assets.list) +@msg no assets to export nessuna risorsa da esportare +@msg no assets yet: collect art for export first (assets.add) ancora nessuna risorsa: raccogli prima la grafica da esportare (assets.add) +@msg no brush named `{n}` nessun pennello chiamato `{n}` +@msg no character at byte {start} nessun carattere al byte {start} +@msg no colour nessun colore +@msg no colour group `{group}` nessun gruppo di colori `{group}` +@msg no colour group `{name}` nessun gruppo di colori `{name}` +@msg no data sets nessun set di dati +@msg no dataset named `{from}` nessun set di dati chiamato `{from}` +@msg no dataset named `{name}` nessun set di dati chiamato `{name}` +@msg no datasets nessun set di dati +@msg no design named `{name}` nessun design chiamato `{name}` +@msg no design named `{n}` nessun design chiamato `{n}` +@msg no design named `{old}` nessun design chiamato `{old}` +@msg no encoder for {_1} yet ancora nessun codificatore per {_1} +@msg no export preset `{name}` (see document.exportPresets.list) nessun predefinito di esportazione `{name}` (vedi document.exportPresets.list) +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder nessuna cartella: passa `path` o imposta Preferenze › Plug-in & Dischi di memoria virtuale › Cartella plug-in aggiuntivi +@msg no fonts to look for: they are all available nessun font da cercare: sono tutti disponibili +@msg no formats to export nessun formato da esportare +@msg no gradient preset `{name}` (see gradient.presets.list) nessun predefinito di sfumatura `{name}` (vedi gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` nessuno stile grafico `{n}` in `{_1}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) nessuna libreria di stili grafici `{key}` (vedi graphicStyle.libraries) +@msg no guide selected nessuna guida selezionata +@msg no guide selected (or give an index) nessuna guida selezionata (o indica un indice) +@msg no guide {i} nessuna guida {i} +@msg no images in the folder nessuna immagine nella cartella +@msg no item at index {_1} nessun elemento all'indice {_1} +@msg no item at index {from} nessun elemento all'indice {from} +@msg no item at one of the indices {idx} nessun elemento in uno degli indici {idx} +@msg no layer nessun livello +@msg no layer {id} (see layer.list) nessun livello {id} (vedi layer.list) +@msg no layers to export nessun livello da esportare +@msg no library `{key}` nessuna libreria `{key}` +@msg no metafile format nessun formato metafile +@msg no object {_1} nessun oggetto {_1} +@msg no objects nessun oggetto +@msg no parent layer nessun livello padre +@msg no perspective grid preset named `{b}` (see perspective.presets.list) nessun predefinito di griglia prospettica chiamato `{b}` (vedi perspective.presets.list) +@msg no perspective grid preset named `{n}` nessun predefinito di griglia prospettica chiamato `{n}` +@msg no point selected (give `index`) nessun punto selezionato (indica `index`) +@msg no preset called `{from}` (see file.newPresets) nessun predefinito chiamato `{from}` (vedi file.newPresets) +@msg no preset called `{name}` (see file.newPresets) nessun predefinito chiamato `{name}` (vedi file.newPresets) +@msg no preset named `{n}` nessun predefinito chiamato `{n}` +@msg no print preset named `{b}` (see print.presets.list) nessun predefinito di stampa chiamato `{b}` (vedi print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) nessun predefinito di stampa chiamato `{name}` (predefiniti: {_1}) +@msg no print preset named `{n}` nessun predefinito di stampa chiamato `{n}` +@msg no profile named `{name}` nessun profilo chiamato `{name}` +@msg no readable files in the folder nessun file leggibile nella cartella +@msg no recovery copy `{f}` (see file.recovery.list) nessuna copia di ripristino `{f}` (vedi file.recovery.list) +@msg no rows nessuna riga +@msg no saved PDF preset named `{name}` nessun predefinito PDF salvato chiamato `{name}` +@msg no saved export preset `{name}` nessun predefinito di esportazione salvato `{name}` +@msg no saved gradient preset called {name} nessun predefinito di sfumatura salvato chiamato {name} +@msg no saved gradient presets nessun predefinito di sfumatura salvato +@msg no saved preset called `{name}` nessun predefinito salvato chiamato `{name}` +@msg no saved presets to export (name [Default] in `names` to export it) nessun predefinito salvato da esportare (indica [Default] in `names` per esportarlo) +@msg no saved presets to export (name built-in ones in `names`) nessun predefinito salvato da esportare (indica quelli incorporati in `names`) +@msg no saved print preset named `{name}` nessun predefinito di stampa salvato chiamato `{name}` +@msg no saved profile called {name} nessun profilo salvato chiamato {name} +@msg no saved selection `{name}` nessuna selezione salvata `{name}` +@msg no saved theme called {name} (see colorTheme.list) nessun tema salvato chiamato {name} (vedi colorTheme.list) +@msg no saved views nessuna vista salvata +@msg no series {i} nessuna serie {i} +@msg no slice {n} in the image (see slice.list) nessuna sezione {n} nell'immagine (vedi slice.list) +@msg no source art nessuna grafica di origine +@msg no stop {i} (the gradient has {n}) nessuna interruzione {i} (la sfumatura ne ha {n}) +@msg no style `{name}` nessuno stile `{name}` +@msg no such assets (see assets.list) risorse inesistenti (vedi assets.list) +@msg no such frame anchor, or the frame would reach past the canvas punto di ancoraggio della cornice inesistente, oppure la cornice supererebbe l'area di lavoro +@msg no such objects oggetti inesistenti +@msg no such path anchor, or the path would reach past the canvas punto di ancoraggio del tracciato inesistente, oppure il tracciato supererebbe l'area di lavoro +@msg no such rows righe inesistenti +@msg no swatch `{keep}` nessun campione `{keep}` +@msg no swatch `{n}` nessun campione `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) nessun campione `{n}` (campioni e gruppi di colori si spostano separatamente) +@msg no swatch library `{key}` (see swatch.library.list) nessuna libreria campioni `{key}` (vedi swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) nessuna libreria campioni `{key}` (vedi swatch.library.list; "document": i campioni del documento) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) nessuna libreria campioni `{key}` (vedi swatch.library.list; "{DOCUMENT_SWATCHES}": i campioni del documento) +@msg no swatch named `{n}` nessun campione chiamato `{n}` +@msg no swatch or colour group `{n}` nessun campione o gruppo di colori `{n}` +@msg no swatch or colour group `{n}` in `{_1}` nessun campione o gruppo di colori `{n}` in `{_1}` +@msg no symbol named `{name}` nessun simbolo chiamato `{name}` +@msg no text objects selected nessun oggetto di testo selezionato +@msg no toolbar named `{_1}` nessuna barra degli strumenti chiamata `{_1}` +@msg no toolbar named `{name}` nessuna barra degli strumenti chiamata `{name}` +@msg no tracing preset `{preset}` (see imageTrace.presets) nessun predefinito di ricalco `{preset}` (vedi imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) nessuna cartella delle librerie utente qui (salva con un percorso, o senza per ottenere i dati) +@msg no variable named `{from}` nessuna variabile chiamata `{from}` +@msg no variable named `{name}` nessuna variabile chiamata `{name}` +@msg no variable named `{var}` nessuna variabile chiamata `{var}` +@msg no variable named `{v}` nessuna variabile chiamata `{v}` +@msg no view named `{name}` nessuna vista chiamata `{name}` +@msg node {_1} is not a graph il nodo {_1} non è un grafico +@msg node {_1} is not an image il nodo {_1} non è un'immagine +@msg node {_1} is not type il nodo {_1} non è testo +@msg not a PDF presets file (format `{_1}`) non è un file di predefiniti PDF (formato `{_1}`) +@msg not a PDF presets file: {e} non è un file di predefiniti PDF: {e} +@msg not a Word document: its WordDocument stream doesn't start with a file information block non è un documento Word: il suo stream WordDocument non inizia con un blocco di informazioni sul file +@msg not a Word document: its compound file has no WordDocument stream non è un documento Word: il suo file composto non ha uno stream WordDocument +@msg not a Word document: its main part is not a WordprocessingML document non è un documento Word: la sua parte principale non è un documento WordprocessingML +@msg not a Word document: its package has no main document part non è un documento Word: il suo pacchetto non ha una parte di documento principale +@msg not a closed path non è un tracciato chiuso +@msg not a flattener presets file (format `{_1}`) non è un file di predefiniti di appiattimento (formato `{_1}`) +@msg not a flattener presets file: {e} non è un file di predefiniti di appiattimento: {e} +@msg not a layer or group non è un livello o un gruppo +@msg not a library file: {e} non è un file di libreria: {e} +@msg not a mesh envelope point non è un punto di un involucro a trama +@msg not a perspective grid presets file (format `{_1}`) non è un file di predefiniti di griglia prospettica (formato `{_1}`) +@msg not a perspective grid presets file: {e} non è un file di predefiniti di griglia prospettica: {e} +@msg not a print presets file (format `{_1}`) non è un file di predefiniti di stampa (formato `{_1}`) +@msg not a print presets file: {e} non è un file di predefiniti di stampa: {e} +@msg not a stored or deflated central record non è un record centrale archiviato o compresso con deflate +@msg not a valid SVGZ file: {e} non è un file SVGZ valido: {e} +@msg not an RTF file: it doesn't start with {\\rtf non è un file RTF: non inizia con {\\rtf +@msg not an image VectorCraft reads (see document.formats) non è un'immagine che VectorCraft legge (vedi document.formats) +@msg not area type (text in a frame) outside perspective non è testo in area (testo in una cornice) fuori dalla prospettiva +@msg not editing an opacity mask non stai modificando una maschera di opacità +@msg not type on a path outside perspective non è testo su tracciato fuori dalla prospettiva +@msg nothing prints non viene stampato nulla +@msg nothing to bind niente da collegare +@msg nothing to change niente da modificare +@msg nothing to change: give dx, dy, hScale, vScale or rotation niente da modificare: indica dx, dy, hScale, vScale o rotation +@msg nothing to crop niente da ritagliare +@msg nothing to export niente da esportare +@msg nothing to export: the document has no visible art niente da esportare: il documento non ha grafica visibile +@msg nothing to mark niente da contrassegnare +@msg nothing to print: every artboard picked is blank niente da stampare: tutte le tavole da disegno scelte sono vuote +@msg nothing to print: the document has no art that prints niente da stampare: il documento non ha grafica stampabile +@msg object is not a path l'oggetto non è un tracciato +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object la trasparenza dell'oggetto non può essere campionata sui caratteri evidenziati; disattiva Trasparenza in Opzioni contagocce o seleziona l'intero oggetto +@msg object {_1} can't be set as point type l'oggetto {_1} non può essere impostato come testo con punto +@msg object {_1} has lost its file's contents: relink it l'oggetto {_1} ha perso il contenuto del suo file: ricollegalo +@msg object {_1} has no path l'oggetto {_1} non ha un tracciato +@msg object {_1} has no size l'oggetto {_1} non ha dimensioni +@msg object {_1} is hidden or locked, or on a locked artboard l'oggetto {_1} è nascosto o bloccato, o si trova su una tavola da disegno bloccata +@msg object {_1} is not a placed document l'oggetto {_1} non è un documento inserito +@msg object {_1} is not an Intertwine group l'oggetto {_1} non è un gruppo Intreccia +@msg object {_1} is not an image l'oggetto {_1} non è un'immagine +@msg object {_1} is not an image or a linked file l'oggetto {_1} non è un'immagine o un file collegato +@msg object {_1} is not an image or a placed document l'oggetto {_1} non è un'immagine o un documento inserito +@msg object {_1} is not in this Intertwine group l'oggetto {_1} non è in questo gruppo Intreccia +@msg object {_1} needs a 3D effect at index {index} l'oggetto {_1} richiede un effetto 3D all'indice {index} +@msg object {id} has anchors 0–{_1} for `corners`, not {past} l'oggetto {id} ha i punti di ancoraggio 0–{_1} per `corners`, non {past} +@msg object {id} has {n} anchors, so no anchor {past} l'oggetto {id} ha {n} punti di ancoraggio, quindi nessun punto di ancoraggio {past} +@msg objects must be none, move or copy objects deve essere none, move o copy +@msg objects were left out alcuni oggetti sono stati esclusi +@msg offset out of range offset fuori intervallo +@msg on must be true or false on deve essere true o false +@msg only groups, layers, type and images have a Contents (Characters) row solo gruppi, livelli, testo e immagini hanno una riga Contenuto (Caratteri) +@msg only interior mesh lines can be deleted solo le linee interne della trama possono essere eliminate +@msg only solid colours can be added (not gradients or patterns) solo i colori pieni possono essere aggiunti (non sfumature o motivi) +@msg only solid-colour swatches can be spot colours solo i campioni di colore pieno possono essere colori spot +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first è stato possibile leggere solo l'anteprima incorporata di questo file Affinity: usa File › Apri per vederla con il relativo avviso, oppure esporta prima un SVG o un PDF da Affinity +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning è stato possibile leggere solo l'anteprima incorporata di questo file Affinity; usa File › Apri per vederla con il relativo avviso +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon e una posizione in colonna appartengono a window.panel.float e window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto, above, below, icon e una posizione in colonna non accettano anchor, side, zone o before +@msg onto, above, below, icon and a column place take no x, y, width or height onto, above, below, icon e una posizione in colonna non accettano x, y, width o height +@msg open a document to look for its linked files apri un documento per cercarne i file collegati +@msg options must be an object (see file.formatOptions) options deve essere un oggetto (vedi file.formatOptions) +@msg order must be leftToRight or rightToLeft order deve essere leftToRight o rightToLeft +@msg orientation must be portrait or landscape, not `{o}` orientation deve essere portrait o landscape, non `{o}` +@msg outlineText {v}: true or false outlineText {v}: true o false +@msg output intent profile: {e} profilo dell'intento di output: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: nessun profilo RGB o CMYK si chiama “{name}” +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: i file {_1} sono CMYK, e “{name}” è un profilo RGB +@msg output.destination: {e} output.destination: {e} +@msg output.inks: no ink is set to print output.inks: nessun inchiostro è impostato per la stampa +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: nessun profilo si chiama “{name}”, e un intento di output {std} incorpora il suo profilo +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent: nessun profilo si chiama “{name}”: un intento di output {std} incorpora un profilo, o indica una condizione di stampa registrata (outputConditionId e registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: i file {std} si stampano in CMYK, e “{name}” è un profilo RGB +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: “{name}” è un profilo in scala di grigi, che non può essere un intento di output {std} +@msg overlap: {o} pt is half a tile or more overlap: {o} pt è metà di un riquadro o più +@msg overlapping Shaper paths must be in the same layer or group i tracciati Shaper sovrapposti devono essere nello stesso livello o gruppo +@msg overlapping edits modifiche sovrapposte +@msg page art conflicts with image data for {key} la grafica della pagina è in conflitto con i dati dell'immagine per {key} +@msg page art conflicts with pattern {name} la grafica della pagina è in conflitto con il motivo {name} +@msg page art conflicts with swatch {name} la grafica della pagina è in conflitto con il campione {name} +@msg page art is missing image data for {key} nella grafica della pagina mancano i dati dell'immagine per {key} +@msg page art is missing pattern {name} nella grafica della pagina manca il motivo {name} +@msg page art is missing swatch {name} nella grafica della pagina manca il campione {name} +@msg page must be a whole number from 1 page deve essere un numero intero a partire da 1 +@msg page thumbnail: {e} miniatura della pagina: {e} +@msg page {page} of the PDF has no objects la pagina {page} del PDF non ha oggetti +@msg page {page}: `{file}` has {n} {artboards} pagina {page}: `{file}` ha {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} pagina {page}: `{name}` ha {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages deve essere un intervallo come "2-3" o un numero di pagina, non {v} +@msg pages: {e} pagine: {e} +@msg panel position must be finite la posizione del pannello deve essere finita +@msg parameters must be an object i parametri devono essere un oggetto +@msg params must be an object params deve essere un oggetto +@msg passwords can have up to {MAX_PASSWORD} bytes le password possono avere fino a {MAX_PASSWORD} byte +@msg path has no open end il tracciato non ha un'estremità aperta +@msg paths must be strings paths deve contenere stringhe +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false richiede che ogni tavola da disegno conservi la grafica modificabile +@msg pdfs `{m}`: single or multiple pdfs `{m}`: single o multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom è "artwork" o "swatches", non {v} +@msg pins and moved must be non-empty lists of the same length pins e moved devono essere elenchi non vuoti della stessa lunghezza +@msg pins must be [[x,y]…] pins deve essere [[x,y]…] +@msg pins must be finite pins deve essere finito +@msg place is above, below or inside, not `{other}` place è above, below o inside, non `{other}` +@msg plane must be left|right|ground plane deve essere left|right|ground +@msg plane must be left|right|ground|none plane deve essere left|right|ground|none +@msg point is outside the mesh il punto è fuori dalla trama +@msg points must be [t, left, right] with non-negative widths points deve essere [t, left, right] con larghezze non negative +@msg points must be a non-empty [[x,y,pressure?]…] list points deve essere un elenco [[x,y,pressure?]…] non vuoto +@msg points must be a non-empty list of finite [x,y] points deve essere un elenco non vuoto di [x,y] finiti +@msg points must be an array or null points deve essere un array o null +@msg points must be finite points deve essere finito +@msg points must be finite [x,y] pairs points deve contenere coppie [x,y] finite +@msg points must contain 1–4096 coordinates points deve contenere 1–4096 coordinate +@msg position must be normal, superscript or subscript position deve essere normal, superscript o subscript +@msg position must be topLeft, topRight, bottomLeft or bottomRight position deve essere topLeft, topRight, bottomLeft o bottomRight +@msg positions must be between 0 and 100000 pt le posizioni devono essere comprese tra 0 e 100000 pt +@msg ppi must be a positive number, not {ppi} ppi deve essere un numero positivo, non {ppi} +@msg ppi must be between 1 and 2400 ppi deve essere compreso tra 1 e 2400 +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` non è transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape deve essere clippingMask|transparency +@msg preset `{id}`: mobile or density preset `{id}`: mobile o density +@msg preset must be standard, diffuse, topLeft or right preset deve essere standard, diffuse, topLeft o right +@msg preset must be the name of a print preset preset deve essere il nome di un predefinito di stampa +@msg previewMode must be default, pixel or overprint previewMode deve essere default, pixel o overprint +@msg print settings: {e} impostazioni di stampa: {e} +@msg procedures nest too deeply le procedure sono annidate troppo in profondità +@msg procedures nested too deeply procedure annidate troppo in profondità +@msg quotes must be {double?, single?} quotes deve essere {double?, single?} +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} deve essere una virgoletta di apertura e una di chiusura, ad es. "“”" +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi deve essere 1–2400 (72 schermo, 150 media, 300 alta) +@msg rating must be a whole number from 0 to {MAX_RATING} rating deve essere un numero intero compreso tra 0 e {MAX_RATING} +@msg rect and {k} contradict each other: give the area or the artboards rect e {k} si contraddicono: indica l'area o le tavole da disegno +@msg rect applies to raster formats, not {_1} rect si applica ai formati raster, non a {_1} +@msg rect is [x, y, width, height] in points rect è [x, y, width, height] in punti +@msg rect must be [x, y, width, height] rect deve essere [x, y, width, height] +@msg rect must be [x, y, width, height] with a positive width and height rect deve essere [x, y, width, height] con larghezza e altezza positive +@msg rect needs finite numbers and a positive width and height rect richiede numeri finiti e larghezza e altezza positive +@msg redraw needs id redraw richiede id +@msg redraw within must be a finite distance of 0 or more within di redraw deve essere una distanza finita pari a 0 o maggiore +@msg registration must be 0..8 registration deve essere 0..8 +@msg release a layer or a group rilascia un livello o un gruppo +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace mantiene la posizione e la trasformazione dell'oggetto sostituito: rimuovi template, at e rect +@msg replace: select the one object to replace replace: seleziona l'unico oggetto da sostituire +@msg resolution must be a number or screen|medium|high resolution deve essere un numero o screen|medium|high +@msg resolution must be between 1 and 2400 ppi resolution deve essere compreso tra 1 e 2400 ppi +@msg rotation must be a number of degrees rotation deve essere un numero di gradi +@msg rotation must be finite rotation deve essere finito +@msg rows and cols must be 1..50 rows e cols devono essere 1..50 +@msg rows and columns must be 1..500 rows e columns devono essere 1..500 +@msg rows of objects whose appearances differ can't be reordered together le righe di oggetti con aspetti diversi non possono essere riordinate insieme +@msg save the document first: Package collects a saved document salva prima il documento: Crea pacchetto raccoglie un documento salvato +@msg save the document first: copy puts the file in its folder salva prima il documento: copy mette il file nella sua cartella +@msg savedPreset: {e} savedPreset: {e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`: un fattore come 2x, una larghezza (100w), un'altezza (100h) o una risoluzione (72ppi) +@msg scale must be a number above 0, at most 64 scale deve essere un numero maggiore di 0, al massimo 64 +@msg scale must be non-zero scale deve essere diverso da zero +@msg scope must be selection or all (got {other}) scope deve essere selection o all (ricevuto {other}) +@msg scribble is too long lo scarabocchio è troppo lungo +@msg segment does not exist il segmento non esiste +@msg select a Shaper group seleziona un gruppo Shaper +@msg select a blend to set easing or color shift seleziona una fusione per impostare l'andamento o lo spostamento del colore +@msg select a compound shape seleziona una forma composta +@msg select a graph seleziona un grafico +@msg select a group or layer with visible, unlocked members seleziona un gruppo o un livello con membri visibili e sbloccati +@msg select a layer seleziona un livello +@msg select a text object seleziona un oggetto di testo +@msg select an image (or an Image Trace group) seleziona un'immagine (o un gruppo di Ricalco immagine) +@msg select an image, or pass id seleziona un'immagine, o passa id +@msg select an object (or pass `id`) seleziona un oggetto (o passa `id`) +@msg select an object with a freeform gradient seleziona un oggetto con una sfumatura a forma libera +@msg select an object with an opacity mask seleziona un oggetto con una maschera di opacità +@msg select area type seleziona un testo in area +@msg select area type (text in a frame) seleziona un testo in area (testo in una cornice) +@msg select area type and closed paths (at least one area type object) seleziona testo in area e tracciati chiusi (almeno un oggetto di testo in area) +@msg select area type whose first line can fit its frame seleziona un testo in area la cui prima riga può entrare nella sua cornice +@msg select artwork to add its colours seleziona la grafica di cui aggiungere i colori +@msg select artwork to make a group of its colours seleziona la grafica con i cui colori creare un gruppo +@msg select artwork to recolour, or give a colour `group` seleziona la grafica da ricolorare, o indica un `group` di colori +@msg select exactly two path ends to join seleziona esattamente due estremità di tracciato da unire +@msg select members of a compound shape seleziona i membri di una forma composta +@msg select objects or give ids seleziona degli oggetti o indica gli ids +@msg select objects to align to the guide seleziona gli oggetti da allineare alla guida +@msg select one Intertwine group seleziona un gruppo Intreccia +@msg select one image or pass id seleziona un'immagine o passa id +@msg select paths seleziona dei tracciati +@msg select paths or compound paths seleziona tracciati o tracciati composti +@msg select rows or objects to collect seleziona righe o oggetti da raccogliere +@msg select something to export seleziona qualcosa da esportare +@msg select something to export (or turn off selectedOnly) seleziona qualcosa da esportare (o disattiva selectedOnly) +@msg select text to redefine the style from seleziona il testo da cui ridefinire lo stile +@msg select the art and, on top of it, the mask object seleziona la grafica e, sopra di essa, l'oggetto maschera +@msg select the art for the design seleziona la grafica per il design +@msg select the art for the pattern tile seleziona la grafica per la porzione del motivo +@msg select the art to add first seleziona prima la grafica da aggiungere +@msg select the art to collect (or give its ids) seleziona la grafica da raccogliere (o indica i suoi ids) +@msg select the content and a path on top seleziona il contenuto e un tracciato sopra di esso +@msg select the images to relink, or pass ids seleziona le immagini da ricollegare, o passa ids +@msg select the images, or pass ids seleziona le immagini, o passa ids +@msg select the linked images or placed documents, or pass ids seleziona le immagini collegate o i documenti inseriti, o passa ids +@msg select the object to copy from seleziona l'oggetto da cui copiare +@msg select the objects (or give their ids) seleziona gli oggetti (o indica i loro ids) +@msg select the objects to attach to the path seleziona gli oggetti da collegare al tracciato +@msg select the objects to bind seleziona gli oggetti da collegare +@msg select two or more objects to intertwine seleziona due o più oggetti da intrecciare +@msg select two or more slices to combine seleziona due o più sezioni da combinare +@msg select type on a path seleziona un testo su tracciato +@msg selection `{m}`: one or each selection `{m}`: one o each +@msg settings is an object of document.exportForScreens params settings è un oggetto di parametri di document.exportForScreens +@msg settings must be an object settings deve essere un oggetto +@msg show `{show}`: all, missing, modified or embedded show `{show}`: all, missing, modified o embedded +@msg show must be true or false show deve essere true o false +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size deve essere "small", "medium", "large" o un numero di punti maggiore di 0 fino a {_1} +@msg size must be positive size deve essere positivo +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode è "fixed" o "pressure", non `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode è "fixed" o "pressure", non {other} +@msg sizeVariation must be 0 or more sizeVariation deve essere 0 o maggiore +@msg smallCaps must be true or false smallCaps deve essere true o false +@msg sort `{x}`: name, kind or status sort `{x}`: name, kind o status +@msg source paths are too large for Shaper i tracciati di origine sono troppo grandi per Shaper +@msg spot, mode and group apply to solid colours only spot, mode e group si applicano solo ai colori pieni +@msg status `{s}`: linked, embedded, missing or modified status `{s}`: linked, embedded, missing o modified +@msg steps must be 1..1000 steps deve essere 1..1000 +@msg steps must be 1..{MAX_STEPS} steps deve essere 1..{MAX_STEPS} +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) interruzione {i}: `plates` deve essere quattro numeri compresi tra 0 e 1 (C, M, Y, K) +@msg stopped before the archive was finished interrotto prima del completamento dell'archivio +@msg strokeMode must be within|along|across, got {m} strokeMode deve essere within|along|across, ricevuto {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions deve essere un oggetto di opzioni di stroke.set +@msg strokeWidth must be a positive number of pixels strokeWidth deve essere un numero positivo di pixel +@msg subpath {si} is not an open subpath il sottotracciato {si} non è un sottotracciato aperto +@msg subpaths required when joining the same path subpaths è obbligatorio quando si unisce lo stesso tracciato +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict deve essere "merge" o "add", non {v} +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text deve essere {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} +@msg text transform is collapsed or unrepresentable la trasformazione del testo è degenere o non rappresentabile +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces deve essere un numero intero compreso tra 2 e 100 +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`: usa "{_1}" o "{_2}" +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: indica {path} o {name, dataBase64} di un file immagine, o una texture come la restituisce effect.list +@msg textureImage: {e} textureImage: {e} +@msg that makes more than {MAX_PIECES} slices questo produce più di {MAX_PIECES} sezioni +@msg that object is not in the overlap quell'oggetto non è nella sovrapposizione +@msg the Affinity document has no page with a size il documento Affinity non ha una pagina con una dimensione +@msg the CMYK image couldn't be written impossibile scrivere l'immagine CMYK +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected le impostazioni di sicurezza PDF sono state salvate senza le relative password: indica di nuovo openPassword o permissionsPassword, oppure security senza passwordRequired per esportare senza protezione +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) il file RTF ha troppi paragrafi o cambi di formattazione per essere inserito come testo (al massimo {MAX_PARAGRAPHS} paragrafi e {MAX_RUNS} sequenze) +@msg the RTF file is too large to place as type (at most {_1} MB) il file RTF è troppo grande per essere inserito come testo (al massimo {_1} MB) +@msg the SVG expands to more than {0} nested SVG images l'SVG si espande in più di {0} immagini SVG annidate +@msg the SVG expands to more than {0} objects l'SVG si espande in più di {0} oggetti +@msg the SVG has no drawable objects l'SVG non ha oggetti disegnabili +@msg the SVGZ file unpacks to more than {_1} MB il file SVGZ si decomprime in più di {_1} MB +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place il documento Word ha più di {MAX_PAGES} pagine di proprietà, troppe per essere inserito +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place il documento Word ha più di {MAX_RUNS} sequenze di proprietà di carattere o di paragrafo, troppe per essere inserito +@msg the Word document has more than {_1} MB of formatting properties, too many to place il documento Word ha più di {_1} MB di proprietà di formattazione, troppe per essere inserito +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) il documento Word ha troppo testo o troppa formattazione per essere inserito come testo (al massimo {MAX_PARAGRAPHS} paragrafi, {MAX_RUNS} sequenze e {_1} MB di testo) +@msg the Word document has too much text to place as type (at most {_1} MB) il documento Word ha troppo testo per essere inserito come testo (al massimo {_1} MB) +@msg the Word document is encrypted or protected with a password; save it without one to place it il documento Word è crittografato o protetto da password; salvalo senza password per inserirlo +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) il documento Word è troppo grande per essere inserito (il suo stream {name} contiene più di {_1} MB) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off il documento Word è stato salvato l'ultima volta con il salvataggio rapido, che VectorCraft non legge; salvalo di nuovo con il salvataggio rapido disattivato +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document il documento Word è stato salvato da Word 6.0 o Word 95, che VectorCraft non legge; salvalo come documento Word 97-2003 +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place il testo del documento Word è composto da più di {MAX_PIECES} parti, troppe per essere inserito +@msg the action set ids are not a list gli id dei set di azioni non sono un elenco +@msg the active paint is not a gradient la pittura attiva non è una sfumatura +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size le tavole da disegno non entrano nell'area di lavoro: usa meno tavole da disegno, una spaziatura minore o una dimensione più piccola +@msg the character's size can't be measured impossibile misurare la dimensione del carattere +@msg the compound shape is too complex to expand la forma composta è troppo complessa da espandere +@msg the container of the listed objects is gone il contenitore degli oggetti elencati non esiste più +@msg the copy `{user_id}` can't be read as a library la copia `{user_id}` non può essere letta come libreria +@msg the copy would leave the canvas la copia uscirebbe dall'area di lavoro +@msg the cut cannot be made on this art: {e} impossibile eseguire il taglio su questa grafica: {e} +@msg the data isn't an EMF or WMF picture i dati non sono un'immagine EMF o WMF +@msg the data isn't an EPS or PostScript picture i dati non sono un'immagine EPS o PostScript +@msg the deferred paste is no longer available l'incolla differito non è più disponibile +@msg the design is too big to draw at every data point of this graph il design è troppo grande da disegnare in ogni punto dati di questo grafico +@msg the design is too big to draw in every column of this graph il design è troppo grande da disegnare in ogni colonna di questo grafico +@msg the dialog it was opened over is no longer open la finestra di dialogo sopra la quale è stata aperta non è più aperta +@msg the dictionary stack overflowed lo stack dei dizionari ha superato il limite +@msg the editing data's hash has no place l'hash dei dati di modifica non ha una posizione +@msg the entities the document declares expand to too much text le entità dichiarate dal documento si espandono in troppo testo +@msg the envelope's content has no bounds il contenuto dell'involucro non ha limiti +@msg the envelope's mesh is malformed la trama dell'involucro non è valida +@msg the file holds no `variableSets` element il file non contiene alcun elemento `variableSets` +@msg the file is larger than {_1} MB il file supera {_1} MB +@msg the file is too large il file è troppo grande +@msg the file was updated or encrypted already il file è già stato aggiornato o crittografato +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) il primo carattere di una riga non ha un carattere precedente con cui applicare la crenatura: si sposta solo in alto e in basso (dy) +@msg the font {_1} is not available il font {_1} non è disponibile +@msg the format has no file extension il formato non ha un'estensione di file +@msg the graphic can't be read: {e} impossibile leggere la grafica: {e} +@msg the graphic is empty la grafica è vuota +@msg the graphic's data is damaged i dati della grafica sono danneggiati +@msg the graphic's document has no artboard il documento della grafica non ha tavole da disegno +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them l'intestazione `{cell}` per `{name}` verrebbe riletta come la variabile `{other}`: rinomina una delle due +@msg the image `{path}` isn't followed: {r} l'immagine `{path}` non viene seguita: {r} +@msg the image has no embedded pixels l'immagine non ha pixel incorporati +@msg the image is empty l'immagine è vuota +@msg the image is fully transparent l'immagine è completamente trasparente +@msg the image is not on an artboard; pass rect l'immagine non è su una tavola da disegno; passa rect +@msg the image's pixels are missing mancano i pixel dell'immagine +@msg the image's pixels aren't in the document i pixel dell'immagine non sono nel documento +@msg the image's pixels can't be read: {e} impossibile leggere i pixel dell'immagine: {e} +@msg the image's transparency doesn't fit it la trasparenza dell'immagine non corrisponde all'immagine +@msg the job would print more than {MAX_PAGES} pages il lavoro stamperebbe più di {MAX_PAGES} pagine +@msg the job would print more than {_1} pages il lavoro stamperebbe più di {_1} pagine +@msg the layers hold no art to mask i livelli non contengono grafica da mascherare +@msg the layout moved il layout si è spostato +@msg the library has a group `{name}` already la libreria ha già un gruppo `{name}` +@msg the library has no group `{name}` la libreria non ha alcun gruppo `{name}` +@msg the library has no item `{item}` la libreria non ha alcun elemento `{item}` +@msg the library has no item `{other}` la libreria non ha alcun elemento `{other}` +@msg the library holds {MAX_GROUPS} groups already la libreria contiene già {MAX_GROUPS} gruppi +@msg the library holds {MAX_ITEMS} items of this kind already la libreria contiene già {MAX_ITEMS} elementi di questo tipo +@msg the name is empty il nome è vuoto +@msg the object has no fill, stroke or effect l'oggetto non ha riempimento, traccia o effetto +@msg the object has no graphic style (pass `name`) l'oggetto non ha uno stile grafico (passa `name`) +@msg the objects don't match the plan gli oggetti non corrispondono al piano +@msg the open and permissions passwords must differ le password di apertura e di autorizzazione devono essere diverse +@msg the operand stack overflowed lo stack degli operandi ha superato il limite +@msg the page has no area la pagina non ha area +@msg the page's type in an unread object's box isn't in its page art il testo della pagina nel riquadro di un oggetto non letto non è nella grafica della pagina +@msg the paint behind the proxy is not a gradient la pittura dietro il proxy non è una sfumatura +@msg the parent must be a layer or sublayer il padre deve essere un livello o un sottolivello +@msg the path can't be extended il tracciato non può essere esteso +@msg the path can't be inside one of the objects to attach il tracciato non può trovarsi all'interno di uno degli oggetti da collegare +@msg the path has no segments to measure il tracciato non ha segmenti da misurare +@msg the path is empty il tracciato è vuoto +@msg the path is too short to shorten that much il tracciato è troppo corto per accorciarlo così tanto +@msg the picture has no objects l'immagine non ha oggetti +@msg the program runs too long il programma dura troppo a lungo +@msg the program uses too much memory il programma usa troppa memoria +@msg the rectangle must have a width and a height il rettangolo deve avere una larghezza e un'altezza +@msg the rectangle needs a size and must lie on the canvas il rettangolo richiede una dimensione e deve trovarsi nell'area di lavoro +@msg the selected art has no width la grafica selezionata non ha larghezza +@msg the selection draws nothing la selezione non disegna nulla +@msg the selection has no area la selezione non ha area +@msg the selection has no art to export la selezione non ha grafica da esportare +@msg the selection has no bounds la selezione non ha limiti +@msg the selection has no graphic style la selezione non ha uno stile grafico +@msg the slice would leave the canvas la sezione uscirebbe dall'area di lavoro +@msg the spot colour couldn't be added impossibile aggiungere il colore spot +@msg the subpath has no ends to extend (it is closed or a single point) il sottotracciato non ha estremità da estendere (è chiuso o è un singolo punto) +@msg the symbols are nested too deeply to expand their instances i simboli sono annidati troppo in profondità per espanderne le istanze +@msg the text file is too large to place as type (at most {_1} MB) il file di testo è troppo grande per essere inserito come testo (al massimo {_1} MB) +@msg the text is empty il testo è vuoto +@msg the text is over {_1} MB il testo supera {_1} MB +@msg the top object must be a path l'oggetto in primo piano deve essere un tracciato +@msg the trace was cancelled il ricalco è stato annullato +@msg the trailer has no size il trailer non ha una dimensione +@msg the type object is flattened to nothing l'oggetto di testo viene appiattito nel nulla +@msg the value of axis `{tag}` must be a number or null il valore dell'asse `{tag}` deve essere un numero o null +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets la libreria di variabili dichiara più di {MAX_LIBRARY_DATASETS} set di dati +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities la libreria di variabili dichiara più di {MAX_LIBRARY_ENTITIES} entità +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables la libreria di variabili dichiara più di {MAX_LIBRARY_VARIABLES} variabili +@msg the variable library declares no variables la libreria di variabili non dichiara alcuna variabile +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes la libreria di variabili supera {MAX_LIBRARY_BYTES} byte +@msg the variable library is not UTF-8 la libreria di variabili non è in UTF-8 +@msg the variable library is not well-formed XML: {e} la libreria di variabili non è XML ben formato: {e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep la libreria di variabili annida elementi a più di {MAX_LIBRARY_NESTING} livelli di profondità +@msg the variable library's document type declaration holds something other than entities of plain text la dichiarazione del tipo di documento della libreria di variabili contiene qualcosa di diverso da entità di testo semplice +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes le entità della libreria di variabili si espandono in più di {MAX_LIBRARY_BYTES} byte +@msg the window has no dock la finestra non ha un'area di ancoraggio +@msg the written PDF has no catalog il PDF scritto non ha un catalogo +@msg the written PDF has no cross-reference table il PDF scritto non ha una tabella dei riferimenti incrociati +@msg the written PDF's metadata can't be edited i metadati del PDF scritto non possono essere modificati +@msg there are no colour swatches to draw non ci sono campioni di colore da disegnare +@msg there is already a design named `{name}` esiste già un design chiamato `{name}` +@msg there is already a design named `{to}` esiste già un design chiamato `{to}` +@msg there is already a toolbar named `{name}` esiste già una barra degli strumenti chiamata `{name}` +@msg there is already a toolbar named `{to}` esiste già una barra degli strumenti chiamata `{to}` +@msg there is no library: create one first non c'è alcuna libreria: creane prima una +@msg these paths are too degenerate for Shaper questi tracciati sono troppo degeneri per Shaper +@msg thumbnail: no page {v} (the {what} has {count}) miniatura: nessuna pagina {v} ({what} ne ha {count}) +@msg tileRange of {what}: {e} tileRange di {what}: {e} +@msg title can't be empty title non può essere vuoto +@msg to (the new name) is required to (il nuovo nome) è obbligatorio +@msg to must be fill or stroke to deve essere fill o stroke +@msg to must be smooth or corner to deve essere smooth o corner +@msg too large for a zip archive: package to a folder troppo grande per un archivio zip: crea il pacchetto in una cartella +@msg too many Shaper edits troppe modifiche Shaper +@msg too many `points` (at most 100000) troppi `points` (al massimo 100000) +@msg too many characters: at most {MAX_CHARS} troppi caratteri: al massimo {MAX_CHARS} +@msg too many dots for this size: lower the frequency troppi punti per questa dimensione: riduci la frequenza +@msg too many filters over one file troppi filtri su un solo file +@msg too many overlapping paths for Shaper troppi tracciati sovrapposti per Shaper +@msg too many saved clips troppi ritagli salvati +@msg too many saved graphics states troppi stati grafici salvati +@msg toolbar (a toolbar's name) must be a string toolbar (il nome di una barra degli strumenti) deve essere una stringa +@msg toolbar name can't be empty il nome della barra degli strumenti non può essere vuoto +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}: "flatten" (il valore predefinito) o "omit" +@msg two saved selections would be named `{_1}` due selezioni salvate si chiamerebbero `{_1}` +@msg type dimension cannot be represented with this text transform la dimensione del testo non può essere rappresentata con questa trasformazione del testo +@msg type dimension must be finite la dimensione del testo deve essere finita +@msg unexpected resources risorse impreviste +@msg unknown CMYK profile `{name}` profilo CMYK sconosciuto `{name}` +@msg unknown File Info field `{k}` campo di Info file sconosciuto `{k}` +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) funzione OpenType sconosciuta `{t}` in featureChanges (i tag accettati da `features`) +@msg unknown PDF preset `{name}` (presets: {_1}) predefinito PDF sconosciuto `{name}` (predefiniti: {_1}) +@msg unknown Warp style stile Altera sconosciuto +@msg unknown alignToPath `{a}` alignToPath sconosciuto `{a}` +@msg unknown arrowhead {v} punta di freccia sconosciuta {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) chiave di artboardLayout sconosciuta `{k}` (layout, columns, spacing, rightToLeft) +@msg unknown attribute `{attribute}` attributo sconosciuto `{attribute}` +@msg unknown attribute `{k}` attributo sconosciuto `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) asse sconosciuto `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` metodo di fusione sconosciuto `{b}` +@msg unknown blend mode {b} metodo di fusione sconosciuto {b} +@msg unknown brush type `{o}` tipo di pennello sconosciuto `{o}` +@msg unknown category `{c}` categoria sconosciuta `{c}` +@msg unknown category `{c}` ({_1}) categoria sconosciuta `{c}` ({_1}) +@msg unknown character `{c}` carattere sconosciuto `{c}` +@msg unknown character style number field campo numerico dello stile di carattere sconosciuto +@msg unknown effect `{e}` effetto sconosciuto `{e}` +@msg unknown end {e} estremità sconosciuta {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) flattenerPreset sconosciuto `{name}` (High Resolution, Medium Resolution, Low Resolution o uno salvato: vedi flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) formato sconosciuto `{f}` (vcswatches, gpl, ase o css) +@msg unknown from {f} from sconosciuto {f} +@msg unknown graph type `{t}` tipo di grafico sconosciuto `{t}` +@msg unknown grid colours `{name}` (one of {_1}) colori della griglia sconosciuti `{name}` (uno tra {_1}) +@msg unknown gridType `{t}` gridType sconosciuto `{t}` +@msg unknown highlight `{v}` evidenziazione sconosciuta `{v}` +@msg unknown intent `{i}` intento sconosciuto `{i}` +@msg unknown issue kind {id} tipo di problema sconosciuto {id} +@msg unknown issue kind {v} tipo di problema sconosciuto {v} +@msg unknown knockout state {k} stato di foratura sconosciuto {k} +@msg unknown language `{language}` (one of {_1}) lingua sconosciuta `{language}` (una tra {_1}) +@msg unknown language `{l}` (one of {_1}) lingua sconosciuta `{l}` (una tra {_1}) +@msg unknown method `{m}` ({_1}) metodo sconosciuto `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) metodo sconosciuto `{other}` (gray, rgb, hsb, lab, cmyk o web) +@msg unknown mode `{other}` (mono|cmyk) metodo sconosciuto `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) metodo sconosciuto `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) modalità sconosciuta `{other}` (set, add, subtract) +@msg unknown mode `{o}` modalità sconosciuta `{o}` +@msg unknown mode {m} modalità sconosciuta {m} +@msg unknown model `{other}` modello sconosciuto `{other}` +@msg unknown model `{other}` (keep, new or document) modello sconosciuto `{other}` (keep, new o document) +@msg unknown op {o} op sconosciuto {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back ordine sconosciuto `{o}`: toggle, front, forward, backward o back +@msg unknown overprints `{v}` (preserve, simulate or discard) overprints sconosciuto `{v}` (preserve, simulate o discard) +@msg unknown panel pannello sconosciuto +@msg unknown pieLegend `{v}` (none, standard or wedges) pieLegend sconosciuto `{v}` (none, standard o wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) piePosition sconosciuto `{v}` (even, ratio o stacked) +@msg unknown pieSort `{v}` (none, all or first) pieSort sconosciuto `{v}` (none, all o first) +@msg unknown plate `{n}` lastra sconosciuta `{n}` +@msg unknown preference `{k}` preferenza sconosciuta `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) profilo sconosciuto {id} (vedi stroke.widthProfile.list) +@msg unknown proof target `{t}` destinazione di prova sconosciuta `{t}` +@msg unknown quotes key `{k}` (double, single) chiave di quotes sconosciuta `{k}` (double, single) +@msg unknown resolution `{v}` risoluzione sconosciuta `{v}` +@msg unknown rule `{rule}` ({_1}) regola sconosciuta `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) regola sconosciuta `{r}` (vedi color.harmony) +@msg unknown setting `{k}` impostazione sconosciuta `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) forma sconosciuta `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) spaziatura sconosciuta `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) tileType sconosciuto `{t}` ({_1}) +@msg unknown units unità sconosciute +@msg unknown units `{u}` unità sconosciute `{u}` +@msg unknown value axis `{v}` (left, right or both) asse dei valori sconosciuto `{v}` (left, right o both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) variazione sconosciuta `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` stile Altera sconosciuto `{s}` +@msg unknown {key} `{v}` (none, short or full) {key} sconosciuto `{v}` (none, short o full) +@msg unknown {key} profile `{n}` profilo {key} sconosciuto `{n}` +@msg unknown {k} key `{key}` (size, position) chiave di {k} sconosciuta `{key}` (size, position) +@msg unreadable object oggetto illeggibile +@msg unsafe destination file name nome del file di destinazione non sicuro +@msg use paths or compound paths usa tracciati o tracciati composti +@msg useArtboards must be true or false, not {v} useArtboards deve essere true o false, non {v} +@msg value for `{var}` must match its variable kind il valore per `{var}` deve corrispondere al tipo della sua variabile +@msg variable `{_1}` is already defined la variabile `{_1}` è già definita +@msg variable `{name}` has no `trait` la variabile `{name}` non ha un `trait` +@msg variable `{name}` is already defined la variabile `{name}` è già definita +@msg variable `{name}` is declared twice la variabile `{name}` è dichiarata due volte +@msg variable `{to}` already exists la variabile `{to}` esiste già +@msg view is required view è obbligatorio +@msg view must be one of {_1} view deve essere uno tra {_1} +@msg warpStyle can only edit an applied Warp effect warpStyle può modificare solo un effetto Altera applicato +@msg width and height must be more than 0 and at most {limit} width e height devono essere maggiori di 0 e al massimo {limit} +@msg width factors must be finite i fattori di larghezza devono essere finiti +@msg width must be positive width deve essere positivo +@msg width out of range width fuori intervallo +@msg width/height must be > 0 width/height devono essere > 0 +@msg x and y must be finite numbers x e y devono essere numeri finiti +@msg x and y must be given together x e y devono essere indicati insieme +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone richiede un ancoraggio: ancora accanto a un pannello con anchor, o in una nuova colonna di ancoraggio con side (Start o End) +@msg zoom must be positive zoom deve essere positivo +@msg {C}: no effect at index {list} {C}: nessun effetto all'indice {list} +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} è protetto e rimane +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} è protetto: salva le impostazioni con un altro nome +@msg {LABEL} produced an empty result {LABEL} ha prodotto un risultato vuoto +@msg {_1} MB of document, at most {_2} MB {_1} MB di documento, al massimo {_2} MB +@msg {_1} MB of images, at most {_2} MB {_1} MB di immagini, al massimo {_2} MB +@msg {_1} bytes of RGBA data for a {width} × {height} image {_1} byte di dati RGBA per un'immagine di {width} × {height} +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} non può essere letto, o è cambiato da quando è stato letto: prima ricollegalo o aggiornalo +@msg {_1} can't go into {into} {_1} non può entrare in {into} +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} non può limitare la stampa alla bassa risoluzione: scegli PDF 1.4 o successivo, o consenti la stampa ad alta risoluzione +@msg {_1} files can't be opened yet i file {_1} non possono ancora essere aperti +@msg {_1} files can't be password-protected: remove the passwords or choose no standard i file {_1} non possono essere protetti da password: rimuovi le password o non scegliere alcuno standard +@msg {_1} files can't be {_2} i file {_1} non possono essere {_2} +@msg {_1} files can't carry editing data: turn off preserveEditing i file {_1} non possono contenere dati di modifica: disattiva preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers i file {_1} non possono avere livelli PDF: disattiva createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one i file {_1} richiedono una data di creazione, e questa piattaforma non ha un orologio che la fornisca +@msg {_1} isn't a path {_1} non è un tracciato +@msg {_1} options: {e} opzioni {_1}: {e} +@msg {_1} paints nothing to rasterize {_1} non dipinge nulla da rasterizzare +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) le password {_1} possono avere fino a {MAX_LEGACY_PASSWORD} caratteri ASCII (PDF 1.7 e successivi accettano qualsiasi carattere) +@msg {_1} with linked images or an image map writes several files: use document.export {_1} con immagini collegate o una mappa immagine scrive più file: usa document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} qui scrive un file per tavola da disegno: usa document.export, o indica una sola tavola da disegno +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} pixel è troppo grande per {_3} (al massimo {max} pixel per lato): riduci la scala +@msg {_1}: belongs to another account {_1}: appartiene a un altro account +@msg {_1}: not a folder of its own {_1}: non è una cartella propria +@msg {_1}: not a plain file {_1}: non è un file normale +@msg {dest}: {e} {dest}: {e} +@msg {doc} has no folder {doc} non ha una cartella +@msg {file} isn't linked {file} non è collegato +@msg {folder} is not a package folder {folder} non è una cartella di pacchetto +@msg {folder}: more than {max} files and folders {folder}: più di {max} file e cartelle +@msg {folder}: {e} {folder}: {e} +@msg {g} is not a Live Paint group {g} non è un gruppo Pittura dinamica +@msg {id} is not a slice {id} non è una sezione +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} deve essere 0–{MAX_ADD_AROUND} pt +@msg {key} must be [x,y] {key} deve essere [x,y] +@msg {key} must be a percentage {min}..100 {key} deve essere una percentuale {min}..100 +@msg {key} must be a positive number, not {n} {key} deve essere un numero positivo, non {n} +@msg {key} must be a positive number, not {v} {key} deve essere un numero positivo, non {v} +@msg {key} must be one of {_1} {key} deve essere uno tra {_1} +@msg {key} must be {_1}, got {s} {key} deve essere {_1}, ricevuto {s} +@msg {key} must name a floating panel {key} deve indicare un pannello mobile +@msg {key} must name a panel in another group {key} deve indicare un pannello in un altro gruppo +@msg {key} must name a panel that shows {key} deve indicare un pannello visibile +@msg {key} must name a registered panel {key} deve indicare un pannello registrato +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) i file {kind} non hanno trasparenza, e il file avrebbe {what}: appiattiscila prima (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) le immagini {kind} non possono essere aperte (vedi document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` non è una delle scelte disponibili +@msg {k} has control characters{_1} {k} contiene caratteri di controllo{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} è troppo lungo (al massimo {MAX_TEXT} caratteri) +@msg {k} must be > 0 {k} deve essere > 0 +@msg {k} must be a number (pt) or a length such as "210 mm" {k} deve essere un numero (pt) o una lunghezza come "210 mm" +@msg {k} must be a number from {min} to {max} {k} deve essere un numero compreso tra {min} e {max} +@msg {k} must be a number or "auto" {k} deve essere un numero o "auto" +@msg {k} must be a string {k} deve essere una stringa +@msg {k} must be text {k} deve essere testo +@msg {k} must be true or false {k} deve essere true o false +@msg {k} must be {{size?, position?}} {k} deve essere {{size?, position?}} +@msg {k}: can't read `{s}` as a length {k}: impossibile leggere `{s}` come lunghezza +@msg {label} must be a number at most {max} {label} deve essere un numero non superiore a {max} +@msg {label} must be a number from -1296 to 1296 {label} deve essere un numero compreso tra -1296 e 1296 +@msg {label} must be a number from 1 to 10000 {label} deve essere un numero compreso tra 1 e 10000 +@msg {name} `{s}`: {ids} {name} `{s}`: {ids} +@msg {name} is built in and can't be deleted {name} è incorporato e non può essere eliminato +@msg {name}: CRC or size mismatch {name}: CRC o dimensione non corrispondente +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}: è stato possibile leggere solo l'anteprima incorporata di questo file Affinity; usa File › Apri per vederla con il relativo avviso, oppure esporta un SVG o un PDF da Affinity prima di inserirlo +@msg {name}: {e} {name}: {e} +@msg {native}; its embedded preview can't be read either ({e}) {native}; nemmeno la sua anteprima incorporata può essere letta ({e}) +@msg {std} files are CMYK, and the file would have colours in {_1} i file {std} sono CMYK, e il file avrebbe colori in {_1} +@msg {std} files embed their fonts, and the file would have one that isn't i file {std} incorporano i loro font, e il file ne avrebbe uno non incorporato +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} è il file da cui è stato letto questo documento, e la lettura ha escluso alcuni elementi ({what}): sovrascriverlo li perderebbe definitivamente. Scrivi invece un altro file, o passa acknowledgeLoss: true per sostituirlo comunque +@msg {ty} brushes need selected art (or params.{k}) i pennelli {ty} richiedono grafica selezionata (o params.{k}) +@msg {v} is not an asset id {v} non è un id di risorsa +@msg {what} can't be intertwined {what} non può essere intrecciato +@msg {what} doesn't fit the paper with its bleed and marks {what} non entra nella carta con l'abbondanza e i segni +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what} verrebbe stampato su più di {MAX_TILES} riquadri: riducilo diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..25c65d7943 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -50,7 +50,7 @@ plus review. Native-speaker review is human work and isn't in the hours. | Language | Code | Translated | Status | |---|---|---|---| | Traditional Chinese (Taiwan) | `zh-hant` | 4,892 rows, 790 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted (80% overall) | partial | -| Italian | `it` | 4,011 / 4,020 | full | +| Italian | `it` | 8,757 rows, 3,422 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 40 missing messages for it | full | | Dutch | `nl` | 7,299 rows, 1,609 of them `@msg` and 47 `@msgplural` (counted 2026-11-13 on `main` at `9a1a158a3`): in `COMPLETE_MESSAGES`, and the untranslated report lists the same missing messages for it as for German; one/other plurals | full | | Danish | `da` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one/other plurals | full | | Russian | `ru` | 4,009 / 4,020 | full | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e7c1b28726..5d298b065e 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -870,6 +870,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Hindi (2026-11-06): Hindi (`hi`): 7,877-row machine-assisted catalog, zero/one singular plurals, desktop font fallback and optional Devanagari craft-fonts fallback; all 55 translation tests pass; native review and a web font remain; scores and estimates unchanged. [Terminology and limitations](localization-hi.md). - Hindi wording (2026-11-13): rows outside the plug-in messages keep their English's meaning, quantities and clauses, and use the catalog's save, swatch, crop, resolution, path and type terms; a test checks those terms and the point unit in every row; native review remains; scores and estimates unchanged. - Hindi terms (2026-11-13): every row that names a variable (data merge, variable fonts and widths, environment variables) says वेरिएबल, and joining paths, anchor points, point type and lines says जोड़ें instead of शामिल हों, which stays for joining the community; a test checks both terms in every row; scores and estimates unchanged. + - Italian messages (2026-11-13): the Italian catalog (`it`) has 1,802 more status and error message rows, and the untranslated report's missing Italian messages go from 1,842 to 40; the translations are VectorCraft's own and await native review; scores and estimates unchanged. See [localization-parity.md](localization-parity.md). - Window › Help Bar shows or hides the tool-help strip at the bottom of the window apart from the status bar (`window.helpBar`). Each setting is kept with the UI preferences and in saved workspaces, and Presentation Mode hides both bars. Layouts saved before the setting existed show the Help Bar where they show the status bar (`help_bar_toggle_is_independent_and_saved_in_workspaces`, `help_bar_and_status_bar_render_independently`, `legacy_help_bar_visibility_follows_each_layouts_status_bar`, `a_help_bar_setting_the_file_lacks_is_saved`). - Czech render-site labels: desktop/web file filters, diagnostics, suggested preset names and contributor tooltips/plurals now use catalog lookups; gradients have Czech adjective agreement and printer marks have a separate context (PDF/Print and Summary). Search, Home, toolbar/panel tooltips and Preferences already look up their labels. Every complete catalog has rows for these labels and for the Roman and Japanese printer mark styles. Evidence: `i18n::tests::czech_render_site_follow_up`, `i18n::tests::complete_catalogs_translate_render_site_labels`, `dialogs::tests_modal::printer_mark_labels_are_distinct_from_language_names`; see [localization-parity.md](localization-parity.md). - The History panel, Edit › Undo and Edit › Redo translate the names of the steps that apply or redefine a character or paragraph style and of the step that redefines a graphic style: each such step keeps its template (`Apply {name}`, `Redefine {name}`, `Redefine Graphic Style “{name}”`) and the style's name, every complete catalog has a row for each template, and the panel and the menu show the style's name as the document holds it, except the Normal styles' names, which they show in the interface language (`steps_named_after_a_style_list_the_template_around_the_name`, `undo_and_redo_name_a_style_step_around_the_style_name`). From aa18c36039234ec2f96d7b1dd99fe81524cccecb Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:11:53 -0800 Subject: [PATCH 10/33] UI commands: catalog row for Save Actions with folder preferences crates/mcp/src/ui_catalog.rs now carries the ui.actionSet.save params text from crates/ui-egui/src/menus/ui_commands.rs, so `cargo xtask ui-commands --check` passes. --- crates/mcp/src/ui_catalog.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index 8836b72ac2..92694697d6 100644 --- a/crates/mcp/src/ui_catalog.rs +++ b/crates/mcp/src/ui_catalog.rs @@ -1537,7 +1537,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[ "ui.actionSet.save", "Save Actions…", "", - "{set?: index, id?, path?} write the Actions panel's set at set (0 is the top set; default: the current set) with its actions to an action set file (.vcactions) at path, else a picked file named after the set (the web downloads it). id holds the panel's own set id, which its menu passes: the set with this id is saved wherever it is now, and set is not read. A set over 8 MiB as a file, with more than 1,000 actions or with an action of more than 10,000 steps is not written; the panel menu's Save Actions… → {path}", + "{set?: index, id?, path?} write the Actions panel's set at set (0 is the top set; default: the current set) with its actions to an action set file (.vcactions) at path, else a picked file named after the set (the web downloads it). id holds the panel's own set id, which its menu passes: the set with this id is saved wherever it is now, and set is not read. A set over 8 MiB as a file, with more than 1,000 actions or with an action of more than 10,000 steps is not written, nor is a set with a prefs.set step (also in a batch step) that sets a folder preference to a folder (an error names the action and the preference; an empty folder and the scratch disk choices Startup and None save); the panel menu's Save Actions… → {path}", ), ( "ui.actionSet.load", From 9594d39bd5aadf9a3dfff97fca03b2b98c5dc9f0 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 14:37:58 -0800 Subject: [PATCH 11/33] Edit Colors: no undo step when a recolor changes nothing Invert Colors, the Convert to commands, Saturate, Adjust Color Balance, the Blend commands and Recolor Artwork recorded an undo step even when they changed no color, for example Invert Colors with fills and strokes off, Saturate at 0 or a second Blend Front to Back. The empty step also cleared the redo steps. paint.invert and paint.complement on whole objects, Apply as Filter (addon.color.filter) and Apply Nearest Swatch run the same pass and did the same. These commands now run on a copy of the document and record the step only when something changed. Convert to Indexed Color uses the same helper. Recolor Artwork also compares the documents when it counted no recolored paint, so rewriting a color group's swatches still records its step. --- crates/engine/src/cmd/addons/color.rs | 4 +- crates/engine/src/cmd/colorcmds.rs | 78 ++++++++++++++++----------- crates/engine/src/cmd/paint.rs | 4 +- crates/engine/src/cmd/recolor.rs | 11 ++-- crates/engine/src/tests/editcolors.rs | 42 +++++++++++++++ crates/ui-egui/src/dialogs/recolor.rs | 4 +- docs/mcp.md | 7 ++- 7 files changed, 108 insertions(+), 42 deletions(-) diff --git a/crates/engine/src/cmd/addons/color.rs b/crates/engine/src/cmd/addons/color.rs index ced10c9912..54923a8440 100644 --- a/crates/engine/src/cmd/addons/color.rs +++ b/crates/engine/src/cmd/addons/color.rs @@ -43,7 +43,7 @@ pub fn specs() -> Vec { "Apply as Filter", ["Object", "Plug-ins", "Color Effects"], None, - "{effect: a colour adjustment's effect id (adjust.brightnessContrast, adjust.curves, adjust.hueSaturation, adjust.levels, adjust.shiftToColor, adjust.temperatureTint, addon.color.exposure, addon.color.desaturate, addon.color.duotone or addon.color.invert), params?: {…} (as effect.apply; missing keys take the dialog defaults), fill?: true, stroke?: true, includeImages?: true, includePatterns?: true, ids?: [id…] (default: the selection)} recolour the objects and everything inside them as the live effect would draw them, but in their colours, as edit.colors.invert does (fills, strokes, type, gradient meshes, embedded images and pattern tiles), as one undo step; their effects stay. Duotone links a colour to its one tone's swatch as the live effect does. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed}", + "{effect: a colour adjustment's effect id (adjust.brightnessContrast, adjust.curves, adjust.hueSaturation, adjust.levels, adjust.shiftToColor, adjust.temperatureTint, addon.color.exposure, addon.color.desaturate, addon.color.duotone or addon.color.invert), params?: {…} (as effect.apply; missing keys take the dialog defaults), fill?: true, stroke?: true, includeImages?: true, includePatterns?: true, ids?: [id…] (default: the selection)} recolour the objects and everything inside them as the live effect would draw them, but in their colours, as edit.colors.invert does (fills, strokes, type, gradient meshes, embedded images and pattern tiles), as one undo step (none when nothing changes); their effects stay. Duotone links a colour to its one tone's swatch as the live effect does. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed}", has_selection, filter ), @@ -70,7 +70,7 @@ pub fn specs() -> Vec { "Apply Nearest Swatch", ["Object", "Plug-ins", "Color Effects"], None, - "{library?: as addon.color.nearestSwatch (default: \"document\"), fill?: true, stroke?: true, ids?: [id…] (default: the selection)} give each solid colour and gradient stop of the selected objects' fills and strokes (their type's and those of the objects inside them; gradient meshes too) the colour of its nearest swatch in the library, as addon.color.nearestSwatch finds it, as one undo step: a global or spot swatch's colour links to it. A library's swatches the colours take are added to the document's first, as swatch.library.add adds them (a spot colour stays spot); images and patterns keep their colours. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed: paints changed, swatches: [the document swatches the colours took], added: [swatches added to the document]}", + "{library?: as addon.color.nearestSwatch (default: \"document\"), fill?: true, stroke?: true, ids?: [id…] (default: the selection)} give each solid colour and gradient stop of the selected objects' fills and strokes (their type's and those of the objects inside them; gradient meshes too) the colour of its nearest swatch in the library, as addon.color.nearestSwatch finds it, as one undo step (none when nothing changes): a global or spot swatch's colour links to it. A library's swatches the colours take are added to the document's first, as swatch.library.add adds them (a spot colour stays spot); images and patterns keep their colours. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed: paints changed, swatches: [the document swatches the colours took], added: [swatches added to the document]}", has_selection, nearest::apply ), diff --git a/crates/engine/src/cmd/colorcmds.rs b/crates/engine/src/cmd/colorcmds.rs index 6de2fbf9a2..2395c51f8e 100644 --- a/crates/engine/src/cmd/colorcmds.rs +++ b/crates/engine/src/cmd/colorcmds.rs @@ -36,7 +36,7 @@ pub fn specs() -> Vec { "Invert Colors", ["Edit", "Edit Colors"], None, - "{fill?: true, stroke?: true, includeImages?: true, includePatterns?: true, ids?} replace each colour by its RGB inverse (keeps the colour model) in fills, strokes, text, gradient meshes (with fills), colorized image fills (their pixels stay unchanged), other embedded images (a recoloured copy) and pattern tiles (a new pattern swatch; the original stays), as one undo step. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed, notChanged: {locked, hidden} (when the selection is everything select.all takes: the locked and hidden objects it left out whose colors would have changed; an object both hidden and locked counts as hidden), warning?: set when notChanged counts any object}", + "{fill?: true, stroke?: true, includeImages?: true, includePatterns?: true, ids?} replace each colour by its RGB inverse (keeps the colour model) in fills, strokes, text, gradient meshes (with fills), colorized image fills (their pixels stay unchanged), other embedded images (a recoloured copy) and pattern tiles (a new pattern swatch; the original stays), as one undo step (none when nothing changes). On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed, notChanged: {locked, hidden} (when the selection is everything select.all takes: the locked and hidden objects it left out whose colors would have changed; an object both hidden and locked counts as hidden), warning?: set when notChanged counts any object}", has_selection, |s, p| recolor(s, p, "edit.colors.invert", "Invert Colors", &invert) ), @@ -104,7 +104,7 @@ pub fn specs() -> Vec { "Blend Front to Back", ["Edit", "Edit Colors"], None, - "{} ≥3 filled objects (solid fills or gradient meshes, which keep their shading): intermediate fills graded between the frontmost and backmost fill. On a path directly in a graph series, the graph also stores the new fill, as for the other Blend commands (docs/mcp.md) → {changed}", + "{} ≥3 filled objects (solid fills or gradient meshes, which keep their shading): intermediate fills graded between the frontmost and backmost fill. On a path directly in a graph series, the graph also stores the new fill, as for the other Blend commands (docs/mcp.md); no undo step when no fill changes → {changed}", has_selection, |s, _| blend(s, BlendOrder::Stack) ), @@ -113,7 +113,7 @@ pub fn specs() -> Vec { "Blend Horizontally", ["Edit", "Edit Colors"], None, - "{} ≥3 filled objects or meshes: fills graded between the leftmost and rightmost → {changed}", + "{} ≥3 filled objects or meshes: fills graded between the leftmost and rightmost; no undo step when no fill changes → {changed}", has_selection, |s, _| blend(s, BlendOrder::Horizontal) ), @@ -122,7 +122,7 @@ pub fn specs() -> Vec { "Blend Vertically", ["Edit", "Edit Colors"], None, - "{} ≥3 filled objects or meshes: fills graded between the topmost and bottommost → {changed}", + "{} ≥3 filled objects or meshes: fills graded between the topmost and bottommost; no undo step when no fill changes → {changed}", has_selection, |s, _| blend(s, BlendOrder::Vertical) ), @@ -457,8 +457,8 @@ pub(crate) fn recolor_links(s: &mut Session, p: &Value, cmd: &str, label: &str, run_pass(s, p, cmd, label, Recolor::with_links(Scope::of(p, cmd)?, f, links)) } -/// Run `pass` on the `ids` param's objects (default: the selection) as one undo step named `label`; -/// errors name the command `cmd`. The reply's `notChanged` counts the locked and hidden objects the +/// Run `pass` on the `ids` param's objects (default: the selection) as one undo step named `label`, +/// or none when the pass changes nothing; errors name the command `cmd`. The reply's `notChanged` counts the locked and hidden objects the /// selection left out ([`left_out`]) that the pass would have changed. fn run_pass(s: &mut Session, p: &Value, cmd: &str, label: &str, pass: Recolor) -> Result { let (ids, left) = match ids_param(p, "ids", cmd)? { @@ -471,20 +471,47 @@ fn run_pass(s: &mut Session, p: &Value, cmd: &str, label: &str, pass: Recolor) - } }; let colorized_ok = super::paint::check_colorized(&s.doc()?.doc).is_ok(); - let (n, not_changed) = s.edit(label, |d, _| { + let (n, not_changed) = edit_if_changed(s, label, |_, d| { let not_changed = NotChanged::of(pass.again().would_change(d, &left).into_iter().map(|(_, hidden)| hidden)); let n = keeping_series_paint(d, &ids, |d| { let n = pass.run(d, &ids); super::paint::check_colorized_kept(colorized_ok, d).map_err(|e| bad(cmd, e))?; Ok(n) })?; - Ok((n, not_changed)) + Ok(((n, not_changed), n > 0)) })?; let mut out = json!({ "changed": n }); not_changed.report(&mut out); Ok(out) } +/// [`Session::edit`] for an Edit Colors pass, which may change nothing: `f` runs on a copy of the +/// document (it gets the document as it is too) and returns its result with whether it changed the +/// copy. Only a changed copy becomes the document, as one undo step named `label`; otherwise the +/// undo and redo steps and the saved state stay as they were. +pub(crate) fn edit_if_changed(s: &mut Session, label: &str, f: impl FnOnce(&Document, &mut Document) -> Result<(T, bool)>) -> Result { + if s.script_running() { + return Err(EngineError::Busy); + } + // A typing session ends here as `s.edit` would end it, before the copy is taken, so that the + // copy holds what was typed. + if !s.previewing && super::typing_in_progress(s) { + super::settle_interaction(s)?; + } + let before = s.doc()?.doc.clone(); + let mut doc = (*before).clone(); + let (v, changed) = f(&before, &mut doc)?; + // The document is shared with `before` no longer, so `s.edit` does not copy it again. + drop(before); + if changed { + s.edit(label, |d, _| { + *d = doc; + Ok(()) + })?; + } + Ok(v) +} + /// Run `recolor` on `d` (it returns how many objects it changed) over `roots`, then store on their graphs what it changed /// on the graph series marks among `roots` and inside them, so the marks keep their new paint when the graph is drawn /// again. @@ -597,11 +624,6 @@ fn pass_targets(s: &Session, p: &Value, cmd: &str) -> Result { fn to_indexed(s: &mut Session, p: &Value) -> Result { const C: &str = "edit.colors.toIndexed"; let indexing = Indexing::of(s, p)?; - // A typing session ends here as `s.edit` would end it, before the copy is taken, so that the - // copy holds what was typed. - if !s.previewing && super::typing_in_progress(s) { - super::settle_interaction(s)?; - } let remake = |b: &ImageBlob| indexing.convert(b); let images = Scope { fill: false, stroke: false, images: true, patterns: false }; let pass = Recolor { remake: Some(&remake), ..Recolor::new(images, &same) }; @@ -609,22 +631,18 @@ fn to_indexed(s: &mut Session, p: &Value) -> Result { // indexed PNG yet; it is not converted to find out. The empty PNG only gives it a new key. let would = |b: &ImageBlob| (!b.indexed_png() && b.pixel_size().is_some()).then(|| ImageBlob::png(vec![])); let (ids, left) = pass_targets(s, p, C)?; - let st = s.doc()?; - let colorized_ok = super::paint::check_colorized(&st.doc).is_ok(); - let not_changed = NotChanged::of( - Recolor { remake: Some(&would), ..Recolor::new(images, &same) }.would_change(&st.doc, &left).into_iter().map(|(_, hidden)| hidden), - ); - let mut doc = (*st.doc).clone(); - let n = pass.run(&mut doc, &ids); - if let Some((w, h)) = indexing.too_large.get() { - return Err(bad(C, format!("an image of {w} × {h} pixels is too large to convert (at most {} pixels)", Indexing::MAX_PIXELS))); - } - if n > 0 { - s.edit("Convert to Indexed Color", |d, _| { - *d = doc; - super::paint::check_colorized_kept(colorized_ok, d).map_err(|e| bad(C, e)) - })?; - } + let (n, not_changed) = edit_if_changed(s, "Convert to Indexed Color", |now, doc| { + let colorized_ok = super::paint::check_colorized(now).is_ok(); + let not_changed = NotChanged::of( + Recolor { remake: Some(&would), ..Recolor::new(images, &same) }.would_change(now, &left).into_iter().map(|(_, hidden)| hidden), + ); + let n = pass.run(doc, &ids); + if let Some((w, h)) = indexing.too_large.get() { + return Err(bad(C, format!("an image of {w} × {h} pixels is too large to convert (at most {} pixels)", Indexing::MAX_PIXELS))); + } + super::paint::check_colorized_kept(colorized_ok, doc).map_err(|e| bad(C, e))?; + Ok(((n, not_changed), n > 0)) + })?; let mut out = json!({ "changed": n }); not_changed.report(&mut out); Ok(out) @@ -923,7 +941,7 @@ fn blend(s: &mut Session, order: BlendOrder) -> Result { super::paint::check_colorized_kept(colorized_ok, d).map_err(|e| bad(label, e))?; Ok(changed) }; - let changed = s.edit(label, |d, _| keeping_series_paint(d, &ids, run))?; + let changed = edit_if_changed(s, label, |_, d| keeping_series_paint(d, &ids, run).map(|n| (n, n > 0)))?; Ok(json!({ "changed": changed })) } diff --git a/crates/engine/src/cmd/paint.rs b/crates/engine/src/cmd/paint.rs index edab9cfe22..cfd6b89b4f 100644 --- a/crates/engine/src/cmd/paint.rs +++ b/crates/engine/src/cmd/paint.rs @@ -88,7 +88,7 @@ pub fn proxy_specs() -> Vec { "Invert", [], None, - "{stroke?: bool (default: the active proxy), ids?|id?, item?: appearance item index|null, target?: \"object\"|\"contents\"} invert the active proxy's colours of the selection (or ids; groups recolour their contents) keeping each colour's model, as edit.colors.invert does; the Appearance panel's active fill/stroke item (appearance.setActiveItem) is inverted instead when it is of that kind and no ids are given; with an object of a dynamic symbol instance selected by Direct Selection and no ids, id or item, that object's solid color changes for this instance only, and a gradient or pattern, or type whose characters differ in color, is refused; with nothing selected, invert the default. On a path directly in a graph series, the graph also stores the new color of the topmost visible fill or stroke (docs/mcp.md) → {changed}", + "{stroke?: bool (default: the active proxy), ids?|id?, item?: appearance item index|null, target?: \"object\"|\"contents\"} invert the active proxy's colours of the selection (or ids; groups recolour their contents) keeping each colour's model, as edit.colors.invert does; the Appearance panel's active fill/stroke item (appearance.setActiveItem) is inverted instead when it is of that kind and no ids are given; with an object of a dynamic symbol instance selected by Direct Selection and no ids, id or item, that object's solid color changes for this instance only, and a gradient or pattern, or type whose characters differ in color, is refused; with nothing selected, invert the default. On a path directly in a graph series, the graph also stores the new color of the topmost visible fill or stroke (docs/mcp.md); no undo step when no color changes → {changed}", has_doc, |s, p| proxy_recolor(s, p, "paint.invert", "Invert", &super::colorcmds::invert) ), @@ -97,7 +97,7 @@ pub fn proxy_specs() -> Vec { "Complement", [], None, - "{stroke?: bool (default: the active proxy), ids?|id?, item?: appearance item index|null, target?: \"object\"|\"contents\"} replace the active proxy's colours by their complements ((highest + lowest) − each component, over RGB or CMY; grey unchanged) keeping each colour's model; honours the Appearance panel's active item and a selected object of a dynamic symbol instance as paint.invert does; with nothing selected, the default → {changed}", + "{stroke?: bool (default: the active proxy), ids?|id?, item?: appearance item index|null, target?: \"object\"|\"contents\"} replace the active proxy's colours by their complements ((highest + lowest) − each component, over RGB or CMY; grey unchanged) keeping each colour's model; honours the Appearance panel's active item and a selected object of a dynamic symbol instance as paint.invert does; with nothing selected, the default; no undo step when no color changes → {changed}", has_doc, |s, p| proxy_recolor(s, p, "paint.complement", "Complement", &|c: Color| c.complement_keep_model()) ), diff --git a/crates/engine/src/cmd/recolor.rs b/crates/engine/src/cmd/recolor.rs index db0c0ac367..ede74545b6 100644 --- a/crates/engine/src/cmd/recolor.rs +++ b/crates/engine/src/cmd/recolor.rs @@ -15,7 +15,7 @@ use vectorcraft_doc::swatches::{node_colors, node_paints, override_paints}; use vectorcraft_doc::{Document, Node, NodeId, NodeKind}; use vectorcraft_geom::{Point, Rect}; -use super::colorcmds::{NotChanged, Recolor, Scope, keeping_series_paint, left_out}; +use super::colorcmds::{NotChanged, Recolor, Scope, edit_if_changed, keeping_series_paint, left_out}; use super::{CommandSpec, Result, Session, always, bad, checked_ids_param, cmd, color_value, has_doc, index, str_param, switch_or, text_param}; pub fn specs() -> Vec { @@ -34,7 +34,7 @@ pub fn specs() -> Vec { "Recolor Artwork", ["Edit", "Edit Colors"], None, - "{map: rows [{from: [colour keys (recolor.colors) or \"#rrggbb\" (every colour shown as that hex)], to: colour (hex, key, {c,m,y,k} or {l,a,b}) | null (in from, to and groupColors, #rrggbbaa and #rgba are refused), exclude?: false (the row's colours are kept), tint?: 100 (percent; with limitTo, art linked to the global or spot swatch the new color is takes this share of its tint)}] | {\"key or #rrggbb\": colour, …} (one row per colour), method?: \"exact\" (default)|\"preserveTints\"|\"scaleTints\"|\"tintsShades\"|\"hueShift\" (how a row's colours take its new colour, relative to the row's darkest, average or most saturated colour), limitTo?: swatch library id or name; or \"document\" (the document's swatches): new colours snap to its nearest colour, group?: colour group to rewrite, groupColors?: [colour] (what `group` is rewritten with; default: the rows' new colours, in order, excluded rows left out), rename?: the group's new name, includeImages?: true, includePatterns?: true, globalAdjust?: {saturation?: 0, brightness?: 0, temperature?: 0, luminosity?: 0} (Global Adjust: percent from -100 to 100, 0 changes nothing; every colour a row that isn't excluded recolours, and the group's new colours, take the same change after the row's new colour: saturation scales HSB saturation by 1 + saturation/100, brightness moves HSB brightness that share of the way to full or to black, temperature adds 0.2 × temperature/100 to red and takes it from blue, luminosity moves Lab lightness that share of the way to 100 or 0 keeping a and b; with limitTo each row's new colour takes the change before it snaps to the library)} recolour the selection (gradients, text, meshes, image pixels and pattern tiles included; each new colour keeps the model of the one it replaces (model?: \"keep\" default | \"new\" (the new colour's own model, so a CMYK colour replacing RGB art gives CMYK art) | \"document\" (the document's colour mode)), except a library swatch's with limitTo: art takes the swatch itself, linked to a global or spot colour at its tints, and the swatch joins the document's swatches; art linked to a global swatch of `group` that is rewritten with its row's new color stays linked to it, at its tint, unless the row converts the color into the art's model; other recolored art, and art in a row linked to a global swatch of `group` that is rewritten with another color, keeps its row's new color and loses its swatch link) and the group, as one undo step. On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed, swatches: [names added to the document's swatches], relinked?: with group, the other paints linked to its global swatches that took their new colors, note?: set when a colour kept a model other than its new colour's (see model), notChanged: {locked, hidden} (as edit.colors.invert), warning?: set when notChanged counts any object}", + "{map: rows [{from: [colour keys (recolor.colors) or \"#rrggbb\" (every colour shown as that hex)], to: colour (hex, key, {c,m,y,k} or {l,a,b}) | null (in from, to and groupColors, #rrggbbaa and #rgba are refused), exclude?: false (the row's colours are kept), tint?: 100 (percent; with limitTo, art linked to the global or spot swatch the new color is takes this share of its tint)}] | {\"key or #rrggbb\": colour, …} (one row per colour), method?: \"exact\" (default)|\"preserveTints\"|\"scaleTints\"|\"tintsShades\"|\"hueShift\" (how a row's colours take its new colour, relative to the row's darkest, average or most saturated colour), limitTo?: swatch library id or name; or \"document\" (the document's swatches): new colours snap to its nearest colour, group?: colour group to rewrite, groupColors?: [colour] (what `group` is rewritten with; default: the rows' new colours, in order, excluded rows left out), rename?: the group's new name, includeImages?: true, includePatterns?: true, globalAdjust?: {saturation?: 0, brightness?: 0, temperature?: 0, luminosity?: 0} (Global Adjust: percent from -100 to 100, 0 changes nothing; every colour a row that isn't excluded recolours, and the group's new colours, take the same change after the row's new colour: saturation scales HSB saturation by 1 + saturation/100, brightness moves HSB brightness that share of the way to full or to black, temperature adds 0.2 × temperature/100 to red and takes it from blue, luminosity moves Lab lightness that share of the way to 100 or 0 keeping a and b; with limitTo each row's new colour takes the change before it snaps to the library)} recolour the selection (gradients, text, meshes, image pixels and pattern tiles included; each new colour keeps the model of the one it replaces (model?: \"keep\" default | \"new\" (the new colour's own model, so a CMYK colour replacing RGB art gives CMYK art) | \"document\" (the document's colour mode)), except a library swatch's with limitTo: art takes the swatch itself, linked to a global or spot colour at its tints, and the swatch joins the document's swatches; art linked to a global swatch of `group` that is rewritten with its row's new color stays linked to it, at its tint, unless the row converts the color into the art's model; other recolored art, and art in a row linked to a global swatch of `group` that is rewritten with another color, keeps its row's new color and loses its swatch link) and the group, as one undo step (none when nothing changes). On a path directly in a graph series, the graph also stores the new paint of the topmost visible fill and stroke (docs/mcp.md) → {changed, swatches: [names added to the document's swatches], relinked?: with group, the other paints linked to its global swatches that took their new colors, note?: set when a colour kept a model other than its new colour's (see model), notChanged: {locked, hidden} (as edit.colors.invert), warning?: set when notChanged counts any object}", has_doc, apply ), @@ -1661,7 +1661,7 @@ fn apply(s: &mut Session, p: &Value) -> Result { }; let rename = text_param(p, "rename", C)?; let colorized_ok = super::paint::check_colorized(&s.doc()?.doc).is_ok(); - let (changed, edit, added, converted, not_changed) = s.edit(label, |d, _| { + let (changed, edit, added, converted, not_changed) = edit_if_changed(s, label, |now, d| { // The rows' library swatches join the document (one it has with that name and colour is // used as it is). let mut added = vec![]; @@ -1716,7 +1716,10 @@ fn apply(s: &mut Session, p: &Value) -> Result { let not_changed = NotChanged::of( before.iter().filter(|(id, colors, _)| d.node(*id).is_some_and(|now| shown_colors(d, now) == *colors)).map(|(_, _, hidden)| *hidden), ); - Ok((changed, edit, added, map.converted.get(), not_changed)) + // Rewriting the group's swatches and relinking its art are not counted in `changed`, so a + // pass that counts nothing compares the documents. + let any = changed > 0 || !added.is_empty() || *d != *now; + Ok(((changed, edit, added, map.converted.get(), not_changed), any)) })?; let mut out = json!({ "changed": changed, "swatches": added }); not_changed.report(&mut out); diff --git a/crates/engine/src/tests/editcolors.rs b/crates/engine/src/tests/editcolors.rs index e3aa59368a..a1abfeb259 100644 --- a/crates/engine/src/tests/editcolors.rs +++ b/crates/engine/src/tests/editcolors.rs @@ -109,6 +109,48 @@ fn saturate_keeps_a_color_it_does_not_change() { assert_eq!(paint(&s, b), cmyk); } +/// An Edit Colors pass, a Blend or Recolor Artwork that changes nothing records no undo step, and the +/// step that redo would bring back stays; one that changes a color still records one. +#[test] +fn edit_colors_that_change_nothing_record_no_undo_step() { + let mut s = session(); + let gray = rect(&mut s, json!("#808080")); + let a = rect(&mut s, json!("#ff0000")); + let b = rect(&mut s, json!("#808080")); + let c = rect(&mut s, json!("#0000ff")); + s.execute("select.set", &json!({"ids": [a.0, b.0, c.0]})).unwrap(); + s.execute("edit.colors.blendFrontToBack", &json!({})).unwrap(); + // A step for redo to bring back. + s.execute("edit.colors.invert", &json!({"ids": [a.0]})).unwrap(); + s.execute("edit.undo", &json!({})).unwrap(); + let history = |s: &Session| { + let h = &s.doc().unwrap().history; + (h.undo.len(), h.redo.len()) + }; + let before = history(&s); + assert_eq!(before.1, 1, "one step to redo"); + let doc = s.doc().unwrap().doc.clone(); + let cases = [ + ("edit.colors.invert", json!({"fill": false, "stroke": false})), + ("edit.colors.saturate", json!({"intensity": 0})), + ("edit.colors.toRGB", json!({})), + ("edit.colors.adjustBalance", json!({"mode": "rgb", "r": 0})), + ("edit.colors.blendFrontToBack", json!({})), + ("recolor.apply", json!({"map": {"#ff0000": "#ff0000"}})), + // The complement of a gray is the same gray. + ("paint.complement", json!({"ids": [gray.0], "stroke": false})), + ("addon.color.filter", json!({"effect": "addon.color.invert", "fill": false, "stroke": false})), + ]; + for (id, p) in cases { + let v = s.execute(id, &p).unwrap(); + assert_eq!(v["changed"], 0, "{id}"); + assert_eq!(history(&s), before, "{id}: no undo step, and redo is kept"); + assert_eq!(s.doc().unwrap().doc, doc, "{id}: the document is unchanged"); + } + s.execute("edit.colors.invert", &json!({})).unwrap(); + assert_eq!(history(&s), (before.0 + 1, 0), "a pass that changes colors is one undo step"); +} + /// Adjust Color Balance refuses a channel that is not a number, which it would otherwise read as 0. #[test] fn adjust_balance_refuses_a_channel_that_is_not_a_number() { diff --git a/crates/ui-egui/src/dialogs/recolor.rs b/crates/ui-egui/src/dialogs/recolor.rs index ccdb4e8ca1..d2bbeb3013 100644 --- a/crates/ui-egui/src/dialogs/recolor.rs +++ b/crates/ui-egui/src/dialogs/recolor.rs @@ -1783,7 +1783,7 @@ mod tests { /// The Color Theme Picker's picks (the `themePicks` field) give the rows that aren't excluded /// the palette of the art picked, in row order, repeating it when the rows outnumber its /// colors, and the art previews them. Picks that touch no art leave the rows as they are, and - /// OK keeps the recolor as one undo step. + /// OK with rows that give the art its own colors back records no undo step. #[test] fn theme_picks_give_the_rows_the_palette_of_the_art_picked() { let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); @@ -1822,7 +1822,7 @@ mod tests { assert_eq!(app.session.doc().unwrap().history.undo.len(), undo, "nothing is committed before OK"); crate::dialogs::confirm(&mut app).unwrap(); assert_eq!((fill_hex(&app, red), fill_hex(&app, blue)), ("#ff0000".into(), "#0000ff".into()), "the excluded row kept red"); - assert_eq!(app.session.doc().unwrap().history.undo.len(), undo + 1, "one undo step"); + assert_eq!(app.session.doc().unwrap().history.undo.len(), undo, "the art is unchanged: no undo step"); } const RED: &str = "rgb 255 0 0"; diff --git a/docs/mcp.md b/docs/mcp.md index 707a414d0c..a37f88055b 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3878,6 +3878,9 @@ For `edit.colors.*`, `fill: false` leaves colorized image fills unchanged. `reco `edit.colors.toGrayscale` and `edit.colors.adjustBalance` with `mode: "gray"` and `convert: true` store an embedded image with one gray channel, so a PDF writes it in gray. `includePatterns: false` leaves patterns out. The Blend commands also grade gradient meshes, which keep their shading. +The `edit.colors.*` commands, including the Blend commands, and `recolor.apply`, `paint.invert`, `paint.complement`, +`addon.color.filter` and `addon.color.applyNearestSwatch` record no undo step when they change nothing, for example +`edit.colors.invert` with `fill: false` and `stroke: false`, or a Blend whose objects already have the graded fills. `edit.colors.saturate` needs `intensity`, a number from -100 to 100 (a number outside the range counts as its nearest end). Without one, or with `null`, it answers `` missing number `intensity` ``; with a value that is not a number, such as `"40"`, it answers `` `intensity` must be a number, not "40" ``. Either way it changes nothing. @@ -10985,7 +10988,7 @@ the other colour adjustments (Effect › Color Adjustments); each colour keeps i `addon.color.filter {effect, params?, fill?, stroke?, includeImages?, includePatterns?, ids?}` applies any colour adjustment, the add-on's or one of Effect › Color Adjustments', to the colours themselves instead of as a live effect: fills, strokes, type, gradient meshes, embedded images and pattern tiles, as `edit.colors.invert` reaches them, as one -undo step. Missing `params` take the effect's dialog defaults; the objects' own effects stay as they are. +undo step (none when nothing changes). Missing `params` take the effect's dialog defaults; the objects' own effects stay as they are. ```json {"name":"run_command","arguments":{"command":"effect.apply","params":{"effect":"addon.color.exposure","params":{"exposure":1.5}}}} @@ -11152,7 +11155,7 @@ spot, deltaE}]}]}` (`values` as a default swatch name reads them: `C=10 M=20 Y=3 `addon.color.applyNearestSwatch {library?, fill?, stroke?, ids?}` (Object › Plug-ins › Color Effects › Find Nearest Swatch…, whose dialog lists each colour's three closest swatches with their difference; Apply runs it) gives each solid colour and gradient stop of the selected objects' fills and strokes (and gradient meshes) the colour of its nearest -swatch, as one undo step. A global or spot swatch's colour links to it. The library's swatches the colours take are +swatch, as one undo step (none when nothing changes). A global or spot swatch's colour links to it. The library's swatches the colours take are added to the document's first, as `swatch.library.add` adds them, so a spot colour from a colour book stays spot; images and patterns keep their colours. → `{changed, swatches: [the document swatches the colours took], added}` From 9484b116ab03e2d14ac98329c7728c87c92e8c11 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:31:04 -0800 Subject: [PATCH 12/33] Tests: screen-mode drags and popups start clear of the task bar In Full Screen Mode with Menu Bar and Full Screen Mode the canvas is about 390 pt wide, so the Contextual Task Bar under the selected rectangle now wraps onto a second row and covers the canvas's center. The middle-button pan, hand, zoom and context menu tests pressed there and reached the bar instead of the canvas. They now press low on the canvas, at a point the helper checks is clear of the bar. --- crates/ui-egui/src/tests/screenmode.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/tests/screenmode.rs b/crates/ui-egui/src/tests/screenmode.rs index 3154ccb6d9..181212bd61 100644 --- a/crates/ui-egui/src/tests/screenmode.rs +++ b/crates/ui-egui/src/tests/screenmode.rs @@ -80,6 +80,14 @@ fn in_screen_mode(ctx: &egui::Context, mode: u8) -> VectorcraftApp { app } +/// A point on the empty canvas, low enough to miss the Contextual Task Bar under the selection +/// (on a narrow canvas its actions wrap onto more rows and can reach the canvas's center). +fn open_canvas_point(app: &VectorcraftApp, ctx: &egui::Context) -> egui::Pos2 { + let p = app.canvas_rect.unwrap().lerp_inside(egui::vec2(0.5, 0.85)); + assert!(crate::canvas::task_bar_rect(ctx).is_none_or(|bar| !bar.expand(40.0).contains(p)), "the task bar covers {p:?}"); + p +} + fn begin_tools_move(app: &mut VectorcraftApp, ctx: &egui::Context) -> (Option<[f32; 2]>, egui::Pos2) { app.run("window.panel.float", json!({"panel": "tools", "x": 120, "y": 140})).unwrap(); frame(app, ctx, vec![]); @@ -241,7 +249,7 @@ fn navigation_drag_keeps_screen_mode(kind: &str) { _ => panic!("unknown navigation drag"), }; frame(&mut app, &ctx, vec![]); - let start = app.canvas_rect.unwrap().center(); + let start = open_canvas_point(&app, &ctx); let end = start + egui::vec2(35.0, 20.0); let pointer = |pos, pressed| egui::Event::PointerButton { pos, button, pressed, modifiers: Default::default() }; frame(&mut app, &ctx, vec![egui::Event::PointerMoved(start), pointer(start, true)]); @@ -551,7 +559,7 @@ fn held_escape_that_cancels_a_dialog_or_leaves_presentation_skips_a_command_boun fn a_popup_takes_escape_before_full_screen_exits() { let ctx = egui::Context::default(); let mut app = in_screen_mode(&ctx, 2); - let pos = app.canvas_rect.unwrap().center(); + let pos = open_canvas_point(&app, &ctx); let button = |pressed| egui::Event::PointerButton { pos, button: egui::PointerButton::Secondary, pressed, modifiers: Default::default() }; frame(&mut app, &ctx, vec![egui::Event::PointerMoved(pos), button(true)]); frame(&mut app, &ctx, vec![button(false)]); From a8a0316da75f972bf40d0d8a545e65d40db53228 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:39:04 -0800 Subject: [PATCH 13/33] Type: Create Outlines keeps the type object's own fills and strokes Type > Create Outlines dropped the fills and strokes of the type object itself (the items around the Appearance panel's Characters row), so the outlines no longer drew what the type drew. On type without effects of its own, Object > Expand outlines the type with the same command, so it lost them before Fill or Stroke could expand them. - Create Outlines puts those items on paths of the type's whole outline: the items below the Characters row on one path under the glyph paths, the others on one path over them, in their order. Unplaced gradients are fixed to the type's layout box, as the canvas paints them. - object.expand.info reports the type object's own strokes, pattern fills and gradient fills, measuring a gradient on the whole outline. It counts them only when the type has an outline: type of only spaces reports none of them. - The stroke flag of object.expand.info counts every stroke Outline Stroke outlines, not only the topmost one, for paths as well as type. Expand skipped Stroke for a visible stroke under a stroke with no paint. Create Outlines still does not carry over the type object's own effects. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/expand.rs | 61 +++++++++-- crates/engine/src/cmd/pathops.rs | 2 +- crates/engine/src/cmd/typecmd.rs | 32 +++++- crates/engine/src/tests/objexpand.rs | 153 +++++++++++++++++++++++++++ crates/engine/src/tests/pathops.rs | 45 ++++++++ docs/mcp.md | 12 ++- docs/target-app-parity.md | 1 + 7 files changed, 293 insertions(+), 13 deletions(-) diff --git a/crates/engine/src/cmd/expand.rs b/crates/engine/src/cmd/expand.rs index d4a6c60802..02dbad5d23 100644 --- a/crates/engine/src/cmd/expand.rs +++ b/crates/engine/src/cmd/expand.rs @@ -25,7 +25,7 @@ use super::dimension::EndedLink; use super::edit::selected_roots; use super::menucmds::{run_raw, squash}; use super::object::as_clipping_path; -use super::pathops::{assemble, node_path, outline_strokes, replace_leaves, styled_copy, with_transparency}; +use super::pathops::{assemble, node_path, outlinable, outline_strokes, replace_leaves, styled_copy, with_transparency}; use super::{CommandSpec, EngineError, Result, Session, bad, cmd, has_selection, number_or, switch_or, text_param}; const C: &str = "object.expand"; @@ -47,7 +47,7 @@ pub fn specs() -> Vec { "Expand…", ["Object"], None, - "{object?: true (symbol instances without effects (their fills' and strokes' included) and not in perspective → their art, as Break Link to Symbol places it (the instances that art holds stay instances), type → outlines, live shapes → paths, effects baked, blends → groups of their steps, repeats → groups of their instances, Intertwine groups → groups of their objects and of the parts drawn over the others (clip groups), envelopes → their distorted content, placed documents → editable copies of their art, as placing the file without link gives, its symbols, patterns, swatches and images joining the document), fill?: true (gradient fills → `gradient` art, pattern fills → their tiles in a clip group shaped like the object), stroke?: true (strokes → filled outlines, the stroked paths that baking effects leaves included), gradient?: \"objects\" (default: `steps` solid strips, concentric ellipses for radial gradients, wedges around the center for conic ones, nested squares for diamond ones) | \"mesh\" (a gradient mesh); both in a clip group shaped like the object (freeform gradients always become a mesh shaped like it), steps?: 1..1000 (255; a gradient that repeats or reflects gets the same whole number of them in each run, at least one, so a run starts on an object's edge; one that needs more than 256 runs across the box stays a gradient)} one undo step; a dimension whose art an option changes becomes a plain group of its art, except one selected with art it measures, directly or in groups without fills, strokes or effects of their own: its lines and label stay as they are, and if the new art no longer fits its link it stays where it was, no longer linked → {ids}", + "{object?: true (symbol instances without effects (their fills' and strokes' included) and not in perspective → their art, as Break Link to Symbol places it (the instances that art holds stay instances), type → outlines (its own fills and strokes on paths of its whole outline, which Fill and Stroke expand), live shapes → paths, effects baked, blends → groups of their steps, repeats → groups of their instances, Intertwine groups → groups of their objects and of the parts drawn over the others (clip groups), envelopes → their distorted content, placed documents → editable copies of their art, as placing the file without link gives, its symbols, patterns, swatches and images joining the document), fill?: true (gradient fills → `gradient` art, pattern fills → their tiles in a clip group shaped like the object), stroke?: true (strokes → filled outlines, the stroked paths that baking effects leaves included), gradient?: \"objects\" (default: `steps` solid strips, concentric ellipses for radial gradients, wedges around the center for conic ones, nested squares for diamond ones) | \"mesh\" (a gradient mesh); both in a clip group shaped like the object (freeform gradients always become a mesh shaped like it), steps?: 1..1000 (255; a gradient that repeats or reflects gets the same whole number of them in each run, at least one, so a run starts on an object's edge; one that needs more than 256 runs across the box stays a gradient)} one undo step; a dimension whose art an option changes becomes a plain group of its art, except one selected with art it measures, directly or in groups without fills, strokes or effects of their own: its lines and label stay as they are, and if the new art no longer fits its link it stays where it was, no longer linked → {ids}", has_selection, expand ), @@ -56,7 +56,7 @@ pub fn specs() -> Vec { "Expand Options", [], None, - "{} what each Expand option would change in the selection → {object, fill, stroke, gradient, appearance, symbolArt?: {fill, stroke, gradient}} (true: something to expand; gradient: a gradient fill that Fill expands (in type, on the outline Object makes of a character), which Expand Gradient To applies to; appearance: appearance attributes Expand Appearance expands first, for which Object › Expand… is dimmed in the menus; symbolArt, when the selection holds symbol instances that Object expands: true when only their art holds it, which Fill and Stroke reach only with Object on)", + "{} what each Expand option would change in the selection → {object, fill, stroke, gradient, appearance, symbolArt?: {fill, stroke, gradient}} (true: something to expand; gradient: a gradient fill that Fill expands (in type, on the outline Object makes of a character, or of the whole type for the type object's own fills), which Expand Gradient To applies to; appearance: appearance attributes Expand Appearance expands first, for which Object › Expand… is dimmed in the menus; symbolArt, when the selection holds symbol instances that Object expands: true when only their art holds it, which Fill and Stroke reach only with Object on)", has_selection, |s, _| { let roots = selected_roots(s)?; @@ -189,21 +189,37 @@ impl Expandable { e.placed |= matches!(c.kind, NodeKind::PlacedDocument(_)); e.repeats |= matches!(c.kind, NodeKind::Repeat(_)); e.intertwines |= matches!(c.kind, NodeKind::Intertwine { .. }); + // Type's outlines take its characters' stroke and fill, and a path of its whole outline + // takes the type object's own fills and strokes (see `type.createOutlines`). + // Any stroke Outline Stroke outlines, not only the topmost one. + let own_stroke = c.appearance.items.iter().any(|i| matches!(i, AppearanceItem::Stroke(st) if outlinable(st))); + let own_pattern = c.appearance.items.iter().any(|i| pattern_fill(d, i).is_some()); + let own_gradient = c.appearance.items.iter().any(|i| gradient_fill(i).is_some()); + // Type without an outline (empty, or only spaces) gets no path for its own fills and strokes. + let whole = match &c.kind { + NodeKind::Text(t) if own_stroke || own_pattern || own_gradient => text_whole_outline(t), + _ => None, + }; e.stroke |= match &c.kind { - // Type's outlines take its characters' stroke (see `type.createOutlines`). - NodeKind::Text(t) => t.runs.iter().any(|r| r.style.has_stroke()), - _ => shape && !c.appearance.stroke_paint().is_none(), + NodeKind::Text(t) => (own_stroke && whole.is_some()) || t.runs.iter().any(|r| r.style.has_stroke()), + _ => shape && own_stroke, }; // Once one is found, the next path is not measured. e.gradient = e.gradient || match &c.kind { - NodeKind::Text(t) => text_gradient_expands(d, t), + NodeKind::Text(t) => { + text_gradient_expands(d, t) + || (own_gradient && whole.as_ref().is_some_and(|(path, tb)| text_object_gradient_expands(d, c, path, *tb))) + } _ => shape && gradient_expands(d, c), }; e.fill |= e.gradient; e.fill |= match &c.kind { - NodeKind::Text(t) => t.runs.iter().any(|r| matches!(&r.style.fill, Paint::Pattern { pattern, .. } if d.pattern(pattern).is_some())), - _ => shape && c.appearance.items.iter().any(|i| pattern_fill(d, i).is_some()), + NodeKind::Text(t) => { + (own_pattern && whole.is_some()) + || t.runs.iter().any(|r| matches!(&r.style.fill, Paint::Pattern { pattern, .. } if d.pattern(pattern).is_some())) + } + _ => shape && own_pattern, }; }); } @@ -275,6 +291,33 @@ fn text_gradient_expands(d: &Document, t: &TextObject) -> bool { || vectorcraft_text::decorations(&lay, vectorcraft_text::FontDb::global(), t).iter().any(|(run, bar)| expands(*run, bar)) } +/// The path of type `t`'s whole outline in the document (glyphs, ruby glyphs and underline and +/// strikethrough bars), which `type.createOutlines` paints with the type object's own fills and +/// strokes, and the layout's bounds there; `None` when the type has no outline. +fn text_whole_outline(t: &TextObject) -> Option<(PathData, Rect)> { + let db = vectorcraft_text::FontDb::global(); + let lay = vectorcraft_text::layout(db, t); + let mut all = BezPath::new(); + for g in lay.glyphs.iter().chain(&lay.ruby) { + all.extend(g.outline.iter()); + } + for (_, bar) in vectorcraft_text::decorations(&lay, db, t) { + all.extend(bar.iter()); + } + let path = PathData::from_bezpath(&all).transformed(t.xf); + (!path.is_empty()).then(|| (path, t.xf.transform_rect_bbox(lay.bounds))) +} + +/// Whether type node `n` has a gradient fill of its own (not its characters') that Fill expands +/// once Object has outlined it: [`gradient_expands`] on `path`, the type's whole outline +/// ([`text_whole_outline`]), with its unplaced gradients fixed to `tb`, the layout's bounds in the +/// document. Leaves the document as it is. +fn text_object_gradient_expands(d: &Document, n: &Node, path: &PathData, tb: Rect) -> bool { + let mut appearance = Appearance { items: n.appearance.items.clone(), ..Default::default() }; + appearance.pin_gradients(tb); + gradient_expands(d, &Node::path(NodeId(u64::MAX), path.clone(), appearance)) +} + /// [`gradient_expands`] for a path that no live-effect plug-in paints ([`expand_piece_fills`]). fn piece_gradient_expands(d: &Document, l: &Node) -> bool { if !l.appearance.items.iter().filter_map(gradient_fill).any(|(_, g)| g.gradient.is_spread()) { diff --git a/crates/engine/src/cmd/pathops.rs b/crates/engine/src/cmd/pathops.rs index 0085c2d9d6..878b3743cf 100644 --- a/crates/engine/src/cmd/pathops.rs +++ b/crates/engine/src/cmd/pathops.rs @@ -962,7 +962,7 @@ pub(crate) fn ordered_map( // ---------- Outline Stroke ---------- /// Can Outline Stroke turn `st` into filled art? -fn outlinable(st: &StrokeLayer) -> bool { +pub(crate) fn outlinable(st: &StrokeLayer) -> bool { st.visible && !st.paint.is_none() && st.width > 0.0 } diff --git a/crates/engine/src/cmd/typecmd.rs b/crates/engine/src/cmd/typecmd.rs index 94ed16ebd8..6b14c98916 100644 --- a/crates/engine/src/cmd/typecmd.rs +++ b/crates/engine/src/cmd/typecmd.rs @@ -4,7 +4,9 @@ use std::sync::Arc; use serde_json::{Value, json}; -use vectorcraft_doc::{AreaFit, Document, Justify, Node, NodeId, NodeKind, RubyAlign, RubyPosition, RubySpan, TextObject}; +use vectorcraft_doc::{ + Appearance, AppearanceItem, AreaFit, Document, Justify, Node, NodeId, NodeKind, RubyAlign, RubyPosition, RubySpan, TextObject, +}; use vectorcraft_geom::{Affine, PathData, Vec2}; use super::edit::selected_roots; @@ -39,7 +41,7 @@ pub fn specs() -> Vec { "Create Outlines", ["Type"], Some("Cmd+Shift+O"), - "{} convert the selected text to groups of compound paths (one per glyph); a frame of threaded text leaves its thread with the text it shows; a dimension whose label is outlined becomes a plain group of its art → {ids}", + "{} convert the selected text to groups of compound paths (one per glyph); the type object's own fills and strokes (not its characters') go on paths of its whole outline, below or above the glyphs as its Characters row places them; a frame of threaded text leaves its thread with the text it shows; a dimension whose label is outlined becomes a plain group of its art → {ids}", has_selection, create_outlines ), @@ -672,13 +674,39 @@ fn create_outlines(s: &mut Session, _: &Value) -> Result { } placed.extend(ruby.map(|(_, r)| r)); let mut children = placed; + // The whole outline the object's own fills and strokes paint: every glyph, ruby glyph + // and bar, as the canvas paints them. + let mut all = kurbo::BezPath::new(); + for g in lay.glyphs.iter().chain(&lay.ruby) { + all.extend(g.outline.iter()); + } // Underline and strikethrough bars become paths of their run's paint too (#847). for (run, bar) in vectorcraft_text::decorations(&lay, vectorcraft_text::FontDb::global(), t) { + all.extend(bar.iter()); let st = t.runs.get(run).map(|r| r.style.clone()).unwrap_or_else(|| t.first_style()); let mut node = shape_node(d, PathData::from_bezpath(&bar).transformed(t.xf), None); node.appearance = st.appearance().into(); children.push(Arc::new(node)); } + // The object's own fills and strokes (not its characters') paint the whole outline: + // those below the Characters row under the characters, the others over them, with + // their unplaced gradients fixed to the layout's bounds in the document. + let all = PathData::from_bezpath(&all).transformed(t.xf); + if !n.appearance.items.is_empty() && !all.is_empty() { + let (below, above) = n.appearance.split_contents(); + let tb = t.xf.transform_rect_bbox(lay.bounds); + let mut whole = |items: &[AppearanceItem]| { + let mut ap = Appearance { items: items.to_vec(), ..Default::default() }; + ap.pin_gradients(tb); + Arc::new(Node::path(d.alloc_id(), all.clone(), ap)) + }; + if !below.is_empty() { + children.insert(0, whole(below)); + } + if !above.is_empty() { + children.push(whole(above)); + } + } let (par, idx, _) = d.position(tid).ok_or(EngineError::NoNode(tid))?; d.remove(tid)?; if children.is_empty() { diff --git a/crates/engine/src/tests/objexpand.rs b/crates/engine/src/tests/objexpand.rs index 32c0cb143f..c8b51033fe 100644 --- a/crates/engine/src/tests/objexpand.rs +++ b/crates/engine/src/tests/objexpand.rs @@ -712,6 +712,159 @@ fn type_in_symbol_art_reports_a_gradient_only_when_it_expands() { } } +/// Selected type "Hi" at 64 pt whose characters have no paint, with fills and strokes of its own: +/// each of `items` is an `appearance.setItem` edit of a new object-level stroke when it has a +/// `weight`, else of a new object-level fill; the first `below` of them paint below the characters. +fn type_with_own_paint(s: &mut Session, items: &[Value], below: usize) -> NodeId { + let t = run(s, "text.create", json!({"x": 25, "y": 110, "text": "Hi", "size": 64}))["id"].as_u64().unwrap(); + run(s, "select.set", json!({"ids": [t]})); + run(s, "paint.setFill", json!({"none": true})); + run(s, "paint.setStroke", json!({"none": true})); + for (i, item) in items.iter().enumerate() { + let add = if item.get("weight").is_some() { "appearance.addStroke" } else { "appearance.addFill" }; + run(s, add, json!({"target": "object"})); + let mut p = item.clone(); + p["index"] = json!(i); + run(s, "appearance.setItem", p); + } + run(s, "appearance.moveItem", json!({"from": "contents", "to": below})); + NodeId(t) +} + +fn blue_stroke() -> Value { + json!({"color": "#0000ff", "weight": 5}) +} + +fn red_blue() -> Value { + json!({"gradient": {"kind": "linear", "stops": [{"offset": 0, "color": "#ff0000"}, {"offset": 1, "color": "#0000ff"}]}}) +} + +/// Create Outlines draws what the type drew: a gradient fill of the type object's own, below its +/// characters, keeps one gradient across both glyphs over the layout's box, and its stroke stays +/// over them. +#[test] +fn create_outlines_draws_the_type_objects_own_paint() { + let mut s = session(); + type_with_own_paint(&mut s, &[red_blue(), blue_stroke()], 1); + let before = render(&s); + run(&mut s, "type.createOutlines", json!({})); + let after = render(&s); + let n = differing(&before, &after, 24); + assert!(n < 40, "{n} pixels differ"); + assert!(differing(&before, &render(&session()), 24) > 1000, "the type paints something"); +} + +/// `object.expand.info` reports the type object's own stroke, pattern fill and gradient fill, which +/// Object turns into paths of its whole outline: a gradient only when that outline's box needs no +/// more runs than are written. +#[test] +fn type_objects_own_stroke_pattern_and_gradient_are_reported() { + let mut s = session(); + type_with_own_paint(&mut s, &[blue_stroke()], 0); + let info = run(&mut s, "object.expand.info", json!({})); + assert_eq!((info["object"].clone(), info["stroke"].clone(), info["fill"].clone()), (json!(true), json!(true), json!(false)), "{info}"); + + let mut s = session(); + pattern_rect(&mut s, Rect::new(150.0, 150.0, 190.0, 190.0), false); + let t = type_with_own_paint(&mut s, &[json!({"swatch": "Dots"})], 0); + run(&mut s, "select.set", json!({"ids": [t.0]})); + let info = run(&mut s, "object.expand.info", json!({})); + assert_eq!((info["fill"].clone(), info["gradient"].clone(), info["stroke"].clone()), (json!(true), json!(false), json!(false)), "{info}"); + + // 0.4 pt runs across about 80 pt: under the limit; 0.02 pt runs: far over it. + for (len, expands) in [(40.0, true), (2.0, false)] { + let mut s = session(); + let gradient = json!({"gradient": {"kind": "linear", "spread": "repeat", "repeats": 100, "start": [25, 100], "end": [25.0 + len, 100], + "stops": [{"offset": 0, "color": "#ffff00"}, {"offset": 1, "color": "#004080"}]}}); + type_with_own_paint(&mut s, &[gradient], 0); + let info = run(&mut s, "object.expand.info", json!({})); + assert_eq!((info["gradient"].clone(), info["fill"].clone()), (json!(expands), json!(expands)), "{len}: {info}"); + run(&mut s, "object.expand", json!({})); + let (gradients, texts) = gradients_and_type(&s); + assert_eq!(texts, 0, "{len}: outlined"); + assert_eq!(gradients == 0, expands, "{len}: {gradients} gradient fills left"); + } +} + +/// Type with only spaces has no outline for its own stroke or pattern fill to go on, so +/// `object.expand.info` reports neither. +#[test] +fn type_without_an_outline_reports_no_own_stroke_or_fill() { + let mut s = session(); + pattern_rect(&mut s, Rect::new(150.0, 150.0, 190.0, 190.0), false); + let t = run(&mut s, "text.create", json!({"x": 25, "y": 110, "text": " ", "size": 64}))["id"].as_u64().unwrap(); + run(&mut s, "select.set", json!({"ids": [t]})); + run(&mut s, "paint.setFill", json!({"none": true})); + run(&mut s, "paint.setStroke", json!({"none": true})); + for (i, item) in [json!({"swatch": "Dots"}), blue_stroke()].into_iter().enumerate() { + let add = if item.get("weight").is_some() { "appearance.addStroke" } else { "appearance.addFill" }; + run(&mut s, add, json!({"target": "object"})); + let mut p = item; + p["index"] = json!(i); + run(&mut s, "appearance.setItem", p); + } + let info = run(&mut s, "object.expand.info", json!({})); + assert_eq!( + (info["object"].clone(), info["stroke"].clone(), info["fill"].clone(), info["gradient"].clone()), + (json!(true), json!(false), json!(false), json!(false)), + "{info}" + ); +} + +/// How many objects in the document have a visible stroke with paint and weight. +fn painted_strokes(s: &Session) -> usize { + let mut n = 0; + s.doc().unwrap().doc.walk(|c| { + n += usize::from( + c.appearance.items.iter().any(|i| matches!(i, AppearanceItem::Stroke(st) if st.visible && !st.paint.is_none() && st.width > 0.0)), + ); + }); + n +} + +/// A visible stroke under a topmost stroke with no paint still has something for Stroke to +/// outline, on type's own appearance and on a path: `object.expand.info` reports it, and Expand +/// outlines it. +#[test] +fn a_stroke_under_an_unpainted_stroke_is_reported_and_outlined() { + let mut s = session(); + type_with_own_paint(&mut s, &[blue_stroke(), json!({"none": true, "weight": 1})], 0); + let mut r = session(); + run(&mut r, "shape.rectangle", json!({"x": 40, "y": 40, "width": 100, "height": 80})); + run(&mut r, "paint.setStroke", json!({"color": "#0000ff"})); + run(&mut r, "stroke.set", json!({"weight": 4})); + run(&mut r, "appearance.addStroke", json!({"target": "object"})); + let top = node(&r, selected(&r).id).appearance.items.len() - 1; + run(&mut r, "appearance.setItem", json!({"index": top, "none": true})); + for (what, s) in [("type", &mut s), ("path", &mut r)] { + let info = run(s, "object.expand.info", json!({})); + assert_eq!(info["stroke"], json!(true), "{what}: {info}"); + run(s, "object.expand", json!({})); + assert_eq!(painted_strokes(s), 0, "{what}: the stroke is outlined"); + } +} + +/// Expand keeps the type object's own stroke and gradient fill through Object: Stroke outlines the +/// stroke and Fill expands the gradient, and with those options off they stay live on the path of +/// the whole outline. The picture stays the same either way. +#[test] +fn expand_keeps_the_type_objects_own_paint() { + for (opts, live) in [(json!({}), false), (json!({"fill": false, "stroke": false}), true)] { + let mut s = session(); + type_with_own_paint(&mut s, &[red_blue(), blue_stroke()], 1); + let before = render(&s); + run(&mut s, "object.expand", opts.clone()); + let after = render(&s); + assert!(mean_diff(&before, &after) < 2.0, "{opts}: {}", mean_diff(&before, &after)); + let (gradients, texts) = gradients_and_type(&s); + assert_eq!(texts, 0, "{opts}: outlined"); + assert_eq!(gradients == 1, live, "{opts}: {gradients} gradient fills"); + let mut strokes = 0; + s.doc().unwrap().doc.walk(|n| strokes += usize::from(!n.appearance.stroke_paint().is_none())); + assert_eq!(strokes == 1, live, "{opts}: {strokes} strokes"); + } +} + #[test] fn the_mesh_option_makes_a_mesh_that_paints_the_gradient() { for gradient in [ diff --git a/crates/engine/src/tests/pathops.rs b/crates/engine/src/tests/pathops.rs index b587740744..0564589d2d 100644 --- a/crates/engine/src/tests/pathops.rs +++ b/crates/engine/src/tests/pathops.rs @@ -1043,6 +1043,51 @@ fn create_outlines_matches_text_bounds() { assert!(s.doc().unwrap().doc.node(t).is_some()); } +/// The type object's own fills and strokes (not its characters') become paths of its whole outline, +/// with ids of their own: those below the Characters row under the glyph paths, the others over +/// them, in their order. The group keeps no appearance of its own, and Undo brings the type back. +#[test] +fn create_outlines_keeps_the_objects_own_fills_and_strokes() { + let mut s = session(); + let t = text(&mut s, "Hi", 64.0); + s.execute("select.set", &json!({"ids": [t.0]})).unwrap(); + for (cmd, p) in [ + ("appearance.addFill", json!({"target": "object"})), + ("appearance.setItem", json!({"index": 0, "color": "#ff0000"})), + ("appearance.addStroke", json!({"target": "object"})), + ("appearance.setItem", json!({"index": 1, "color": "#0000ff", "weight": 5})), + ("appearance.addFill", json!({"target": "object"})), + ("appearance.setItem", json!({"index": 2, "color": "#00ff00", "opacity": 50})), + // The red fill paints below the characters; the stroke and the green fill over them. + ("appearance.moveItem", json!({"from": "contents", "to": 1})), + ] { + s.execute(cmd, &p).unwrap_or_else(|e| panic!("{cmd} {p}: {e}")); + } + let own = node(&s, t).appearance.items.clone(); + assert_eq!(own.len(), 3); + let r = ids(&s.execute("type.createOutlines", &json!({})).unwrap()); + let g = node(&s, r[0]); + assert!(g.appearance.items.is_empty(), "the group paints nothing of its own"); + let ch = g.children().unwrap(); + assert_eq!(ch.len(), 4, "a path below, the two glyphs, a path above"); + let (first, last) = (&ch[0], &ch[3]); + assert_eq!(first.appearance.items, own[..1], "the item below the characters"); + assert_eq!(last.appearance.items, own[1..], "the items over them, in their order"); + for c in [first, last] { + assert!(matches!(c.kind, NodeKind::Path { .. }), "{:?}", c.kind); + assert!(c.id != t && c.id != g.id, "a fresh id"); + } + assert!(first.id != last.id); + for c in &ch[1..3] { + assert_eq!(c.appearance.fill_paint(), Paint::solid(Color::BLACK), "the characters keep their own paint"); + } + // The whole-outline paths cover both glyphs. + let glyphs = ch[1].geometric_bounds().unwrap().union(ch[2].geometric_bounds().unwrap()); + assert!(close(first.geometric_bounds().unwrap(), glyphs, 1e-6), "{:?} vs {glyphs:?}", first.geometric_bounds()); + s.execute("edit.undo", &json!({})).unwrap(); + assert_eq!(node(&s, t).appearance.items, own, "one undo step brings the type back"); +} + /// Colour bitmap glyphs (emoji) become embedded images where they sat; glyphs showing one image /// share its pixels. #[test] diff --git a/docs/mcp.md b/docs/mcp.md index 912ac32562..c82f17688b 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -4911,7 +4911,9 @@ stay live. `object.expand.info` answers which options have something to expand i (`{object, fill, stroke, gradient, appearance, symbolArt?}`; `gradient` says whether Fill has a gradient fill, which Expand Gradient To applies to, that it turns into art: it is false when every such fill repeats or reflects in more than 256 runs across the box it fills, which for type, symbol art included, is the box of each outline Object makes of a character, -ruby character, underline or strikethrough; `appearance` whether the selection has appearance attributes Expand Appearance expands first, +ruby character, underline or strikethrough, and for a fill of the type object itself the box of its whole outline; +`stroke` and `fill` count the type object's own strokes and pattern fills as well as its characters' (its own +only when the type has an outline, which type of only spaces does not); `appearance` whether the selection has appearance attributes Expand Appearance expands first, for which Object → Expand… is dimmed in the menus; `symbolArt: {fill, stroke, gradient}`, present when the selection holds symbol instances that `object` expands, whether only their art holds it, which Fill and Stroke reach only with `object` on), and `object.mesh.create` on a gradient-filled object colours the @@ -8328,6 +8330,14 @@ indent and bullet or number again. {"name":"run_command","arguments":{"command":"text.thread.add","params":{"from":42,"at":[300,120]}}} ``` +`type.createOutlines` (Type › Create Outlines) makes a group with a path per glyph, ruby glyph and underline or +strikethrough bar, each painted as its characters are. The type object's own fills and strokes (the items around the +Appearance panel's Characters row, not the characters' formatting) go on paths of the type's whole outline: the items +below the Characters row on one path under the glyph paths, the others on one path over them, in their order and with +their opacity, blend mode, effects and stroke settings. Their gradients that are not placed are fixed to the type's +layout box, as the canvas paints them, so one gradient spans every glyph. The group itself has no fills or strokes. +`object.expand` outlines type with this command, so Fill and Stroke expand those paths' gradients, patterns and strokes. + `type.createOutlines` (Type › Create Outlines) and `effect.expandAppearance` (when it outlines type, as Expand Appearance describes) take an outlined frame of threaded text out of its thread with the text it shows, and the other frames keep the text they hold. When the thread's last frame is outlined, the text it has no room for, which its diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..d5b3d6d4e4 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -667,6 +667,7 @@ since 2026-10-01. - **Colour emoji with bitmap glyphs:** `sbix` (Apple Color Emoji) and `CBDT` (Noto Color Emoji) glyphs draw as their largest strike's PNG, placed with the type and unpainted by its fill; PDF writes them in Type 3 glyphs or as images, SVG as images with outlined text, EPS and legacy `.ai` as images, and Create Outlines makes embedded images. Synthetic test fonts in `crates/text/src/test_fonts.rs`; tests `crates/text/src/tests/bitmaps.rs`, `crates/render/src/tests/bitmapglyphs.rs`, `crates/svg/tests/it/colour_glyphs.rs`, `pdf::tests::colour_bitmap_glyphs_export_as_images`, the `eps` `colour_bitmap_glyphs_*` tests and `tests_pathops::create_outlines_turns_bitmap_glyphs_into_images`. COLR and OpenType SVG fonts remain. - **COLR colour fonts:** v0 and v1 glyphs draw as vector layers (solid colours, linear and radial gradients, clips, blend modes, the foreground colour from the run's fill) on the canvas, as paths in SVG, EPS, legacy `.ai` and PDF with outlined text, as Type 3 text in PDF, and as editable groups with Create Outlines; sweep gradients and Porter-Duff compositing draw the glyph as an image. Measured by `tests_colr`, `render::tests_colrglyphs`, `pdf::tests::colr_glyphs_export_as_vector_art`, `svg/tests/it/colour_glyphs.rs`, `eps` `colr_glyphs_*`, `tests_pathops::create_outlines_turns_colr_glyphs_into_layer_groups`. Scores unchanged (estimated). - **OpenType SVG colour fonts:** each glyph's SVG document is read by the SVG importer (registered by the engine) into vector art in the run's colours, then drawn, exported and outlined as COLR glyphs are; PDF real text writes them as text that draws nothing and draws their art where they sit. Measured by `tests_svg_glyphs`, `svg/tests/it/svg_glyphs.rs`, `tests_colourfonts::create_outlines_turns_svg_glyphs_into_groups`, `tests_colourfonts::pdf_real_text_draws_svg_glyphs_as_art_and_keeps_their_characters`. Scores unchanged (estimated). +- **Create Outlines keeps the type object's own appearance:** Type › Create Outlines and Object › Expand put the type object's own fills and strokes (the Appearance panel's items around its Characters row) on paths of the whole outline, below or above the glyph paths as that row places them, with one gradient across every glyph, so the outlines draw what the type drew; `object.expand.info` reports those strokes, pattern fills and gradient fills. Outlined characters keep their stroke and fill (the help page, ); a type object's appearance is separate from its characters' (the help page, ); where the object's items go is VectorCraft's own rule. Measured by `tests::pathops::create_outlines_keeps_the_objects_own_fills_and_strokes`, `tests::objexpand::create_outlines_draws_the_type_objects_own_paint`, `tests::objexpand::type_objects_own_stroke_pattern_and_gradient_are_reported`, `tests::objexpand::type_without_an_outline_reports_no_own_stroke_or_fill`, `tests::objexpand::a_stroke_under_an_unpainted_stroke_is_reported_and_outlined` and `tests::objexpand::expand_keeps_the_type_objects_own_paint`. Scores unchanged (estimated). - **Composite fonts that two documents define differently:** when two open documents each define a composite font (Type › Composite Fonts…) by the same name with different parts, each document draws and exports its type in its own definition: in a tiled or floating window drawn while the front document draws, when it is placed in a document that defines the name differently, and in an export made while another document is in front. Evidence: `tests::composite::nested_loans_of_one_name_use_each_documents_definition` and `threads_lending_one_name_at_once_use_their_own_definitions` (`crates/text`), `tests::fontchange::a_placed_document_draws_its_own_definition_of_a_name_the_page_defines` (`crates/render`). Scores unchanged (estimated). - **Character-style Auto leading:** capture and explicit style attributes retain Auto as a reset, distinct from omitted leading. Redefine and later numeric updates preserve local leading overrides; the Character Styles marker recognizes Auto correctly. Regression assertions cover partial definitions, Undo/Redo and native save/load serialization. Public references: [Character and paragraph styles](https://helpx.adobe.com/illustrator/using/character-paragraph-styles.html) and [Leading](https://helpx.adobe.com/illustrator/using/line-character-spacing.html). - **Character-style numeric bounds:** `charStyle.new`, `charStyle.edit`, and `charStyle.setAttrs` accept size from 0.1 to 1296 pt, explicit leading from 0.1 to 5000 pt, horizontal and vertical scale from 1% to 10,000%, and baseline shift and Tate-chu-yoko offsets from -1296 to 1296 pt. Out-of-range input leaves the style and Undo history unchanged; Auto leading remains available. These limits match the Character panel controls. From c477db87ba079d6d0498eddd89497653fd45e318 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:39:04 -0800 Subject: [PATCH 14/33] Live Paint: an edit that changes nothing records no undo step A Live Paint Bucket click or drag over faces that already have the fill, a stroke on an edge that already has the paint and width, Gap Options with the options the groups have and no gap to close, and Make or Merge given one Live Paint group alone now leave the document, the undo and redo steps and the saved state as they are, and reply as usual. Such a fill or stroke also leaves the recent colors as they are. Gap Options with Close Gaps With Paths makes a group's faces again only when it adds a path or what paint stops at changes, so the faces keep their ids otherwise. --- crates/engine/src/cmd/buildcmds.rs | 10 +- crates/engine/src/cmd/buildcmds/live_paint.rs | 168 ++++++++++++---- crates/engine/src/tests/build.rs | 187 ++++++++++++++++++ crates/ui-egui/src/dialogs/live_paint_gaps.rs | 34 ++++ docs/mcp.md | 6 + docs/target-app-parity.md | 1 + 6 files changed, 368 insertions(+), 38 deletions(-) diff --git a/crates/engine/src/cmd/buildcmds.rs b/crates/engine/src/cmd/buildcmds.rs index 94fe55c119..3fc5953619 100644 --- a/crates/engine/src/cmd/buildcmds.rs +++ b/crates/engine/src/cmd/buildcmds.rs @@ -72,7 +72,7 @@ pub fn specs() -> Vec { "Make", ["Object", "Live Paint"], Some("Cmd+Alt+X"), - "{ids?|id?} build a Live Paint group from the selected paths (paths, compound paths and groups of them; type, images and other objects are refused, nothing changes); each covered face starts with its front-most covering source's fill; with a Live Paint group among them, add the others to it as livePaint.merge does → {id, faces, edges}", + "{ids?|id?} build a Live Paint group from the selected paths (paths, compound paths and groups of them; type, images and other objects are refused, nothing changes); each covered face starts with its front-most covering source's fill; with a Live Paint group among them, add the others to it as livePaint.merge does; a Live Paint group alone has nothing to add, so it is selected and nothing changes (no undo step) → {id, faces, edges}", has_selection_or_ids, lp_make ), @@ -81,7 +81,7 @@ pub fn specs() -> Vec { "Merge", ["Object", "Live Paint"], None, - "{ids?|id?} add the other selected paths (compound paths, groups of paths and Live Paint groups too; type, images and other objects are refused, nothing changes) to the bottom-most Live Paint group, keeping its id, its paint and its own properties (name, lock, visibility, transparency, appearance, opacity mask, graphic style link) → {id, faces, edges}", + "{ids?|id?} add the other selected paths (compound paths, groups of paths and Live Paint groups too; type, images and other objects are refused, nothing changes) to the bottom-most Live Paint group, keeping its id, its paint and its own properties (name, lock, visibility, transparency, appearance, opacity mask, graphic style link); a Live Paint group alone has nothing to add, so it is selected and nothing changes (no undo step) → {id, faces, edges}", has_multi_or_ids, lp_merge ), @@ -108,7 +108,7 @@ pub fn specs() -> Vec { "Gap Options", [], None, - "{id?, ids?: [id…] (instead of id; default: the selected Live Paint groups), detect?: bool (Gap Detection: paint stops at gaps up to size wide), size?: \"small\" (3 pt) | \"medium\" (6 pt) | \"large\" (12 pt) | pt (custom, over 0 up to 72) (Paint Stops At), color?: #rrggbb or a layer color's name such as \"Red\" (Gap Preview Color; a hex color with an alpha part, #rrggbbaa or #rgba, is an error), closeGaps?: bool (add an unpainted path across each gap up to size wide to the group's paths)} set the Gap Options of Live Paint groups (what is not given stays as it was; a group starts with detect false, small and Red) and make their faces again, keeping their paint → {ids, faces, gaps: [[x0,y0,x1,y1]…] (the gaps up to size wide that no path closes, from a loose end of a path to where it closes to; detect closes them for paint), closed (paths added)}", + "{id?, ids?: [id…] (instead of id; default: the selected Live Paint groups), detect?: bool (Gap Detection: paint stops at gaps up to size wide), size?: \"small\" (3 pt) | \"medium\" (6 pt) | \"large\" (12 pt) | pt (custom, over 0 up to 72) (Paint Stops At), color?: #rrggbb or a layer color's name such as \"Red\" (Gap Preview Color; a hex color with an alpha part, #rrggbbaa or #rgba, is an error), closeGaps?: bool (add an unpainted path across each gap up to size wide to the group's paths)} set the Gap Options of Live Paint groups (what is not given stays as it was; a group starts with detect false, small and Red) and make their faces again, keeping their paint; when every group already has the options and closeGaps finds no gap, nothing changes (no undo step) → {ids, faces, gaps: [[x0,y0,x1,y1]…] (the gaps up to size wide that no path closes, from a loose end of a path to where it closes to; detect closes them for paint), closed (paths added)}", has_selection_or_ids, lp_gap_options ), @@ -117,7 +117,7 @@ pub fn specs() -> Vec { "Live Paint Bucket", [], None, - "{group?: id | ids?|id?: [id…] (paths to make live first, as livePaint.make does), point: [x,y], points?: [[x,y]…] (a drag polyline starting at point; 1 to 4096 finite canvas points; fills every crossed face of the same group), continuation?: [[[x,y]…]…] (further path chunks of 1 to 4096 finite canvas points each; each starts at the preceding chunk's last point; requires points), color?|swatch?|gradient?|none? (default: current fill; a color in #rrggbbaa or #rgba is refused), tint?: 0..100, keepModel?: bool (both as paint.setFill)} fill the face containing point, skipping hidden faces → {group, face}", + "{group?: id | ids?|id?: [id…] (paths to make live first, as livePaint.make does), point: [x,y], points?: [[x,y]…] (a drag polyline starting at point; 1 to 4096 finite canvas points; fills every crossed face of the same group), continuation?: [[[x,y]…]…] (further path chunks of 1 to 4096 finite canvas points each; each starts at the preceding chunk's last point; requires points), color?|swatch?|gradient?|none? (default: current fill; a color in #rrggbbaa or #rgba is refused), tint?: 0..100, keepModel?: bool (both as paint.setFill)} fill the face containing point, skipping hidden faces; when every face it reaches already has the paint, nothing changes (no undo step, and the recent colors stay as they are) → {group, face}", has_doc, lp_fill ), @@ -126,7 +126,7 @@ pub fn specs() -> Vec { "Live Paint Bucket (Stroke)", [], None, - "{group: id, point: [x,y], color?|swatch?|none? (default: current stroke), width?: pt (default: current weight), tolerance?: pt (4), tint?: 0..100, keepModel?: bool (both as paint.setFill)} paint the edge nearest point → {group, edge}", + "{group: id, point: [x,y], color?|swatch?|none? (default: current stroke), width?: pt (default: current weight), tolerance?: pt (4), tint?: 0..100, keepModel?: bool (both as paint.setFill)} paint the edge nearest point; when it already has the paint and the width, nothing changes (no undo step, and the recent colors stay as they are) → {group, edge}", has_doc, lp_stroke_edge ), diff --git a/crates/engine/src/cmd/buildcmds/live_paint.rs b/crates/engine/src/cmd/buildcmds/live_paint.rs index e5e7757486..fb9e6c32ac 100644 --- a/crates/engine/src/cmd/buildcmds/live_paint.rs +++ b/crates/engine/src/cmd/buildcmds/live_paint.rs @@ -52,8 +52,34 @@ fn make_live(d: &mut Document, sel: &mut Selection, roots: &[NodeId]) -> Result< Ok((gid, nf, ne)) } +/// Select `ids` alone, without an undo step, when they are not selected that way already. +fn select_only(s: &mut Session, ids: &[NodeId]) -> Result<()> { + let st = s.doc()?; + let mut sel = st.selection.clone(); + sel.set(ids.iter().copied()); + if sel != st.selection { + s.select(|_, selection| *selection = sel)?; + } + Ok(()) +} + +/// Make or Merge given one Live Paint group and nothing else has nothing to add to it, so it leaves +/// the document as it is, records no undo step and selects the group: → its reply. `None` for any +/// other `roots`. +fn lone_live_group(s: &mut Session, roots: &[NodeId]) -> Result> { + let d = &s.doc()?.doc; + let Some(&g) = roots.first().filter(|g| roots.iter().all(|r| r == *g)) else { return Ok(None) }; + let Some(n) = d.node(g).filter(|n| is_live_paint(n)) else { return Ok(None) }; + let reply = json!({ "id": g.0, "faces": b::faces(n).len(), "edges": b::edges(n).len() }); + select_only(s, &[g])?; + Ok(Some(reply)) +} + pub(super) fn lp_make(s: &mut Session, p: &Value) -> Result { let roots = root_ids(s, p)?; + if let Some(reply) = lone_live_group(s, &roots)? { + return Ok(reply); + } let (id, faces, edges) = s.edit("Make Live Paint", |d, sel| make_live(d, sel, &roots))?; Ok(json!({ "id": id.0, "faces": faces, "edges": edges })) } @@ -130,6 +156,9 @@ fn merge_live(d: &mut Document, sel: &mut Selection, roots: &[NodeId]) -> Result pub(super) fn lp_merge(s: &mut Session, p: &Value) -> Result { let roots = root_ids(s, p)?; + if let Some(reply) = lone_live_group(s, &roots)? { + return Ok(reply); + } let (id, faces, edges) = s.edit("Merge Live Paint", |d, sel| merge_live(d, sel, &roots))?; Ok(json!({ "id": id.0, "faces": faces, "edges": edges })) } @@ -230,27 +259,58 @@ pub(super) fn lp_gap_options(s: &mut Session, p: &Value) -> Result { }; let close = switch_or(p, "closeGaps", false, C)?; let targets = live_targets(s, p)?; + let with_params = |mut options: GapOptions| { + options.detect = detect.unwrap_or(options.detect); + options.size = size.unwrap_or(options.size); + options.color = color.unwrap_or(options.color); + options + }; + // When every group already has these options and Close Gaps With Paths has no gap to close, + // nothing changes: no undo step, and the reply is the same. + let unchanged = { + let d = &s.doc()?.doc; + let (mut faces, mut gaps, mut same) = (0, vec![], true); + for g in &targets { + let old = d.node(*g).ok_or(EngineError::NoNode(*g))?; + let options = old.gap_options(); + if with_params(options) != options { + same = false; + break; + } + let open = open_gaps(old, options); + if close && !open.is_empty() { + same = false; + break; + } + faces += b::faces(old).len(); + gaps.extend(open); + } + same.then_some((faces, gaps)) + }; + if let Some((faces, gaps)) = unchanged { + select_only(s, &targets)?; + return Ok(json!({ "ids": ids_json(&targets), "faces": faces, "gaps": gaps_json(&gaps), "closed": 0 })); + } let (faces, gaps, closed) = s.edit("Gap Options", |d, sel| { let (mut faces, mut gaps, mut closed) = (0, vec![], 0); let mut changed = Beside::replacing(d, &targets); for g in &targets { let old = d.node(*g).cloned().ok_or(EngineError::NoNode(*g))?; - let mut options = old.gap_options(); - options.detect = detect.unwrap_or(options.detect); - options.size = size.unwrap_or(options.size); - options.color = color.unwrap_or(options.color); + let options = with_params(old.gap_options()); let mut sources = source_nodes(&old); + let mut added = 0; if close { // Close Gaps With Paths adds an unpainted line across each gap to the group's paths. for gap in po::find_gaps(&source_shapes(&sources).0, options.size.points()) { let line = PathData::single(SubPath::polyline(&gap, false)); sources.push(Arc::new(Node::path(d.alloc_id(), line, Appearance::default()))); - closed += 1; + added += 1; } } + closed += added; let mut n = old.clone(); // The faces change only with what paint stops at, or with new paths. - if close || options.max_gap() != old.gap_options().max_gap() { + if added > 0 || options.max_gap() != old.gap_options().max_gap() { let (children, _, _) = build_children(d, sources, options.max_gap())?; if let Some(ch) = n.children_mut() { *ch = children; @@ -305,27 +365,9 @@ pub(super) fn lp_fill(s: &mut Session, p: &Value) -> Result { let paint = paint_from(s, p)?.unwrap_or_else(|| s.paint.fill.clone()); let group = object_param(p, "group", C)?; let roots = if group.is_none() { root_ids(s, p)? } else { vec![] }; - let (g, face) = s.edit("Live Paint Bucket", |d, sel| { - let gid = match group { - Some(g) => { - if !d.node(g).is_some_and(is_live_paint) { - return Err(bad(C, format!("{g} is not a Live Paint group"))); - } - g - } - None => { - let live_at = |face: fn(&Node, Point) -> Option<&Arc>| { - roots.iter().copied().find(|r| d.node(*r).is_some_and(|n| is_live_paint(n) && face(n, pt).is_some())) - }; - match (live_at(b::visible_face_at), live_at(face_at)) { - (Some(g), _) => g, - // At a point on a hidden face of a Live Paint group among the objects, the command returns an error - // and does not merge the objects. - (None, Some(_)) => return Err(EngineError::Other("no Live Paint face at that point".into())), - (None, None) => make_live(d, sel, &roots)?.0, - } - } - }; + // The faces the click or drag paints in Live Paint group `gid`: the visible face at the point, + // and every visible face the drag crosses; → (the face at the point, the faces). + let targets_in = |d: &Document, gid: NodeId| -> Result<(NodeId, std::collections::BTreeSet)> { let fid = d .node(gid) .and_then(|n| b::visible_face_at(n, pt)) @@ -355,11 +397,58 @@ pub(super) fn lp_fill(s: &mut Session, p: &Value) -> Result { targets.extend(map.hits_along(&chunk, None).regions.iter().filter_map(|i| face_ids.get(*i).copied())); } } - for target in targets { - d.node_mut(target).ok_or(EngineError::NoNode(target))?.appearance.set_fill(paint.clone()); + Ok((fid, targets)) + }; + // The Live Paint group the fill paints; `None` when the objects are made into one first. + let live = { + let d = &s.doc()?.doc; + match group { + Some(g) => { + if !d.node(g).is_some_and(is_live_paint) { + return Err(bad(C, format!("{g} is not a Live Paint group"))); + } + Some(g) + } + None => { + let live_at = |face: fn(&Node, Point) -> Option<&Arc>| { + roots.iter().copied().find(|r| d.node(*r).is_some_and(|n| is_live_paint(n) && face(n, pt).is_some())) + }; + match (live_at(b::visible_face_at), live_at(face_at)) { + (Some(g), _) => Some(g), + // At a point on a hidden face of a Live Paint group among the objects, the command returns an error + // and does not merge the objects. + (None, Some(_)) => return Err(EngineError::Other("no Live Paint face at that point".into())), + (None, None) => None, + } + } } - Ok((gid, fid)) - })?; + }; + let (g, face) = match live { + Some(gid) => { + let (fid, targets) = targets_in(&s.doc()?.doc, gid)?; + // Faces that already have the paint stay as they are: when every face has it, nothing + // changes and no undo step is recorded. + let d = &s.doc()?.doc; + let unpainted: Vec = targets.into_iter().filter(|t| d.node(*t).is_none_or(|f| f.appearance.fill_paint() != paint)).collect(); + if unpainted.is_empty() { + return Ok(json!({ "group": gid.0, "face": fid.0 })); + } + s.edit("Live Paint Bucket", |d, _| { + for target in unpainted { + d.node_mut(target).ok_or(EngineError::NoNode(target))?.appearance.set_fill(paint.clone()); + } + Ok((gid, fid)) + })? + } + None => s.edit("Live Paint Bucket", |d, sel| { + let gid = make_live(d, sel, &roots)?.0; + let (fid, targets) = targets_in(d, gid)?; + for target in targets { + d.node_mut(target).ok_or(EngineError::NoNode(target))?.appearance.set_fill(paint.clone()); + } + Ok((gid, fid)) + })?, + }; s.remember_paint(&paint); Ok(json!({ "group": g.0, "face": face.0 })) } @@ -375,9 +464,22 @@ pub(super) fn lp_stroke_edge(s: &mut Session, p: &Value) -> Result { return Err(bad(C, "width out of range")); } let tol = number_or(p, "tolerance", 4.0, C)?.clamp(0.0, 1000.0); - let edge = s.edit("Live Paint Bucket", |d, _| { + let (eid, unchanged) = { + let d = &s.doc()?.doc; let n = d.node(g).filter(|n| is_live_paint(n)).ok_or_else(|| bad(C, format!("{g} is not a Live Paint group")))?; - let eid = b::visible_edge_near(n, pt, tol).map(|e| e.id).ok_or_else(|| EngineError::Other("no Live Paint edge near that point".into()))?; + let e = b::visible_edge_near(n, pt, tol).ok_or_else(|| EngineError::Other("no Live Paint edge near that point".into()))?; + // An edge that already has the paint and the width (or has no stroke and gets none) stays as + // it is, and no undo step is recorded. + let unchanged = match e.appearance.stroke() { + Some(st) => st.paint == paint && st.width == width, + None => paint.is_none(), + }; + (e.id, unchanged) + }; + if unchanged { + return Ok(json!({ "group": g.0, "edge": eid.0 })); + } + let edge = s.edit("Live Paint Bucket", |d, _| { let e = d.node_mut(eid).ok_or(EngineError::NoNode(eid))?; e.appearance.set_stroke(paint.clone()); if let Some(st) = e.appearance.stroke_mut() { diff --git a/crates/engine/src/tests/build.rs b/crates/engine/src/tests/build.rs index cb61ac1b9e..cb908efafd 100644 --- a/crates/engine/src/tests/build.rs +++ b/crates/engine/src/tests/build.rs @@ -2446,6 +2446,193 @@ fn live_paint_stroke_edge() { assert!(s.execute("livePaint.strokeEdge", &json!({"group": g.0, "point": [300, 300]})).is_err()); } +/// The undo and redo depths, the unsaved state and the revision of the active document. +fn history_state(s: &Session) -> (usize, usize, bool, u64) { + let st = s.doc().unwrap(); + (st.history.undo.len(), st.history.redo.len(), st.is_dirty(), st.revision) +} + +/// Draw a rectangle and undo it, so a redo step is waiting, then mark the document saved with `g` +/// alone selected. +fn redo_waiting_and_saved(s: &mut Session, g: NodeId) { + rect(s, 300.0, 300.0, 10.0, 10.0); + s.execute("edit.undo", &json!({})).unwrap(); + s.execute("select.set", &json!({"ids": [g.0]})).unwrap(); + s.doc_mut().unwrap().mark_saved(); +} + +/// The active document and its [`history_state`]. +type Snapshot = (vectorcraft_doc::Document, (usize, usize, bool, u64)); + +fn snapshot(s: &Session) -> Snapshot { + ((*s.doc().unwrap().doc).clone(), history_state(s)) +} + +/// After a Live Paint edit that changed nothing: the document, the undo and redo steps, the saved +/// state and the revision are as `before`, and Redo still brings back the rectangle +/// [`redo_waiting_and_saved`] undid. Undoes the rectangle again, marks the document saved and +/// takes a new `before`. +fn assert_unchanged(s: &mut Session, before: &mut Snapshot, what: &str) { + assert!(*s.doc().unwrap().doc == before.0, "{what}: the document is as it was"); + assert_eq!(history_state(s), before.1, "{what}: no undo step, the redo step kept, still saved"); + let children = layer_children(s); + s.execute("edit.redo", &json!({})).unwrap(); + assert_eq!(layer_children(s), children + 1, "{what}: Redo brings back the rectangle"); + s.execute("edit.undo", &json!({})).unwrap(); + s.doc_mut().unwrap().mark_saved(); + *before = snapshot(s); +} + +#[test] +fn live_paint_fill_same_paint_is_no_op() { + let mut s = session(); + let g = live(&mut s); + let drag = json!({"group": g.0, "point": [25, 50], "points": [[25, 50], [125, 50]], "color": "#00ff00"}); + s.execute("livePaint.fill", &drag).unwrap(); + redo_waiting_and_saved(&mut s, g); + // A color used since stays the newest recent color and the last color. + let blue = Color::rgb(0.0, 0.0, 1.0); + s.remember_paint_now(&Paint::solid(blue)); + let recent = (s.recent_colors.clone(), s.last_solid); + let mut before = snapshot(&s); + let click = s.execute("livePaint.fill", &json!({"group": g.0, "point": [75, 50], "color": "#00ff00"})).unwrap(); + let face = vectorcraft_tools::builder::face_at(&node(&s, g), Point::new(75.0, 50.0)).unwrap().id; + assert_eq!(click, json!({"group": g.0, "face": face.0}), "the usual reply"); + assert_unchanged(&mut s, &mut before, "a click on a face with the paint"); + s.execute("livePaint.fill", &drag).unwrap(); + assert_unchanged(&mut s, &mut before, "a drag over faces with the paint"); + // Without a group, the click finds the Live Paint group among the selected objects. + s.execute("livePaint.fill", &json!({"point": [125, 50], "color": "#00ff00"})).unwrap(); + assert_unchanged(&mut s, &mut before, "a click in the selected group"); + assert_eq!((s.recent_colors.clone(), s.last_solid), recent, "the recent colors are as they were"); + // A drag that reaches one face without the paint is one undo step and remembers the color. + let red = Paint::solid(Color::from_hex("#ff0000").unwrap()); + for x in [25, 75] { + s.execute("livePaint.fill", &json!({"group": g.0, "point": [x, 50], "color": "#ff0000"})).unwrap(); + } + s.remember_paint_now(&Paint::solid(blue)); + let depth = s.doc().unwrap().history.undo.len(); + s.execute("livePaint.fill", &json!({"group": g.0, "point": [25, 50], "points": [[25, 50], [125, 50]], "color": "#ff0000"})).unwrap(); + assert_eq!(s.doc().unwrap().history.undo.len(), depth + 1); + assert_eq!(Paint::solid(s.last_solid), red); + let gn = node(&s, g); + for x in [25.0, 75.0, 125.0] { + assert_eq!(vectorcraft_tools::builder::face_at(&gn, Point::new(x, 50.0)).unwrap().appearance.fill_paint(), red, "{x}"); + } +} + +#[test] +fn live_paint_bucket_click_with_the_face_paint_is_no_op() { + let mut s = session(); + let g = live(&mut s); + let v = ViewInfo::default(); + s.select_tool("livePaintBucket", v).unwrap(); + s.paint.fill = Paint::solid(Color::rgb(0.0, 1.0, 1.0)); + let click = |s: &mut Session, x: f64| { + s.pointer(&PointerEvent::new(PointerKind::Down, x, 50.0), v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Up, x, 50.0), v).unwrap(); + }; + click(&mut s, 125.0); + redo_waiting_and_saved(&mut s, g); + let mut before = snapshot(&s); + click(&mut s, 125.0); + assert_unchanged(&mut s, &mut before, "a Bucket click on a face with the paint"); + // A drag over faces with the paint changes nothing either. + click(&mut s, 25.0); + click(&mut s, 75.0); + redo_waiting_and_saved(&mut s, g); + let mut before = snapshot(&s); + s.pointer(&PointerEvent::new(PointerKind::Down, 25.0, 50.0), v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Drag, 125.0, 50.0), v).unwrap(); + s.pointer(&PointerEvent::new(PointerKind::Up, 125.0, 50.0), v).unwrap(); + assert_unchanged(&mut s, &mut before, "a Bucket drag over faces with the paint"); +} + +#[test] +fn live_paint_stroke_edge_same_paint_and_width_is_no_op() { + let mut s = session(); + let g = live(&mut s); + let call = json!({"group": g.0, "point": [100, 50], "color": "#ff0000", "width": 3}); + let first = s.execute("livePaint.strokeEdge", &call).unwrap(); + redo_waiting_and_saved(&mut s, g); + s.remember_paint_now(&Paint::solid(Color::rgb(0.0, 0.0, 1.0))); + let last = s.last_solid; + let mut before = snapshot(&s); + assert_eq!(s.execute("livePaint.strokeEdge", &call).unwrap(), first, "the usual reply"); + assert_unchanged(&mut s, &mut before, "an edge with the paint and the width"); + assert_eq!(s.last_solid, last, "the last color is as it was"); + // Another width is an edit. + s.execute("livePaint.strokeEdge", &json!({"group": g.0, "point": [100, 50], "color": "#ff0000", "width": 2})).unwrap(); + assert_eq!(history_state(&s).0, before.1.0 + 1); +} + +#[test] +fn live_paint_gap_options_same_values_are_no_op() { + let mut s = session(); + let g = live_square_with_a_gap(&mut s); + let set = json!({"detect": true, "size": "small", "color": "#123456"}); + let first = s.execute("livePaint.gapOptions", &set).unwrap(); + redo_waiting_and_saved(&mut s, g); + let mut before = snapshot(&s); + assert_eq!(s.execute("livePaint.gapOptions", &set).unwrap(), first, "the usual reply"); + assert_unchanged(&mut s, &mut before, "the options the group has"); + s.execute("livePaint.gapOptions", &json!({})).unwrap(); + assert_unchanged(&mut s, &mut before, "no options given"); + // Close Gaps With Paths with a gap to close is an edit. + let r = s.execute("livePaint.gapOptions", &json!({"closeGaps": true})).unwrap(); + assert_eq!(r["closed"], 1); + assert_eq!(history_state(&s).0, before.1.0 + 1); +} + +#[test] +fn live_paint_gap_options_close_without_gaps_is_no_op() { + let mut s = session(); + let g = live(&mut s); + redo_waiting_and_saved(&mut s, g); + let mut before = snapshot(&s); + let r = s.execute("livePaint.gapOptions", &json!({"id": g.0, "closeGaps": true})).unwrap(); + assert_eq!((ids(&r), &r["faces"], &r["closed"], gaps_of(&r).len()), (vec![g], &json!(3), &json!(0), 0)); + assert_unchanged(&mut s, &mut before, "no gap to close"); + // With other options, the faces keep their ids when no path is added. + let faces = |s: &Session| vectorcraft_tools::builder::faces(&node(s, g)).iter().map(|f| f.id).collect::>(); + let ids_before = faces(&s); + s.execute("livePaint.gapOptions", &json!({"id": g.0, "closeGaps": true, "color": "#00ff00"})).unwrap(); + assert_eq!(history_state(&s).0, before.1.0 + 1); + assert_eq!(faces(&s), ids_before); +} + +#[test] +fn live_paint_make_existing_group_is_no_op() { + let mut s = session(); + let g = live(&mut s); + redo_waiting_and_saved(&mut s, g); + let mut before = snapshot(&s); + let r = s.execute("livePaint.make", &json!({})).unwrap(); + assert_eq!((NodeId(r["id"].as_u64().unwrap()), &r["faces"]), (g, &json!(3))); + assert_eq!(r["edges"].as_u64().unwrap(), vectorcraft_tools::builder::edges(&node(&s, g)).len() as u64); + assert_unchanged(&mut s, &mut before, "Make with the group alone"); +} + +#[test] +fn live_paint_merge_single_group_is_no_op() { + let mut s = session(); + let g = live(&mut s); + let other = rect(&mut s, 300.0, 0.0, 50.0, 50.0); + redo_waiting_and_saved(&mut s, g); + // Given by id, the group is selected without an undo step. + s.execute("select.set", &json!({"ids": [other.0]})).unwrap(); + let before = snapshot(&s); + let r = s.execute("livePaint.merge", &json!({"ids": [g.0]})).unwrap(); + assert_eq!((NodeId(r["id"].as_u64().unwrap()), &r["faces"]), (g, &json!(3))); + assert_eq!(s.doc().unwrap().selection.objects, vec![g]); + assert!(*s.doc().unwrap().doc == before.0); + let (undo, redo, dirty, _) = history_state(&s); + assert_eq!((undo, redo, dirty), (before.1.0, before.1.1, before.1.2)); + // A path to add is an edit. + s.execute("livePaint.merge", &json!({"ids": [g.0, other.0]})).unwrap(); + assert_eq!(history_state(&s).0, before.1.0 + 1); +} + /// Shape Builder refuses an on/off option that is not true or false and a tolerance or gap that is /// not a number, given as text or as another value of the wrong kind, naming the key; the document /// and the undo history stay as they were. True, false and null still work. diff --git a/crates/ui-egui/src/dialogs/live_paint_gaps.rs b/crates/ui-egui/src/dialogs/live_paint_gaps.rs index e01a08e9a4..f741d767c4 100644 --- a/crates/ui-egui/src/dialogs/live_paint_gaps.rs +++ b/crates/ui-egui/src/dialogs/live_paint_gaps.rs @@ -263,6 +263,40 @@ mod tests { assert_eq!((d.str("size"), d.f64("custom", 0.0)), ("large".to_string(), GapSize::LARGE)); } + /// OK on the options the group has, with Preview on or off, closes the dialog and records no + /// undo step: the redo step stays and a saved document stays saved. + #[test] + fn ok_on_unchanged_options_records_no_undo_step() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 300, "height": 300})).unwrap(); + app.run("shape.rectangle", json!({"x": 50, "y": 50, "width": 100, "height": 100})).unwrap(); + app.run("select.all", json!({})).unwrap(); + let g = app.run("livePaint.make", json!({})).unwrap()["id"].as_u64().unwrap(); + app.run("shape.rectangle", json!({"x": 200, "y": 200, "width": 10, "height": 10})).unwrap(); + app.run("edit.undo", json!({})).unwrap(); + app.run("select.set", json!({"ids": [g]})).unwrap(); + app.session.doc_mut().unwrap().mark_saved(); + let state = |app: &VectorcraftApp| { + let st = app.session.doc().unwrap(); + (st.history.undo.len(), st.history.redo.len(), st.is_dirty()) + }; + let before = state(&app); + for preview in [false, true] { + app.run("ui.livePaintGapOptions", json!({})).unwrap(); + app.ui.dialog.as_mut().unwrap().fields.insert("preview".into(), json!(preview)); + frame(&mut app); + crate::dialogs::confirm(&mut app).unwrap(); + assert!(app.ui.dialog.is_none(), "preview={preview}: OK closes the dialog"); + assert_eq!(state(&app), before, "preview={preview}: no undo step"); + assert_eq!(options(&app, g), GapOptions::default()); + } + // A changed option is one undo step. + app.run("ui.livePaintGapOptions", json!({})).unwrap(); + app.ui.dialog.as_mut().unwrap().fields.insert("detect".into(), json!(true)); + crate::dialogs::confirm(&mut app).unwrap(); + assert_eq!(state(&app).0, before.0 + 1); + } + /// A Gap Preview Color with an alpha part is refused and leaves the group's options as they /// were; `#rrggbb` still sets them. #[test] diff --git a/docs/mcp.md b/docs/mcp.md index 912ac32562..604152e445 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3511,6 +3511,12 @@ already walks Image Trace. Short agent entry points: `livePaint.fill` does not fill a face that is hidden on its own. With `group`, a point on a hidden face is an error. With `ids`, at a point on a hidden face it fills the face there of another Live Paint group among the objects; otherwise it returns an error and does not merge the objects. + A Live Paint command that changes nothing records no undo step, keeps the redo steps, leaves a saved document saved + and replies as usual: `livePaint.fill` (a click, or a Bucket drag) when every face it reaches already has the paint, + `livePaint.strokeEdge` when the edge already has the paint and the width, `livePaint.gapOptions` (and the Gap Options + dialog's OK) when every group already has the options and `closeGaps` finds no gap, and `livePaint.make` or + `livePaint.merge` given one Live Paint group alone, which they select. Such a fill or stroke also leaves the recent + colors and the last color as they are. `livePaint.gapOptions` makes a group's faces again, with new ids, only when it adds a path or when Gap Detection is turned on or off or given a new size while it is on. `brush.selectionArt {ids?|id?}` (a query) replies `{art}`: the object that `brush.new` given the same `ids` or `id`, or the same selection, takes as a new scatter or art brush's art and as a new pattern brush's side tile. Several diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..4260184db8 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1429,6 +1429,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur View › Show Live Paint Gaps (`view.livePaintGaps`) draws the gaps of the selected Live Paint groups up to their Paint Stops At size, each in its group's Gap Preview Color, until Hide Live Paint Gaps or Hide Edges hides them ([Live Paint gaps](https://web.archive.org/web/20260105111201/https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/find-and-close-gaps-in-live-paint-groups.html)). Double-clicking the Live Paint Selection tool opens its Tool Options ([Live Paint Selection tool options](https://web.archive.org/web/20250904012025/https://helpx.adobe.com/illustrator/using/selecting-objects.html)): with Select Fills off, a click selects an edge instead of a face; with Select Strokes off, Alt-click selects a face instead of an edge; Color and Width set the color and width of the highlight on the face or edge under the pointer (red and 4 px by default). The options last across tool switches and are saved with the preferences (`tool.setOption {tool: "livePaintSelection"}`). As VectorCraft's own rule, OK in Live Paint Selection Tool Options, Live Paint Bucket Options, Gap Options and Tile Edge Color gives an error for a color with an alpha part (`#rrggbbaa` or `#rgba`) and changes nothing. + As VectorCraft's own rule, a Live Paint edit that changes nothing records no undo step, keeps the redo steps and leaves a saved document saved: a Live Paint Bucket click or drag over faces that already have the fill, a stroke on an edge that already has the paint and weight, Gap Options OK with the options the groups have and no gap to close, and Make or Merge with one Live Paint group alone. Such a fill or stroke also leaves the recent colors as they are. Regression tests: `live_paint_fill_same_paint_is_no_op`, `live_paint_bucket_click_with_the_face_paint_is_no_op`, `live_paint_stroke_edge_same_paint_and_width_is_no_op`, `live_paint_gap_options_same_values_are_no_op`, `live_paint_gap_options_close_without_gaps_is_no_op`, `live_paint_make_existing_group_is_no_op` and `live_paint_merge_single_group_is_no_op` in `crates/engine/src/tests/build.rs`, and `ok_on_unchanged_options_records_no_undo_step` in `crates/ui-egui/src/dialogs/live_paint_gaps.rs`. As VectorCraft's own rule, `tool.setOption` for the Live Paint Bucket and the Live Paint Selection tool, chosen or not, checks every value before it sets any: a switch that is not `true` or `false`, a `color` that is not an RGB color or a layer color's name, or that has an alpha part, a `width` that is not a number and an option the tool does not have give an error naming the option, and nothing is set or saved. A `width` outside 1 to 20 px is still set to the nearest end of that range. Saved settings that hold an invalid value still load, and the tool keeps its default for that option. Evidence: `tests::build::{live_paint_tool_options_refuse_invalid_values_and_change_nothing, live_paint_tool_options_take_rgb_colors_and_clamp_the_width, live_paint_tools_load_saved_settings_with_invalid_values}`. As VectorCraft's own rule, `tool.setOption` for the Shape Builder, chosen or not, checks every value the same way: a switch that is not `true` or `false`, a `pickColorFrom` or `selection` that is not one of its names, a `gapLength` that is not a number, a `highlightColor` that is not an RGB color or a layer color's name, or that has an alpha part, and an option the tool does not have give an error naming the option, and nothing is set or saved. A `gapLength` outside 0 to 72 pt is still set to the nearest end of that range. Evidence: `tests::build::{shape_builder_tool_options_refuse_invalid_values_and_change_nothing, shape_builder_tool_options_take_valid_values_and_clamp_the_gap}`. A drag of 3 screen pixels or more with the Live Paint Selection tool draws a marquee and selects the faces and edges it touches, partly enclosed ones included, and a Shift-drag adds them to the selection or removes them from it ([Modify Live Paint groups](https://web.archive.org/web/20261006115247/https://helpx.adobe.com/illustrator/desktop/paint-and-fill/learn-painting-basics/modify-live-paint-groups.html)). A click and a marquee change the selection on release, and Escape before the release leaves the selection as it was. Locked Live Paint groups, those on a locked layer or artboard, and in isolation mode those outside the isolated art are not highlighted or selected. From d7da446cd7aa5a10fbd6c1de19ff5c83ae92c252 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:39:15 -0800 Subject: [PATCH 15/33] Text Wrap: Offset is a distance in the document's units Text Wrap Options showed Offset as a plain number in points. It is now a distance field in the document's General unit: a bare number is in that unit, a typed unit is read as typed, and OK sends points. The field's range is the one object.textWrap.options keeps an Offset to, -1000 to 1000 pt, read from the engine's TEXT_WRAP_OFFSET_RANGE. A value typed outside it, or text that doesn't read, is refused: the field flashes and the dialog stays open. The ends as the field shows them in another unit (352.7778 mm for 1000 pt) read back as the ends. In the dialogs drawn by the shared dialog window, a click on OK no longer confirms the dialog when the field it takes the focus from refuses its text, as Enter already did not. OK pressed from the keyboard after such a click still confirms. --- crates/engine/src/cmd/textwrap.rs | 6 +- crates/engine/src/lib.rs | 1 + crates/ui-egui/src/dialogs.rs | 17 +++- crates/ui-egui/src/dialogs/command.rs | 137 ++++++++++++++++++++++++++ crates/ui-egui/src/dialogs/form.rs | 9 +- crates/ui-egui/src/widgets.rs | 12 +-- crates/ui-egui/src/widgets/numeric.rs | 29 +++++- crates/ui-egui/src/widgets/text.rs | 15 ++- docs/target-app-parity.md | 1 + 9 files changed, 212 insertions(+), 15 deletions(-) diff --git a/crates/engine/src/cmd/textwrap.rs b/crates/engine/src/cmd/textwrap.rs index c7c9adb261..d5eefe9d4d 100644 --- a/crates/engine/src/cmd/textwrap.rs +++ b/crates/engine/src/cmd/textwrap.rs @@ -34,8 +34,12 @@ pub fn specs() -> Vec { ] } +/// The Offset a wrap object takes, in points. +pub const TEXT_WRAP_OFFSET_RANGE: std::ops::RangeInclusive = -1000.0..=1000.0; + fn wrap_param(p: &Value, base: TextWrap) -> TextWrap { - TextWrap { offset: f64_or(p, "offset", base.offset).clamp(-1000.0, 1000.0), invert: bool_or(p, "invert", base.invert) } + let (lo, hi) = (*TEXT_WRAP_OFFSET_RANGE.start(), *TEXT_WRAP_OFFSET_RANGE.end()); + TextWrap { offset: f64_or(p, "offset", base.offset).clamp(lo, hi), invert: bool_or(p, "invert", base.invert) } } fn make(s: &mut Session, p: &Value) -> Result { diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index e3f2b91ee7..4b47966463 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -33,6 +33,7 @@ pub use cmd::params::{check_keys, check_params}; pub use cmd::pathops::auto_simplify_level; pub use cmd::rasterfx::{export_pdf, flatten_raster_effects}; pub use cmd::select::{path_fingerprint, storable_selection, stored_path_params, stored_path_step}; +pub use cmd::textwrap::TEXT_WRAP_OFFSET_RANGE; pub use cmd::{CommandInfo, CommandSpec, command_specs, find_command}; pub use cmd::{EyedropperOptions, PathfinderOptions}; pub use journal::Journal; diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index 552fbc0c06..1f3a01528c 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -783,6 +783,7 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { return window(app, ctx); } let mut ok = false; + let mut ok_clicked = false; let mut ok_enabled = true; let mut cancel = false; let mut reset = false; @@ -843,7 +844,7 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { if let Some(label) = spec.ok.map(|ok| spec.ok_label.map_or(ok, |f| f(app))) && widgets::primary_button_enabled(ui, label, ok_enabled).clicked() { - ok = true; + ok_clicked = true; } ui.add_space(8.0); // While Alt (Option) is held, Cancel reads Reset on a dialog that has one. @@ -878,6 +879,20 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) { }); }); let rejected = ctx.data_mut(|d| d.remove_temp::(widgets::dialog_reject_commit_flag())).unwrap_or(false); + // A field that refused its text when the press on OK took its focus keeps the dialog open, as + // Enter does. Only a pointer click has such a press; the keyboard and screen readers press OK + // in one frame. + let press = egui::Id::unique("dialog-press-pass"); + let (pressed, released) = ctx.input(|i| (i.pointer.any_pressed(), i.pointer.any_released())); + if pressed { + let pass = ctx.cumulative_pass_nr(); + ctx.data_mut(|m| m.insert_temp(press, pass)); + } + let (pressed_at, refused_at) = ctx.data(|m| (m.get_temp::(press), m.get_temp::(widgets::dialog_reject_pass()))); + let refused_during_click = released && matches!((refused_at, pressed_at), (Some(r), Some(p)) if r >= p); + if ok_clicked && !rejected && !refused_during_click { + ok = true; + } if spec.ok.is_some() && ok_enabled && !after_panel && !rejected && crate::shortcuts::pressed_anew(ctx, egui::Key::Enter) { ok = true; } diff --git a/crates/ui-egui/src/dialogs/command.rs b/crates/ui-egui/src/dialogs/command.rs index cb23628a4c..3f591ccd30 100644 --- a/crates/ui-egui/src/dialogs/command.rs +++ b/crates/ui-egui/src/dialogs/command.rs @@ -128,10 +128,20 @@ fn lengths(command: &str) -> &'static [&'static str] { "type.pathOptions" => &["spacing"], "object.pathfinder.trap" => &["thickness"], crate::addons::cutting::BRIDGES => &["width"], + "object.textWrap.options" => &["offset"], _ => &[], } } +/// The range (points) of a distance parameter of the commands this dialog edits, where the +/// command has one: the field refuses a distance typed outside it. +pub(super) fn length_range(command: &str, key: &str) -> Option> { + match (command, key) { + ("object.textWrap.options", "offset") => Some(vectorcraft_engine::TEXT_WRAP_OFFSET_RANGE), + _ => None, + } +} + /// The order the fields of the commands this dialog edits come in, where the order of their /// names isn't it (the rest follow, by name). fn order(command: &str) -> &'static [&'static str] { @@ -505,4 +515,131 @@ mod tests { assert_eq!(crate::panels::pattern_options::repeat_fields(&app).unwrap()["instances"], 4); assert!(!app.session.in_interaction()); } + + /// Text Wrap Options › Offset is a distance, and its range is the one `object.textWrap.options` + /// keeps an Offset to. + #[test] + fn text_wrap_offset_is_a_distance_within_the_commands_range() { + assert!(lengths("object.textWrap.options").contains(&"offset")); + assert!(!lengths("object.textWrap.options").contains(&"invert")); + let range = length_range("object.textWrap.options", "offset").expect("a range"); + assert_eq!(range, vectorcraft_engine::TEXT_WRAP_OFFSET_RANGE); + assert_eq!(length_range("object.textWrap.options", "invert"), None); + assert_eq!(length_range("text.areaOptions", "inset"), None); + let mut s = Session::new(); + s.execute("file.new", &json!({"width": 400, "height": 400})).unwrap(); + s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap(); + s.execute("select.all", &json!({})).unwrap(); + s.execute("object.textWrap.make", &json!({})).unwrap(); + for (sent, end) in [(5000.0, *range.end()), (-5000.0, *range.start())] { + assert_eq!(s.execute("object.textWrap.options", &json!({ "offset": sent })).unwrap()["offset"], json!(end)); + } + } + + /// Replaces the text of the dialog's Offset field with `text`, leaving the field focused. + fn type_offset(h: &mut egui_kittest::Harness<'_, VectorcraftApp>, text: &str) { + use egui::accesskit::Role; + use egui_kittest::kittest::Queryable; + let all = egui::Modifiers { ctrl: true, command: true, ..Default::default() }; + h.get_by_role_and_label(Role::TextInput, "Offset:").focus(); + h.run_steps(2); + h.key_press_modifiers(all, egui::Key::A); + h.get_by_role_and_label(Role::TextInput, "Offset:").type_text(text); + h.run_steps(2); + } + + /// Replaces the text of the dialog's Offset field with `text` and presses Enter. + fn enter_offset(h: &mut egui_kittest::Harness<'_, VectorcraftApp>, text: &str) { + type_offset(h, text); + h.key_press(egui::Key::Enter); + h.run_steps(3); + } + + /// Text Wrap Options › Offset reads a bare number in the document's units and a typed unit as + /// typed, and OK sends points. A distance typed outside the command's range, or text that + /// doesn't read, is refused: the dialog stays open and the Offset isn't changed. + #[test] + fn text_wrap_offset_field_takes_document_units_and_refuses_values_outside_its_range() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 400, "height": 400})).unwrap(); + app.run("document.setUnits", json!({"units": "millimeters"})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap(); + app.run("select.set", json!({"ids": [id]})).unwrap(); + app.run("object.textWrap.make", json!({})).unwrap(); + let mut fonts = false; + let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1100.0, 900.0)).build_ui_state( + move |ui, app: &mut VectorcraftApp| { + if !std::mem::replace(&mut fonts, true) { + crate::theme::install_fonts(ui.ctx()); + return; + } + crate::dialogs::show(app, ui.ctx()); + }, + app, + ); + let offset = |h: &mut egui_kittest::Harness<'_, VectorcraftApp>| { + h.state_mut().session.execute("object.textWrap.options", &json!({})).unwrap()["offset"].as_f64().unwrap() + }; + // The ends as the field shows them in millimeters (352.7778 mm is 1000.00006 pt) read + // back as the ends. + let ends = [("-1000 pt", -1000.0), ("352.7778", 1000.0), ("-352.7778", -1000.0), ("1000 pt", 1000.0)]; + for (text, want) in [("10", 10.0 * 72.0 / 25.4), ("-12 pt", -12.0)].into_iter().chain(ends) { + crate::menus::invoke(h.state_mut(), "object.textWrap.options", json!({})); + h.run_steps(3); + enter_offset(&mut h, text); + assert!(h.state().ui.dialog.is_none(), "{text}: OK applied it"); + assert!((offset(&mut h) - want).abs() < 1e-9, "{text}: {} pt", offset(&mut h)); + } + for text in ["1000.5 pt", "-2000", "abc"] { + crate::menus::invoke(h.state_mut(), "object.textWrap.options", json!({})); + h.run_steps(3); + enter_offset(&mut h, text); + let d = h.state().ui.dialog.as_ref().unwrap_or_else(|| panic!("{text}: the dialog stays open")); + assert_eq!(d.fields.get("offset"), Some(&json!(1000.0)), "{text}: the field keeps its value"); + crate::dialogs::cancel(h.state_mut()); + assert_eq!(offset(&mut h), 1000.0, "{text}: the Offset is unchanged"); + } + // A click on OK with a value outside the range still in the field is refused too. + crate::menus::invoke(h.state_mut(), "object.textWrap.options", json!({})); + h.run_steps(3); + type_offset(&mut h, "2000 pt"); + click_ok(&mut h); + assert!(h.state().ui.dialog.is_some(), "OK leaves the dialog open"); + assert_eq!(offset(&mut h), 1000.0, "the Offset is unchanged"); + // After it, a value inside the range committed with Tab and OK pressed from the keyboard + // apply. + type_offset(&mut h, "-7 pt"); + h.key_press(egui::Key::Tab); + h.run_steps(2); + { + use egui_kittest::kittest::Queryable; + h.get_by_label("OK").focus(); + } + h.run_steps(2); + h.key_press(egui::Key::Space); + h.run_steps(3); + assert!(h.state().ui.dialog.is_none(), "OK from the keyboard applied it"); + assert_eq!(offset(&mut h), -7.0); + // One inside it applies. + crate::menus::invoke(h.state_mut(), "object.textWrap.options", json!({})); + h.run_steps(3); + type_offset(&mut h, "-5 pt"); + click_ok(&mut h); + assert!(h.state().ui.dialog.is_none(), "OK applied it"); + assert_eq!(offset(&mut h), -5.0); + } + + /// Clicks the dialog's OK button with the pointer, the press and the release in frames of + /// their own. + fn click_ok(h: &mut egui_kittest::Harness<'_, VectorcraftApp>) { + use egui_kittest::kittest::Queryable; + let ok = h.get_by_label("OK").rect().center(); + let (button, modifiers) = (egui::PointerButton::Primary, egui::Modifiers::default()); + h.event(egui::Event::PointerMoved(ok)); + h.run_steps(1); + h.event(egui::Event::PointerButton { pos: ok, button, pressed: true, modifiers }); + h.run_steps(1); + h.event(egui::Event::PointerButton { pos: ok, button, pressed: false, modifiers }); + h.run_steps(3); + } } diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index bbddd222f5..033e194e50 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -453,7 +453,14 @@ pub(super) fn param_fields( if is_length(&k) { // An effect's documented range keeps the field to the distance it applies. let range = vectorcraft_effects::length_range(&d.str("__effect"), &k); - if spinners { + // A command's own range refuses a distance typed outside it. + if let Some(bounds) = super::command::length_range(&d.str("__command"), &k) { + let value = d.fields.contains_key(&k).then(|| d.f64(&k, 0.0)); + if let Some(x) = crate::widgets::num_field_refusing(ui, ("dlg-len", k.as_str()), value, unit, 140.0, bounds) { + d.fields.insert(k.clone(), json!(x)); + changed = true; + } + } else if spinners { let bounds = range.unwrap_or(if k == "blur" { 0.0..=f64::INFINITY } else { crate::widgets::ANY }); let value = d.fields.contains_key(&k).then(|| d.f64(&k, 0.0)); if let Some(x) = crate::widgets::spin_length_within(ui, ("dlg-len", k.as_str()), value, unit, 140.0, bounds) { diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index 4e1bc4f6a5..36145224d5 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -56,18 +56,18 @@ pub use labels::{dim_label, dim_name, divider, field_label, label_row, link_labe pub(crate) use labels::{label_field, take_previous_label, text_width}; pub(crate) use numeric::{ANY, round_to, set_bare_numbers_are_points, typed_unit}; pub use numeric::{ - SpinPick, mixed_field, mixed_field_named, num_field, num_field_live, num_field_named, num_field_within, num_field_within_empty, opt_field, - opt_field_named, plain_field_live, range_field, range_field_live, range_field_named, range_field_stepped, size_field, size_field_named, slider, - spin_field, spin_field_auto, spin_field_auto_named, spin_field_named, spin_field_stops_named, spin_kerning, spin_length_within, spin_plain, - spin_plain_named, spin_range, + SpinPick, mixed_field, mixed_field_named, num_field, num_field_live, num_field_named, num_field_refusing, num_field_within, + num_field_within_empty, opt_field, opt_field_named, plain_field_live, range_field, range_field_live, range_field_named, range_field_stepped, + size_field, size_field_named, slider, spin_field, spin_field_auto, spin_field_auto_named, spin_field_named, spin_field_stops_named, spin_kerning, + spin_length_within, spin_plain, spin_plain_named, spin_range, }; #[cfg(test)] use numeric::{decimal_point, field_number, share_of, spinner_step, step_through, step_to_multiple, within_a_millionth}; pub(crate) use panel_chrome::{PANEL_FOOTER, panel_footer, panel_footer_bottom, panel_footer_space, panel_list_height}; pub use panel_chrome::{bottom_bar, layout_button, list_box, orientation_button, tab_bar}; pub(crate) use text::{ - arm_field_focus, dialog_focus_flag, dialog_reject_commit_flag, disarm_field_focus, enter_modifiers, focus_field, own_field_shift_enter, - reject_dialog_commit, set_dialog_open, take_dialog_focus, + arm_field_focus, dialog_focus_flag, dialog_reject_commit_flag, dialog_reject_pass, disarm_field_focus, enter_modifiers, focus_field, + own_field_shift_enter, reject_dialog_commit, set_dialog_open, take_dialog_focus, }; pub use text::{ exact_text_field, exact_text_field_named, plain_field, plain_field_named, search_field, text_field, text_field_named, text_presets, diff --git a/crates/ui-egui/src/widgets/numeric.rs b/crates/ui-egui/src/widgets/numeric.rs index 3eec21b928..89ce8d70d5 100644 --- a/crates/ui-egui/src/widgets/numeric.rs +++ b/crates/ui-egui/src/widgets/numeric.rs @@ -67,6 +67,21 @@ pub fn num_field_within( unit_field(ui, id, label, None, value, unit, width, false, bounds, &[], false, "") } +/// [`num_field_within`] that refuses a value typed outside `bounds` (points), as it refuses text +/// that doesn't read: the field flashes, keeps its value and holds the dialog open. ↑/↓, the wheel +/// and a scrub still stop at the ends. +pub fn num_field_refusing( + ui: &mut Ui, + id: impl std::hash::Hash, + value: Option, + unit: Unit, + width: f32, + bounds: RangeInclusive, +) -> Option { + let label = take_previous_label(ui); + unit_field_from(ui, id, label, None, value, unit, width, false, bounds, &[], false, None, "", true) +} + /// [`num_field_within`] whose empty value reads `empty` (Transform Each's Absolute scales read /// "Mixed" when the selected objects differ): typing replaces it, as it replaces the value shown. pub fn num_field_within_empty( @@ -122,11 +137,12 @@ fn unit_field( live: bool, empty: &str, ) -> Option { - unit_field_from(ui, id, label, name, value, unit, width, share_of_value, bounds, stops, live, None, empty) + unit_field_from(ui, id, label, name, value, unit, width, share_of_value, bounds, stops, live, None, empty, false) } /// [`unit_field`] whose blank field (`value` is `None`) steps from `blank_from` (points) with -/// ↑/↓ and the wheel instead of doing nothing. +/// ↑/↓ and the wheel instead of doing nothing. With `refuse_outside`, a value typed outside +/// `bounds` is refused as text that doesn't read is, rather than kept at the nearer end. #[allow(clippy::too_many_arguments)] fn unit_field_from( ui: &mut Ui, @@ -142,6 +158,7 @@ fn unit_field_from( live: bool, blank_from: Option, empty: &str, + refuse_outside: bool, ) -> Option { let id = id_with(ui.scope_id(), id); let shown = value.map(|v| unit.format(v)).unwrap_or_else(|| empty.to_string()); @@ -163,7 +180,11 @@ fn unit_field_from( let scrubbed = scrub::field(ui, id, rect, &buf, value.map(|v| unit.from_pt(v)), STEP_DECIMALS, &bounds_unit); let commit = commit && buf != shown; if commit { - match read(&buf).or_else(|| if share_of_value { share_of(&buf, value?) } else { None }) { + let typed_value = read(&buf).or_else(|| if share_of_value { share_of(&buf, value?) } else { None }); + // A refused value is one outside `bounds` that the field doesn't show as an end: the end + // shown in the field's unit ("352.7778 mm" for 1000 pt) reads back as that end. + let typed_value = typed_value.filter(|&v| !refuse_outside || bounds.contains(&v) || unit.format(within(v, &bounds)) == unit.format(v)); + match typed_value { Some(v) => { let clamped = within(v, &bounds); let formatted = unit.format(clamped); @@ -1017,7 +1038,7 @@ pub fn spin_field_stops_named( let stops = if value.is_none() { &BLANK_WEIGHT_STOPS[..] } else { presets }; let fmt = |v: f64| unit.format(v); spin_generic(ui, label, value, Some(blank_from), None, None, None, width, step, min, presets, stops, &fmt, &mut |ui, fw, label| { - unit_field_from(ui, id, label, name, value, unit, fw, false, min..=f64::INFINITY, stops, false, Some(blank_from), "") + unit_field_from(ui, id, label, name, value, unit, fw, false, min..=f64::INFINITY, stops, false, Some(blank_from), "", false) }) .and_then(SpinPick::value) } diff --git a/crates/ui-egui/src/widgets/text.rs b/crates/ui-egui/src/widgets/text.rs index eec3cd3a94..8a9bfe1b8d 100644 --- a/crates/ui-egui/src/widgets/text.rs +++ b/crates/ui-egui/src/widgets/text.rs @@ -94,9 +94,20 @@ pub(crate) fn dialog_reject_commit_flag() -> egui::Id { egui::Id::unique("dialog-reject-commit") } -/// Suppress confirming an open dialog on Enter during this frame when an invalid value was committed. +/// The pass (`cumulative_pass_nr`) in which a field last rejected an invalid commit: a click on a +/// dialog's OK whose press came at or before it doesn't confirm the dialog. +pub(crate) fn dialog_reject_pass() -> egui::Id { + egui::Id::unique("dialog-reject-pass") +} + +/// Suppress confirming an open dialog on Enter during this frame, or by the click on OK that took +/// the focus from the field, when an invalid value was committed. pub(crate) fn reject_dialog_commit(ctx: &egui::Context) { - ctx.data_mut(|d| d.insert_temp(dialog_reject_commit_flag(), true)); + let pass = ctx.cumulative_pass_nr(); + ctx.data_mut(|d| { + d.insert_temp(dialog_reject_commit_flag(), true); + d.insert_temp(dialog_reject_pass(), pass); + }); } /// The app sets this at the start of each frame: while a dialog is open, Shift+Enter in a field diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..5ae03e1093 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1286,6 +1286,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur A text wrap object's Offset may be negative as well as positive ([the help page](https://help.adobe.com/archive/en_US/illustrator/cs4/illustrator_cs4_help.pdf#page=307)). A negative Offset (to -1000 pt) shrinks the wrap object by that distance, measured from each edge itself and from the edges of its holes, so lines beside it, above it and below it run that far into it, and an object narrower than twice the distance no longer moves text aside. With Invert Wrap, a negative Offset keeps the text that far inside the object. These are VectorCraft's own rules for the shape of the shrunk object. Text Wrap Options, `object.textWrap.make` and `object.textWrap.options` and the MCP `text_wrap` tool take the negative value, and layout uses it (`crates/text/src/layout/region.rs` tests, `cmd::textwrap::tests::text_wrap_options_negative_offset_reflows_and_round_trips`). In area type of a shape other than a rectangle, each line's whole band, ascent to descent, stays inside the frame: where an edge pinches in within the band, the line ends at the narrowest point, and a line runs beside a hole that starts and ends within its band, not across it. In area type of any shape, a line also stays clear of a text wrap object that reaches into any part of its band, and inside an Invert Wrap object over its whole band (`crates/text/src/layout/region.rs` tests). With Invert Wrap, the spans a line keeps inside a wrap shape of many parts (a comb of 500 teeth) are found by stepping through the line's spans and the shape's spans once for each piece of the band, so the work grows with the number of spans, not with its square (`crates/text/src/layout/region.rs` tests). + Text Wrap Options › Offset is a distance field in the document's units: a bare number is in those units, a typed unit ("12 pt") is read as typed, and OK sends points. An Offset typed outside -1000 to 1000 pt, VectorCraft's own range for the command, or text that doesn't read, is refused: the field flashes and the dialog stays open, whether Enter or a click on OK ends the entry; in every dialog drawn by the shared dialog window, a click on OK no longer confirms it when the distance field it takes the focus from refuses its text (`dialogs::command::tests::text_wrap_offset_field_takes_document_units_and_refuses_values_outside_its_range`). Behavior reference: [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/enter-values-in-panels-and-dialog-boxes.html). The Character panel's Small Caps, Superscript and Subscript buttons and the panel menu's items of the same names turn small caps and the superscript or subscript position on and off for the selected type, or for the text the Type tool has selected (`text.setFormat` or `text.setRangeStyle` `{smallCaps, position}`), in Document Setup's proportions. Turning Superscript on turns Subscript off, and turning Subscript on turns Superscript off. A button or item shows as on when the first selected character (at a caret, the character before it) has its attribute. The Character panel's Anti-aliasing Method dropdown, under Character Rotation, sets how the edges of the selected type objects are drawn as pixels (`text.setFormat {antiAlias}`), for the whole object, with each frame of a thread keeping its own method and graph labels keeping it when the graph is drawn again, on the canvas, in raster exports and when the type is rasterized. VectorCraft defines the methods: Sharp, the default, draws edges as before; None paints a pixel only where the type covers at least half of it; Crisp shows a pixel covered by a share c at `2c - 0.5`, and Strong shows it at `2c`, each kept between 0 and 1. SVG, PDF and EPS export are unchanged. Scores and hours unchanged. The OpenType panel's Stylistic Alternates and Titling Alternates checkboxes turn `salt` and `titl` on and off for the selected type, and its Figure menu lists Default Figure, Tabular Lining, Proportional Lining, Proportional Oldstyle and Tabular Oldstyle; Proportional Lining sets `pnum` and `lnum` and turns `tnum` and `onum` off, Tabular Lining turns `pnum` off and keeps `lnum`, Proportional Oldstyle turns `lnum` off and keeps `pnum`, and Default Figure and Tabular Oldstyle turn `pnum` and `lnum` off. Behavior reference: [Format text with OpenType features](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/format-text-with-opentype-features.html) and [Use swashes, titling alternates, or stylistic alternates](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/use-swashes-titling-alternates-or-stylistic-alternates.html) (public documentation). Tests: `panels::opentype::tests`. From dd411ec6b411f87c378bb1a8b6bd409ae9635b0d Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:47:30 -0800 Subject: [PATCH 16/33] Image Trace: a JPEG's 8 x 8 blocks no longer trace as staircase steps A JPEG rounds each 8 x 8 block's tone on its own, which leaves a small step at every block edge. Where a dark ground changes slowly, those steps decided which of two palette colors a whole block got, and the trace followed the block edges as a staircase. The new deblock pass finds the block grid in the image: the mean step between neighboring opaque pixels must peak at the same one of eight positions at both block edges of a 16 px span, across and down. Cropped JPEGs and JPEGs saved in another format are found the same way; pictures of flat 8 x 8 cells are not. At each block edge, a stretch of eight lines whose step is under the limit and whose pixels hold no edge has the step spread over the eight pixels around the edge, as the deblocking filters of ITU-T H.263 Annex J and MPEG-4 Part 2 Annex F.3.1 do. Larger steps are kept. The limit, the edge guard and how much of each line's own step is smoothed depend on the mode: Black and White smooths only the smallest steps, and traces of more than 16 colors move every line of a stretch by the same amount. The pass runs before the photo pre-filter and works in strips of rows, so only its output is as large as the image. --- crates/trace/src/deblock.rs | 472 +++++++++++++++++++++++++++++++++++ crates/trace/src/lib.rs | 1 + crates/trace/src/pipeline.rs | 20 +- crates/trace/src/tests.rs | 68 +++++ docs/target-app-parity.md | 1 + 5 files changed, 558 insertions(+), 4 deletions(-) create mode 100644 crates/trace/src/deblock.rs diff --git a/crates/trace/src/deblock.rs b/crates/trace/src/deblock.rs new file mode 100644 index 0000000000..be44c46c50 --- /dev/null +++ b/crates/trace/src/deblock.rs @@ -0,0 +1,472 @@ +//! The 8 x 8 block edges of a block-coded image (a JPEG, or a copy of one saved in another format) +//! smoothed away before it is traced. +//! +//! A JPEG codes each 8 x 8 block on its own and rounds each block's brightness in coarse steps, which +//! leaves a small step at every block edge. Where an area's tone changes slowly, as a dark ground does, +//! those steps decide which of two palette colors a whole block gets, and the trace follows the block +//! edges as a staircase. The block grid is found in the image itself: the mean step between +//! neighboring pixels peaks at one position in every eight, across and down, at every block edge and +//! not only at every other one (a pattern of 16 px squares has that too). Where it does, the step +//! at each block edge is spread evenly over the eight pixels around it when it is small and those +//! pixels hold no edge. Larger steps are left as they are. How large a step is smoothed, and whether +//! each row keeps part of its own step, depends on the trace's mode and number of colors (see +//! [`Strength`]). The deblocking filters of ITU-T H.263 +//! Annex J and ISO/IEC 14496-2 (MPEG-4 Part 2) Annex F.3.1 treat block edges the same way: small steps +//! between blocks become ramps, and large ones are kept. + +use std::sync::Mutex; + +use crate::{Raster, parallel_map, quantize}; + +/// The block size of a JPEG. +const BLOCK: usize = 8; +/// Steps between neighboring pixels of this many levels or more are edges, left out when the grid is +/// measured. No block-edge step this large is smoothed. +const EDGE: f64 = 16.0; +/// The mean step at the block edges must be this many times the mean step at the other positions, +/// across and down, for the image to count as block-coded. +const PEAK: f64 = 1.5; +/// The smallest limit on the block-edge steps that are smoothed (levels, after the image's own slope +/// is taken out); the limit is under `EDGE` too. +const LEAST: f64 = 2.5; +/// Fixed-point scale of the working copy: sixteenths of a level. +const SCALE: f64 = 16.0; + +/// How the block edges are smoothed. +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct Strength { + /// The largest block-edge step that is smoothed: this many times the extra step the block edges + /// show on average, at least `LEAST` and under `EDGE`. + reach: f64, + /// A block edge with a step of this many levels or more between neighbors in the pixels around + /// it is left as it is. + guard: f64, + /// How far one row's step may differ from its stretch's mean step and still be smoothed, as a + /// share of the limit. The rest of a row's own step is kept. + row_share: f64, +} + +/// For a trace in black and white, which thresholds the image without smoothing it first: small +/// steps only, each row with its own. +pub(crate) const BLACK_AND_WHITE: Strength = Strength { reach: 2.0, guard: 8.0, row_share: 0.5 }; +/// For a trace of up to 16 colors: each row with its own step. +pub(crate) const FEW_COLORS: Strength = Strength { reach: 8.0, guard: 13.0, row_share: 0.5 }; +/// For a trace of more than 16 colors: larger steps, but only each stretch's mean step, so that +/// every row of a stretch moves by the same amount. +pub(crate) const MANY_COLORS: Strength = Strength { reach: 4.0, guard: EDGE, row_share: 0.0 }; + +/// Where the block edges of a block-coded image are, and how large their steps are. +#[derive(Clone, Copy, Debug, PartialEq)] +struct Grid { + /// A block edge lies between columns `x - 1` and `x` when `x % 8 == across`. + across: usize, + /// A block edge lies between rows `y - 1` and `y` when `y % 8 == down`. + down: usize, + /// The extra step the block edges show on average over the other positions, across or down, + /// whichever is smaller. + extra: f64, +} + +/// Positions counted when the grid is measured: two blocks, so that a pattern of 16 px or more +/// (a checkerboard, cells of a picture enlarged 16 times), whose edges fall on every other block +/// edge only, is not taken for blocks. +const SPAN: usize = 2 * BLOCK; + +/// For one direction, from the mean step between neighboring opaque pixels at each of 16 positions: +/// the position (of eight) where it peaks at both block edges of the span, the extra step those +/// show over the other positions, and whether there is no step at all at the other positions; `None` +/// when no position stands out. +fn peak(sum: &[f64; SPAN], count: &[u64; SPAN]) -> Option<(usize, f64, bool)> { + if count.contains(&0) { + return None; + } + let mean: Vec = sum.iter().zip(count).map(|(&s, &n)| s / n as f64).collect(); + let pair = |at: usize| (mean.get(at).copied().unwrap_or(0.0), mean.get(at + BLOCK).copied().unwrap_or(0.0)); + let at = (0..BLOCK).max_by(|&a, &b| { + let (pa, pb) = (pair(a), pair(b)); + pa.0.min(pa.1).total_cmp(&pb.0.min(pb.1)) + })?; + let (first, second) = pair(at); + let others = || mean.iter().enumerate().filter(|&(i, _)| i != at && i != at + BLOCK).map(|(_, &m)| m); + let rest = others().sum::() / (SPAN - 2) as f64; + (first.min(second) > PEAK * rest).then_some((at, (first + second) / 2.0 - rest, others().all(|m| m == 0.0))) +} + +/// The block grid of `img`, if it has one. +fn grid(img: &Raster) -> Option { + let (w, h) = (img.width as usize, img.height as usize); + if w < 2 * SPAN || h < 2 * SPAN || img.rgba.len() != w * h * 4 { + return None; + } + let px = img.rgba.as_chunks::<4>().0; + let luma = |p: &[u8; 4]| (p[3] == 255).then(|| f64::from(quantize::luma(*p))); + let (mut across, mut down) = (([0.0; SPAN], [0u64; SPAN]), ([0.0; SPAN], [0u64; SPAN])); + for y in 0..h { + for x in 0..w { + let Some(here) = px.get(y * w + x).and_then(luma) else { continue }; + let add = |bins: &mut ([f64; SPAN], [u64; SPAN]), at: usize, other: Option| { + if let Some(step) = other.map(|o| (here - o).abs()).filter(|&s| s < EDGE) { + bins.0[at % SPAN] += step; + bins.1[at % SPAN] += 1; + } + }; + if x > 0 { + add(&mut across, x, px.get(y * w + x - 1).and_then(luma)); + } + if y > 0 { + add(&mut down, y, px.get((y - 1) * w + x).and_then(luma)); + } + } + } + let (across, extra_across, flat_across) = peak(&across.0, &across.1)?; + let (down, extra_down, flat_down) = peak(&down.0, &down.1)?; + // Blocks that are each one flat color, with no step at all inside them, are cells of a picture + // enlarged eight times, which stay as they are. + if flat_across && flat_down { + return None; + } + Some(Grid { across, down, extra: extra_across.min(extra_down) }) +} + +/// Smooths one stretch of a block edge in `band` (values in sixteenths of a level, 3 channels per +/// pixel): `lines` (1 to 8) lines that cross the edge, the pixel at position `i` (0 to 7; the edge lies between +/// 3 and 4) of line `l` starting at `band[index(l, i)]`, where `opaque(l, i)` says whether it is fully +/// opaque. The stretch's step is the mean over its lines of the step across the edge less the slope +/// on either side. A stretch of opaque pixels whose step is under `limit` on every channel, with no +/// step of `strength.guard` or more between neighbors in its pixels (across, the edge itself aside, or +/// along), has that step spread over the eight pixels of each line: with the line's own difference +/// from it, to at most `strength.row_share` of `limit` either way, each pixel moves by the step times +/// 3.5/8, 2.5/8, 1.5/8 or 0.5/8 by its distance from the edge, so the pixels in the middle of the +/// blocks hardly move. +fn smooth_edge( + band: &mut [i16], + lines: usize, + index: impl Fn(usize, usize) -> usize, + opaque: impl Fn(usize, usize) -> bool, + limit: f64, + strength: Strength, +) { + if lines == 0 || lines > BLOCK || !(0..lines).all(|l| (0..BLOCK).all(|i| opaque(l, i))) { + return; + } + let at = |l: usize, i: usize, k: usize| f64::from(band.get(index(l, i) + k).copied().unwrap_or(0)) / SCALE; + // Each line's step on each channel, and the largest step within the pixels around the edge. + let mut steps = [[0.0; 3]; BLOCK]; + let steps = steps.get_mut(..lines).unwrap_or_default(); + let mut inner = 0.0f64; + for (l, step) in steps.iter_mut().enumerate() { + for (k, s) in step.iter_mut().enumerate() { + let p: [f64; BLOCK] = std::array::from_fn(|i| at(l, i, k)); + *s = (p[4] - p[3]) - ((p[3] - p[2]) + (p[5] - p[4])) / 2.0; + for i in (0..BLOCK - 1).filter(|&i| i != 3) { + inner = inner.max((p[i + 1] - p[i]).abs()); + } + // And from this line to the next: a line along the edge is an edge too. + if l + 1 < lines { + for (i, &here) in p.iter().enumerate() { + inner = inner.max((at(l + 1, i, k) - here).abs()); + } + } + } + } + let mean: [f64; 3] = std::array::from_fn(|k| steps.iter().map(|s| s[k]).sum::() / lines as f64); + if inner >= strength.guard || mean.iter().any(|m| m.abs() >= limit) { + return; + } + let own = limit * strength.row_share; + for (l, step) in steps.iter().enumerate() { + for k in 0..3 { + let step = mean[k] + (step[k] - mean[k]).clamp(-own, own); + for i in 0..BLOCK / 2 { + let shift = (step * (3.5 - i as f64) / BLOCK as f64 * SCALE).round() as i16; + if let Some(p) = band.get_mut(index(l, 3 - i) + k) { + *p = p.saturating_add(shift); + } + if let Some(p) = band.get_mut(index(l, 4 + i) + k) { + *p = p.saturating_sub(shift); + } + } + } + } +} + +/// Where the stretches of eight lines start along a block edge `len` lines long, when they start at +/// the lines with `l % 8 == at`: the first, from line 0, can be shorter. Each as (first line, count). +fn stretches(len: usize, at: usize) -> Vec<(usize, usize)> { + let mut out: Vec<(usize, usize)> = if at > 0 { vec![(0, at.min(len))] } else { vec![] }; + out.extend((at..len).step_by(BLOCK).map(|s| (s, BLOCK.min(len - s)))); + out +} + +/// `f` of each of `bands` (with what it needs to know about the band), which it may change, spread +/// over the available cores. +fn each_band(bands: Vec<(K, &mut [T])>, f: impl Fn(K, &mut [T]) + Sync) { + let bands: Vec> = bands.into_iter().map(Mutex::new).collect(); + parallel_map(&bands, |band| { + // Each band is locked by one worker only, so the lock is never held elsewhere. + if let Ok(mut band) = band.lock() { + let (first, values) = &mut *band; + f(*first, values); + } + }); +} + +/// The first block edge (of positions `at`, `at + 8`, ...) with four pixels before it. +fn first_edge(at: usize) -> usize { + if at >= BLOCK / 2 { at } else { at + BLOCK } +} + +/// Smooths the block edges of `v`, rows `top` on of the image (`w` pixels of 3 channels, in +/// sixteenths of a level, from the first row of a stretch of rows to the last row of one), where +/// `opaque(y, x)` is set: the edges between columns `x - 1` and `x` with `x % 8 == across` in stretches +/// of eight rows that start at the rows with `y % 8 == down`, then the edges between rows `e - 1` and +/// `e` for each `e` of `edges` in stretches of eight columns that start at the columns with +/// `x % 8 == across` (see `smooth_edge`). +fn smooth_strip( + v: &mut [i16], + top: usize, + w: usize, + grid: &Grid, + edges: &[usize], + opaque: impl Fn(usize, usize) -> bool, + (limit, strength): (f64, Strength), +) { + let row = w * 3; + if w < BLOCK || row == 0 { + return; + } + for (at, rows) in stretches(v.len() / row, (grid.down + BLOCK - top % BLOCK) % BLOCK) { + let Some(band) = v.get_mut(at * row..(at + rows) * row) else { continue }; + for e in (first_edge(grid.across)..=w - BLOCK / 2).step_by(BLOCK) { + smooth_edge(band, rows, |l, i| (l * w + e - BLOCK / 2 + i) * 3, |l, i| opaque(top + at + l, e - BLOCK / 2 + i), limit, strength); + } + } + for &e in edges { + let Some(band) = e.checked_sub(top + BLOCK / 2).and_then(|at| v.get_mut(at * row..(at + BLOCK) * row)) else { continue }; + for (left, columns) in stretches(w, grid.across) { + smooth_edge(band, columns, |l, i| (i * w + left + l) * 3, |l, i| opaque(e - BLOCK / 2 + i, left + l), limit, strength); + } + } +} + +/// The edges between rows smoothed in one piece of work, with the stretches of rows around them. +const STRIP: usize = 8; + +/// `img` with its block edges smoothed away as `strength` says, or `None` when it shows no block grid. +/// The image is worked on in strips of rows, each with the stretches of rows its `STRIP` edges between +/// rows need, so only the copy it returns is as large as the image. +pub(crate) fn deblocked(img: &Raster, strength: Strength) -> Option { + let grid = grid(img)?; + let limit = (strength.reach * grid.extra).clamp(LEAST, EDGE); + let (w, h) = (img.width as usize, img.height as usize); + let px = img.rgba.as_chunks::<4>().0; + let opaque = |y: usize, x: usize| px.get(y * w + x).is_some_and(|p| p[3] == 255); + let edges: Vec = (first_edge(grid.down)..=h.saturating_sub(BLOCK / 2)).step_by(BLOCK).collect(); + let groups: Vec<&[usize]> = edges.chunks(STRIP).collect(); + // The rows each strip writes: from four rows above its first edge (the top of the image for the + // first strip) to four rows below its last (the bottom of the image for the last strip). + let mut rgba = img.rgba.clone(); + let mut bands = vec![]; + let mut rest: &mut [u8] = &mut rgba; + let mut above = 0; + for (n, group) in groups.iter().enumerate() { + let below = if n + 1 == groups.len() { h } else { group.last().map_or(h, |e| e + BLOCK / 2) }; + let (band, after) = std::mem::take(&mut rest).split_at_mut_checked(below.checked_sub(above)? * w * 4)?; + bands.push(((above, *group), band)); + rest = after; + above = below; + } + // The first row of the stretch of rows that holds row `y`, and the row after the stretch. + let stretch = |y: usize| { + let first = if y < grid.down { 0 } else { y - (y - grid.down) % BLOCK }; + (first, if y < grid.down { grid.down } else { first + BLOCK }.min(h)) + }; + each_band(bands, |(above, group), band| { + let below = above + band.len() / (w * 4); + let (first, last) = (stretch(above).0, stretch(below.saturating_sub(1)).1); + let Some(source) = px.get(first * w..last * w) else { return }; + let mut v: Vec = source.iter().flat_map(|p| [p[0], p[1], p[2]].map(|c| i16::from(c) * SCALE as i16)).collect(); + smooth_strip(&mut v, first, w, &grid, group, opaque, (limit, strength)); + let skip = (above - first) * w; + for (p, c) in band.as_chunks_mut::<4>().0.iter_mut().zip(v.as_chunks::<3>().0.iter().skip(skip)) { + for k in 0..3 { + p[k] = ((i32::from(c[k]) + 8).div_euclid(16)).clamp(0, 255) as u8; + } + } + }); + Some(Raster { width: img.width, height: img.height, rgba }) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A number in -1..1 that looks random, from `x`, `y` and `k`. + fn jitter(x: u32, y: u32, k: u32) -> f64 { + let mut n = x.wrapping_mul(0x9E37_79B1) ^ y.wrapping_mul(0x85EB_CA77) ^ k.wrapping_mul(0xC2B2_AE3D); + n ^= n >> 15; + n = n.wrapping_mul(0x2C1B_3C6D); + n ^= n >> 12; + f64::from(n % 2001) / 1000.0 - 1.0 + } + + /// A dark ground that brightens slowly to the right, with a level of noise and, when `blocks`, + /// every 8 x 8 block (counted from `(dx, dy)`) moved by up to 2 levels, as a JPEG leaves it. + fn ground(w: u32, h: u32, blocks: bool, dx: u32, dy: u32) -> Raster { + Raster::from_fn(w, h, |x, y| { + let offset = if blocks { 2.0 * jitter((x + dx) / 8, (y + dy) / 8, 9) } else { 0.0 }; + let v = 20.0 + 30.0 * f64::from(x) / f64::from(w) + offset + jitter(x, y, 0); + let v = v.round().clamp(0.0, 255.0) as u8; + [v, v, v, 255] + }) + } + + /// The mean step between neighboring columns at the block edges (`x % 8 == across`) and elsewhere. + fn steps(img: &Raster, across: u32) -> (f64, f64) { + let (mut on, mut off) = ((0.0, 0), (0.0, 0)); + for y in 0..img.height { + for x in 1..img.width { + let d = (f64::from(img.pixel(x, y)[0]) - f64::from(img.pixel(x - 1, y)[0])).abs(); + let bin = if x % 8 == across { &mut on } else { &mut off }; + bin.0 += d; + bin.1 += 1; + } + } + (on.0 / f64::from(on.1), off.0 / f64::from(off.1)) + } + + #[test] + fn the_block_grid_is_found_where_the_blocks_are() { + let g = grid(&ground(160, 120, true, 0, 0)).unwrap(); + assert_eq!((g.across, g.down), (0, 0)); + // The same image with 3 columns and 5 rows cut off: its block edges are at x % 8 == 5, y % 8 == 3. + let g = grid(&ground(160, 120, true, 3, 5)).unwrap(); + assert_eq!((g.across, g.down), (5, 3)); + assert!(g.extra > 0.5, "{}", g.extra); + } + + #[test] + fn an_image_without_blocks_is_left_as_it_is() { + assert_eq!(grid(&ground(160, 120, false, 0, 0)), None); + assert!(deblocked(&ground(160, 120, false, 0, 0), FEW_COLORS).is_none()); + // A picture of four flat colors enlarged eight times has steps only at its cells' edges, and + // none within them; two of its colors are 6 levels apart. + let colors = [[30, 30, 36, 255], [36, 30, 30, 255], [200, 60, 40, 255], [240, 220, 120, 255]]; + let cells = Raster::from_fn(160, 120, |x, y| colors[((jitter(x / 8, y / 8, 1) + 1.0) * 2.0).min(3.0) as usize]); + assert_eq!(grid(&cells), None); + // So does a picture of many shades enlarged eight times, as a photo's would. + let shades = Raster::from_fn(160, 120, |x, y| { + let v = (40.0 + 0.6 * f64::from(x / 8 * 8) + 0.5 * f64::from(y / 8 * 8) + 3.0 * jitter(x / 8, y / 8, 4)).round() as u8; + [v, v.saturating_sub(10), v.saturating_add(10), 255] + }); + assert_eq!(grid(&shades), None); + // A noisy checkerboard of 16 px squares 10 levels apart has its steps on every other block edge. + let squares = Raster::from_fn(160, 120, |x, y| { + let v = (20.0 + 10.0 * f64::from((x / 16 + y / 16) % 3) + 4.0 * jitter(x, y, 2)).round() as u8; + [v, v, v, 255] + }); + assert_eq!(grid(&squares), None); + // Too small to hold two spans of 16 px each way. + assert_eq!(grid(&ground(31, 120, true, 0, 0)), None); + } + + #[test] + fn the_steps_at_block_edges_become_ramps() { + for ((dx, dy), strength) in [((0, 0), FEW_COLORS), ((3, 5), FEW_COLORS), ((0, 0), MANY_COLORS), ((0, 0), BLACK_AND_WHITE)] { + let img = ground(160, 120, true, dx, dy); + let across = (8 - dx % 8) % 8; + let (on, off) = steps(&img, across); + assert!(on > 2.0 * off, "before: {on} at the block edges, {off} elsewhere"); + let out = deblocked(&img, strength).unwrap(); + let (on, off) = steps(&out, across); + assert!(on < 1.25 * off, "after: {on} at the block edges, {off} elsewhere"); + // The ground's own slope stays: 30 levels across the image. + let mean = |r: &Raster, x: u32| (0..r.height).map(|y| f64::from(r.pixel(x, y)[0])).sum::() / f64::from(r.height); + assert!((mean(&out, 156) - mean(&out, 4) - (mean(&img, 156) - mean(&img, 4))).abs() < 0.5); + } + } + + #[test] + fn stretches_cover_every_line_once() { + assert_eq!(stretches(20, 0), [(0, 8), (8, 8), (16, 4)]); + assert_eq!(stretches(20, 3), [(0, 3), (3, 8), (11, 8), (19, 1)]); + assert_eq!(stretches(2, 3), [(0, 2)]); + } + + #[test] + fn with_many_colors_every_row_of_a_stretch_moves_alike() { + let img = ground(160, 120, true, 0, 0); + let v: Vec = img.rgba.chunks(4).flat_map(|p| [p[0], p[1], p[2]].map(|c| i16::from(c) * SCALE as i16)).collect(); + // How far each pixel moves when the edges between columns are smoothed. + let shifts = |strength: Strength| -> Vec { + let mut out = v.clone(); + for top in (0..120).step_by(8) { + let band = &mut out[top * 160 * 3..(top + 8) * 160 * 3]; + for e in (8..=156).step_by(8) { + smooth_edge(band, 8, |l, i| (l * 160 + e - 4 + i) * 3, |_, _| true, 4.0, strength); + } + } + out.iter().zip(&v).map(|(o, i)| o - i).step_by(3).collect() + }; + let (many, few) = (shifts(MANY_COLORS), shifts(FEW_COLORS)); + let differ = |s: &[i16]| { + (0..160) + .flat_map(|x| (0..120).step_by(8).map(move |top| (x, top))) + .filter(|&(x, top)| (top..top + 8).any(|y| s[y * 160 + x] != s[top * 160 + x])) + .count() + }; + assert!(many.iter().filter(|&&s| s != 0).count() > 160 * 120 / 2); + assert_eq!(differ(&many), 0); + // With few colors, each row keeps part of its own step. + assert!(differ(&few) > 160 * 15 / 2, "{}", differ(&few)); + } + + #[test] + fn edges_and_fine_lines_stay_sharp() { + // Black lines 1 to 4 px wide and white gaps between them on a block-coded mid-gray ground, one + // line on a block edge. + let base = ground(160, 120, true, 0, 0); + let lines = [(16, 1), (31, 2), (40, 3), (61, 4)]; + let img = Raster::from_fn(160, 120, |x, y| { + if lines.iter().any(|&(at, wide)| (at..at + wide).contains(&x)) { + [0, 0, 0, 255] + } else { + let v = base.pixel(x, y)[0] + 80; + [v, v, v, 255] + } + }); + let out = deblocked(&img, FEW_COLORS).unwrap(); + for y in 0..120 { + for &(at, wide) in &lines { + for x in at..at + wide { + assert_eq!(out.pixel(x, y), [0, 0, 0, 255], "line pixel ({x}, {y})"); + } + // The ground beside a line moves by no more than a block's offset (the edges between + // rows in its column are smoothed; the one across the line is not). + for x in [at - 1, at + wide] { + let moved = (i32::from(out.pixel(x, y)[0]) - i32::from(img.pixel(x, y)[0])).abs(); + assert!(moved <= 3, "beside the line, ({x}, {y}) moved {moved}"); + } + } + } + } + + #[test] + fn transparent_pixels_keep_their_colors() { + let base = ground(160, 120, true, 0, 0); + let img = Raster::from_fn(160, 120, |x, y| { + let mut p = base.pixel(x, y); + if x >= 100 { + p[3] = if x < 104 { 128 } else { 0 }; + } + p + }); + let out = deblocked(&img, FEW_COLORS).unwrap(); + for y in 0..120 { + for x in 100..160 { + assert_eq!(out.pixel(x, y), img.pixel(x, y), "({x}, {y})"); + } + } + assert_eq!(out.rgba.chunks(4).map(|p| p[3]).collect::>(), img.rgba.chunks(4).map(|p| p[3]).collect::>()); + } +} diff --git a/crates/trace/src/lib.rs b/crates/trace/src/lib.rs index c9e4289119..0d99a2acfc 100644 --- a/crates/trace/src/lib.rs +++ b/crates/trace/src/lib.rs @@ -49,6 +49,7 @@ mod centerline; mod contour; +mod deblock; mod fit; mod gradient; mod group; diff --git a/crates/trace/src/pipeline.rs b/crates/trace/src/pipeline.rs index 2dd54af93b..cb635e7493 100644 --- a/crates/trace/src/pipeline.rs +++ b/crates/trace/src/pipeline.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use super::layers::{Layers, parallel_map_at_most}; use super::params::{Method, Mode, TraceParams}; use super::raster::{Raster, TraceError}; -use super::{AtomicUsize, PathData, Quantized, TRANSPARENT, TracedGradient, TracedShape, gradient, group, logo, quantize, smooth}; +use super::{AtomicUsize, PathData, Quantized, TRANSPARENT, TracedGradient, TracedShape, deblock, gradient, group, logo, quantize, smooth}; /// One traced shape: an outer contour plus its holes, filled with `color`, or (with `stroke`) /// lines stroked with it. @@ -209,9 +209,21 @@ fn trace_paths(img: &Raster, params: &TraceParams, max_anchors: usize, steps: &( Some(name) if params.ignore_white => Some(logo::parse_hex(name).ok_or_else(|| TraceError::BadPalette(name.clone()))?), _ => None, }; - // A photo is traced from a copy with its noise smoothed away (see `smooth`). - let smoothed = (params.mode != Mode::BlackAndWhite && quantize::continuous_tone(img)).then(|| smooth::smoothed(img)); - let img = smoothed.as_ref().unwrap_or(img); + // A JPEG's 8 x 8 block edges are smoothed away first (see `deblock`). + let strength = match params.mode { + Mode::BlackAndWhite => deblock::BLACK_AND_WHITE, + _ if params.colors > 16 => deblock::MANY_COLORS, + _ => deblock::FEW_COLORS, + }; + // A photo is traced from a copy with its noise smoothed away (see `smooth`); the deblocked copy is + // dropped once it is smoothed. + let photo = |img: &Raster| params.mode != Mode::BlackAndWhite && quantize::continuous_tone(img); + let prepared = match deblock::deblocked(img, strength) { + Some(deblocked) if photo(&deblocked) => Some(smooth::smoothed(&deblocked)), + Some(deblocked) => Some(deblocked), + None => photo(img).then(|| smooth::smoothed(img)), + }; + let img = prepared.as_ref().unwrap_or(img); go_on()?; // Gradients are found first and kept out of the palette: no colour is spent on bands of them. // Grayscale finds them in the image's grays (as its flat colours are), so their stops are gray. diff --git a/crates/trace/src/tests.rs b/crates/trace/src/tests.rs index fbedb07e29..a4f0bccba2 100644 --- a/crates/trace/src/tests.rs +++ b/crates/trace/src/tests.rs @@ -2086,6 +2086,74 @@ fn small_accents_of_other_colors_leave_a_tinted_photo_continuous_tone() { } } +/// `img` saved as a JPEG of `quality` and read back. +fn through_jpeg(img: &Raster, quality: u8) -> Raster { + let rgb: Vec = img.rgba.chunks(4).flat_map(|p| [p[0], p[1], p[2]]).collect(); + let mut bytes = vec![]; + image::codecs::jpeg::JpegEncoder::new_with_quality(&mut bytes, quality) + .encode(&rgb, img.width, img.height, image::ExtendedColorType::Rgb8) + .unwrap(); + Raster::decode(&bytes).unwrap() +} + +/// The straight segments of `res`' paths that run across or down, at least 2 px long: for each, whether +/// it runs down, its column (down) or row (across), its length, and whether it is a step's riser, the +/// segments before and after it both running the other way and turning the path the same way. +fn axis_segments(res: &TraceResult) -> Vec<(bool, f64, f64, bool)> { + let mut out = vec![]; + for sp in res.paths.iter().flat_map(|p| &p.path.subpaths) { + let n = sp.anchors.len(); + let seg = |i: usize| { + let (a, b) = (&sp.anchors[i % n], &sp.anchors[(i + 1) % n]); + let straight = a.h_out.distance(a.p) < 1e-6 && b.h_in.distance(b.p) < 1e-6; + let (dx, dy) = (b.p.x - a.p.x, b.p.y - a.p.y); + // 1 across, 2 down, 0 neither; and the direction along it. + let axis = if !straight || dx.hypot(dy) < 2.0 { + 0 + } else if dy.abs() < 0.05 { + 1 + } else if dx.abs() < 0.05 { + 2 + } else { + 0 + }; + (axis, dx.signum(), dy.signum(), dx.hypot(dy), a.p) + }; + let last = if sp.closed { n } else { n.saturating_sub(1) }; + for i in 0..last { + let (axis, _, _, len, p) = seg(i); + if axis == 0 { + continue; + } + let (before, after) = (seg(i + n - 1), seg(i + 1)); + let riser = sp.closed && before.0 != 0 && before.0 != axis && after.0 == before.0 && before.1 == after.1 && before.2 == after.2; + out.push((axis == 2, if axis == 2 { p.x } else { p.y }, len, riser)); + } + } + out +} + +#[test] +fn a_jpegs_blocks_are_not_traced_as_steps() { + // A dark noisy ground whose tone changes slowly across and down, saved as a JPEG of quality 40. + let img = through_jpeg( + &Raster::from_fn(320, 240, |x, y| { + let v = 14.0 + 22.0 * f64::from(x) / 320.0 + 14.0 * f64::from(y) / 240.0; + let c = [0.9 * v, v, 0.8 * v].map(|c| (c + 8.0 * jitter(x, y, 3)).round().clamp(0.0, 255.0) as u8); + [c[0], c[1], c[2], 255] + }), + 40, + ); + let res = trace(&img, &preset("16 Colors").unwrap()); + // The image's border is left out. + let segments: Vec<_> = axis_segments(&res).into_iter().filter(|s| s.1 > 0.6 && s.1 < if s.0 { 319.4 } else { 239.4 }).collect(); + let on_grid: Vec<_> = segments.iter().filter(|s| (s.1 / 8.0 - (s.1 / 8.0).round()).abs() * 8.0 < 0.6).collect(); + // By chance, 15 in 100 segments are within 0.6 px of a block edge. + assert!(on_grid.len() * 4 < segments.len().max(1), "{} of {} straight segments on the block edges: {segments:?}", on_grid.len(), segments.len()); + let risers: Vec<_> = on_grid.iter().filter(|s| s.3 && s.2 >= 4.0).collect(); + assert!(risers.is_empty(), "risers of 4 px or more on the block edges: {risers:?}"); +} + #[test] fn tinted_flat_art_stays_flat_art() { let tints: [(&str, &[[f64; 3]]); 4] = [("gray", &GRAY_RAMP), ("sepia", &SEPIA), ("blue", &BLUE), ("duotone", &DUOTONE)]; diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 8ae723dc35..1d6b2ee351 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1367,6 +1367,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Image Trace, curve fitting tolerance:** the outline fitter keeps cutting a run while a point stays farther than the tolerance from its curve, so a run of fewer than six points or one cut 14 times no longer keeps a piece over the tolerance. Pieces already within the tolerance do not change, and noisy art can get more anchors. Tests: `logo::bezier::tests`, `logo::outline::tests`. Scores and hours unchanged. - **Image Trace, gray photos:** Color and Grayscale smooth a gray photo within its tones before choosing its colors, as they do a color photo. A picture with at least 95% of its opaque pixels within 8 levels across their channels counts as continuous tone when 8 colors leave an error of more than 6 levels and more than 0.065 of the error of one color, instead of 0.1, unless 16 colors leave at most a fifth of the error of 8 (VectorCraft's own values). That exception keeps gray flat art of up to 16 clean levels on its old route; anti-aliased or noisy gray flat art can now count as continuous tone too. Most gray photos the change reaches, and very noisy gray scans of flat art, trace with fewer specks and fewer staircase edges. Some 16 Colors and High Fidelity Photo palettes of gray photos grow toward the requested count, with more anchors and regions. Some gray illustrations and sketches get more anchors and staircase edges at 16 Colors and High Fidelity Photo, and some 6 Colors traces get more staircase edges. Tests: `tests::a_gray_photo_is_continuous_tone_as_its_color_original_is`, `tests::a_gray_photo_is_smoothed_before_it_is_traced`. Scores and hours unchanged. - **Image Trace, tinted photos:** Color and Grayscale smooth a sepia, single-hue or duotone photo within its tones before choosing its colors, as they do a gray photo. A picture with at least 95% of its opaque pixels within 6 levels of its tone curve counts as continuous tone under the same 0.065 rule as a neutral picture. The tone curve runs through the median color of each 8-level stretch along the direction in which the picture's colors spread the most, and a pixel on it must also lie within 6 levels of the plane the colors spread along most, so art in several unrelated colors is not counted (VectorCraft's own rule and values). The neutral rule's exception for 16 colors leaving at most a fifth of the error of 8 applies to it too. Clean tinted bands of 2, 3 and 9 levels and tinted scans of flat art with up to 8 levels of noise stay on the flat-art route. On the synthetic tinted photos the 16 Colors trace keeps 12 to 15 colors instead of 6 to 13, with 525 to 708 anchors instead of up to 1,402. A duotone with strong noise on each channel can still miss the curve and stay unsmoothed. Tests: `tests::a_tinted_photo_is_continuous_tone_as_a_gray_photo_is`, `tests::small_accents_of_other_colors_leave_a_tinted_photo_continuous_tone`, `tests::tinted_flat_art_stays_flat_art`, `tests::pictures_without_tones_are_not_continuous_tone`, `tests::a_tinted_photo_is_smoothed_before_it_is_traced`. Scores and hours unchanged. +- **Image Trace, JPEG blocks:** Color, Grayscale and Black and White traces smooth the edges of a JPEG's 8 × 8 blocks before tracing, so a dark ground whose tone changes slowly is no longer traced as staircases along the block edges. The block grid is found in the image itself: the mean step between neighboring pixels must peak at one position in eight, across and down. A cropped JPEG and a JPEG saved in another format are smoothed too. A picture of flat 8 × 8 cells is left as it is, and art made of 8 × 8 cells with slight texture inside each cell is smoothed as a JPEG copy would be. A small step at a block edge, with no edge in the eight pixels around it, is spread over those eight pixels, and larger steps are kept, as the deblocking filters of ITU-T H.263 Annex J and MPEG-4 Part 2 Annex F.3.1 do. Black and White smooths only the smallest steps, and traces of more than 16 colors move every row of a stretch of eight by the same amount (VectorCraft's own rule and values). On a 4000 × 3000 photo saved as JPEG, the straight outline segments that lie on the block grid fall from 52% and 51% (across and down) to 24% and 29% at 16 Colors, and from 54% and 51% to 43% and 42% at High Fidelity Photo; staircase runs fall from 83 to 67 (16 Colors), 11 to 1 (6 Colors), 30 to 13 (Black and White Logo) and 428 to 403 (High Fidelity Photo). A 600 px photo saved as a quality-40 JPEG has 4 staircase runs instead of 2 at 16 Colors. Images without a block grid trace as before. Tests: `tests::a_jpegs_blocks_are_not_traced_as_steps`, `deblock::tests`. Scores and hours unchanged. - **Image Trace, Document Library color groups:** with Document Swatches, the panel's Colors menu and `"colorGroup"` trace with one color group's colors instead of every swatch, as the public panel documentation describes Document Library; code-drawn tests in `crates/engine` and the panel. - **Image Trace, Document Library colors:** with Document Swatches, traced fills and strokes use the displayed colors of process, global, spot and tint swatches without links, so editing a swatch leaves the trace unchanged; swatch-library colors are also unlinked; code-drawn tests in `crates/engine`. - **Image Trace, flattened slivers:** in a Color, Grayscale or Black and White trace with Fills, a traced region whose outline straightens onto one line (a single pixel, or a one-pixel row along an edge, its pieces shared with the regions beside it) is left out instead of becoming a selectable path with no area, which paints nothing. Snap Curves To Lines straightens more outlines, so it leaves out more of these. Without Fills, the region's 1 px stroke draws it, and it is kept. Code-drawn tests in `crates/trace`. From 9a355b1a75bceb1ab598dca39bdc6dd85819d39b Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:58:36 -0800 Subject: [PATCH 17/33] i18n: German leaves out the rows #7211, #7214 and #7116 add Open #7211, #7214 and #7116 add German rows for 38 of the messages this branch translates. A second row for the same key fails bundled_catalogs_are_consistent once both sides merge, so this branch drops its 38 rows and leaves those messages to the other pull requests. The German lines in docs/localization-parity.md and docs/target-app-parity.md give the new counts. --- crates/ui-egui/src/i18n/de.tsv | 38 ---------------------------------- docs/localization-parity.md | 2 +- docs/target-app-parity.md | 2 +- 3 files changed, 2 insertions(+), 40 deletions(-) diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index d8124857aa..7366d41834 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -7150,9 +7150,6 @@ breakText Glyphs Glyphen @msg `{KEY}` must be a list `{KEY}` muss eine Liste sein @msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` nimmt höchstens {MAX_PARAGRAPH_INDENTS} Einträge (erhalten: {_1}) @msg `{KEY}` takes no `{other}` beside it `{KEY}` nimmt kein `{other}` daneben -@msg `{_1}` can't name a composite font `{_1}` kann keine zusammengesetzte Schrift benennen -@msg `{_1}` is the name of an installed font `{_1}` ist der Name einer installierten Schrift -@msg `{_1}`: {_2} is set in `{font}`, a composite font `{_1}`: {_2} ist in `{font}` gesetzt, einer zusammengesetzten Schrift @msg `{base}` is larger than {_1} MB `{base}` ist größer als {_1} MB @msg `{base}` is not in the DCS file's folder `{base}` ist nicht im Ordner der DCS-Datei @msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` enthält mehr als {MAX_EDIT_WORDS} Wörter @@ -7171,20 +7168,13 @@ breakText Glyphs Glyphen @msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` muss ein Objekt nennen, das an den Pfad einer Gruppe „Objekte auf Pfad“ angehängt ist @msg `{key}` names an object that is hidden or locked `{key}` nennt ein Objekt, das ausgeblendet oder gesperrt ist @msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` ist eine eigene Datei, die nur neben dem Pfad der DCS-Datei gefunden werden kann -@msg `{name}`: give the kanji part `{name}`: Geben Sie den Kanji-Teil an -@msg `{name}`: parts must be an object `{name}`: parts muss ein Objekt sein -@msg `{name}`: unknown class `{k}`; the classes are kanji, kana, punctuation, symbols, roman and numerals `{name}`: unbekannte Klasse `{k}`; die Klassen sind kanji, kana, punctuation, symbols, roman und numerals -@msg `{name}`: unknown key `{k}` in {_1} `{name}`: unbekannter Schlüssel `{k}` in {_1} -@msg `{name}`: {_1} must be an object `{name}`: {_1} muss ein Objekt sein @msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} -@msg `{raw}` can't name a composite font (1 to {COMPOSITE_NAME_MAX} characters, no control characters) `{raw}` kann keine zusammengesetzte Schrift benennen (1 bis {COMPOSITE_NAME_MAX} Zeichen, keine Steuerzeichen) @msg `{u}` is no Unicode character `{u}` ist kein Unicode-Zeichen @msg `{word}` isn't in the custom dictionary `{word}` ist nicht im Benutzerwörterbuch @msg a CMap has too many mappings eine CMap hat zu viele Zuordnungen @msg a \\u escape needs four hexadecimal digits ein \\u-Escape braucht vier Hexadezimalziffern @msg a comma in an XPath expression must separate function arguments ein Komma in einem XPath-Ausdruck muss Funktionsargumente trennen @msg a document holds at most {_1} SVG filters ein Dokument enthält höchstens {_1} SVG-Filter -@msg a document keeps at most {COMPOSITE_FONTS_MAX} composite fonts ein Dokument enthält höchstens {COMPOSITE_FONTS_MAX} zusammengesetzte Schriften @msg a document with unsaved changes has no recovery copy ein Dokument mit nicht gespeicherten Änderungen hat keine Wiederherstellungskopie @msg a font's data is too large die Daten einer Schrift sind zu groß @msg a font's subroutines are too large die Subroutinen einer Schrift sind zu groß @@ -7232,15 +7222,11 @@ breakText Glyphs Glyphen @msg character dimensions cannot be represented with the target text transform Zeichenmaße können mit der Texttransformation des Ziels nicht dargestellt werden @msg continuation must be an array of path chunks continuation muss ein Array aus Pfadstücken sein @msg continuation requires points continuation erfordert Punkte -@msg dataBase64 is not base64 dataBase64 ist kein Base64 -@msg each font must be {name, parts} jede Schrift muss {name, parts} sein -@msg each font needs a name jede Schrift braucht einen Namen @msg each path chunk must contain 1 to 4096 points jedes Pfadstück muss 1 bis 4096 Punkte enthalten @msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) jede Auswahl ist {{at: [x, y]}} oder {{rect: [x0, y0, x1, y1]}} (erhalten: {v}) @msg each weight must be a number of 0 or more (got {v}) jede Gewichtung muss eine Zahl von 0 oder größer sein (erhalten: {v}) @msg finally table {t} out of range finally-Tabelle {t} außerhalb des gültigen Bereichs @msg flags cannot be given with a RegExp object mit einem RegExp-Objekt können keine Flags angegeben werden -@msg fonts must be a list of composite fonts fonts muss eine Liste zusammengesetzter Schriften sein @msg function declaration index out of range Index der Funktionsdeklaration außerhalb des gültigen Bereichs @msg give `char`, `text`, `code` or `unicode` geben Sie `char`, `text`, `code` oder `unicode` an @msg give `count` or `spacing`, not both geben Sie `count` oder `spacing` an, nicht beides @@ -7248,10 +7234,8 @@ breakText Glyphs Glyphen @msg give `path`, an SVG file, or `svg`, SVG text geben Sie `path` an, eine SVG-Datei, oder `svg`, SVG-Text @msg give `xml`, one SVG element geben Sie `xml` an, ein SVG-Element @msg give exactly one of `path`, `data` and `dataBase64` geben Sie genau eines von `path`, `data` und `dataBase64` an -@msg give fonts: [{name, parts}…] geben Sie fonts: [{name, parts}…] an @msg give index or indexes, not both geben Sie index oder indexes an, nicht beides @msg give one element geben Sie ein einzelnes Element an -@msg give one of path, data or dataBase64 geben Sie eines von path, data oder dataBase64 an @msg give opacity or blend geben Sie opacity oder blend an @msg handler at {_1} expects {_2} values but its range starts with {d} der Handler bei {_1} erwartet {_2} Werte, aber sein Bereich beginnt mit {d} @msg handler depth too large Handler-Tiefe zu groß @@ -7296,8 +7280,6 @@ breakText Glyphs Glyphen @msg must be an object with `kind` and `value` muss ein Objekt mit `kind` und `value` sein @msg must have `kind` alert, confirm or prompt muss `kind` alert, confirm oder prompt haben @msg must have exactly `kind` and `value` muss genau `kind` und `value` haben -@msg names must be a list of names names muss eine Liste von Namen sein -@msg names must be strings names müssen Strings sein @msg needs a closing quote after the name braucht ein schließendes Anführungszeichen nach dem Namen @msg needs a name braucht einen Namen @msg needs a name of at most 64 bytes braucht einen Namen mit höchstens 64 Byte @@ -7309,9 +7291,6 @@ breakText Glyphs Glyphen @msg no SVG filter at index {k} kein SVG-Filter bei Index {k} @msg no arrowheads file here hier ist keine Pfeilspitzendatei @msg no character has code {code} kein Zeichen hat den Code {code} -@msg no composite font keine zusammengesetzte Schrift -@msg no composite font named `{n}` keine zusammengesetzte Schrift namens `{n}` -@msg no composite fonts to export keine zusammengesetzten Schriften zum Exportieren @msg no custom arrowhead {name} (see stroke.arrowheads.list) keine eigene Pfeilspitze {name} (siehe stroke.arrowheads.list) @msg no file to open keine Datei zum Öffnen @msg no instructions keine Anweisungen @@ -7323,8 +7302,6 @@ breakText Glyphs Glyphen @msg node {_1} already has the text variable `{_2}` Knoten {_1} hat bereits die Textvariable `{_2}` @msg not a DCS file keine DCS-Datei @msg not a RegExp object kein RegExp-Objekt -@msg not a composite fonts file: no compositeFonts list keine Datei mit zusammengesetzten Schriften: keine compositeFonts-Liste -@msg not a composite fonts file: {e} keine Datei mit zusammengesetzten Schriften: {e} @msg not a regular expression kein regulärer Ausdruck @msg not an action set: the text doesn't start with /version and a number kein Aktionssatz: Der Text beginnt nicht mit /version und einer Zahl @msg nothing to repeat nichts zu wiederholen @@ -7339,7 +7316,6 @@ breakText Glyphs Glyphen @msg operand too large Operand zu groß @msg order is a whole number from 0 to {others}, not `{v}` order ist eine ganze Zahl von 0 bis {others}, nicht `{v}` @msg path chunks must share their boundary point Pfadstücke müssen ihren Grenzpunkt teilen -@msg path is empty der Pfad ist leer @msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false braucht die Zeichenflächen, um die bearbeitbare Grafik zu behalten, wenn useArtboards false ist @msg point x must be a number Punkt x muss eine Zahl sein @msg point y must be a number Punkt y muss eine Zahl sein @@ -7391,10 +7367,6 @@ breakText Glyphs Glyphen @msg the document has no SVG filter named {r} das Dokument hat keinen SVG-Filter namens {r} @msg the document has no color swatches das Dokument hat keine Farbfelder @msg the document's swatches hold {_1} colors; the document reduction takes at most 255 die Farbfelder des Dokuments enthalten {_1} Farben; die Reduzierung auf Dokumentfarben nimmt höchstens 255 -@msg the file holds more than {COMPOSITE_FONTS_MAX} composite fonts die Datei enthält mehr als {COMPOSITE_FONTS_MAX} zusammengesetzte Schriften -@msg the file holds no composite fonts die Datei enthält keine zusammengesetzten Schriften -@msg the file holds two composite fonts named `{_1}` die Datei enthält zwei zusammengesetzte Schriften namens `{_1}` -@msg the file is larger than 4 MB die Datei ist größer als 4 MB @msg the file isn't valid SVG: {e} die Datei ist kein gültiges SVG: {e} @msg the file names no plate die Datei nennt keine Platte @msg the function's prototype is not an object der Prototyp der Funktion ist kein Objekt @@ -7440,10 +7412,8 @@ breakText Glyphs Glyphen @msg toString is not a function toString ist keine Funktion @msg too many arguments zu viele Argumente @msg too many quantifiers zu viele Quantifizierer -@msg two composite fonts are named `{_1}` zwei zusammengesetzte Schriften heißen `{_1}` @msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) unbekannte OpenType-Funktion `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; mit vorangestelltem - ausschalten; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom und aalt nehmen die Nummer einer Alternative als `tag=N` mit N von 0 bis {_1}, etwa `salt=2`) @msg unknown dither `{d}` (diffusion, pattern, noise or none) unbekanntes Dithering `{d}` (diffusion, pattern, noise oder none) -@msg unknown key `{k}`; a font takes name and parts unbekannter Schlüssel `{k}`; eine Schrift nimmt name und parts @msg unknown panel `{panel}` unbekanntes Bedienfeld `{panel}` @msg unknown parameter `{key}` unbekannter Parameter `{key}` @msg unknown parameter `{within}settings.{format}`; {why} unbekannter Parameter `{within}settings.{format}`; {why} @@ -7464,13 +7434,7 @@ breakText Glyphs Glyphen @msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: Der Pfad einer eigenen Pfeilspitze braucht eine zu füllende Fläche und höchstens {most} Ankerpunkte und {most} Teilpfade, jeder Ankerpunkt höchstens {far} Spitzeneinheiten von ihrer Spitze entfernt @msg {k}: bad custom arrowhead path: {e} {k}: ungültiger Pfad einer eigenen Pfeilspitze: {e} @msg {k}: bad custom arrowhead: {e} {k}: ungültige eigene Pfeilspitze: {e} -@msg {k}: baseline must be {_1} to {_2}% {k}: baseline muss {_1} bis {_2} % sein @msg {k}: custom must be a name {k}: custom muss ein Name sein -@msg {k}: give a font {k}: Geben Sie eine Schrift an -@msg {k}: only kana can be scaled at the center {k}: nur Kana können an der Mitte skaliert werden -@msg {k}: only kana, roman and numerals can be scaled {k}: nur Kana, lateinische Zeichen und Ziffern können skaliert werden -@msg {k}: scale must be {_1} to {_2}% {k}: scale muss {_1} bis {_2} % sein -@msg {k}: size must be {_1} to {_2}% {k}: size muss {_1} bis {_2} % sein @msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: Kopieren nimmt eine DCS-Datei nur, wenn ihre Platten darin enthalten sind; verknüpfen Sie sie dort neu, wo ihre Plattendateien liegen @msg {path}: larger than {_1} MB {path}: größer als {_1} MB @msg {shown} is not an XPath 1.0 function {shown} ist keine XPath-1.0-Funktion @@ -7739,7 +7703,6 @@ breakText Glyphs Glyphen @msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` ist ein Arbeitsfarbraum: Wählen Sie zuerst mit edit.colorSettings einen anderen @msg `{name}` is built in and stays `{name}` ist integriert und bleibt erhalten @msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` nimmt eine Farbe seiner eigenen Art (Farbe, Verlauf oder Muster) -@msg `{name}`: Minimum must not exceed Desired, nor Desired Maximum `{name}`: Minimum darf Optimal nicht übersteigen und Optimal nicht Maximum @msg `{n}` can't be deleted `{n}` kann nicht gelöscht werden @msg `{n}` can't be merged `{n}` kann nicht zusammengeführt werden @msg `{n}` can't be moved `{n}` kann nicht verschoben werden @@ -8490,7 +8453,6 @@ breakText Glyphs Glyphen @msg select a graph wählen Sie ein Diagramm aus @msg select a group or layer with visible, unlocked members wählen Sie eine Gruppe oder Ebene mit sichtbaren, nicht gesperrten Elementen aus @msg select a layer wählen Sie eine Ebene aus -@msg select a path wählen Sie einen Pfad aus @msg select a text object wählen Sie ein Textobjekt aus @msg select an image (or an Image Trace group) wählen Sie ein Bild (oder eine Bildnachzeichner-Gruppe) aus @msg select an image, or pass id wählen Sie ein Bild aus, oder übergeben Sie id diff --git a/docs/localization-parity.md b/docs/localization-parity.md index a6078c1ba2..c0e4e477bf 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -35,7 +35,7 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker | Portuguese | `pt-br` | 6,106 catalog entries (counted 2026-10-31 on `main` at `d26dd933a`): menus, `tl!` literals and messages complete when counted; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) | | Indonesian | `id` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one plural form | dialogs and tooltips all; 750 message rows (French has 796) | Latin | no | full | — (review) | | Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) | -| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (3,463 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists no missing messages for it) | Latin | contributor-written, not reviewed | full | — (review) | +| German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (3,433 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists 48 missing messages for it) | Latin | contributor-written, not reviewed | full | — (review) | | Korean | `ko` | 4,870 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | all | Hangul from an installed system font on the desktop (Apple SD Gothic Neo, Malgun Gothic, Noto Sans CJK KR…); the web build has no Korean UI font yet (craft-fonts); IME path exists | no (AI-drafted) | full | — (review; web font) | | Vietnamese | `vi` | 4,886 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form; 4,046 plain + 18 `@plural` + 28 contextual + 794 `@msg` | all | Latin with Vietnamese diacritics (stacked marks attach in precomposed, decomposed and keyboard forms, #4491; glyph tests pass) | contributor is a Vietnamese speaker; AI-assisted draft, further native review welcome | full | — (review) | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6bcc551a89..6de9ffd2bf 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -888,7 +888,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur The status bar's messages for documents opened or saved with notes and for discarded or restored recovered documents, and the Global Edit options' Results line, do the same (`open_save_and_recovery_messages_take_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`); the warnings for linked images and placed documents output as their previews and the errors for a page past a placed file's last page or artboard name their counts in the singular or the plural in English. - Edit › Undo and Edit › Redo, in the in-window and native menus, name the step in the interface language as the History panel lists it (`panels::history::step_name`), so when the catalog has no plain row for a step named after an effect, such as Feather, the menu shows the translation of the effect's menu row without its ellipsis (`undo_and_redo_name_the_step_as_the_history_panel_does`). - The History panel translates the undo step names the engine, the tools and the shell write as literals: the untranslated report (`untranslated_report`) lists any a complete catalog lacks, and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis. - - The German catalog (`crates/ui-egui/src/i18n/de.tsv`) has an `@msg` row for every status and error message the untranslated report (`untranslated_report`) finds in the sources, 3,463 `@msg` rows in all: the errors of commands, scripts, file import and export and plug-ins. The report's German section lists no messages. A message with two placeholders in a row (`Template::parse` in `crates/ui-egui/src/i18n/catalog.rs`) is still shown in English. + - The German catalog (`crates/ui-egui/src/i18n/de.tsv`) has an `@msg` row for the status and error messages the untranslated report (`untranslated_report`) finds in the sources, 3,433 `@msg` rows in all: the errors of commands, scripts, file import and export and plug-ins. The report lists 48 German messages; 38 of them are the composite font, select-a-path and justification order errors that open #7211, #7214 and #7116 translate. A message with two placeholders in a row (`Template::parse` in `crates/ui-egui/src/i18n/catalog.rs`) is still shown in English. - The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set. - The History panel menu's Clear History (`edit.clearHistory`) removes every step to undo and to redo and leaves the document as it is; it is not an undo step itself, and it is disabled when there is no step to clear. - The New Document from State button at the bottom of the History panel (`edit.newDocumentFromState`) makes a new document, which becomes active, from the state the panel has selected ([public source](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/undo-edits-and-manage-design-history.html)). VectorCraft's own rules: the new document takes the next Untitled-N name, is marked unsaved, has nothing selected and has no steps to undo or redo, and the source document and its steps stay as they are. From d5509c3b6c2f50b9e49ab55200eac6b240908076 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:58:43 -0800 Subject: [PATCH 18/33] Guides: stripping and mapping ruler guides look at each child of a layer once Node::strip_ruler_guides and Node::map_ruler_guide_positions asked a layer whether its whole subtree held a ruler guide, then asked each sublayer the same question again when filtering the children and again when the recursive call started. A layer nested d deep was tested 2d + 1 times, so a chain of N layers cost about N^2 layer tests. strip_ruler_guides runs when a document's objects are placed or pasted (Clipboard::from_document), when a copy of the document is saved (Document::guides_to_saved) and when a temporary editing layer is removed (Document::remove_edit_layer). map_ruler_guide_positions runs when an imported EPS is scaled. A new held_guides reads the layer once and records which children are ruler guides and which sublayers hold one; strip_held and map_held then visit and copy only those sublayers. The guides removed, the order f is called in, the art and its order, and the sharing of layers without a guide are the same as before. Neither method records an undo step. --- crates/doc/src/node/ops.rs | 81 ++++++++++---- crates/doc/src/node/tests.rs | 204 +++++++++++++++++++++++++++++++++++ 2 files changed, 265 insertions(+), 20 deletions(-) diff --git a/crates/doc/src/node/ops.rs b/crates/doc/src/node/ops.rs index 2bb655236a..fb6e8de8d9 100644 --- a/crates/doc/src/node/ops.rs +++ b/crates/doc/src/node/ops.rs @@ -763,33 +763,19 @@ impl Node { children.iter().rposition(|c| !c.is_ruler_guide()).map_or(0, |i| i + 1) } - /// Remove the ruler guides of this layer and of its sublayers. Other nodes hold none. + /// Remove the ruler guides of this layer and of its sublayers. Other nodes hold none. Only + /// layers that hold one are copied. pub fn strip_ruler_guides(&mut self) { - if !self.holds_ruler_guides() { - return; - } - if let Some(ch) = self.children_mut() { - ch.retain(|c| !c.is_ruler_guide()); - for c in ch.iter_mut().filter(|c| c.holds_ruler_guides()) { - Arc::make_mut(c).strip_ruler_guides(); - } + if let Some(held) = held_guides(self, &Self::is_ruler_guide) { + strip_held(self, held); } } /// Call `f` on the position (along the axis it crosses) of each ruler guide of this layer and /// of its sublayers, with whether the guide is vertical. Only layers that hold one are copied. pub fn map_ruler_guide_positions(&mut self, f: &mut impl FnMut(bool, &mut f64)) { - if !self.holds_ruler_guides() { - return; - } - if let Some(ch) = self.children_mut() { - for c in ch.iter_mut().filter(|c| c.is_ruler_guide() || c.holds_ruler_guides()) { - let c = Arc::make_mut(c); - match &mut c.kind { - NodeKind::RulerGuide { vertical, pos, .. } => f(*vertical, pos), - _ => c.map_ruler_guide_positions(f), - } - } + if let Some(held) = held_guides(self, &Self::is_ruler_guide) { + map_held(self, held, f); } } @@ -834,6 +820,61 @@ impl Node { } } +/// The ruler guides a layer holds: its children that are one, by index in order (each with +/// nothing inside), and its sublayers holding one (with what they hold). +#[derive(Debug, Default)] +pub(super) struct HeldGuides(Vec<(usize, HeldGuides)>); + +/// The ruler guides, by `is_guide`, that `n` holds if it is a layer, or `None` when it holds +/// none. `is_guide` looks at each child of `n` and of its sublayers once. +pub(super) fn held_guides(n: &Node, is_guide: &impl Fn(&Node) -> bool) -> Option { + if !n.is_layer() { + return None; + } + let held: Vec<_> = n + .children() + .into_iter() + .flatten() + .enumerate() + .filter_map(|(i, c)| if is_guide(c) { Some((i, HeldGuides::default())) } else { held_guides(c, is_guide).map(|h| (i, h)) }) + .collect(); + (!held.is_empty()).then_some(HeldGuides(held)) +} + +/// Remove the ruler guides of `layer` and of its sublayers that [`held_guides`] found (`held`). +/// Only the sublayers holding one are copied, and only those are visited. +pub(super) fn strip_held(layer: &mut Node, held: HeldGuides) { + let Some(ch) = layer.children_mut() else { return }; + let mut held = held.0.into_iter().peekable(); + let mut next = 0; + ch.retain_mut(|c| { + let at = next; + next += 1; + let Some((_, inner)) = held.next_if(|(i, _)| *i == at) else { return true }; + // A sublayer holds something; a ruler guide has nothing inside. + if inner.0.is_empty() { + return false; + } + strip_held(Arc::make_mut(c), inner); + true + }); +} + +/// Call `f` (whether it is vertical, its position) on each ruler guide of `layer` and of its +/// sublayers that [`held_guides`] found (`held`), in the order they lie in. Only the sublayers +/// holding one, and the guides, are copied, and only those sublayers are visited. +pub(super) fn map_held(layer: &mut Node, held: HeldGuides, f: &mut impl FnMut(bool, &mut f64)) { + let Some(ch) = layer.children_mut() else { return }; + for (i, inner) in held.0 { + let Some(c) = ch.get_mut(i) else { continue }; + if !inner.0.is_empty() { + map_held(Arc::make_mut(c), inner, f); + } else if let NodeKind::RulerGuide { vertical, pos, .. } = &mut Arc::make_mut(c).kind { + f(*vertical, pos); + } + } +} + impl Node { /// The Appearance panel's row for what this object's own fills and strokes paint around: /// "Contents" for a group or layer (its members) and for an image (its pixels), "Characters" diff --git a/crates/doc/src/node/tests.rs b/crates/doc/src/node/tests.rs index 9981514879..06e68a79d0 100644 --- a/crates/doc/src/node/tests.rs +++ b/crates/doc/src/node/tests.rs @@ -598,3 +598,207 @@ fn compound_stroke_path_is_its_members_in_order() { assert_eq!(bp.elements(), expected.elements()); assert!(!bp.elements().is_empty()); } + +/// The sublayers in [`guide_layers`]'s chain, each inside the one before. +const GUIDE_CHAIN: u64 = 30; + +/// How many children the layers of [`guide_layers`] hold: the layer's 6, 1 in the sublayer +/// without a guide, 2 at each of the two places the shared sublayer lies, the innermost +/// sublayer's 2 and 3 in each sublayer around it. +const GUIDE_LAYER_CHILDREN: usize = 6 + 1 + 2 * 2 + 2 + (GUIDE_CHAIN as usize - 1) * 3; + +/// How many ruler guides the layers of [`guide_layers`] hold: one in each sublayer of the chain, +/// one at each place the shared sublayer lies and the layer's own. +const GUIDE_LAYER_GUIDES: usize = GUIDE_CHAIN as usize + 2 + 1; + +/// A layer holding, in order: [`GUIDE_CHAIN`] sublayers, each inside the one before and holding an +/// object and a ruler guide; a sublayer with an object only; a sublayer with an object and a +/// guide; an object; that last sublayer again; and a guide. Guides differ in direction, artboard +/// and lock. Returns the layer, the sublayer without a guide and the one that lies twice. +fn guide_layers() -> (Node, Arc, Arc) { + let rect = |id| Arc::new(Node::path(NodeId(id), shapes::rectangle(Rect::new(0.0, 0.0, 10.0, 10.0)), Appearance::default_art())); + let guide = |id: u64| { + let kind = NodeKind::RulerGuide { vertical: id.is_multiple_of(2), pos: id as f64, artboard: id.is_multiple_of(3).then_some(7) }; + let mut g = Node::new(NodeId(id), kind); + g.locked = id.is_multiple_of(5); + Arc::new(g) + }; + let sublayer = |id, children: Vec>| { + let mut l = Node::layer(NodeId(id), "Sub", LayerColor::Preset(1)); + if let Some(ch) = l.children_mut() { + *ch = children; + } + Arc::new(l) + }; + let mut chain = sublayer(100, vec![rect(150), guide(200)]); + for depth in 1..GUIDE_CHAIN { + chain = sublayer(100 + depth, vec![rect(600 + depth), chain, guide(300 + depth)]); + } + let still = sublayer(500, vec![rect(501)]); + let twice = sublayer(510, vec![rect(511), guide(512)]); + let mut layer = Node::layer(NodeId(1), "Layer", LayerColor::Preset(0)); + if let Some(ch) = layer.children_mut() { + *ch = vec![chain, Arc::clone(&still), Arc::clone(&twice), rect(2), Arc::clone(&twice), guide(3)]; + } + (layer, still, twice) +} + +/// Whether `a` and `b` hold the same children, none of them copied. +fn same_children(a: &Node, b: &Node) -> bool { + let (a, b) = (a.children().unwrap(), b.children().unwrap()); + a.len() == b.len() && a.iter().zip(b).all(|(a, b)| Arc::ptr_eq(a, b)) +} + +/// Stripping the ruler guides of a layer looks at each child of the layer and of its sublayers +/// once, however deep the sublayers nest. The art and its order stay, a sublayer without a guide is +/// not copied, and a copy of the layer held elsewhere keeps its guides. +#[test] +fn stripping_ruler_guides_looks_at_each_child_of_nested_layers_once() { + use std::cell::Cell; + let (whole, still, twice) = guide_layers(); + let ids = |n: &Node| { + let mut out = vec![]; + n.walk(&mut |c| out.push(c.id)); + out + }; + let mut art = vec![]; + whole.walk(&mut |c| { + if !c.is_ruler_guide() { + art.push(c.id); + } + }); + assert_eq!(ids(&whole).len(), art.len() + GUIDE_LAYER_GUIDES); + + let mut n = whole.clone(); + let looked = Cell::new(0_usize); + let pick = |c: &Node| { + looked.set(looked.get() + 1); + c.is_ruler_guide() + }; + let held = super::ops::held_guides(&n, &pick).unwrap(); + assert_eq!(looked.get(), GUIDE_LAYER_CHILDREN, "each child of the layers is looked at once"); + super::ops::strip_held(&mut n, held); + assert_eq!(looked.get(), GUIDE_LAYER_CHILDREN, "removing looks at nothing again"); + assert_eq!(ids(&n), art, "the art stays, in its order"); + let ch = n.children().unwrap(); + assert!(Arc::ptr_eq(&ch[1], &still), "a sublayer without a guide is not copied"); + assert!(Arc::ptr_eq(&ch[3], &whole.children().unwrap()[3]), "an object is not copied"); + assert!(!Arc::ptr_eq(&ch[2], &twice) && !Arc::ptr_eq(&ch[4], &twice) && !Arc::ptr_eq(&ch[2], &ch[4])); + assert_eq!( + (ch[2].children().map(Vec::len), ch[4].children().map(Vec::len)), + (Some(1), Some(1)), + "a sublayer lying twice is stripped at both places" + ); + // The copies held elsewhere (an undo step's, say) keep their guides. + assert_eq!(twice.children().map(Vec::len), Some(2)); + assert_eq!(ids(&whole).len(), art.len() + GUIDE_LAYER_GUIDES); + + // The method strips the same guides. + let mut same = whole.clone(); + same.strip_ruler_guides(); + assert_eq!(same, n); + assert!(Arc::ptr_eq(&same.children().unwrap()[1], &still)); + // With no guide left there is nothing to remove, and nothing is copied. + let before = n.clone(); + n.strip_ruler_guides(); + assert!(same_children(&n, &before)); + let mut empty = Node::layer(NodeId(900), "Empty", LayerColor::Preset(0)); + empty.strip_ruler_guides(); + assert_eq!(empty.children().map(Vec::len), Some(0)); + // Only a layer holds ruler guides: a group is left as it is, on its own and in a layer. + let mut group = Node::group(NodeId(901), vec![Arc::clone(&twice)]); + assert!(super::ops::held_guides(&group, &Node::is_ruler_guide).is_none()); + group.strip_ruler_guides(); + assert!(Arc::ptr_eq(&group.children().unwrap()[0], &twice)); + let group = Arc::new(group); + let mut around = Node::layer(NodeId(902), "Around", LayerColor::Preset(0)); + around.children_mut().unwrap().push(Arc::clone(&group)); + around.strip_ruler_guides(); + assert!(Arc::ptr_eq(&around.children().unwrap()[0], &group)); +} + +/// Changing the positions of the ruler guides of a layer looks at each child of the layer and of +/// its sublayers once, however deep the sublayers nest. Each guide is changed once, in the order +/// the guides lie in, and keeps everything else about it; the art is unchanged, a sublayer without +/// a guide is not copied, and a copy of the layer held elsewhere keeps its positions. +#[test] +fn mapping_ruler_guide_positions_looks_at_each_child_of_nested_layers_once() { + use std::cell::Cell; + let (whole, still, twice) = guide_layers(); + let mut lines = vec![]; + whole.walk(&mut |c| { + if let NodeKind::RulerGuide { vertical, pos, .. } = c.kind { + lines.push((vertical, pos)); + } + }); + assert_eq!(lines.len(), GUIDE_LAYER_GUIDES); + assert!(lines.iter().any(|l| l.0) && lines.iter().any(|l| !l.0), "guides in both directions"); + + let mut n = whole.clone(); + let looked = Cell::new(0_usize); + let pick = |c: &Node| { + looked.set(looked.get() + 1); + c.is_ruler_guide() + }; + let held = super::ops::held_guides(&n, &pick).unwrap(); + assert_eq!(looked.get(), GUIDE_LAYER_CHILDREN, "each child of the layers is looked at once"); + let mut seen = vec![]; + super::ops::map_held(&mut n, held, &mut |vertical, pos| { + seen.push((vertical, *pos)); + *pos = *pos * 2.0 + 1.0; + }); + assert_eq!(looked.get(), GUIDE_LAYER_CHILDREN, "changing looks at nothing again"); + assert_eq!(seen, lines, "each guide once, in the order the guides lie in"); + + // The same nodes in the same order: a guide differs in its position alone, and the art not + // at all. + let (mut was, mut now) = (vec![], vec![]); + whole.walk(&mut |c| was.push(c)); + n.walk(&mut |c| now.push(c)); + assert_eq!(was.len(), now.len()); + for (a, b) in was.into_iter().zip(now) { + if a.is_layer() { + assert_eq!((b.id, b.is_layer(), b.children().map(Vec::len)), (a.id, true, a.children().map(Vec::len))); + continue; + } + let mut want = a.clone(); + if let NodeKind::RulerGuide { pos, .. } = &mut want.kind { + *pos = *pos * 2.0 + 1.0; + } + assert_eq!(b, &want); + } + let ch = n.children().unwrap(); + assert!(Arc::ptr_eq(&ch[1], &still), "a sublayer without a guide is not copied"); + assert!(Arc::ptr_eq(&ch[3], &whole.children().unwrap()[3]), "an object is not copied"); + assert!(!Arc::ptr_eq(&ch[2], &twice) && !Arc::ptr_eq(&ch[4], &twice) && !Arc::ptr_eq(&ch[2], &ch[4])); + // The copy held elsewhere keeps its position. + assert!(matches!(twice.children().unwrap()[1].kind, NodeKind::RulerGuide { pos, .. } if pos == 512.0)); + + // The method changes the same guides, each once. + let mut same = whole.clone(); + let mut calls = 0; + same.map_ruler_guide_positions(&mut |_, pos| { + calls += 1; + *pos = *pos * 2.0 + 1.0; + }); + assert_eq!((calls, &same), (GUIDE_LAYER_GUIDES, &n)); + assert!(Arc::ptr_eq(&same.children().unwrap()[1], &still)); + // A function that leaves the positions as they are is still called on each guide. + let mut kept = whole.clone(); + let mut calls = 0; + kept.map_ruler_guide_positions(&mut |_, _| calls += 1); + assert_eq!((calls, &kept), (GUIDE_LAYER_GUIDES, &whole)); + // A layer without a guide, an empty layer and a group: nothing to change, nothing copied. + let mut bare = whole.clone(); + bare.strip_ruler_guides(); + let before = bare.clone(); + let mut calls = 0; + bare.map_ruler_guide_positions(&mut |_, _| calls += 1); + assert!(same_children(&bare, &before)); + let mut empty = Node::layer(NodeId(900), "Empty", LayerColor::Preset(0)); + empty.map_ruler_guide_positions(&mut |_, _| calls += 1); + let mut group = Node::group(NodeId(901), vec![Arc::clone(&twice)]); + group.map_ruler_guide_positions(&mut |_, _| calls += 1); + assert!(Arc::ptr_eq(&group.children().unwrap()[0], &twice)); + assert_eq!(calls, 0); +} From 187032fbb2bac6ed81117fea3fbcb1ebf275c5f8 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:03:08 -0800 Subject: [PATCH 19/33] Path Precision Tools: Corner commands refuse unknown item keys addon.pathtools.cornerSet and cornerRemove now refuse a key other than id and anchors in an `items` member, with the standard unknown-parameter error, instead of ignoring it. A misspelled `anchor` used to fall back to the request's `anchors`, else the path's direct-selected anchors, else every corner of the path. They also refuse `items` given together with `ids`, which `items` replaces. Saved steps played with replay still run as recorded. Docs: docs/mcp.md, docs/target-app-parity.md. --- .../engine/src/cmd/addons/pathtools/tests.rs | 65 +++++++++++++++++++ crates/engine/src/cmd/params.rs | 31 +++++++-- docs/mcp.md | 5 +- docs/target-app-parity.md | 3 + 4 files changed, 97 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/cmd/addons/pathtools/tests.rs b/crates/engine/src/cmd/addons/pathtools/tests.rs index f98880e204..a72075d233 100644 --- a/crates/engine/src/cmd/addons/pathtools/tests.rs +++ b/crates/engine/src/cmd/addons/pathtools/tests.rs @@ -713,6 +713,71 @@ fn corner_commands_refuse_bad_params() { } } +#[test] +fn corner_commands_refuse_an_unknown_item_key() { + let mut s = session(); + let id = square(&mut s); + let i = id.0; + let set = json!({"items": [{"id": i, "anchor": [0]}], "radius": 5}); + let e = s.execute("addon.pathtools.cornerSet", &set).unwrap_err().to_string(); + assert!(e.contains("unknown parameter `items[0].anchor`; did you mean `items[0].anchors`?"), "{e}"); + let set_late = json!({"items": [{"id": i, "anchors": [0]}, {"id": i, "radius": 3}], "radius": 5}); + let e = s.execute("addon.pathtools.cornerSet", &set_late).unwrap_err().to_string(); + assert!(e.contains("unknown parameter `items[1].radius`; the command takes: items[1].id, items[1].anchors"), "{e}"); + refuses(&mut s, "addon.pathtools.cornerSet", &[set, set_late]); + s.execute("addon.pathtools.cornerSet", &json!({"radius": 10})).unwrap(); + let remove = json!({"items": [{"id": i, "anchor": [0]}]}); + let e = s.execute("addon.pathtools.cornerRemove", &remove).unwrap_err().to_string(); + assert!(e.contains("unknown parameter `items[0].anchor`; did you mean `items[0].anchors`?"), "{e}"); + refuses(&mut s, "addon.pathtools.cornerRemove", &[remove]); + assert_eq!(drawn(&s, id, 0), 10.0); +} + +#[test] +fn corner_items_take_their_documented_keys() { + let mut s = session(); + let id = square(&mut s); + let i = id.0; + let r = s.execute("addon.pathtools.cornerSet", &json!({"items": [{"id": i, "anchors": [0]}], "radius": 5})).unwrap(); + assert_eq!((r["corners"].clone(), drawn(&s, id, 0), drawn(&s, id, 1)), (json!(1), 5.0, 0.0)); + // A member without `anchors` takes the request's. + s.execute("addon.pathtools.cornerSet", &json!({"items": [{"id": i}], "anchors": [2], "radius": 6})).unwrap(); + assert_eq!(drawn(&s, id, 2), 6.0); + let r = s.execute("addon.pathtools.cornerRemove", &json!({"items": [{"id": i, "anchors": [0]}]})).unwrap(); + assert_eq!((r["corners"].clone(), drawn(&s, id, 0), drawn(&s, id, 2)), (json!(1), 0.0, 6.0)); + // A null `ids` beside `items` counts as not given. + s.execute("addon.pathtools.cornerRemove", &json!({"items": [{"id": i}], "ids": null})).unwrap(); + assert_eq!(drawn(&s, id, 2), 0.0); +} + +#[test] +fn corner_commands_refuse_items_with_ids() { + let mut s = session(); + let id = square(&mut s); + let other = line(&mut s, &[(350.0, 10.0), (390.0, 10.0), (390.0, 50.0)], false); + let both = json!({"items": [{"id": id.0, "anchors": [0]}], "ids": [other.0], "radius": 5}); + let e = s.execute("addon.pathtools.cornerSet", &both).unwrap_err().to_string(); + assert!(e.contains("give `items` or `ids`, not both"), "{e}"); + refuses(&mut s, "addon.pathtools.cornerSet", &[both]); + s.execute("addon.pathtools.cornerSet", &json!({"ids": [id.0], "radius": 5})).unwrap(); + let both = json!({"items": [{"id": id.0}], "ids": [id.0]}); + let e = s.execute("addon.pathtools.cornerRemove", &both).unwrap_err().to_string(); + assert!(e.contains("give `items` or `ids`, not both"), "{e}"); + refuses(&mut s, "addon.pathtools.cornerRemove", &[both]); +} + +#[test] +fn saved_corner_steps_with_older_item_keys_still_play() { + let mut s = session(); + let id = square(&mut s); + let other = line(&mut s, &[(350.0, 10.0), (390.0, 10.0), (390.0, 50.0)], false); + let saved = json!({"items": [{"id": id.0, "anchors": [0], "old": 1}], "ids": [other.0], "radius": 5}); + s.replay("addon.pathtools.cornerSet", &saved).unwrap(); + assert_eq!((drawn(&s, id, 0), drawn(&s, id, 1)), (5.0, 0.0)); + s.replay("addon.pathtools.cornerRemove", &json!({"items": [{"id": id.0, "old": 1}]})).unwrap(); + assert_eq!(drawn(&s, id, 0), 0.0); +} + /// A closed five-pointed star about (200, 150): its points at the even indices 0, 2… (the odd /// numbers counting from 1), its inner corners at the odd ones. fn star_path(s: &mut Session) -> NodeId { diff --git a/crates/engine/src/cmd/params.rs b/crates/engine/src/cmd/params.rs index 20e5054aeb..fe8431a6c6 100644 --- a/crates/engine/src/cmd/params.rs +++ b/crates/engine/src/cmd/params.rs @@ -10,7 +10,7 @@ //! save or Export for Screens: those are checked against the options of the formats the command //! can write, in `marks`, `bleed` and the other objects too ([`check_export_options`]); and in the //! objects of an array whose keys `MEMBER_KEYS` lists, such as the `items` of -//! `object.setLiveShape`. +//! `object.setLiveShape` and `addon.pathtools.cornerSet`. //! //! [`Session::execute`] answers a key the description doesn't name with a parameter error //! ([`check_params`]), for every caller (the app, the control channel, MCP, the CLI) and for the @@ -256,6 +256,22 @@ pub fn check_keys(id: &str, doc: &'static str, params: &Value) -> Result<()> { } } +/// Parameters a command takes instead of one another: (command, key, other key). Giving both, +/// neither null, is a parameter error ([`check_exclusive_keys`]). +const EXCLUSIVE_KEYS: &[(&str, &str, &str)] = &[("addon.pathtools.cornerSet", "items", "ids"), ("addon.pathtools.cornerRemove", "items", "ids")]; + +/// `params` of command `id` give at most one of each pair in [`EXCLUSIVE_KEYS`], or the parameter +/// error naming the pair. +fn check_exclusive_keys(id: &str, params: &Value) -> Result<()> { + let given = |key: &str| params.get(key).filter(|v| !v.is_null()); + for (_, key, other) in EXCLUSIVE_KEYS.iter().filter(|(c, ..)| *c == id) { + if given(key).is_some() && given(other).is_some() { + return Err(bad(id, format!("give `{key}` or `{other}`, not both"))); + } + } + Ok(()) +} + /// The parameter error of command `id` for `key`, which isn't one of `keys`: with the nearest of /// them, or all of them. pub(crate) fn unknown(id: &str, key: &str, keys: &[&str]) -> crate::EngineError { @@ -273,7 +289,11 @@ pub(crate) fn unknown(id: &str, key: &str, keys: &[&str]) -> crate::EngineError /// Commands whose params hold an array of objects with keys of their own: the command, the key of /// the array and the keys each of its objects takes. -const MEMBER_KEYS: &[(&str, &str, &[&str])] = &[("object.setLiveShape", "items", &["id", "corners"])]; +const MEMBER_KEYS: &[(&str, &str, &[&str])] = &[ + ("object.setLiveShape", "items", &["id", "corners"]), + ("addon.pathtools.cornerSet", "items", &["id", "anchors"]), + ("addon.pathtools.cornerRemove", "items", &["id", "anchors"]), +]; /// Each object in the arrays [`MEMBER_KEYS`] names for command `id` has only the keys it lists, or /// the parameter error naming the first other one with its place (`items[0].corner`), and the @@ -848,9 +868,9 @@ fn check_export_options(id: &str, params: &Value) -> Result<()> { check_option_keys(id, "", given, &keys, why_not, &trees) } -/// [`check_keys`] for an engine command, the steps of a `command.batch` or an `action.batch` included, -/// [`check_effect_params`] for the effect `effect.apply` adds and [`check_export_options`] for an -/// export. A command the engine doesn't have passes: running it reports that. +/// [`check_keys`], [`check_exclusive_keys`] and [`check_member_keys`] for an engine command, the steps of a `command.batch` or an +/// `action.batch` included, [`check_effect_params`] for the effect `effect.apply` adds and +/// [`check_export_options`] for an export. A command the engine doesn't have passes: running it reports that. pub fn check_params(id: &str, params: &Value) -> Result<()> { check_params_in(id, params, false) } @@ -869,6 +889,7 @@ fn check_params_in(id: &str, params: &Value, in_batch: bool) -> Result<()> { return Ok(()); } check_keys(id, spec.params, params)?; + check_exclusive_keys(id, params)?; check_member_keys(id, params)?; if in_batch { crate::schemas::check(spec, params, false)?; diff --git a/docs/mcp.md b/docs/mcp.md index 912ac32562..eb7f06da84 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -10753,7 +10753,8 @@ Commands: - `addon.pathtools.cornerSet {ids?, anchors?, items?, radius, kind?, every?, minAngle?, maxAngle?}` cuts live corners, the ones Direct Selection's corner widgets and the Corners dialog edit, as one undo step. `ids` (default: the selection) are the paths and `anchors` the anchor indices of each path's outline with no corners cut; without `anchors`, a path's direct-selected - anchors, or all of them. `items: [{id, anchors?}…]` gives each path its own instead. Only anchors where two sides meet + anchors, or all of them. `items: [{id, anchors?}…]` gives each path its own instead; a member key other than `id` + and `anchors`, or `items` given with `ids`, is an error. Only anchors where two sides meet at an angle take a corner: smooth anchors and open ends are skipped, and it is an error if none is left. `radius` is 0 to 1,000,000 pt, drawn no larger than fits; `kind` is `round`, `invertedRound` or `chamfer` (default: each corner's own). `every` (`all`, `odd` or `even`; default `all`) keeps, of those anchors, only the odd- or even-numbered ones, @@ -10763,7 +10764,7 @@ Commands: be more than `maxAngle`, and it is an error, saying so, if no chosen corner is in the range. Object › Plug-ins › Path Precision Tools › Set Corners… (also in the Anchor Inspector's Corners section) sets these with Preview. → `{ids, corners}`. - `addon.pathtools.cornerRemove {ids?, anchors?, items?}` makes those corners sharp again, as one undo step; a plain path - with none left is plain again. → `{ids, corners}`. + with none left is plain again. Its `items` take the same keys as `cornerSet`'s. → `{ids, corners}`. - `addon.pathtools.slideAnchor {id, subpath?, anchor, t}` moves an anchor along its path, the segments beside it refitted so the outline stays (the anchor count and index stay). `t`, -0.98 to 0.98, says where: positive, a curve parameter on the segment after the anchor; negative, back from the end of the one before. An anchor at a sharp corner loses it, as diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..b019a547b7 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -2064,6 +2064,9 @@ Corners section in the Anchor Inspector sets the radius and kind, counts the sel Set Corners… (in the submenu and the Corners section) sets the radius and kind with Preview, on all the chosen anchors or only the odd- or even-numbered ones (`every`, #5546), and only the corners whose angle is in a range (`minAngle`/`maxAngle`, #5545). +`cornerSet` and `cornerRemove` refuse an `items` member key other than `id` and `anchors`, and `items` given with `ids`; +saved steps still play (`corner_commands_refuse_an_unknown_item_key`, `corner_commands_refuse_items_with_ids`, +`saved_corner_steps_with_older_item_keys_still_play`). Slide Anchor Along Path drags an anchor along its curve, the two segments beside it refitted by the keep-shape kernel (Shift: where the tangent is a multiple of 45°). Commands `addon.pathtools.cornerSet`, `cornerRemove` and `slideAnchor`, each one undo step; `vectorcraft_pathops::addons::pathtools::slide_anchor`. Tests: `addons::pathtools` in From c0e70819f4aac834723062ef13d28e29c521887a Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:06:26 -0800 Subject: [PATCH 20/33] i18n: the untranslated report checks the effects, map_err messages and schema phrases The untranslated report missed messages that reach the status bar: - MESSAGE_CRATES now includes effects, color, render, three-d and pathops, whose errors the engine and the effects pass on (SVG filters, swatch libraries, raster sizes and encoders, 3D solids, path operations). - The scan reads the messages a .map_err closure returns, from each if branch, match arm and the last expression of a block, the messages of a handler that .map_err(name) names (a closure bound with let, or a function), and the message given to a variant of an enum whose name ends in Fail (CompileFail::Syntax). The steps of a script result's path ([{i}]{place}, .{k}{place}) are not messages. - The report and the VECTORCRAFT_I18N_DUMP_MESSAGES dump include the phrases a parameter schema's error builds at run time (SCHEMA_PHRASES). Missing translations stay report-only. On main at 0b29b2b1f6 the report lists 126 more messages, missing in all 23 languages that translate every message: 43,084 missing rows before, 45,983 after. --- crates/ui-egui/src/i18n/tests.rs | 259 +++++++++++++++++++++++++++++-- docs/development.md | 8 +- docs/target-app-parity.md | 1 + 3 files changed, 255 insertions(+), 13 deletions(-) diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index 6c9deb97d0..bb9d348736 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -1486,7 +1486,8 @@ fn string_literals(e: &str, keep: impl Fn(&str, &str) -> bool) -> Vec { /// For translators, `cargo test -p vectorcraft-ui-egui dump_source_strings` writes every English /// interface string to `VECTORCRAFT_I18N_DUMP=`, and every status and error message as a -/// catalog template to `VECTORCRAFT_I18N_DUMP_MESSAGES=`, one per line. +/// catalog template that [`untranslated_report`] checks to `VECTORCRAFT_I18N_DUMP_MESSAGES=`, +/// one per line. #[test] fn dump_source_strings() { if let Ok(path) = std::env::var("VECTORCRAFT_I18N_DUMP") { @@ -1498,10 +1499,27 @@ fn dump_source_strings() { std::fs::write(path, text).expect("write dump"); } if let Ok(path) = std::env::var("VECTORCRAFT_I18N_DUMP_MESSAGES") { - std::fs::write(path, message_literals().iter().map(|m| format!("{m}\n")).collect::()).expect("write dump"); + std::fs::write(path, message_dump()).expect("write dump"); } } +/// The text [`dump_source_strings`] writes to `VECTORCRAFT_I18N_DUMP_MESSAGES`. +fn message_dump() -> String { + reported_messages().iter().map(|m| format!("{m}\n")).collect() +} + +/// The translators' message dump lists every message the untranslated report checks, the +/// phrases a parameter schema's error builds included. +#[test] +fn message_dump_lists_the_reported_messages() { + let dump = message_dump(); + let lines: std::collections::BTreeSet<&str> = dump.lines().collect(); + for phrase in SCHEMA_PHRASES { + assert!(lines.contains(phrase), "the dump skips {phrase:?}"); + } + assert!(lines.contains("the color book is cut short")); +} + fn cs() -> Lang { Lang::from_code("cs").expect("cs registered") } @@ -1640,7 +1658,7 @@ fn untranslated_report() { undo.extend(liquify.iter().filter_map(|k| vectorcraft_tools::distort::liquify::LiquifyKind::parse(k)).map(|k| format!("{} Tool", k.label()))); assert_eq!(scan.formatted.iter().collect::>(), ["{} Tool"], "step names built with format! (make them with Session::edit_named)"); assert!(scan.templates.len() >= 3, "scan found only the templates {:?}", scan.templates); - let messages = message_literals(); + let messages = reported_messages(); assert!(messages.len() > 300, "scan found only {} messages", messages.len()); let (swatches, groups) = vectorcraft_color::default_swatches(vectorcraft_color::cms::Model::Rgb); // The greys' `K=` values read the same in any language. @@ -1741,6 +1759,36 @@ fn untranslated_report() { println!("{out}"); } +/// The status and error messages [`untranslated_report`] checks: the message literals of +/// [`MESSAGE_CRATES`] and the phrases a parameter schema's error builds at run time +/// ([`SCHEMA_PHRASES`]), which no literal holds. +fn reported_messages() -> std::collections::BTreeSet { + let mut messages = message_literals(); + messages.extend(SCHEMA_PHRASES.iter().map(|p| p.to_string())); + messages +} + +/// The untranslated report checks the messages of the effects and of the crates whose errors the +/// engine and the effects pass on, and the phrases a parameter schema's error builds. +#[test] +fn untranslated_report_checks_every_message_crate_and_schema_phrase() { + let reported = reported_messages(); + let passed_on = [ + ("effects", "the SVG filter has no primitives"), + ("color", "not a swatch library (.vcswatches or .gpl)"), + ("render", "invalid scale {scale}"), + ("three-d", "3D effects require one to ten lights"), + ("pathops", "the shapes are too degenerate to combine"), + ]; + for (krate, m) in passed_on { + assert!(message_literals_in(&[krate]).contains(m), "{krate}: {m}"); + assert!(reported.contains(m), "the report skips {krate}: {m}"); + } + for phrase in SCHEMA_PHRASES { + assert!(reported.contains(*phrase), "the report skips the schema phrase {phrase:?}"); + } +} + /// The names [`MENU_KEEP_AS_IS`] lists are menu labels, and no catalog that keeps them in English /// translates them. #[test] @@ -2723,6 +2771,84 @@ fn f(i: &mut Interp, interp: &mut Interp) -> JsResult { assert_eq!(found, ["{what}: the object is not a File or a Folder", "File.seek: the mode must be 0, 1 or 2", "the path has an empty step"]); } +/// The scan reads the message a `.map_err` closure returns, a literal, a `format!` string or a +/// constant, whatever the closure's parameter, from each branch of an `if` and each arm of a +/// `match`, and given to a variant of an enum whose name ends in `Fail`, and not a closure that +/// passes the error on, a `json!` value's keys, nor the steps of a script result's path +/// ([`NOT_MESSAGES`]). +#[test] +fn message_scan_reads_map_err_closures() { + let code = r#" +fn f(text: &str, n: usize) -> Result { + let doc = parse(text).map_err(|e| format!("the filter isn't valid XML: {e}"))?; + let a = at(doc).map_err(|place| format!("[{i}]{place}"))?; + let b = at(doc).map_err(|place| format!(".{k}{place}"))?; + let c = at(doc).map_err(|e| format!("{name}: {e}"))?; + let n = u32::try_from(n).map_err(|_| "too many blocks".to_string())?; + let m = u16::try_from(n).map_err(move |(a, b)| LIMIT.into())?; + let k = read(doc).map_err(|e| e.to_string())?; + let j = read(doc).map_err(|e| bad(C, e))?; + let i = read(doc).map_err(|e| if e == "{x}" { CUT_SHORT.into() } else { e })?; + let h = read(doc).map_err(|e| match e { + Error::Io(_) => "the file can't be read".to_string(), + other => fail(json!({"objects": [other.to_string()]})), + })?; + let g = i32::try_from(n).map_err(|_| CompileFail::Syntax("the pattern is too large".into()))?; + let f = read(doc).map_err(|e| { + self.published = None; + format!("the clipboard can't be read: {e}") + })?; + Ok(n) +} +"#; + let consts = + [("LIMIT", "the name is too long"), ("CUT_SHORT", "the book is cut short")].map(|(k, v)| (k.to_string(), vec![v.to_string()])).into(); + let want = [ + "the pattern is too large", + "the filter isn't valid XML: {e}", + "{name}: {e}", + "too many blocks", + "the name is too long", + "the book is cut short", + "the file can't be read", + "the clipboard can't be read: {e}", + ]; + assert_eq!(source_messages(code, &[], &consts), want); + assert!(message_literals_in(&["color"]).contains("the color book is cut short")); + assert!(message_literals_in(&["color"]).contains("not a swatch library: {e}")); + assert!(message_literals_in(&["js"]).contains("regular expression too large")); + assert!(message_literals_in(&["effects"]).contains("the SVG filter isn't valid XML: {e}")); +} + +/// The scan reads the messages of an error handler that a `.map_err(name)` names: a closure bound +/// to that name, up to its `;`, and a function of that name, each of its statements. +#[test] +fn message_scan_reads_named_map_err_handlers() { + let code = r#" +fn encode(img: &Image) -> Result, String> { + let fail = |e: gif::EncodingError| format!("GIF encoding failed: {e}"); + let gone = move |_| GONE.to_string(); + let size = if img.is_empty() { WRONG.into() } else { 0 }; + let mut enc = Encoder::new(img).map_err(fail)?; + enc.write(img).map_err(fail)?; + enc.check().map_err(gone)?; + enc.close().map_err(unreadable)?; + enc.into_inner().map_err(Error::Io) +} + +fn unreadable(e: impl std::fmt::Display) -> String { + let e = e.to_string(); + format!("the file can't be read: {e}") +} +"#; + let consts = [("GONE", "the encoder is gone"), ("WRONG", "the image is wrong")].map(|(k, v)| (k.to_string(), vec![v.to_string()])).into(); + let want = ["GIF encoding failed: {e}", "the encoder is gone", "the file can't be read: {e}"]; + assert_eq!(source_messages(code, &[], &consts), want); + let render = message_literals_in(&["render"]); + assert!(render.contains("GIF encoding failed: {e}") && render.contains("JPEG encoding failed: {e}")); + assert!(message_literals_in(&["engine"]).contains("Data Recovery can't keep copies in {path}: {e}")); +} + /// The scan stops at a test module whose `cfg` is `test` or `all(test, …)`, and reads on past a /// declaration of a test file and a module that is not only for tests. #[test] @@ -3880,8 +4006,13 @@ const COMPLETE_MESSAGES: &[&str] = &[ "nl", ]; -/// Crates whose error and status messages reach the status bar. -const MESSAGE_CRATES: &[&str] = &["ui-egui", "engine", "doc", "format", "svg", "pdf", "eps", "text", "plugins", "metafile", "cad", "trace", "js"]; +/// Crates whose error and status messages reach the status bar, the crates whose errors the +/// engine and the effects pass on included (effects, swatch libraries, raster sizes and encoders, +/// 3D solids, path operations). +const MESSAGE_CRATES: &[&str] = &[ + "ui-egui", "engine", "doc", "format", "svg", "pdf", "eps", "text", "plugins", "metafile", "cad", "trace", "js", "effects", "color", "render", + "three-d", "pathops", +]; /// Where a message literal starts: a status, an error value, a `thiserror` message. const MESSAGE_MARKERS: &[&str] = &[".status(", "ui.status = ", "status = ", "Other(", "Err(", "ok_or(", "ok_or_else(|| ", "#[error("]; @@ -3926,9 +4057,10 @@ const NAME_VARIANTS: &[&str] = &["UnknownCommand", "Ps"]; /// `EngineError::Disabled(command id, reason)`. const LATER_MESSAGE_VARIANTS: &[(&str, usize)] = &[("Disabled", 1)]; -/// Messages that are signals, not text for people, and the name of a plug-in export that an -/// error message holds (`vc_manifest points outside memory`). -const NOT_MESSAGES: &[&str] = &["quit", "cancelled", "vc_manifest"]; +/// Messages that are signals, not text for people, the name of a plug-in export that an error +/// message holds (`vc_manifest points outside memory`), and the steps of the path to a script +/// result's part that an error message holds (`vc.run: the result{place} is too large for a script`). +const NOT_MESSAGES: &[&str] = &["quit", "cancelled", "vc_manifest", "[{i}]{place}", ".{k}{place}"]; /// Every status and error message literal in [`MESSAGE_CRATES`] (test modules aside), as catalog /// templates: `{}` becomes `{_1}`, `{_2}` … and format specs are dropped (`{n:?}` → `{n}`). @@ -4046,8 +4178,11 @@ fn named_const<'a>(rest: &str, consts: &'a std::collections::BTreeMap(rest: &str, consts: &'a std::collections::BTreeMap>) -> Vec { let code = &without_comments(code); let after_markers = MESSAGE_MARKERS.iter().flat_map(|marker| code.match_indices(marker).map(|(at, _)| &code[at + marker.len()..])); - let in_variants = code.match_indices("Error::").filter_map(|(at, m)| { + let in_variants = ["Error::", "Fail::"].into_iter().flat_map(|enum_end| code.match_indices(enum_end)).filter_map(|(at, m)| { let rest = &code[at + m.len()..]; let (variant, args) = rest.split_at(rest.find(|c: char| !c.is_ascii_alphanumeric() && c != '_').unwrap_or(rest.len())); let named = variant.starts_with(|c: char| c.is_ascii_uppercase()) && !NAME_VARIANTS.contains(&variant); let before = LATER_MESSAGE_VARIANTS.iter().find(|(v, _)| *v == variant).map_or(0, |&(_, n)| n); if named { skip_args(args.strip_prefix('(')?, before) } else { None } }); + let in_map_err = code.match_indices(".map_err(").flat_map(|(at, m)| { + let rest = code[at + m.len()..].trim_start(); + let params = rest.strip_prefix("move").map_or(rest, str::trim_start).strip_prefix('|'); + params.and_then(|p| p.find('|').map(|end| &p[end + 1..])).map(closure_returns).unwrap_or_default() + }); + let mut handlers = vec![]; + for (at, m) in code.match_indices(".map_err(") { + let rest = &code[at + m.len()..]; + let name = &rest[..rest.find(|c: char| !c.is_ascii_alphanumeric() && c != '_').unwrap_or(rest.len())]; + if name.starts_with(|c: char| c.is_ascii_lowercase()) && rest[name.len()..].starts_with(')') && !handlers.contains(&name) { + handlers.push(name); + } + } + let in_handlers = handlers.into_iter().flat_map(|name| handler_returns(code, name)); let in_helpers = helpers.iter().flat_map(|&(call, before)| { code.match_indices(call) .filter(|&(at, _)| call.starts_with('.') || !code[..at].ends_with(|c: char| c.is_alphanumeric() || c == '_' || c == '.')) .filter_map(move |(at, _)| skip_args(&code[at + call.len()..], before)) }); let mut out = vec![]; - for rest in after_markers.chain(in_variants).chain(in_helpers) { + for rest in after_markers.chain(in_variants).chain(in_map_err).chain(in_handlers).chain(in_helpers) { let rest = rest.trim_start(); let rest = rest.strip_prefix('&').map_or(rest, str::trim_start); let rest = rest.strip_prefix("format!(").map_or(rest, str::trim_start); @@ -4119,6 +4268,94 @@ fn skip_args(args: &str, n: usize) -> Option<&str> { None } +/// Where each value the error handler `name` of a `.map_err(name)` in `code` can return starts: +/// the closure the file binds to that name (`let fail = |e| format!("…: {e}");`, up to its `;`) +/// or the body of the function of that name (`fn unreadable(e: …) -> String { … }`), read as +/// [`closure_returns`] reads a closure. +fn handler_returns<'a>(code: &'a str, name: &str) -> Vec<&'a str> { + let (bound, declared) = (format!("let {name} = "), format!("fn {name}(")); + let mut out = vec![]; + for (at, m) in code.match_indices(bound.as_str()).chain(code.match_indices(declared.as_str())) { + if code[..at].ends_with(|c: char| c.is_alphanumeric() || c == '_') { + continue; + } + let rest = &code[at + m.len()..]; + if m == bound { + let rest = rest.strip_prefix("move").map_or(rest, str::trim_start); + if let Some(body) = rest.strip_prefix('|').and_then(|p| p.find('|').map(|end| &p[end + 1..])) { + out.extend(closure_returns(statement(body))); + } + } else if let Some(open) = rest.find('{') { + out.extend(closure_returns(&rest[open + 1..])); + } + } + out +} + +/// The statement `code` starts with, up to its `;` or the bracket that closes the block it is in. +fn statement(code: &str) -> &str { + let (mut depth, mut chars) = (0usize, code.char_indices()); + while let Some((at, c)) = chars.next() { + match c { + '(' | '[' | '{' => depth += 1, + ')' | ']' | '}' => match depth.checked_sub(1) { + Some(d) => depth = d, + None => return &code[..at], + }, + ';' if depth == 0 => return &code[..at], + '"' => loop { + match chars.next() { + Some((_, '\\')) => { + chars.next(); + } + Some((_, '"')) | None => break, + _ => {} + } + }, + _ => {} + } + } + code +} + +/// Where each value a closure can return starts, `body` being the text after its parameters: the +/// body itself, each block or `if`/`else` branch in it (`{ ACB_CUT_SHORT.into() }`, not the +/// `{` of a `json!({…})` value), each `match` arm (`=>`) and each statement after a `;` (the +/// last expression of a block or a function body), up to the bracket that closes the call the +/// closure is given to or the function. +fn closure_returns(body: &str) -> Vec<&str> { + let mut out = vec![body]; + let (mut depth, mut chars) = (0usize, body.char_indices()); + while let Some((at, c)) = chars.next() { + match c { + '(' | '[' => depth += 1, + '{' => { + depth += 1; + if !body[..at].trim_end().ends_with('(') { + out.push(&body[at + 1..]); + } + } + ')' | ']' | '}' => match depth.checked_sub(1) { + Some(d) => depth = d, + None => break, + }, + ';' => out.push(&body[at + 1..]), + '=' if body[at + 1..].starts_with('>') => out.push(&body[at + 2..]), + '"' => loop { + match chars.next() { + Some((_, '\\')) => { + chars.next(); + } + Some((_, '"')) | None => break, + _ => {} + } + }, + _ => {} + } + } + out +} + /// Source up to its inline test module (`#[cfg(test)] mod tests {`, or a `cfg(all(test, …))` one); /// declarations of test files (`#[cfg(test)] mod tests_x;`) don't end it. fn without_test_module(text: &str) -> &str { diff --git a/docs/development.md b/docs/development.md index 05062e18b8..d20d085c4e 100644 --- a/docs/development.md +++ b/docs/development.md @@ -296,10 +296,14 @@ other `sv-*` locale resolve to it), Georgian (`ka`, complete; `ka-GE` and `ka_GE `Couldn't open {name}: {e}`; `{_1}`, `{_2}` … stand for the format string's `{}`, and the values in the placeholders are translated in turn (the reason after `: {e}` is often a message too). Czech, German, Greek, Spanish, French, Georgian, Hungarian, Italian, Japanese, Korean, Polish, Brazilian Portuguese, Russian, Simplified Chinese, Traditional Chinese, Swedish, Ukrainian, Vietnamese, Finnish, Turkish, Catalan and Dutch translate status and error messages (`COMPLETE_MESSAGES` in `i18n::tests`); the untranslated report lists the - message literals the scan finds that one of them has no row for. The scan reads `Err("…")`, `Other(…)`, + message literals the scan finds, and the phrases a parameter schema's error builds at run time + (`SCHEMA_PHRASES`), that one of them has no row for. The scan covers the crates in `MESSAGE_CRATES`, the + effects and the crates whose errors the engine passes on included. It reads `Err("…")`, `Other(…)`, `#[error(…)]` and `status(…)` messages, messages given to an error enum's variant (`PsError::Limit(…)`, the reason in `EngineError::Disabled(id, …)`), to the engine's `bad(cmd, …)`, `err(…)` or `error(…)`, and to the PDF writer's - `bad(…)` or `failed(…)`, and skips calls inside comments + `bad(…)` or `failed(…)`, and the messages a `.map_err(|e| …)` closure returns (from each `if` branch, + `match` arm and statement of a block) or the handler a `.map_err(name)` names returns (a closure bound with `let name = |e| …;` or a + function `fn name(…)`), and skips calls inside comments (`VECTORCRAFT_I18N_DUMP_MESSAGES=messages.txt cargo test -p vectorcraft-ui-egui dump_source_strings` lists them all). A missing row shows the English message. Other languages show messages in English until they add `@msg` rows. diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..1f36e5fe48 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -907,6 +907,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - The errors of the 3D effects (Extrude & Bevel, Revolve, Inflate, Rotate and their Classic forms) show in the status bar in the interface language: the art a 3D effect cannot build a solid from, invalid light, graphics and surface settings, and the notes for an object that draws no 3D faces or more wireframe edges than the limit. Each has a row in every catalog that translates all status and error messages, and the effect's name and the values in the message are translated or kept as written (`three_d_refusals_are_translated_in_every_complete_catalog`, `three_d_refusals_translate_with_their_values`). - The errors of the parameter checks that many commands share show in the status bar in the interface language: a value that is not a number where a number goes (`object.move` with `"dx": "60 mm"`, and `object.rotate`, `object.setBounds` and the other commands that call `check_numbers`), a missing number or index, `ids` that are not a list of object ids, an object id that is not a non-negative integer or names no object, and a date that is not Unix seconds. Each has a row in every catalog that translates all status and error messages, and in Bulgarian, Danish, Indonesian and Dutch (`shared_param_check_errors_are_translated`). - A parameter schema's errors show in the status bar with what the value must be in the interface language, for every type a schema has (a number with each kind of range and with a unit, an integer and its range, true or false, a string, a list of names, an object id, a list of object ids, a color, a point, an object and a list), in every catalog that translates all status and error messages; the path, the refused value, the bounds, the unit and the names stay as the English gives them. The error for a value that is not one of its names is also translated in Bulgarian, Bosnian, Danish, Indonesian and Dutch (`parameter_schema_expected_phrases_are_translated`, `bracketed_message_parts_translate_only_as_exact_messages`). + - The untranslated report lists the status and error messages of the effects, swatch libraries, raster sizes and encoders, 3D solids and path operations that a catalog lacks, the messages that a `.map_err` closure or a named error handler returns, and the phrases a parameter schema's error builds for what a value must be (`untranslated_report_checks_every_message_crate_and_schema_phrase`, `message_scan_reads_map_err_closures`, `message_scan_reads_named_map_err_handlers`). - Export commands run through MCP, the control channel or the CLI refuse a key that no format takes, and warn about a key that only another format takes (`{"format": "pdf", "quality": 80}` writes the PDF with the warning "`quality` was not used: PDF files don't read it"; `an_option_only_another_format_reads_gets_a_note`, `control_export_notes_an_option_the_format_does_not_read`, `convert_warns_about_an_unused_option_only_when_one_was_given`), and a command that writes one format (`document.exportPdf`, `document.exportEps`, `document.exportDxf`) or takes one format's options (`document.pdfSettings`, `pdf.preset.save`) refuses a key that format doesn't take. An unknown key inside `marks`, `bleed`, `compression`, `output`, `advanced`, `security` or `flattener` is refused by its path. - Save commands run through MCP, the control channel or the CLI (`document.save`, `file.saveAs`, `file.saveCopy`, `file.saveAsTemplate`, `app.save`) refuse a key that no save format takes, beside the path or in `options`. Export for Screens refuses a key it doesn't take, and a key that no format takes in a format row or in a format's settings, by its path (`formats[1].marks.useDocumentBleed`); Export Assets (`assets.export`, `assets.settings.set`) checks the rows and settings it is given the same way. - Commands that read their `ids` (or `slices`) list themselves refuse a list with an entry that is not a non-negative integer, and a value that is not a list, and change nothing; they no longer drop the entry and act on the rest (Copy Appearance From, the Edit Colors commands and Overprint Black, Find and Replace, `layer.highlight`, `layer.move`, `library.add`, the Links panel commands, `object.transform`, Make Pattern, the Repeat commands, the slice commands, Join, Average and Join, `plugin.run`, `symbol.edit`, Make Opacity Mask; `tests::ids_without_selection::an_ids_entry_that_is_not_an_id_is_refused_and_changes_nothing`). The error for an entry of an object id list names the key, the entry and its value ("`ids` entry 1 is true, not a non-negative integer object id"), with a row in every catalog that has the other object id errors. From d358baf9cc672c2799d2538039ce9e8899151387 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:13:53 -0800 Subject: [PATCH 21/33] i18n: Brazilian Portuguese translates 1,812 of the status and error messages the untranslated report lists pt-br is in COMPLETE_MESSAGES, but the untranslated report listed 1,851 status and error messages with no pt-br row, so with the interface in Brazilian Portuguese those messages showed in English. - pt-br.tsv gains 1,812 `@msg` rows. The translations are VectorCraft's own, written from the English meaning with the catalog's terms. Placeholders, backticked names, command ids, parameter names, double-quoted values and bare true, false and null stay as the English writes them. - The 39 messages that open #7211, #7214, #7116 and #7330 add pt-br rows for are left out. bundled_catalogs_are_consistent rejects a second row for the same key. - The Portuguese row of docs/localization-parity.md gives the catalog's counts: 9,260 rows, 3,445 of them `@msg` and 48 `@msgplural`. --- crates/ui-egui/src/i18n/pt-br.tsv | 1812 +++++++++++++++++++++++++++++ docs/localization-parity.md | 4 +- 2 files changed, 1814 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index c2349359ed..7c5c1fd5d6 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -7555,9 +7555,1821 @@ swatch Yellow Green Verde-amarelado Margin Margem Box Caixa Swatch Amostra +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions 'caller', 'callee' e 'arguments' não podem ser usados em funções em modo estrito +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. Um perfil aceita um único efeito Revolver. Edite as opções do efeito existente. +@msg Add Points at Crossings: a path went missing Adicionar Pontos nos Cruzamentos: um caminho deixou de existir +@msg Alignment must be one of the choices O alinhamento deve ser uma das opções +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} foi chamado em um valor que não é um Boolean +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB As imagens CMYK cujas amostras não puderam ser lidas em CMYK foram importadas em RGB +@msg CSS options: {e} Opções de CSS: {e} +@msg CSV has a header but no data rows O CSV tem um cabeçalho, mas nenhuma linha de dados +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark O CSV contém bytes NUL: salve-o como UTF-8 ou como UTF-16 com marca de ordem de bytes +@msg CSV is empty O CSV está vazio +@msg CSV is larger than {MAX_CSV_BYTES} bytes O CSV tem mais de {MAX_CSV_BYTES} bytes +@msg CSV is not UTF-16 O CSV não está em UTF-16 +@msg CSV is not UTF-8 O CSV não está em UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes O CSV teria mais de {MAX_CSV_BYTES} bytes +@msg Cannot write to the location of “{name}”; choose where to save it Não é possível gravar no local de “{name}”; escolha onde salvá-lo +@msg Choose original artwork, not a dimension's generated paths Escolha a arte original, não os caminhos gerados por uma cota +@msg Contour: select paths or shapes Contorno: selecione caminhos ou formas +@msg Contour: the Offset Path effect is not available Contorno: o efeito Deslocar Caminho não está disponível +@msg Create needs fills, strokes or both Criar precisa de preenchimentos, traçados ou ambos +@msg DCS files can only be placed linked: use File › Place with Link on Arquivos DCS só podem ser colocados como vinculados: use Arquivo › Colocar com Vincular ativado +@msg DXF options: {e} Opções de DXF: {e} +@msg DXF scale must be a positive number up to {_1}, not {scale} A escala do DXF deve ser um número positivo de até {_1}, não {scale} +@msg DXF {field} `{s}`: {allowed} {field} do DXF `{s}`: {allowed} +@msg Date.prototype method called on a value that is not a Date object Método de Date.prototype chamado em um valor que não é um objeto Date +@msg Dimension is locked or hidden A cota está bloqueada ou oculta +@msg Dimension mode does not match its source O modo da cota não corresponde à sua origem +@msg Draw Inside needs exactly one selected path, compound path or text Desenhar Dentro precisa de exatamente um caminho, caminho composto ou texto selecionado +@msg EPS Save writes all artwork: use document.export for selected-only EPS Salvar em EPS grava toda a arte: use document.export para um EPS só com a seleção +@msg EPS flattener: {e} Achatador de EPS: {e} +@msg EPS options: {e} Opções de EPS: {e} +@msg EPS {field} `{s}`: {allowed} {field} do EPS `{s}`: {allowed} +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString foi chamado em um valor que não é um objeto +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft Exportar para Telas grava png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai ou vectorcraft +@msg Flare click coordinates must be finite numbers As coordenadas do clique do Brilho de Lente devem ser números finitos +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString foi chamado em um valor que não é uma função +@msg HTML Text applies to object slices of type only Texto HTML se aplica somente a fatias de objeto de texto +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) Identificar Objetos Por está em ID XML: o nome deve ser um ID XML válido (começar com letra, sublinhado ou dois-pontos; sem espaços) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) O Rastreamento de Imagem falhou: erro interno: a thread dele parou (informe este erro) +@msg Image Trace failed: {e} O Rastreamento de Imagem falhou: {e} +@msg Image Trace finished, but its document was closed O Rastreamento de Imagem terminou, mas o documento dele foi fechado +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again O Rastreamento de Imagem terminou, mas a imagem ou as configurações dela mudaram nesse meio-tempo: rastreie-a novamente +@msg Import SVG Filter reads SVG files, not SVGZ Importar Filtro SVG lê arquivos SVG, não SVGZ +@msg Invalid dimension offset Deslocamento de cota inválido +@msg Invalid point Ponto inválido +@msg JSON.parse: {why} (at offset {at}) JSON.parse: {why} (no deslocamento {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify não pode gravar uma estrutura que contém a si mesma +@msg Join: a subpath went missing Unir: um subcaminho deixou de existir +@msg Line Gap must be a number O Espaço entre Linhas deve ser um número +@msg Lock Workspace is not available in VectorCraft Bloquear Espaço de Trabalho não está disponível no VectorCraft +@msg Move Panel to New Window is not available: floating panels stay in the main window Mover Painel para Nova Janela não está disponível: os painéis flutuantes ficam na janela principal +@msg Namespace.prototype.toString needs a Namespace Namespace.prototype.toString precisa de um Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have Os perfis de Novo Documento precisam da pasta de preferências, que esta execução não tem +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} foi chamado em um valor que não é um número +@msg Object.create's prototype must be an object or null O protótipo de Object.create deve ser um objeto ou null +@msg Object.{function} was given a value that is not an object Object.{function} recebeu um valor que não é um objeto +@msg Offset Path: the path is too complex to offset Deslocar Caminho: o caminho é complexo demais para deslocar +@msg Offset Path: {e} Deslocar Caminho: {e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported O conteúdo do PDF está incompleto ou excede os limites do leitor; nenhum documento foi importado +@msg PDF flattener: {e} Achatador de PDF: {e} +@msg PDF options: {e} Opções de PDF: {e} +@msg QName.prototype.toString needs a QName QName.prototype.toString precisa de um QName +@msg Recolor Artwork is no longer open Recolorir Arte não está mais aberto +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} foi chamado em um valor que não é um objeto RegExp +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 O SVG não está em UTF-8, UTF-16 nem ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export Salvar grava uma prancheta: indique uma, ou exporte várias com document.export +@msg Select a dimension first Selecione uma cota primeiro +@msg Select dimensions to update Selecione cotas para atualizar +@msg Select only dimensions Selecione somente cotas +@msg Select two or more objects, or a group Selecione dois ou mais objetos, ou um grupo +@msg Shaper needs 1–64 source paths O Formador precisa de 1–64 caminhos de origem +@msg Stencil Bridges: the selected art is too large or too detailed to bridge Pontes de Estêncil: a arte selecionada é grande ou detalhada demais para receber pontes +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString foi chamado em um valor que não é uma string +@msg Switch x/y is for scatter graphs Alternar x/y é para gráficos de dispersão +@msg Text options: {e} Opções de texto: {e} +@msg The {name} profile could not be read, so the new document has the default settings: {reason} Não foi possível ler o perfil {name}, então o novo documento tem as configurações padrão: {reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time Armadilha: mais de {_1} pares de objetos estão próximos o bastante para a armadilha, ou objetos demais compartilham uma faixa horizontal; aplique a armadilha a menos objetos por vez +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off Caixa e Linhas de Descasque: nenhuma linha de descasque cabe fora da arte, e a caixa de descasque está desativada +@msg \ at end of pattern \ no fim do padrão +@msg `alignOn` cannot be empty `alignOn` não pode ficar vazio +@msg `alignOn` must be a string `alignOn` deve ser uma string +@msg `align` is start, center or end `align` é start, center ou end +@msg `align` must be a string `align` deve ser uma string +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` deve ser uma matriz de pares [subcaminho, ponto de ancoragem] +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` deve listar índices de pontos de ancoragem (0, 1, 2…), não {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` deve listar índices de pontos de ancoragem (0, 1, 2…), não {v} +@msg `answer` for this {kind} dialog must be one of: {_1} `answer` para esta caixa de diálogo {kind} deve ser um de: {_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` tem mais de {MAX_ANSWERS} respostas +@msg `answers` is larger than 1 MiB `answers` tem mais de 1 MiB +@msg `answers` must be a list of answers `answers` deve ser uma lista de respostas +@msg `artboard` must be the index of an artboard of the document `artboard` deve ser o índice de uma prancheta do documento +@msg `artboardsAt` must be [x, y] `artboardsAt` deve ser [x, y] +@msg `artboards` must be a list of artboard indices `artboards` deve ser uma lista de índices de pranchetas +@msg `artboards` names an artboard the document does not have `artboards` indica uma prancheta que o documento não tem +@msg `artboards` names an artboard twice `artboards` indica uma prancheta duas vezes +@msg `asset` is the asset id (see assets.list) `asset` é o id do ativo (veja assets.list) +@msg `at` must be [x, y] (got {at}) `at` deve ser [x, y] (recebido {at}) +@msg `at` must be a byte offset `at` deve ser um deslocamento em bytes +@msg `at` must be a point [x, y] `at` deve ser um ponto [x, y] +@msg `autoLeading` must be a number `autoLeading` deve ser um número +@msg `background` must be "", "matte" or #rrggbb `background` deve ser "", "matte" ou #rrggbb +@msg `beside` needs `side` `beside` precisa de `side` +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` deve ser "none", "standard" ou "forced" +@msg `center` must be [x, y] `center` deve ser [x, y] +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` deve ser "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" ou "icfBottom" +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` deve ser "default", "leftToRight" ou "rightToLeft" +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` `clearStroke` exige `stroke: false`, `ids: []` e nenhum `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` deve ser uma lista de ids de objetos de texto, não {v} +@msg `clear` must list type objects; {_1} is not one `clear` deve listar objetos de texto; {_1} não é um +@msg `clear` must not list `id` `clear` não deve listar `id` +@msg `code` must be a whole number `code` deve ser um número inteiro +@msg `colorMode` must be one of rgb, cmyk `colorMode` deve ser um de rgb, cmyk +@msg `color` needs a `rule` to make the theme from `color` precisa de uma `rule` para criar o tema +@msg `colors` does not apply to the {name} reduction `colors` não se aplica à redução {name} +@msg `colors` is empty `colors` está vazio +@msg `colors` must be a whole number from 2 to 256 `colors` deve ser um número inteiro de 2 a 256 +@msg `colors` must be at least 9 for the uniform reduction `colors` deve ser pelo menos 9 para a redução uniforme +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` deve estar entre 1 e {MAX_THEME_COLORS} (recebido {n}) +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` é vertical, uniform, repeating ou sliding +@msg `columns` must be a whole number 1..50 `columns` deve ser um número inteiro 1..50 +@msg `commands` must not be empty `commands` não deve ficar vazio +@msg `contents` goes with `to` `contents` acompanha `to` +@msg `corners` must be sharp, round or square, not `{other}` `corners` deve ser sharp, round ou square, não `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` deve listar índices de pontos de ancoragem (0, 1, 2…), não {v} +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` deve ser um número inteiro de 0 a {MAX_COUNT} +@msg `count` must be a whole number from 1 to 100 `count` deve ser um número inteiro de 1 a 100 +@msg `dataBase64` is larger than the module limit `dataBase64` excede o limite do módulo +@msg `dataBase64` is not valid base64 `dataBase64` não é base64 válido +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` não pode ser combinado com `ids`, `reverse` ou `append` +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` e `childLimit` recortam o resumo: passe `summary: true` +@msg `design` is a design name, or null for plain columns `design` é o nome de um projeto, ou null para colunas simples +@msg `design` is a design name, or null for the default marker `design` é o nome de um projeto, ou null para o marcador padrão +@msg `destinationFolder` is required when destination is `folder` `destinationFolder` é obrigatório quando o destino é `folder` +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` deve ser `none`, `saveAndClose` ou `folder` +@msg `diameter` must be from 0.1 to 2500 pt `diameter` deve estar entre 0.1 e 2500 pt +@msg `digits` must be one of {_1} `digits` deve ser um de {_1} +@msg `dimImages` is a percentage or false `dimImages` é uma porcentagem ou false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` deve ser "auto", "leftToRight" ou "rightToLeft" +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` deve ser um número de 0 a 100 +@msg `each` is the value one design stands for, more than 0 `each` é o valor que um projeto representa, maior que 0 +@msg `end` must be "start" or "end" `end` deve ser "start" ou "end" +@msg `ends` applies to joining exactly two paths `ends` se aplica à união de exatamente dois caminhos +@msg `event` must be one of {_1} `event` deve ser um de {_1} +@msg `every` must be "all", "odd" or "even", not {v} `every` deve ser "all", "odd" ou "even", não {v} +@msg `expand` must be true or false `expand` deve ser true ou false +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` deve estar entre {lo} e {hi} pt +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` deve ter de 1 a 1000 caminhos de arquivos de fonte encontrados pela última pesquisa de text.findFontFiles +@msg `focal` and `radius` cannot be used together `focal` e `radius` não podem ser usados juntos +@msg `focal` must be an array of [subpath, anchor] pairs `focal` deve ser uma matriz de pares [subcaminho, ponto de ancoragem] +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` indica [{si}, {ai}], que não é um ponto de ancoragem do caminho +@msg `folder` must be an absolute folder path `folder` deve ser um caminho absoluto de pasta +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` deve ser um objeto de tags de eixo e valores, como {"wght"} +@msg `format` must be a string `format` deve ser uma string +@msg `format` must be one of {_1} `format` deve ser um de {_1} +@msg `fractions` is chop or scale `fractions` é chop ou scale +@msg `from` and `to` must differ `from` e `to` devem ser diferentes +@msg `from` has an empty frame `from` tem uma moldura vazia +@msg `from` must be a non-negative integer `from` deve ser um inteiro não negativo +@msg `from` must be area type or type on a path `from` deve ser texto de área ou texto em um caminho +@msg `gid` must be a glyph id, not {v} `gid` deve ser um id de glifo, não {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` pertence a um caractere, não a um estilo de caractere (text.setGlyphAlternate o define) +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` aceita somente null, que remove uma escolha de glifo (text.setGlyphAlternate faz uma) +@msg `group` must be one of {_1} `group` deve ser um de {_1} +@msg `group` must be the Objects on Path group of `member` and `target` `group` deve ser o grupo Objetos no Caminho de `member` e `target` +@msg `guide` must be a guide index (guide.list) `guide` deve ser um índice de guia (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` deve ser default, left, center ou right +@msg `id` is an object id, not {v} `id` é um id de objeto, não {v} +@msg `id` must be a node id `id` deve ser um id de nó +@msg `id` must be an object id, not {v} `id` deve ser um id de objeto, não {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` deve indicar grupos Objetos no Caminho ou objetos dentro deles +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` aceita no máximo {MAX_INSPECT_IDS} ids +@msg `imageMap` must be none, rectangle or polygon `imageMap` deve ser none, rectangle ou polygon +@msg `includeArtboardFill` must be true or false `includeArtboardFill` deve ser true ou false +@msg `includeSubdirs` must be true or false `includeSubdirs` deve ser true ou false +@msg `index` must be a point index `index` deve ser um índice de ponto +@msg `index` must be a whole number or null `index` deve ser um número inteiro ou null +@msg `indices` must be a non-empty array of item indices `indices` deve ser uma matriz não vazia de índices de itens +@msg `item` needs `index` `item` precisa de `index` +@msg `items` is a list of {kind, item} `items` é uma lista de {kind, item} +@msg `items` is empty `items` está vazio +@msg `items` must be a list of {id, points} `items` deve ser uma lista de {id, points} +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` deve ser uma lista de {{id, anchors?}}, não {v} +@msg `items` must be an array of {id, anchors} `items` deve ser uma matriz de {id, anchors} +@msg `items` must be an array of {{id, corners?}}, not {v} `items` deve ser uma matriz de {{id, corners?}}, não {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` deve ser "none", "short", "medium", "long" ou "stylistic" +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` pertence ao primeiro caractere de uma linha, não a um estilo de caractere (text.setRangeStyle o define) +@msg `kind` is one of {_1} `kind` é um de {_1} +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` deve ser "round", "invertedRound" ou "chamfer", não {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` deve ser `text`, `visibility`, `image` ou `graph` +@msg `kind` must be a string `kind` deve ser uma string +@msg `kind` must be image, noImage or htmlText `kind` deve ser image, noImage ou htmlText +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` deve ser "pushInFirst", "pushOutFirst" ou "pushOutOnly" +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` deve ser "none", "hard" ou "soft" +@msg `label` must be a non-empty name of at most 200 characters `label` deve ser um nome não vazio de no máximo 200 caracteres +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` deve ser um nome ou uma tag de idioma, como "Turkish" ou "tr" +@msg `leader` must be a string `leader` deve ser uma string +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` deve ser "romanBaseline" ou "emBoxTop" +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` deve estar entre 0.1 e 5000, ou null para Automático +@msg `length` must be a number of at most 1000000 pt either way `length` deve ser um número de no máximo 1000000 pt em qualquer sentido +@msg `line` must be true or false `line` deve ser true ou false +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` deve ser de 1 a {MAX_SUGGESTIONS} +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) não pode ser maior que `maxAngle` ({max_angle}°) +@msg `mode` must be "handles", "straight" or "smooth" `mode` deve ser "handles", "straight" ou "smooth" +@msg `mode` must be "straight", "bezier" or "arc" `mode` deve ser "straight", "bezier" ou "arc" +@msg `mode` must be one of {_1} `mode` deve ser um de {_1} +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` deve ser "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" ou "lineEndHalf" +@msg `name` must be a name that is not empty `name` deve ser um nome não vazio +@msg `name` must not be empty `name` não deve ficar vazio +@msg `names` is a list of preset names `names` é uma lista de nomes de predefinições +@msg `names` lists preset names `names` lista nomes de predefinições +@msg `names` must be a list of names `names` deve ser uma lista de nomes +@msg `names` must be a list of swatch names `names` deve ser uma lista de nomes de amostras +@msg `names` must be an array of style names `names` deve ser uma matriz de nomes de estilos +@msg `new` is a design name, or true `new` é o nome de um projeto, ou true +@msg `origin` must be [x, y] in points `origin` deve ser [x, y] em pontos +@msg `path` must be a folder `path` deve ser uma pasta +@msg `path` must be a path object `path` deve ser um objeto de caminho +@msg `picks` must be an array (got {v}) `picks` deve ser uma matriz (recebido {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` deve conter de 1 a {MAX_PICKS} escolhas (recebido {_1}) +@msg `pivot` must be a whole number from 0 to 8 `pivot` deve ser um número inteiro de 0 a 8 +@msg `points` must be a list of [x, y] points `points` deve ser uma lista de pontos [x, y] +@msg `points` must be a list of point indexes `points` deve ser uma lista de índices de pontos +@msg `points` must be an array of point indices `points` deve ser uma matriz de índices de pontos +@msg `points` must be point indexes `points` deve conter índices de pontos +@msg `points` must hold from 2 to 100000 points `points` deve conter de 2 a 100000 pontos +@msg `port` must be "in" or "out" `port` deve ser "in" ou "out" +@msg `position` is above or below `position` é above ou below +@msg `preset` must be a string `preset` deve ser uma string +@msg `radius` must be 0 or more `radius` deve ser 0 ou mais +@msg `ratio` must be from 0.1 to 0.5 `ratio` deve estar entre 0.1 e 0.5 +@msg `rect` must be [x, y, width, height] `rect` deve ser [x, y, width, height] +@msg `rect` must be [x, y, width, height] with a positive size `rect` deve ser [x, y, width, height] com um tamanho positivo +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` deve ser [x0, y0, x1, y1] (recebido {r}) +@msg `relation` must be overlap, inside or outside `relation` deve ser overlap, inside ou outside +@msg `replace` must be a string `replace` deve ser uma string +@msg `reversed` must be true or false `reversed` deve ser true ou false +@msg `rotate` must be a finite number `rotate` deve ser um número finito +@msg `rows` must be lists of numbers or nulls, not {r} `rows` deve conter listas de números ou valores null, não {r} +@msg `rule` must be nonZero or evenOdd `rule` deve ser nonZero ou evenOdd +@msg `scale` must be a number from 0.001 to 1000 `scale` deve ser um número de 0.001 a 1000 +@msg `scale` must be a number in (0, 100] `scale` deve ser um número em (0, 100] +@msg `scope.{key}` must be a non-empty array `scope.{key}` deve ser uma matriz não vazia +@msg `scope` must be "document", {artboards} or {layers} `scope` deve ser "document", {artboards} ou {layers} +@msg `scope` must be "selection" or "document", not {other} `scope` deve ser "selection" ou "document", não {other} +@msg `script` must be a string `script` deve ser uma string +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` é uma lista não vazia de índices de séries +@msg `seriesIndexes` needs a `type` or a `valueAxis` `seriesIndexes` precisa de um `type` ou de um `valueAxis` +@msg `shift` must be a number of points `shift` deve ser um número de pontos +@msg `showCenter` must be true or false `showCenter` deve ser true ou false +@msg `side` must be "left", "right", "top" or "bottom" `side` deve ser "left", "right", "top" ou "bottom" +@msg `side` places the artboard: leave out x, y, width and height `side` posiciona a prancheta: omita x, y, width e height +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` deve ser "fullJustify", "alignLeft", "alignCenter" ou "alignRight" +@msg `size` is a percentage of the base text's size, 1 to 100 `size` é uma porcentagem do tamanho do texto base, de 1 a 100 +@msg `source` and `target` are the same object `source` e `target` são o mesmo objeto +@msg `source` is the only path in series group `target` `source` é o único caminho no grupo de série `target` +@msg `source` must be `folder` or `datasets` `source` deve ser `folder` ou `datasets` +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` gera mais de {MAX_COUNT} linhas ao longo da caixa +@msg `spacing` must be a number `spacing` deve ser um número +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` deve ser um número de pontos, 0 ou mais, ou "auto" +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` deve ser maior que 0 e no máximo {MAX_DISTANCE} pt +@msg `start` must be a finite number `start` deve ser um número finito +@msg `starts` must be an array of anchor indices (or nulls) `starts` deve ser uma matriz de índices de pontos de ancoragem (ou valores null) +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` deve ser linked, embedded, missing ou modified, ou uma lista deles +@msg `stop` must be a stop index `stop` deve ser um índice de parada +@msg `story` styles the whole story: give no `start` or `end` `story` aplica o estilo ao texto encadeado inteiro: não informe `start` nem `end` +@msg `story` takes the whole story: give no `start` or `end` `story` abrange o texto encadeado inteiro: não informe `start` nem `end` +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` se aplica a traçados (stroke: true) +@msg `style` must be roman or japanese `style` deve ser roman ou japanese +@msg `subpaths` must be two integer indices `subpaths` deve conter dois índices inteiros +@msg `symbol` must be a symbol name `symbol` deve ser um nome de símbolo +@msg `target` must be "object" or "contents", not {v} `target` deve ser "object" ou "contents", não {v} +@msg `target` must be an object of the same Objects on Path group as `member` `target` deve ser um objeto do mesmo grupo Objetos no Caminho que `member` +@msg `tint` applies to global and spot colours `tint` se aplica a cores globais e especiais +@msg `tint` goes with a global or spot `swatch` `tint` acompanha uma `swatch` global ou especial +@msg `tint` must be a number (%) `tint` deve ser um número (%) +@msg `tint` needs a global or spot colour `tint` precisa de uma cor global ou especial +@msg `to` is already threaded `to` já está encadeado +@msg `to` is the design's new name `to` é o novo nome do projeto +@msg `to` must be [x, y] `to` deve ser [x, y] +@msg `to` must be a closed path, area type or type on a path other than `from` `to` deve ser um caminho fechado, texto de área ou texto em um caminho diferente de `from` +@msg `to` must be a whole number `to` deve ser um número inteiro +@msg `to` takes the rows of one object's stack, not aligned rows `to` aceita as linhas da pilha de um único objeto, não linhas alinhadas +@msg `tolerance` must be from 0 to 100 pt `tolerance` deve estar entre 0 e 100 pt +@msg `useLab` must be true or false `useLab` deve ser true ou false +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` deve ser default, top, middle, baseline ou bottom +@msg `values` must be a list `values` deve ser uma lista +@msg `values` must be an object `values` deve ser um objeto +@msg `values` must name hex, rgb, cmyk, lab or gray `values` deve indicar hex, rgb, cmyk, lab ou gray +@msg `values` names a kind twice `values` indica um tipo duas vezes +@msg `version` must be 0 or {_1} `version` deve ser 0 ou {_1} +@msg `version` must be 0 or {newest} `version` deve ser 0 ou {newest} +@msg `warichuAlign` must be one of {_1} `warichuAlign` deve ser um de {_1} +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` deve ser um número inteiro de {_1} a {_2} +@msg `weight` must be from 0.01 to 1000 pt `weight` deve estar entre 0.01 e 1000 pt +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` deve ser uma lista de {_1} números, um por linha (recebido {v}) +@msg `xml` must be a string `xml` deve ser uma string +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it a entrada {n} de `{KEY}` dá ao parágrafo no byte {start} de {_1} recuos diferentes dos de uma entrada anterior +@msg `{KEY}` entry {n} must be an object a entrada {n} de `{KEY}` deve ser um objeto +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` a entrada {n} de `{KEY}` não define `leftIndent` nem `firstLineIndent` +@msg `{KEY}` entry {n}: `id` must be an object id entrada {n} de `{KEY}`: `id` deve ser um id de objeto +@msg `{KEY}` entry {n}: `start` must be a byte offset entrada {n} de `{KEY}`: `start` deve ser um deslocamento em bytes +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt entrada {n} de `{KEY}`: `{k}` deve ser um número de -{MAX_PARA} a {MAX_PARA} pt +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} entrada {n} de `{KEY}`: nenhum parágrafo de {_1} começa no byte {start} +@msg `{KEY}` entry {n}: unknown key `{k}` entrada {n} de `{KEY}`: chave desconhecida `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object entrada {n} de `{KEY}`: {_1} não é um objeto de texto +@msg `{KEY}` is empty `{KEY}` está vazio +@msg `{KEY}` must be a list `{KEY}` deve ser uma lista +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` aceita no máximo {MAX_PARAGRAPH_INDENTS} entradas (recebido {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` não aceita `{other}` junto com ele +@msg `{_1}` can't be edited or removed `{_1}` não pode ser editado nem removido +@msg `{_1}` is built in and always listed `{_1}` é integrado e sempre aparece na lista +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` não é um arquivo de fonte encontrado pela última pesquisa de text.findFontFiles +@msg `{_1}` would be overwritten by the package: choose a different folder or name `{_1}` seria sobrescrito pelo pacote: escolha outra pasta ou outro nome +@msg `{base}` is larger than {_1} MB `{base}` tem mais de {_1} MB +@msg `{base}` is not in the DCS file's folder `{base}` não está na pasta do arquivo DCS +@msg `{file}` has no art to place `{file}` não tem arte para colocar +@msg `{file}` is over {_1} MB `{file}` tem mais de {_1} MB +@msg `{f}` belongs to a VectorCraft that is running `{f}` pertence a um VectorCraft em execução +@msg `{f}` is the copy of a document open now `{f}` é a cópia de um documento aberto agora +@msg `{id}` is no writable format (see document.formats) `{id}` não é um formato gravável (veja document.formats) +@msg `{id}` isn't a loaded library `{id}` não é uma biblioteca carregada +@msg `{key}` has no colours `{key}` não tem cores +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` contém mais de {MAX_EDIT_WORDS} palavras +@msg `{key}` is a design name `{key}` é o nome de um projeto +@msg `{key}` is a design name or a list of them `{key}` é o nome de um projeto ou uma lista deles +@msg `{key}` is longer than {MAX_NAME} characters `{key}` tem mais de {MAX_NAME} caracteres +@msg `{key}` is out of range `{key}` está fora do intervalo +@msg `{key}` must be 0 or more `{key}` deve ser 0 ou mais +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` deve ser [min, desired, max] ou {{min?, desired?, max?}}, em porcentagem +@msg `{key}` must be [x, y] numbers `{key}` deve conter os números [x, y] +@msg `{key}` must be a list of words `{key}` deve ser uma lista de palavras +@msg `{key}` must be a list, not {v} `{key}` deve ser uma lista, não {v} +@msg `{key}` must be a non-negative integer `{key}` deve ser um inteiro não negativo +@msg `{key}` must be a number or null, not {v} `{key}` deve ser um número ou null, não {v} +@msg `{key}` must be a number {_1}..{_2} `{key}` deve ser um número {_1}..{_2} +@msg `{key}` must be a whole number of 0 or more `{key}` deve ser um número inteiro igual ou maior que 0 +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` deve ser um número inteiro de pelo menos 0, não {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` deve ser um índice de item de aparência (ordem de pintura) ou null +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` deve ser uma matriz de ids de ativos (veja assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` deve estar entre 0 e 180 graus, não {a} +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` deve estar entre 0 e {MAX_DISTANCE} pt +@msg `{key}` must be from {_1} to {shift_max} `{key}` deve estar entre {_1} e {shift_max} +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` deve estar entre {lo} e {hi}, não {v} +@msg `{key}` must be one of auto, always, never `{key}` deve ser um de auto, always, never +@msg `{key}` must be one of miter, round, bevel `{key}` deve ser um de miter, round, bevel +@msg `{key}` must be one word of at most {_1} characters `{key}` deve ser uma única palavra de no máximo {_1} caracteres +@msg `{key}` must hold single words of at most {_1} characters `{key}` deve conter palavras isoladas de no máximo {_1} caracteres +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` deve indicar um objeto anexado ao caminho de um grupo Objetos no Caminho +@msg `{key}` names an object that is hidden or locked `{key}` indica um objeto oculto ou bloqueado +@msg `{key}`: not a colour: {v} `{key}`: não é uma cor: {v} +@msg `{k}.family` is required `{k}.family` é obrigatório +@msg `{k}` is a number 0..1 `{k}` é um número 0..1 +@msg `{k}` is true or false `{k}` é true ou false +@msg `{k}` isn't an export setting: {_1} `{k}` não é uma configuração de exportação: {_1} +@msg `{k}` must be a byte offset `{k}` deve ser um deslocamento em bytes +@msg `{k}` must be a number `{k}` deve ser um número +@msg `{k}` must be a number of points `{k}` deve ser um número de pontos +@msg `{k}` must be a string `{k}` deve ser uma string +@msg `{k}` must be a whole number `{k}` deve ser um número inteiro +@msg `{k}` must be from {lo} to {hi} for {key} `{k}` deve estar entre {lo} e {hi} para {key} +@msg `{k}` must be {what}, not {v} `{k}` deve ser {what}, não {v} +@msg `{l}` isn't a global or spot colour `{l}` não é uma cor global nem especial +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` não é um bitmap: use clipboard.importSvg, importPdf ou importText +@msg `{name}` can't be deleted `{name}` não pode ser excluído +@msg `{name}` can't be duplicated `{name}` não pode ser duplicado +@msg `{name}` can't be edited `{name}` não pode ser editado +@msg `{name}` can't be edited this way `{name}` não pode ser editado dessa forma +@msg `{name}` can't be renamed `{name}` não pode ser renomeado +@msg `{name}` can't name a folder `{name}` não pode ser o nome de uma pasta +@msg `{name}` has no art to place `{name}` não tem arte para colocar +@msg `{name}` has no text to place `{name}` não tem texto para colocar +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text `{name}` no documento do Word não é texto UTF-8 nem UTF-16 +@msg `{name}` in the Word document is not well-formed XML: {e} `{name}` no documento do Word não é XML bem formado: {e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep `{name}` no documento do Word aninha elementos em mais de {MAX_NESTING} níveis +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` é uma predefinição integrada e é somente leitura: salve-a com outro nome +@msg `{name}` is a built-in preset: save under another name `{name}` é uma predefinição integrada: salve com outro nome +@msg `{name}` is a built-in profile and stays `{name}` é um perfil integrado e permanece +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` é um arquivo separado, que só pode ser encontrado ao lado do caminho do arquivo DCS +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` é um matiz de `{base}`: edite `{base}` (uma amostra de matiz só aceita um novo nome) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` é um espaço de trabalho de cores: escolha outro com edit.colorSettings primeiro +@msg `{name}` is built in and stays `{name}` é integrado e permanece +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` aceita uma pintura do seu próprio tipo (cor, gradiente ou padrão) +@msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} +@msg `{n}` can't be deleted `{n}` não pode ser excluído +@msg `{n}` can't be merged `{n}` não pode ser mesclado +@msg `{n}` can't be moved `{n}` não pode ser movido +@msg `{n}` is not a colour swatch `{n}` não é uma amostra de cor +@msg `{n}` isn't a solid colour `{n}` não é uma cor sólida +@msg `{n}`: colour groups hold solid colours only `{n}`: os grupos de cores contêm apenas cores sólidas +@msg `{other}` doesn't trace an image `{other}` não rastreia uma imagem +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`: as referências da etapa copiam mais de {_1} MB de valores +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` não é uma tag de eixo: uma tag tem quatro caracteres, como "wght" +@msg `{to}` is empty or already used `{to}` está vazio ou já está em uso +@msg `{u}` is no Unicode character `{u}` não é um caractere Unicode +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` tem mais de {MAX_TEXT} bytes +@msg `{what}` must be text `{what}` deve ser texto +@msg `{word}` isn't in the custom dictionary `{word}` não está no dicionário personalizado +@msg a CMap has too many mappings um CMap tem mapeamentos demais +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard um desenho DXF tem uma única origem: passe useArtboards: true para um desenho por prancheta +@msg a PDF presets file with over {_1} presets um arquivo de predefinições de PDF com mais de {_1} predefinições +@msg a \u escape needs four hexadecimal digits um escape \u precisa de quatro dígitos hexadecimais +@msg a batch preview cannot switch documents uma prévia de lote não pode trocar de documento +@msg a brush named `{nn}` exists já existe um pincel chamado `{nn}` +@msg a comma in an XPath expression must separate function arguments uma vírgula em uma expressão XPath deve separar argumentos de função +@msg a container of the listed objects is gone um contêiner dos objetos listados não existe mais +@msg a content stream can't be read não é possível ler um fluxo de conteúdo +@msg a content stream isn't a stream um fluxo de conteúdo não é um fluxo +@msg a design holds at most {_1} objects um projeto contém no máximo {_1} objetos +@msg a design needs a name um projeto precisa de um nome +@msg a dictionary grew too large um dicionário ficou grande demais +@msg a document holds at most {_1} SVG filters um documento contém no máximo {_1} filtros SVG +@msg a document keeps at most {_1} graph designs um documento mantém no máximo {_1} projetos de gráfico +@msg a document keeps at most {_1} saved selections um documento mantém no máximo {_1} seleções salvas +@msg a document keeps at most {_1} views um documento mantém no máximo {_1} vistas +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files um documento vincula no máximo {MAX_SVG_SCRIPTS} arquivos JavaScript +@msg a document with unsaved changes has no recovery copy um documento com alterações não salvas não tem cópia de recuperação +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes a ordem de um filtro feConvolveMatrix é {x}×{y}, o que estoura a multiplicação do tamanho do kernel feita pelo usvg +@msg a font's data is too large os dados de uma fonte são grandes demais +@msg a font's subroutines are too large as sub-rotinas de uma fonte são grandes demais +@msg a function declaration without a name uma declaração de função sem nome +@msg a gradient preset is already called {n} já existe uma predefinição de gradiente chamada {n} +@msg a graph has at most {_1} series, so it transposes with at most that many categories um gráfico tem no máximo {_1} séries, então ele é transposto com no máximo essa quantidade de categorias +@msg a host value has too many entries um valor do host tem entradas demais +@msg a host value is nested too deep um valor do host está aninhado em níveis demais +@msg a layer can't be merged into its own sublayer uma camada não pode ser mesclada em sua própria subcamada +@msg a layer's form box isn't an array a caixa do formulário de uma camada não é uma matriz +@msg a layer's form can't be read não é possível ler o formulário de uma camada +@msg a layer's form has no box o formulário de uma camada não tem caixa +@msg a layer's form has no dictionary o formulário de uma camada não tem dicionário +@msg a legend draws at most {MAX_CHIPS} swatches uma legenda desenha no máximo {MAX_CHIPS} amostras +@msg a name names one asset: give multiple: false, or one object um nome indica um único ativo: informe multiple: false, ou um único objeto +@msg a namespace declaration uses a reserved prefix or URI uma declaração de namespace usa um prefixo ou URI reservado +@msg a namespace qualifier is undefined um qualificador de namespace não está definido +@msg a namespace with an empty URI cannot have a prefix um namespace com URI vazio não pode ter prefixo +@msg a number has no digits um número não tem dígitos +@msg a number's exponent has no digits o expoente de um número não tem dígitos +@msg a number's fraction has no digits a parte fracionária de um número não tem dígitos +@msg a page can't be read não é possível ler uma página +@msg a page object is used by something else um objeto de página é usado por outra coisa +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) uma miniatura de página deve ter pixels RGB, de 1 a {THUMBNAIL_SIZE} de lado (recebido {w} × {h}) +@msg a pattern layer doesn't end what it starts uma camada de padrão não termina o que começa +@msg a pattern layer's colors hold something else as cores de uma camada de padrão contêm outra coisa +@msg a pattern named `{n}` exists já existe um padrão chamado `{n}` +@msg a plug-in with id {_1} is already installed um plug-in com o id {_1} já está instalado +@msg a preset named `{n}` exists já existe uma predefinição chamada `{n}` +@msg a print presets file with over {_1} presets um arquivo de predefinições de impressão com mais de {_1} predefinições +@msg a profile is already called {n} já existe um perfil chamado {n} +@msg a property descriptor cannot have both a value or writable and a get or set um descritor de propriedade não pode ter value ou writable junto com get ou set +@msg a property descriptor must be an object um descritor de propriedade deve ser um objeto +@msg a property's {name} must be a function or undefined o {name} de uma propriedade deve ser uma função ou undefined +@msg a row can't go into itself uma linha não pode entrar em si mesma +@msg a series goes on the left or the right value axis uma série fica no eixo de valores esquerdo ou direito +@msg a start must be an anchor index ≥ 0 or null um início deve ser um índice de ponto de ancoragem ≥ 0 ou null +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied um fluxo dele seria decodificado em mais de 256 MB, ou os parâmetros de decodificação não podem ser aplicados +@msg a string holds a control character uma string contém um caractere de controle +@msg a string holds an escape sequence JSON does not have uma string contém uma sequência de escape que o JSON não tem +@msg a string is not closed uma string não foi fechada +@msg a style named `{name}` already exists já existe um estilo chamado `{name}` +@msg a style named `{to}` already exists já existe um estilo chamado `{to}` +@msg a swatch needs a colour, gradient or pattern uma amostra precisa de uma cor, um gradiente ou um padrão +@msg a symbol named `{n}` already exists já existe um símbolo chamado `{n}` +@msg a theme has 1 to {THEME_SIZE} colours um tema tem de 1 a {THEME_SIZE} cores +@msg a theme is already called {name} (replace: "{name}" overwrites it) já existe um tema chamado {name} (replace: "{name}" o sobrescreve) +@msg a toolbar name has at most {MAX_NAME} characters o nome de uma barra de ferramentas tem no máximo {MAX_NAME} caracteres +@msg a view named `{_1}` exists já existe uma vista chamada `{_1}` +@msg a view named `{name}` exists já existe uma vista chamada `{name}` +@msg a workspace keeps at most {MAX} toolbars um espaço de trabalho mantém no máximo {MAX} barras de ferramentas +@msg a {_1} series can't go in a {_2} graph uma série {_1} não pode ficar em um gráfico {_2} +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}: "pdf" (compatível com PDF, o padrão) ou "7" (.ai legado, versão 7) +@msg already editing an opacity mask uma máscara de opacidade já está em edição +@msg already editing another object's opacity mask a máscara de opacidade de outro objeto já está em edição +@msg already in pattern editing mode o modo de edição de padrão já está ativo +@msg an SVG filter is named by its id: give `xml` with `name` um filtro SVG é nomeado pelo seu id: informe `xml` com `name` +@msg an XML element has two attributes with the same name um elemento XML tem dois atributos com o mesmo nome +@msg an XML value cannot be inserted into itself or its descendants um valor XML não pode ser inserido em si mesmo nem em seus descendentes +@msg an XML value's children cannot be assigned by position os filhos de um valor XML não podem ser atribuídos por posição +@msg an XML value's children cannot be deleted by position os filhos de um valor XML não podem ser excluídos por posição +@msg an XMLList converts to XML only when it has one item um XMLList só é convertido em XML quando tem um único item +@msg an XPath node test is missing falta um teste de nó XPath +@msg an XPath operand is missing falta um operando XPath +@msg an XPath operator is missing falta um operador XPath +@msg an XPath predicate or step cannot follow here um predicado ou passo XPath não pode vir aqui +@msg an XPath predicate or step follows a value that is not a node-set um predicado ou passo XPath vem depois de um valor que não é um conjunto de nós +@msg an XPath step is missing falta um passo XPath +@msg an artboard is listed twice uma prancheta aparece duas vezes na lista +@msg an empty artboard has no width or height to scale to uma prancheta vazia não tem largura nem altura para servir de escala +@msg an image couldn't be decoded and is missing from the picture uma imagem não pôde ser decodificada e não aparece na figura +@msg an image is too large for DXF and was left out uma imagem é grande demais para o DXF e foi omitida +@msg an image of {_1} MB, at most {_2} MB uma imagem de {_1} MB, no máximo {_2} MB +@msg an image of {declared_w} × {declared_h} pixels is too large to draw uma imagem de {declared_w} × {declared_h} pixels é grande demais para desenhar +@msg an image of {width} × {height} pixels is too large to draw uma imagem de {width} × {height} pixels é grande demais para desenhar +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) uma imagem de {w} × {h} pixels é grande demais para converter (no máximo {_1} pixels) +@msg an index must be a whole number um índice deve ser um número inteiro +@msg an inline graphic has no glyphs um gráfico em linha não tem glifos +@msg an invisible text form can't be read não é possível ler um formulário de texto invisível +@msg an object is missing falta um objeto +@msg an object's header is missing falta o cabeçalho de um objeto +@msg an overprinting form can't be read não é possível ler um formulário com sobreimpressão +@msg an unread object has no readable bounds um objeto não lido não tem limites legíveis +@msg an unread object holds a frame of threaded type um objeto não lido contém uma moldura de texto encadeado +@msg an unread object is missing from its layer um objeto não lido não está na camada dele +@msg anchor `smooth` must be true or false `smooth` do ponto de ancoragem deve ser true ou false +@msg anchor `{key}` must be [x, y] `{key}` do ponto de ancoragem deve ser [x, y] +@msg anchor `{key}` must be a number `{key}` do ponto de ancoragem deve ser um número +@msg angle and roundness must be finite angle e roundness devem ser finitos +@msg angles must be a list as long as pins (degrees or null) angles deve ser uma lista do mesmo tamanho que pins (graus ou null) +@msg appearance must be flat|center|edge appearance deve ser flat|center|edge +@msg apply is "fill" or "stroke", not `{other}` apply é "fill" ou "stroke", não `{other}` +@msg apply was called on a value that is not a function apply foi chamado em um valor que não é uma função +@msg area must be a closed polygon of 3 to 100000 [x, y] points area deve ser um polígono fechado de 3 a 100000 pontos [x, y] +@msg area type in a thread cannot use Dynamic Text: {frames} o texto de área de um texto encadeado não pode usar Texto Dinâmico: {frames} +@msg area type needs a path that encloses an area o texto de área precisa de um caminho que delimite uma área +@msg area type {_1} is named twice o texto de área {_1} é indicado duas vezes +@msg array literal too long literal de matriz longo demais +@msg arrowAlign must be extend|tip, got {o} arrowAlign deve ser extend|tip, recebido {o} +@msg arrowScale must be [start, end] arrowScale deve ser [start, end] +@msg artboard is a 0-based index artboard é um índice com base 0 +@msg artboard must be a 0-based index artboard deve ser um índice com base 0 +@msg artboard {i} doesn't print a prancheta {i} não é impressa +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout deve ser {layout?, columns?, spacing?, rightToLeft?} +@msg artboardLayout.columns must be 1 or more artboardLayout.columns deve ser 1 ou mais +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout deve ser gridByRow, gridByColumn, row ou column +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft deve ser true ou false +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing deve ser 0–{MAX_SPACING} pt +@msg artboards must be a non-empty list artboards deve ser uma lista não vazia +@msg artboards must be a number artboards deve ser um número +@msg asset `{_1}` has no art to export o ativo `{_1}` não tem arte para exportar +@msg assignment to a constant binding atribuição a uma vinculação constante +@msg at least one row that isn't excluded needs a weight above 0 pelo menos uma linha não excluída precisa de um peso acima de 0 +@msg at most {MAX_KEYWORDS} keywords no máximo {MAX_KEYWORDS} palavras-chave +@msg at most {MAX_MATCHES} matches are kept no máximo {MAX_MATCHES} correspondências são mantidas +@msg at most {MAX_POINTS} points no máximo {MAX_POINTS} pontos +@msg at most {MAX_QUEUE} files no máximo {MAX_QUEUE} arquivos +@msg at must be [x, y] at deve ser [x, y] +@msg at/rect lie outside the canvas at/rect ficam fora da tela +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute deve ser size|leading|tracking|kerning|baselineShift +@msg attrs must be an object attrs deve ser um objeto +@msg automation path rejected: the folder's name isn't valid text caminho de automação rejeitado: o nome da pasta não é texto válido +@msg automation path rejected: {_1} isn't valid text caminho de automação rejeitado: {_1} não é texto válido +@msg automation path rejected: {why}: {path} caminho de automação rejeitado: {why}: {path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis deve ser "vertical", "horizontal" ou um ângulo em graus, não `{given}` +@msg backgroundContents must be transparent or white backgroundContents deve ser transparent ou white +@msg bad AES key chave AES inválida +@msg bad `runs`: {e} `runs` inválido: {e} +@msg bad arrowScale arrowScale inválido +@msg bad base64 base64 inválido +@msg bad color {c} cor inválida {c} +@msg bad colour cor inválida +@msg bad colour in `colors` cor inválida em `colors` +@msg bad colour {v} cor inválida {v} +@msg bad colour {v} in groupColors cor inválida {v} em groupColors +@msg bad cross-reference entry entrada de referência cruzada inválida +@msg bad cross-reference table tabela de referência cruzada inválida +@msg bad current colour {v} cor atual inválida {v} +@msg bad dataBase64 dataBase64 inválido +@msg bad edit edição inválida +@msg bad fill {v} preenchimento inválido {v} +@msg bad fit: {e} ajuste inválido: {e} +@msg bad model {other} (keep, new or document) modelo inválido {other} (keep, new ou document) +@msg bad new colour {v} nova cor inválida {v} +@msg bad object objeto inválido +@msg bad object number número de objeto inválido +@msg bad offset deslocamento inválido +@msg bad path data: {e} dados de caminho inválidos: {e} +@msg bad point {v}: expected [x, y] ponto inválido {v}: esperado [x, y] +@msg bad point {v}: expected [x, y] or [x, y, pressure] ponto inválido {v}: esperado [x, y] ou [x, y, pressure] +@msg bad reference referência inválida +@msg bad trailer size tamanho do trailer inválido +@msg bad {k} colour cor {k} inválida +@msg batches cannot nest lotes não podem ser aninhados +@msg bind something to a variable first vincule algo a uma variável primeiro +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed deve ser 0–{MAX_BLEED} pt: um número, [top, bottom, left, right] ou {{top, bottom, left, right}} +@msg blend must be a mode name blend deve ser o nome de um modo +@msg both sides of an XPath | must select nodes os dois lados de um | do XPath devem selecionar nós +@msg brush must be a name or null brush deve ser um nome ou null +@msg by must be a number by deve ser um número +@msg can't add page thumbnails: {why} não é possível adicionar miniaturas de página: {why} +@msg can't add the editing data for other apps: {why} não é possível adicionar os dados de edição para outros apps: {why} +@msg can't edit the written PDF: {why} não é possível editar o PDF gravado: {why} +@msg can't encrypt the PDF: {why} não é possível criptografar o PDF: {why} +@msg can't linearise the PDF: {why} não é possível linearizar o PDF: {why} +@msg can't open `{_1}`: {_2} não é possível abrir `{_1}`: {_2} +@msg can't open `{_1}`: {e} não é possível abrir `{_1}`: {e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) não é possível abrir `{name}`: não é um formato que o VectorCraft lê (veja document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) não é possível colocar `{name}`: não é um formato que o VectorCraft lê (veja document.formats) +@msg can't read back the written PDF: {e} não é possível reler o PDF gravado: {e} +@msg can't read this Word document: {why} não é possível ler este documento do Word: {why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp não é possível gravar imagens .{e}: use .png, .jpg, .gif, .webp, .tif ou .bmp +@msg can't write PDF layers, overprint or invisible text: {why} não é possível gravar camadas de PDF, sobreimpressão ou texto invisível: {why} +@msg can't write the file's copyright: {why} não é possível gravar os direitos autorais do arquivo: {why} +@msg can't write the file's modification date: {why} não é possível gravar a data de modificação do arquivo: {why} +@msg can't write the pages' user units: {why} não é possível gravar as unidades de usuário das páginas: {why} +@msg canceled cancelado +@msg cannot convert the object to a primitive value não é possível converter o objeto em um valor primitivo +@msg cannot convert undefined or null to an object não é possível converter undefined ou null em um objeto +@msg cannot distort a zero-size or non-finite bounding box não é possível distorcer uma caixa delimitadora de tamanho zero ou não finita +@msg cannot move an object into itself não é possível mover um objeto para dentro de si mesmo +@msg case must be upper|lower|title|sentence case deve ser upper|lower|title|sentence +@msg category `{c}`: one of {_1} categoria `{c}`: uma de {_1} +@msg center must be [x, y] center deve ser [x, y] +@msg character dimensions cannot be represented with the source text transform as dimensões dos caracteres não podem ser representadas com a transformação do texto de origem +@msg character dimensions cannot be represented with the target text transform as dimensões dos caracteres não podem ser representadas com a transformação do texto de destino +@msg choose at least one artboard escolha pelo menos uma prancheta +@msg choose at least one status escolha pelo menos um status +@msg clip must be true or false clip deve ser true ou false +@msg clippingMask must be true or false clippingMask deve ser true ou false +@msg closed must be a boolean closed deve ser um booleano +@msg collapsed must be true or false collapsed deve ser true ou false +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: nenhum perfil RGB ou CMYK se chama “{profile}” +@msg colorMode must be rgb or cmyk, not `{m}` colorMode deve ser rgb ou cmyk, não `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel deve ser {own} (o modo do documento), grayscale ou bitmap +@msg colour groups can't go into colour groups grupos de cores não podem ficar dentro de grupos de cores +@msg colour, mode and spot apply to solid-colour swatches only colour, mode e spot se aplicam somente a amostras de cor sólida +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read coluna {column}: `{cell}` é uma coluna `%` de arquivos de dados de gráfico, que não são lidos +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable coluna {column}: `{cell}` indica `{name}`, que é uma variável de {_1} +@msg column {column}: `{cell}` names no variable coluna {column}: `{cell}` não indica nenhuma variável +@msg column {column}: the variable name is longer than {MAX_NAME} characters coluna {column}: o nome da variável tem mais de {MAX_NAME} caracteres +@msg column: true floats the whole column and takes no onto, above or below column: true torna flutuante a coluna inteira e não aceita onto, above nem below +@msg command must be a shape.* command command deve ser um comando shape.* +@msg commands[{i}]: missing `command` commands[{i}]: falta `command` +@msg composer must be "singleLine" or "everyLine" composer deve ser "singleLine" ou "everyLine" +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes formas compostas aceitam caminhos, caminhos compostos, grupos, formas compostas, texto, mesclagens e envelopes +@msg compound {_1} must contain exactly one open path to join o composto {_1} deve conter exatamente um caminho aberto para unir +@msg connect needs a path other than the one extended connect precisa de um caminho diferente do que foi estendido +@msg connect needs id connect precisa de id +@msg connect path has no open end o caminho de connect não tem extremidade aberta +@msg continuation must be an array of path chunks continuation deve ser uma matriz de trechos de caminho +@msg continuation requires points continuation exige points +@msg copy needs path: the file to copy into the document's folder copy precisa de path: o arquivo a copiar para a pasta do documento +@msg copy takes images only: relink placed documents where their files are copy aceita somente imagens: revincule os documentos colocados no local onde estão os arquivos +@msg copying a file isn't available on the web copiar um arquivo não está disponível na web +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus deve ser unknown, copyrighted ou publicDomain +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl deve ser um URL (sem espaços, no máximo 2048 caracteres) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners deve ter 4 pontos [x, y] (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`: item ou file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`: somente páginas de PDF têm caixas de arte, aparo, sangria e mídia; use "crop" ou "bounding" +@msg crop rectangle doesn't overlap the image o retângulo de corte não se sobrepõe à imagem +@msg cross-reference and object streams aren't supported fluxos de referência cruzada e de objetos não são aceitos +@msg damaged compressed data: {e} dados compactados danificados: {e} +@msg dash lengths must be numbers, got {v} os comprimentos de traço devem ser números, recebido {v} +@msg dash must be a list of lengths or null, got {v} dash deve ser uma lista de comprimentos ou null, recebido {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) chave de dados `{k}`: letras, dígitos, -, _ e . (não name nem vc-…) +@msg data must be an object of key: value, not {v} data deve ser um objeto de chave: valor, não {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare o conjunto de dados `{name}` contém um valor para `{var}`, que a biblioteca não declara +@msg data set `{name}` holds two values for `{var}` o conjunto de dados `{name}` contém dois valores para `{var}` +@msg data set `{name}` is declared twice o conjunto de dados `{name}` é declarado duas vezes +@msg data takes at most {MAX_DATA} keys at once data aceita no máximo {MAX_DATA} chaves por vez +@msg dataset `{name}` already exists o conjunto de dados `{name}` já existe +@msg dataset `{to}` already exists o conjunto de dados `{to}` já existe +@msg date `{d}`: YYYY-MM-DD data `{d}`: YYYY-MM-DD +@msg decoded data is too large os dados decodificados são grandes demais +@msg define a variable first defina uma variável primeiro +@msg detect must be true or false detect deve ser true ou false +@msg direct-select anchor points of a path selecione diretamente pontos de ancoragem de um caminho +@msg distance must be > 0 distance deve ser > 0 +@msg dx and dy give the arrow's direction dx e dy indicam a direção da seta +@msg dx and dy must be numbers dx e dy devem ser números +@msg dxf fitTo must be [width, height], positive fitTo de dxf deve ser [width, height], positivo +@msg dxf must be an object of DXF import options dxf deve ser um objeto de opções de importação de DXF +@msg dxf scale must be a positive number up to {_1}, not {s} scale de dxf deve ser um número positivo de até {_1}, não {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt unit de dxf `{u}`: uma unidade como mm, cm, in ou pt +@msg dxf: {e} dxf: {e} +@msg each format is an object {format, scale?, suffix?} cada formato é um objeto {format, scale?, suffix?} +@msg each item needs an id cada item precisa de um id +@msg each match needs `kind` cada correspondência precisa de `kind` +@msg each match needs a `name` cada correspondência precisa de um `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them cada item de `fonts` é {family, style?}; de 1 a 1000 deles +@msg each of `items` is {kind, item} cada item de `items` é {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} cada item de `items` é {{id, corners?}} com um id de objeto, não {it} +@msg each path chunk must contain 1 to 4096 points cada trecho de caminho deve conter de 1 a 4096 pontos +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) cada escolha é {{at: [x, y]}} ou {{rect: [x0, y0, x1, y1]}} (recebido {v}) +@msg each row needs `from` cada linha precisa de `from` +@msg each stop needs a position cada parada precisa de uma posição +@msg each subpath needs an anchors array cada subcaminho precisa de uma matriz anchors +@msg each weight must be a number of 0 or more (got {v}) cada peso deve ser um número igual ou maior que 0 (recebido {v}) +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType deve ser easeIn, easeOut, easeInOut ou easeOutIn +@msg editingData must be true or false editingData deve ser true ou false +@msg editingData must be true, false or "always" editingData deve ser true, false ou "always" +@msg edits overlap as edições se sobrepõem +@msg effects of objects whose appearances differ can't be moved together os efeitos de objetos com aparências diferentes não podem ser movidos juntos +@msg empty header in column {_1} cabeçalho vazio na coluna {_1} +@msg encoding `{_1}`: utf8 or utf16 codificação `{_1}`: utf8 ou utf16 +@msg ends must be two of "first" and "last" ends deve conter dois valores entre "first" e "last" +@msg every numbered name for the result is taken todos os nomes numerados para o resultado já estão em uso +@msg expanded SVG styles exceed the import size limit os estilos SVG expandidos excedem o limite de tamanho de importação +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes o arquivo de predefinições de exportação tem mais de {MAX_PRESET_FILE_BYTES} bytes +@msg exportText must be editable or appearance exportText deve ser editable ou appearance +@msg exportType must be movieClip or graphic, not `{s}` exportType deve ser movieClip ou graphic, não `{s}` +@msg extend needs id extend precisa de id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges lista no máximo {MAX_FEATURE_CHANGES} tags +@msg featureChanges must be a list of tags featureChanges deve ser uma lista de tags +@msg features must be a list of tags features deve ser uma lista de tags +@msg fidelity must be 0..100 fidelity deve ser 0..100 +@msg files must be {name, dataBase64} files deve ser {name, dataBase64} +@msg finally table {t} out of range tabela finally {t} fora do intervalo +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit deve ser none|autoHeight|shrinkText|block, recebido {id} +@msg flags cannot be given with a RegExp object flags não pode ser informado com um objeto RegExp +@msg flattener: {e} achatador: {e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`: high, medium, low ou uma salva (veja flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile deve ser along|across, recebido {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`: none, rows, format, item ou itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels pastas com mais de {MAX_BATCH_DEPTH} níveis de profundidade +@msg folders deeper than {MAX_DEPTH} levels pastas com mais de {MAX_DEPTH} níveis de profundidade +@msg font family `{family}` is not available a família de fontes `{family}` não está disponível +@msg font family `{tf}` is not available a família de fontes `{tf}` não está disponível +@msg format `{f}`: pdf or postscript formato `{f}`: pdf ou postscript +@msg format `{f}`: text or json formato `{f}`: text ou json +@msg format is `{format}` o formato é `{format}` +@msg formats must be an array of rows, not {v} formats deve ser uma matriz de linhas, não {v} +@msg frequency must be between 1 and 300 lines per inch frequency deve estar entre 1 e 300 linhas por polegada +@msg from and to must be finite from e to devem ser finitos +@msg from must be [x0, y0, x1, y1] from deve ser [x0, y0, x1, y1] +@msg function declaration index out of range índice de declaração de função fora do intervalo +@msg give `before` or `after`, not both informe `before` ou `after`, não ambos +@msg give `char`, `text`, `code` or `unicode` informe `char`, `text`, `code` ou `unicode` +@msg give `colors`, or `color` and `rule` informe `colors`, ou `color` e `rule` +@msg give `count` or `spacing`, not both informe `count` ou `spacing`, não ambos +@msg give `event`, such as onclick informe `event`, como onclick +@msg give `file`, the JavaScript file's path or URL informe `file`, o caminho ou URL do arquivo JavaScript +@msg give `fill` and/or `stroke` informe `fill` e/ou `stroke` +@msg give `fill`, `stroke` or both informe `fill`, `stroke` ou ambos +@msg give `folder` informe `folder` +@msg give `fonts`, or open a document informe `fonts`, ou abra um documento +@msg give `from` or `indices`, not both informe `from` ou `indices`, não ambos +@msg give `id` (no text is being edited) informe `id` (nenhum texto está em edição) +@msg give `id` and `anchors`, or `items`, not both informe `id` e `anchors`, ou `items`, não ambos +@msg give `id` or `guide`, not both informe `id` ou `guide`, não ambos +@msg give `length` or `to` informe `length` ou `to` +@msg give `matches`: [{kind, name, target}] informe `matches`: [{kind, name, target}] +@msg give `name` (or edit a pattern) informe `name` (ou edite um padrão) +@msg give `name` or `names` informe `name` ou `names` +@msg give `name`, an SVG filter's name informe `name`, o nome de um filtro SVG +@msg give `path` (a .wasm file) or `dataBase64` (the module) informe `path` (um arquivo .wasm) ou `dataBase64` (o módulo) +@msg give `path`, `data` or `dataBase64` informe `path`, `data` ou `dataBase64` +@msg give `path`, `data`, `dataBase64` or `csv` informe `path`, `data`, `dataBase64` ou `csv` +@msg give `path`, `xml` or `dataBase64` informe `path`, `xml` ou `dataBase64` +@msg give `path`, an SVG file, or `svg`, SVG text informe `path`, um arquivo SVG, ou `svg`, texto SVG +@msg give `script`, the JavaScript the event runs ("" removes the event) informe `script`, o JavaScript que o evento executa ("" remove o evento) +@msg give `text` (the reading) or `remove` informe `text` (a leitura) ou `remove` +@msg give `x` and `y` (and a `tolerance` of 0 or more) informe `x` e `y` (e uma `tolerance` igual ou maior que 0) +@msg give `xml`, one SVG element informe `xml`, um elemento SVG +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments informe um modo ou ajustes de r/g/b, c/m/y/k, gray ou tint +@msg give a name, or paint the active proxy with a saved preset informe um nome, ou pinte o proxy ativo com uma predefinição salva +@msg give a name, or select a stroke with a saved profile informe um nome, ou selecione um traçado com um perfil salvo +@msg give a pattern name or select an object painted with a pattern informe o nome de um padrão ou selecione um objeto pintado com um padrão +@msg give at least one of clip, invert, disabled, linked informe pelo menos um de clip, invert, disabled, linked +@msg give color, none, swatch or gradient informe color, none, swatch ou gradient +@msg give dataBase64 informe dataBase64 +@msg give either at (a click) or area (a path drawn around the overlaps) informe at (um clique) ou area (um caminho desenhado em volta das sobreposições) +@msg give exactly one of `at`, `rect` and `points` informe exatamente um de `at`, `rect` e `points` +@msg give exactly one of `path`, `data` and `dataBase64` informe exatamente um de `path`, `data` e `dataBase64` +@msg give formats or a preset, not both informe formats ou uma predefinição, não ambos +@msg give index or indexes, not both informe index ou indexes, não ambos +@msg give newName or delete informe newName ou delete +@msg give offset or by informe offset ou by +@msg give one element informe um elemento +@msg give one of `to`, `rect` or `at` informe um de `to`, `rect` ou `at` +@msg give one of assets, layers, selection or fullDocument informe um de assets, layers, selection ou fullDocument +@msg give only one of onto, above, below, icon and a column place informe apenas um de onto, above, below, icon e uma posição de coluna +@msg give opacity or blend informe opacity ou blend +@msg give origin: [x, y], or reset: true informe origin: [x, y], ou reset: true +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note informe overprintFill, overprintStroke, showCenter, imageMap, url ou note +@msg give path (a file) or folder informe path (um arquivo) ou folder +@msg give path, or name and dataBase64 informe path, ou name e dataBase64 +@msg give paths or files informe paths ou files +@msg give rows, rowHeight, columns or columnWidth informe rows, rowHeight, columns ou columnWidth +@msg give svg, or dataBase64 of an SVG or SVGZ file informe svg, ou o dataBase64 de um arquivo SVG ou SVGZ +@msg give text informe text +@msg give the asset a name dê um nome ao ativo +@msg give the preset a `name` dê um `name` à predefinição +@msg give the preset a name dê um nome à predefinição +@msg give the preset a name (1–64 characters) dê um nome à predefinição (1–64 caracteres) +@msg give the settings to change, e.g. {"formats"}]} informe as configurações a alterar, por exemplo {"formats"}]} +@msg give the style a name dê um nome ao estilo +@msg give two or more style `names` informe dois ou mais `names` de estilos +@msg give two or more swatches informe duas ou mais amostras +@msg give x, y, or a handle's length or angle informe x, y, ou o comprimento ou ângulo de uma alça +@msg give {key, value} or {values} informe {key, value} ou {values} +@msg global must be true or false global deve ser true ou false +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) o glifo {_1} não é desenhado para {ch} com o estilo do caractere (como Todas Maiúsculas, Versaletes ou um sistema de numerais) +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} o glifo {g} não é uma alternativa de {ch} em {_1} {_2} +@msg gradient must be "objects" or "mesh" gradient deve ser "objects" ou "mesh" +@msg grays must be "profile" or "black", not `{g}` grays deve ser "profile" ou "black", não `{g}` +@msg gridColors must be a preset name or two colours gridColors deve ser o nome de uma predefinição ou duas cores +@msg gridSize must be small, medium or large gridSize deve ser small, medium ou large +@msg guide {g} is not selected a guia {g} não está selecionada +@msg handle must be 0..3 handle deve ser 0..3 +@msg handler at {_1} expects {_2} values but its range starts with {d} o manipulador em {_1} espera {_2} valores, mas o intervalo dele começa com {d} +@msg handler depth too large profundidade do manipulador grande demais +@msg handler range {_1}..{_2} out of range intervalo do manipulador {_1}..{_2} fora do intervalo +@msg hiddenLayers must be true or false hiddenLayers deve ser true ou false +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}: "layer" (o padrão), "omit" ou "visible" +@msg highlight a layer to merge into destaque uma camada na qual mesclar +@msg highlight two or more rows in one layer or group destaque duas ou mais linhas em uma mesma camada ou grupo +@msg highlight two or more rows to merge destaque duas ou mais linhas para mesclar +@msg hvcC hvcC +@msg icon docks a panel in the icon column: use window.panel.dock icon encaixa um painel na coluna de ícones: use window.panel.dock +@msg id must be a dimension id id deve ser um id de cota +@msg image has no bounds a imagem não tem limites +@msg image {_1} is linked already a imagem {_1} já está vinculada +@msg imageMap `{m}`: none, client or server imageMap `{m}`: none, client ou server +@msg images in a spot ink whose samples couldn't be read were imported in RGB as imagens em uma tinta especial cujas amostras não puderam ser lidas foram importadas em RGB +@msg images past the most pixels one file may hold were left out as imagens além do máximo de pixels que um arquivo pode conter foram omitidas +@msg images too large to read were left out as imagens grandes demais para ler foram omitidas +@msg index out of range índice fora do intervalo +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes pode conter no máximo {MAX_GUIDE_INDEXES} entradas +@msg indexes must be an array of guide indexes indexes deve ser uma matriz de índices de guias +@msg instances must be 1..1000 instances deve ser 1..1000 +@msg instruction out of range instrução fora do intervalo +@msg instruction {pc} ({op}) pops below the stack a instrução {pc} ({op}) retira valores abaixo da pilha +@msg instruction {pc} reached with stack depths {seen} and {d} instrução {pc} alcançada com profundidades de pilha {seen} e {d} +@msg invalid `color` `color` inválido +@msg invalid anchor index {v}: expected a non-negative integer índice de ponto de ancoragem inválido {v}: esperado um inteiro não negativo +@msg invalid anchor reference {v}: expected [subpath, anchor] referência de ponto de ancoragem inválida {v}: esperado [subcaminho, ponto de ancoragem] +@msg invalid angle ângulo inválido +@msg invalid array length comprimento de matriz inválido +@msg invalid brush: {e} pincel inválido: {e} +@msg invalid color cor inválida +@msg invalid colour {c} cor inválida {c} +@msg invalid delta delta inválido +@msg invalid group grupo inválido +@msg invalid object id {v}: expected a non-negative integer id de objeto inválido {v}: esperado um inteiro não negativo +@msg invalid point ponto inválido +@msg invalid regular expression flag '{_1}' sinalizador de expressão regular inválido '{_1}' +@msg invalid regular expression: {m} expressão regular inválida: {m} +@msg invalid size tamanho inválido +@msg invalid t t inválido +@msg invalid time value valor de tempo inválido +@msg invalid tolerance tolerância inválida +@msg is a confirm's answer, whose `value` must be true or false é a resposta de um confirm, cujo `value` deve ser true ou false +@msg is a prompt's answer, whose `value` must be a string or null é a resposta de um prompt, cujo `value` deve ser uma string ou null +@msg is an alert's answer, whose `value` must be "ok" é a resposta de um alert, cujo `value` deve ser "ok" +@msg is longer than 16 KiB tem mais de 16 KiB +@msg it draws its image through a clipping path desenha a imagem através de um caminho de recorte +@msg it draws no image não desenha nenhuma imagem +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes tem mais de {MAX_DICTIONARY_FILE_BYTES} bytes +@msg it isn't a file não é um arquivo +@msg it names {_1} plates, and at most {MAX_PLATES} are read indica {_1} placas, e no máximo {MAX_PLATES} são lidas +@msg its art is too long or too detailed for a head a arte é longa ou detalhada demais para uma ponta de seta +@msg its art paints nothing a arte não pinta nada +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) todas as pranchetas ficam além da tela (a mais de {_1} pt da origem) +@msg its clipping masks are nested too deeply as máscaras de recorte estão aninhadas em níveis demais +@msg its image is too large to read a imagem é grande demais para ler +@msg its metadata has no description os metadados não têm descrição +@msg its metadata has no direct length os metadados não têm comprimento direto +@msg its plates are too large: more than {MAX_SAMPLES} pixels together as placas são grandes demais: mais de {MAX_SAMPLES} pixels no total +@msg its plates are too large: {_1} plates of {w} × {h} pixels as placas são grandes demais: {_1} placas de {w} × {h} pixels +@msg its plates can't be kept together as placas não podem ser mantidas juntas +@msg its shapes could not be combined into one as formas não puderam ser combinadas em uma só +@msg join two different paths una dois caminhos diferentes +@msg jump to {pc}, past the end salto para {pc}, além do fim +@msg justify must be auto|left|center|right|justifyAll justify deve ser auto|left|center|right|justifyAll +@msg kbps must be a positive number, not {kbps} kbps deve ser um número positivo, não {kbps} +@msg keywords must be a list of words or a comma-separated string keywords deve ser uma lista de palavras ou uma string separada por vírgulas +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters as palavras-chave devem ter no máximo {MAX_KEYWORD} caracteres, sem caracteres de controle +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind deve ser 1, 2 ou 3, ou informe o `name` de uma predefinição (veja perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind deve ser graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle ou paraStyle +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout deve ser "on", "off", "neutral" ou um booleano, não {v} +@msg layer `{name}` has no art to export a camada `{name}` não tem arte para exportar +@msg layer must name a layer or sublayer layer deve indicar uma camada ou subcamada +@msg layers can't be intertwined: select the objects in them camadas não podem ser entrelaçadas: selecione os objetos dentro delas +@msg layers go only at the top level or in layers, objects in layers and groups as camadas só ficam no nível superior ou dentro de camadas; os objetos, dentro de camadas e grupos +@msg layers must be true or false layers deve ser true ou false +@msg layout must be gridByRow, gridByColumn, row or column layout deve ser gridByRow, gridByColumn, row ou column +@msg leading must be a number or "auto" leading deve ser um número ou "auto" +@msg left/right must be non-negative widths left/right devem ser larguras não negativas +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding deve ser um de {_1} +@msg level `{t}`: 2 or 3 level `{t}`: 2 ou 3 +@msg line {line}: more than {MAX_CSV_COLS} columns linha {line}: mais de {MAX_CSV_COLS} colunas +@msg line {line}: unclosed quote linha {line}: aspas não fechadas +@msg line {line}: value for `{var}` must match its variable kind linha {line}: o valor de `{var}` deve corresponder ao tipo da variável +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`: lf ou crlf +@msg loading profiles from disk isn't available in the browser carregar perfis do disco não está disponível no navegador +@msg localize: the first argument must be a localization object or a string localize: o primeiro argumento deve ser um objeto de localização ou uma string +@msg margin: a {m} pt margin leaves no imageable area on the paper margin: uma margem de {m} pt não deixa área imprimível no papel +@msg matrix must be finite and invertible, depth finite matrix deve ser finita e invertível, e depth, finito +@msg matte: {e} matte: {e} +@msg member is for order front, forward, backward or back member é para order front, forward, backward ou back +@msg missing `artboards` falta `artboards` +@msg missing `at` [x, y] falta `at` [x, y] +@msg missing `attrs` falta `attrs` +@msg missing `color` or `opacity` falta `color` ou `opacity` +@msg missing `colors` falta `colors` +@msg missing `commands` falta `commands` +@msg missing `destinationFolder` falta `destinationFolder` +@msg missing `effect` id falta o id de `effect` +@msg missing `family` falta `family` +@msg missing `find` falta `find` +@msg missing `gid` (a glyph id, or null for the default glyph) falta `gid` (um id de glifo, ou null para o glifo padrão) +@msg missing `group` falta `group` +@msg missing `id` falta `id` +@msg missing `id`: give one, or select one type object falta `id`: informe um, ou selecione um objeto de texto +@msg missing `index` (a point index, or null to clear) falta `index` (um índice de ponto, ou null para limpar) +@msg missing `index` (a stop index, or null to clear) falta `index` (um índice de parada, ou null para limpar) +@msg missing `index` (an item index or null) falta `index` (um índice de item ou null) +@msg missing `item` falta `item` +@msg missing `library` falta `library` +@msg missing `library` (see graphicStyle.libraries) falta `library` (veja graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) falta `library` (veja swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) falta `map` (linhas [{from, to}] ou {current}) +@msg missing `matrix` [a,b,c,d,e,f] falta `matrix` [a,b,c,d,e,f] +@msg missing `name` (see gradient.presets.list) falta `name` (veja gradient.presets.list) +@msg missing `offset` (a distance in points) falta `offset` (uma distância em pontos) +@msg missing `path` falta `path` +@msg missing `path` (a path object id) falta `path` (um id de objeto de caminho) +@msg missing `picks` falta `picks` +@msg missing `rule` falta `rule` +@msg missing `source` id falta o id de `source` +@msg missing `start` (the byte offset of a character) falta `start` (o deslocamento em bytes de um caractere) +@msg missing `status`: linked, embedded, missing or modified falta `status`: linked, embedded, missing ou modified +@msg missing `target` id falta o id de `target` +@msg missing `text` falta `text` +@msg missing `to` falta `to` +@msg missing `to` (an index) falta `to` (um índice) +@msg missing `to`: a non-negative integer falta `to`: um inteiro não negativo +@msg missing `type` falta `type` +@msg missing `value` falta `value` +@msg missing `word` falta `word` +@msg missing `{key}` falta `{key}` +@msg missing `{k}` falta `{k}` +@msg missing anchor falta o ponto de ancoragem +@msg missing anchors faltam os pontos de ancoragem +@msg missing anchors [[subpath, anchor]…] faltam os pontos de ancoragem [[subcaminho, ponto de ancoragem]…] +@msg missing array `{key}` falta a matriz `{key}` +@msg missing case falta case +@msg missing color falta a cor +@msg missing commands faltam os comandos +@msg missing destination folder falta a pasta de destino +@msg missing from falta from +@msg missing group falta o grupo +@msg missing id or ids falta id ou ids +@msg missing ids falta ids +@msg missing indexes falta indexes +@msg missing matrix [a,b,c,d,e,f] falta a matriz [a,b,c,d,e,f] +@msg missing name falta o nome +@msg missing op falta op +@msg missing or invalid `color` `color` ausente ou inválido +@msg missing or unknown `tool` `tool` ausente ou desconhecido +@msg missing point falta o ponto +@msg missing point [x,y] falta o ponto [x,y] +@msg missing point `{key}` ([x, y]) falta o ponto `{key}` ([x, y]) +@msg missing points faltam os pontos +@msg missing points [[x,y]…] faltam os pontos [[x,y]…] +@msg missing segment falta o segmento +@msg missing stops faltam as paradas +@msg missing subpaths faltam os subcaminhos +@msg missing target falta o destino +@msg missing to falta to +@msg missing x, y falta x, y +@msg mode is set, add or toggle, not `{other}` mode é set, add ou toggle, não `{other}` +@msg mode must be "cmyk" or "rgb" mode deve ser "cmyk" ou "rgb" +@msg mode must be area|onPath, got {o} mode deve ser area|onPath, recebido {o} +@msg more than 10000 linked files: package dependencies in smaller parts mais de 10000 arquivos vinculados: empacote as dependências em partes menores +@msg more than 256 commands mais de 256 comandos +@msg more than {MAX_BATCH_FILES} data sets mais de {MAX_BATCH_FILES} conjuntos de dados +@msg more than {MAX_BATCH_FILES} files mais de {MAX_BATCH_FILES} arquivos +@msg more than {MAX_CSV_ROWS} data rows mais de {MAX_CSV_ROWS} linhas de dados +@msg more than {MAX_FILES} images mais de {MAX_FILES} imagens +@msg more than {MAX_IMPORT} presets mais de {MAX_IMPORT} predefinições +@msg moved must be [[x,y]…] moved deve ser [[x,y]…] +@msg must be an object with `kind` and `value` deve ser um objeto com `kind` e `value` +@msg must have `kind` alert, confirm or prompt deve ter `kind` alert, confirm ou prompt +@msg must have exactly `kind` and `value` deve ter exatamente `kind` e `value` +@msg name (a toolbar's name) is required name (o nome de uma barra de ferramentas) é obrigatório +@msg name (a toolbar's name) must be a string name (o nome de uma barra de ferramentas) deve ser uma string +@msg name must be a toolbar's name name deve ser o nome de uma barra de ferramentas +@msg names is empty names está vazio +@msg names must be an array of symbol names names deve ser uma matriz de nomes de símbolos +@msg need `anchor` or `segment` + `t` é preciso `anchor` ou `segment` + `t` +@msg need at least one anchor é preciso pelo menos um ponto de ancoragem +@msg need at least one point é preciso pelo menos um ponto +@msg need at least two points são precisos pelo menos dois pontos +@msg needs a closing quote after the name precisa de aspas de fechamento depois do nome +@msg needs a name precisa de um nome +@msg needs a name of at most 64 bytes precisa de um nome de no máximo 64 bytes +@msg needs a name of letters, digits and _, or a quoted name precisa de um nome com letras, dígitos e _, ou de um nome entre aspas +@msg needs a name without control characters precisa de um nome sem caracteres de controle +@msg needs points precisa de pontos +@msg nesting too deep aninhamento profundo demais +@msg nineSlice must be true or false nineSlice deve ser true ou false +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides deve ser [left, top, right, bottom], cada um -16..16, com left + right < 1 e top + bottom < 1 +@msg no HP-GL format nenhum formato HP-GL +@msg no New Document profile called `{shown}` ({_1}) nenhum perfil de Novo Documento chamado `{shown}` ({_1}) +@msg no PDF preset named `{n}` nenhuma predefinição de PDF chamada `{n}` +@msg no SVG filter at index {k} nenhum filtro SVG no índice {k} +@msg no action set is current nenhum conjunto de ações é o atual +@msg no active data set nenhum conjunto de dados ativo +@msg no appearance item {index} nenhum item de aparência {index} +@msg no arrowheads file here nenhum arquivo de pontas de seta aqui +@msg no artboard {_1} nenhuma prancheta {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) nenhuma prancheta {artboard} (com base 0; o documento tem {_1}) +@msg no asset {id} nenhum ativo {id} +@msg no asset {id} (see assets.list) nenhum ativo {id} (veja assets.list) +@msg no assets to export nenhum ativo para exportar +@msg no assets yet: collect art for export first (assets.add) ainda não há ativos: colete a arte para exportação primeiro (assets.add) +@msg no brush named `{n}` nenhum pincel chamado `{n}` +@msg no character at byte {start} nenhum caractere no byte {start} +@msg no character has code {code} nenhum caractere tem o código {code} +@msg no colour nenhuma cor +@msg no colour group `{group}` nenhum grupo de cores `{group}` +@msg no colour group `{name}` nenhum grupo de cores `{name}` +@msg no custom arrowhead {name} (see stroke.arrowheads.list) nenhuma ponta de seta personalizada {name} (veja stroke.arrowheads.list) +@msg no data sets nenhum conjunto de dados +@msg no dataset named `{from}` nenhum conjunto de dados chamado `{from}` +@msg no dataset named `{name}` nenhum conjunto de dados chamado `{name}` +@msg no datasets nenhum conjunto de dados +@msg no design named `{name}` nenhum projeto chamado `{name}` +@msg no design named `{n}` nenhum projeto chamado `{n}` +@msg no design named `{old}` nenhum projeto chamado `{old}` +@msg no encoder for {_1} yet ainda não há codificador para {_1} +@msg no export preset `{name}` (see document.exportPresets.list) nenhuma predefinição de exportação `{name}` (veja document.exportPresets.list) +@msg no file to open nenhum arquivo para abrir +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder nenhuma pasta: passe `path` ou defina Preferências › Plug-ins & Discos de Cópia › Pasta Adicional de Plug-ins +@msg no fonts to look for: they are all available nenhuma fonte a procurar: todas estão disponíveis +@msg no formats to export nenhum formato para exportar +@msg no gradient preset `{name}` (see gradient.presets.list) nenhuma predefinição de gradiente `{name}` (veja gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` nenhum estilo gráfico `{n}` em `{_1}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) nenhuma biblioteca de estilos gráficos `{key}` (veja graphicStyle.libraries) +@msg no guide selected nenhuma guia selecionada +@msg no guide selected (or give an index) nenhuma guia selecionada (ou informe um índice) +@msg no guide {i} nenhuma guia {i} +@msg no images in the folder nenhuma imagem na pasta +@msg no instructions nenhuma instrução +@msg no item at index {_1} nenhum item no índice {_1} +@msg no item at index {from} nenhum item no índice {from} +@msg no item at one of the indices {idx} nenhum item em um dos índices {idx} +@msg no layer nenhuma camada +@msg no layer to rename nenhuma camada para renomear +@msg no layer {id} (see layer.list) nenhuma camada {id} (veja layer.list) +@msg no layers to export nenhuma camada para exportar +@msg no library `{key}` nenhuma biblioteca `{key}` +@msg no metafile format nenhum formato de metarquivo +@msg no object {_1} nenhum objeto {_1} +@msg no objects nenhum objeto +@msg no parent layer nenhuma camada pai +@msg no perspective grid preset named `{b}` (see perspective.presets.list) nenhuma predefinição de grade de perspectiva chamada `{b}` (veja perspective.presets.list) +@msg no perspective grid preset named `{n}` nenhuma predefinição de grade de perspectiva chamada `{n}` +@msg no point selected (give `index`) nenhum ponto selecionado (informe `index`) +@msg no preset called `{from}` (see file.newPresets) nenhuma predefinição chamada `{from}` (veja file.newPresets) +@msg no preset called `{name}` (see file.newPresets) nenhuma predefinição chamada `{name}` (veja file.newPresets) +@msg no preset named `{n}` nenhuma predefinição chamada `{n}` +@msg no print preset named `{b}` (see print.presets.list) nenhuma predefinição de impressão chamada `{b}` (veja print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) nenhuma predefinição de impressão chamada `{name}` (predefinições: {_1}) +@msg no print preset named `{n}` nenhuma predefinição de impressão chamada `{n}` +@msg no profile named `{name}` nenhum perfil chamado `{name}` +@msg no random numbers ({e}) sem números aleatórios ({e}) +@msg no readable files in the folder nenhum arquivo legível na pasta +@msg no recovery copy `{f}` (see file.recovery.list) nenhuma cópia de recuperação `{f}` (veja file.recovery.list) +@msg no rows nenhuma linha +@msg no saved PDF preset named `{name}` nenhuma predefinição de PDF salva chamada `{name}` +@msg no saved export preset `{name}` nenhuma predefinição de exportação salva `{name}` +@msg no saved gradient preset called {name} nenhuma predefinição de gradiente salva chamada {name} +@msg no saved gradient presets nenhuma predefinição de gradiente salva +@msg no saved preset called `{name}` nenhuma predefinição salva chamada `{name}` +@msg no saved presets to export (name [Default] in `names` to export it) nenhuma predefinição salva para exportar (indique [Default] em `names` para exportá-la) +@msg no saved presets to export (name built-in ones in `names`) nenhuma predefinição salva para exportar (indique as integradas em `names`) +@msg no saved print preset named `{name}` nenhuma predefinição de impressão salva chamada `{name}` +@msg no saved profile called {name} nenhum perfil salvo chamado {name} +@msg no saved selection `{name}` nenhuma seleção salva `{name}` +@msg no saved theme called {name} (see colorTheme.list) nenhum tema salvo chamado {name} (veja colorTheme.list) +@msg no saved views nenhuma vista salva +@msg no selected or given object has a visible 3D effect nenhum objeto selecionado ou informado tem um efeito 3D visível +@msg no series {i} nenhuma série {i} +@msg no slice {n} in the image (see slice.list) nenhuma fatia {n} na imagem (veja slice.list) +@msg no source art nenhuma arte de origem +@msg no stop {i} (the gradient has {n}) nenhuma parada {i} (o gradiente tem {n}) +@msg no style `{name}` nenhum estilo `{name}` +@msg no such artboard {v} prancheta inexistente {v} +@msg no such assets (see assets.list) ativos inexistentes (veja assets.list) +@msg no such frame anchor, or the frame would reach past the canvas ponto de ancoragem de moldura inexistente, ou a moldura ultrapassaria a tela +@msg no such objects objetos inexistentes +@msg no such path anchor, or the path would reach past the canvas ponto de ancoragem de caminho inexistente, ou o caminho ultrapassaria a tela +@msg no such rows linhas inexistentes +@msg no swatch `{keep}` nenhuma amostra `{keep}` +@msg no swatch `{n}` nenhuma amostra `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) nenhuma amostra `{n}` (amostras e grupos de cores são movidos separadamente) +@msg no swatch library `{key}` (see swatch.library.list) nenhuma biblioteca de amostras `{key}` (veja swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) nenhuma biblioteca de amostras `{key}` (veja swatch.library.list; "document": as amostras do documento) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) nenhuma biblioteca de amostras `{key}` (veja swatch.library.list; "{DOCUMENT_SWATCHES}": as amostras do documento) +@msg no swatch named `{n}` nenhuma amostra chamada `{n}` +@msg no swatch or colour group `{n}` nenhuma amostra ou grupo de cores `{n}` +@msg no swatch or colour group `{n}` in `{_1}` nenhuma amostra ou grupo de cores `{n}` em `{_1}` +@msg no symbol named `{_1}` nenhum símbolo chamado `{_1}` +@msg no symbol named `{name}` nenhum símbolo chamado `{name}` +@msg no text objects selected nenhum objeto de texto selecionado +@msg no toolbar named `{_1}` nenhuma barra de ferramentas chamada `{_1}` +@msg no toolbar named `{name}` nenhuma barra de ferramentas chamada `{name}` +@msg no tracing preset `{preset}` (see imageTrace.presets) nenhuma predefinição de rastreamento `{preset}` (veja imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) nenhuma pasta de bibliotecas do usuário aqui (salve com um caminho, ou sem ele para obter os dados) +@msg no variable named `{from}` nenhuma variável chamada `{from}` +@msg no variable named `{name}` nenhuma variável chamada `{name}` +@msg no variable named `{var}` nenhuma variável chamada `{var}` +@msg no variable named `{v}` nenhuma variável chamada `{v}` +@msg no view named `{name}` nenhuma vista chamada `{name}` +@msg node {_1} already has the text variable `{_2}` o nó {_1} já tem a variável de texto `{_2}` +@msg node {_1} is not a graph o nó {_1} não é um gráfico +@msg node {_1} is not an image o nó {_1} não é uma imagem +@msg node {_1} is not type o nó {_1} não é texto +@msg not a DCS file não é um arquivo DCS +@msg not a PDF presets file (format `{_1}`) não é um arquivo de predefinições de PDF (formato `{_1}`) +@msg not a PDF presets file: {e} não é um arquivo de predefinições de PDF: {e} +@msg not a RegExp object não é um objeto RegExp +@msg not a Word document: its WordDocument stream doesn't start with a file information block não é um documento do Word: o fluxo WordDocument não começa com um bloco de informações do arquivo +@msg not a Word document: its compound file has no WordDocument stream não é um documento do Word: o arquivo composto não tem fluxo WordDocument +@msg not a Word document: its main part is not a WordprocessingML document não é um documento do Word: a parte principal não é um documento WordprocessingML +@msg not a Word document: its package has no main document part não é um documento do Word: o pacote não tem a parte principal do documento +@msg not a closed path não é um caminho fechado +@msg not a flattener presets file (format `{_1}`) não é um arquivo de predefinições do achatador (formato `{_1}`) +@msg not a flattener presets file: {e} não é um arquivo de predefinições do achatador: {e} +@msg not a layer or group não é uma camada nem um grupo +@msg not a library file: {e} não é um arquivo de biblioteca: {e} +@msg not a mesh envelope point não é um ponto de envelope de malha +@msg not a perspective grid presets file (format `{_1}`) não é um arquivo de predefinições de grade de perspectiva (formato `{_1}`) +@msg not a perspective grid presets file: {e} não é um arquivo de predefinições de grade de perspectiva: {e} +@msg not a print presets file (format `{_1}`) não é um arquivo de predefinições de impressão (formato `{_1}`) +@msg not a print presets file: {e} não é um arquivo de predefinições de impressão: {e} +@msg not a regular expression não é uma expressão regular +@msg not a stored or deflated central record não é um registro central armazenado nem compactado com deflate +@msg not a valid SVGZ file: {e} não é um arquivo SVGZ válido: {e} +@msg not an action set: the text doesn't start with /version and a number não é um conjunto de ações: o texto não começa com /version e um número +@msg not an image VectorCraft reads (see document.formats) não é uma imagem que o VectorCraft lê (veja document.formats) +@msg not area type (text in a frame) outside perspective não é texto de área (texto em uma moldura) fora da perspectiva +@msg not editing an opacity mask nenhuma máscara de opacidade está em edição +@msg not type on a path outside perspective não é texto em um caminho fora da perspectiva +@msg nothing prints nada é impresso +@msg nothing to bind nada para vincular +@msg nothing to change nada para alterar +@msg nothing to change: give dx, dy, hScale, vScale or rotation nada para alterar: informe dx, dy, hScale, vScale ou rotation +@msg nothing to crop nada para cortar +@msg nothing to export nada para exportar +@msg nothing to export: the document has no visible art nada para exportar: o documento não tem arte visível +@msg nothing to mark nada para marcar +@msg nothing to print: every artboard picked is blank nada para imprimir: todas as pranchetas escolhidas estão em branco +@msg nothing to print: the document has no art that prints nada para imprimir: o documento não tem arte imprimível +@msg nothing to repeat nada para repetir +@msg numbers out of order in {_1} quantifier números fora de ordem no quantificador {_1} +@msg object is not a path o objeto não é um caminho +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object a transparência do objeto não pode ser aplicada aos caracteres destacados; desative Transparência nas Opções do Conta-gotas ou selecione o objeto inteiro +@msg object {_1} can't be set as point type o objeto {_1} não pode ser definido como texto de ponto +@msg object {_1} has lost its file's contents: relink it o objeto {_1} perdeu o conteúdo do arquivo: revincule-o +@msg object {_1} has no path o objeto {_1} não tem caminho +@msg object {_1} has no size o objeto {_1} não tem tamanho +@msg object {_1} is hidden or locked, or on a locked artboard o objeto {_1} está oculto ou bloqueado, ou em uma prancheta bloqueada +@msg object {_1} is not a placed document o objeto {_1} não é um documento colocado +@msg object {_1} is not an Intertwine group o objeto {_1} não é um grupo de Entrelaçar +@msg object {_1} is not an image o objeto {_1} não é uma imagem +@msg object {_1} is not an image or a linked file o objeto {_1} não é uma imagem nem um arquivo vinculado +@msg object {_1} is not an image or a placed document o objeto {_1} não é uma imagem nem um documento colocado +@msg object {_1} is not in this Intertwine group o objeto {_1} não está neste grupo de Entrelaçar +@msg object {_1} needs a 3D effect o objeto {_1} precisa de um efeito 3D +@msg object {_1} needs a 3D effect at index {index} o objeto {_1} precisa de um efeito 3D no índice {index} +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic objeto {_1}: uma Extrusão e Bisel oca não tem tampas: ative Tampa para mapear um gráfico +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front objeto {_1}: com profundidade 0, o objeto tem uma única face: mapeie os gráficos na face frontal +@msg object {_1}: {_2} objeto {_1}: {_2} +@msg object {_1}: {e} objeto {_1}: {e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams objeto {_1}: {name} no índice {index} não tem predefinições de iluminação; defina as luzes com effect.setParams +@msg object {id} has anchors 0–{_1} for `corners`, not {past} o objeto {id} tem os pontos de ancoragem 0–{_1} para `corners`, não {past} +@msg object {id} has {n} anchors, so no anchor {past} o objeto {id} tem {n} pontos de ancoragem, então não há ponto de ancoragem {past} +@msg objects must be none, move or copy objects deve ser none, move ou copy +@msg objects were left out objetos foram omitidos +@msg offset out of range deslocamento fora do intervalo +@msg on must be true or false on deve ser true ou false +@msg only groups, layers, type and images have a Contents (Characters) row somente grupos, camadas, texto e imagens têm uma linha Conteúdo (Caracteres) +@msg only interior mesh lines can be deleted somente as linhas internas da malha podem ser excluídas +@msg only solid colours can be added (not gradients or patterns) somente cores sólidas podem ser adicionadas (não gradientes nem padrões) +@msg only solid-colour swatches can be spot colours somente amostras de cor sólida podem ser cores especiais +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first só foi possível ler a prévia incorporada deste arquivo do Affinity: use Arquivo › Abrir para vê-la com o aviso, ou exporte SVG ou PDF pelo Affinity primeiro +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning só foi possível ler a prévia incorporada deste arquivo do Affinity; use Arquivo › Abrir para vê-la com o aviso +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon e uma posição de coluna pertencem a window.panel.float e window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto, above, below, icon e uma posição de coluna não aceitam anchor, side, zone nem before +@msg onto, above, below, icon and a column place take no x, y, width or height onto, above, below, icon e uma posição de coluna não aceitam x, y, width nem height +@msg opacity must be a number opacity deve ser um número +@msg open a document to look for its linked files abra um documento para procurar os arquivos vinculados a ele +@msg operand too large operando grande demais +@msg options must be an object (see file.formatOptions) options deve ser um objeto (veja file.formatOptions) +@msg order is a whole number from 0 to {others}, not `{v}` order é um número inteiro de 0 a {others}, não `{v}` +@msg order must be leftToRight or rightToLeft order deve ser leftToRight ou rightToLeft +@msg orientation must be portrait or landscape, not `{o}` orientation deve ser portrait ou landscape, não `{o}` +@msg outlineText {v}: true or false outlineText {v}: true ou false +@msg output intent profile: {e} perfil do propósito de saída: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: nenhum perfil RGB ou CMYK se chama “{name}” +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: os arquivos {_1} são CMYK, e “{name}” é um perfil RGB +@msg output.destination: {e} output.destination: {e} +@msg output.inks: no ink is set to print output.inks: nenhuma tinta está definida para impressão +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: nenhum perfil se chama “{name}”, e um propósito de saída {std} incorpora seu perfil +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent: nenhum perfil se chama “{name}”: um propósito de saída {std} incorpora um perfil, ou indica uma condição de impressão registrada (outputConditionId e registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: os arquivos {std} são impressos em CMYK, e “{name}” é um perfil RGB +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: “{name}” é um perfil de cinza, que não pode ser um propósito de saída {std} +@msg overlap: {o} pt is half a tile or more overlap: {o} pt é metade de um ladrilho ou mais +@msg overlapping Shaper paths must be in the same layer or group os caminhos sobrepostos do Formador devem estar na mesma camada ou no mesmo grupo +@msg overlapping edits edições sobrepostas +@msg page art conflicts with image data for {key} a arte da página conflita com os dados de imagem de {key} +@msg page art conflicts with pattern {name} a arte da página conflita com o padrão {name} +@msg page art conflicts with swatch {name} a arte da página conflita com a amostra {name} +@msg page art is missing image data for {key} faltam na arte da página os dados de imagem de {key} +@msg page art is missing pattern {name} falta na arte da página o padrão {name} +@msg page art is missing swatch {name} falta na arte da página a amostra {name} +@msg page must be a whole number from 1 page deve ser um número inteiro a partir de 1 +@msg page thumbnail: {e} miniatura de página: {e} +@msg page {page} of the PDF has no objects a página {page} do PDF não tem objetos +@msg page {page}: `{file}` has {n} {artboards} página {page}: `{file}` tem {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} página {page}: `{name}` tem {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages deve ser um intervalo como "2-3" ou um número de página, não {v} +@msg pages: {e} pages: {e} +@msg panel position must be finite a posição do painel deve ser finita +@msg parameters must be an object os parâmetros devem ser um objeto +@msg params must be an object params deve ser um objeto +@msg passwords can have up to {MAX_PASSWORD} bytes as senhas podem ter até {MAX_PASSWORD} bytes +@msg path chunks must share their boundary point os trechos de caminho devem compartilhar o ponto de fronteira +@msg path has no open end o caminho não tem extremidade aberta +@msg paths must be strings paths deve conter strings +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false precisa de todas as pranchetas para manter a arte editável +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false precisa das pranchetas para manter a arte editável quando useArtboards é false +@msg pdfs `{m}`: single or multiple pdfs `{m}`: single ou multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom é "artwork" ou "swatches", não {v} +@msg pins and moved must be non-empty lists of the same length pins e moved devem ser listas não vazias do mesmo tamanho +@msg pins must be [[x,y]…] pins deve ser [[x,y]…] +@msg pins must be finite pins deve conter valores finitos +@msg place is above, below or inside, not `{other}` place é above, below ou inside, não `{other}` +@msg plane must be left|right|ground plane deve ser left|right|ground +@msg plane must be left|right|ground|none plane deve ser left|right|ground|none +@msg point is outside the mesh o ponto está fora da malha +@msg point x must be a number x do ponto deve ser um número +@msg point y must be a number y do ponto deve ser um número +@msg points must be [t, left, right] with non-negative widths points deve ser [t, left, right] com larguras não negativas +@msg points must be [x,y] pairs points deve conter pares [x,y] +@msg points must be a non-empty [[x,y,pressure?]…] list points deve ser uma lista não vazia [[x,y,pressure?]…] +@msg points must be a non-empty list of finite [x,y] points deve ser uma lista não vazia de [x,y] finitos +@msg points must be an array or null points deve ser uma matriz ou null +@msg points must be finite points deve conter valores finitos +@msg points must be finite [x,y] pairs points deve conter pares [x,y] finitos +@msg points must be finite canvas points points deve conter pontos finitos da tela +@msg points must contain 1–4096 coordinates points deve conter 1–4096 coordenadas +@msg position must be normal, superscript or subscript position deve ser normal, superscript ou subscript +@msg position must be topLeft, topRight, bottomLeft or bottomRight position deve ser topLeft, topRight, bottomLeft ou bottomRight +@msg positions must be between 0 and 100000 pt positions deve estar entre 0 e 100000 pt +@msg ppi must be a positive number, not {ppi} ppi deve ser um número positivo, não {ppi} +@msg ppi must be between 1 and 2400 ppi deve estar entre 1 e 2400 +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` não é transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape deve ser clippingMask|transparency +@msg preset `{id}`: mobile or density preset `{id}`: mobile ou density +@msg preset must be standard, diffuse, topLeft or right preset deve ser standard, diffuse, topLeft ou right +@msg preset must be the name of a print preset preset deve ser o nome de uma predefinição de impressão +@msg previewMode must be default, pixel or overprint previewMode deve ser default, pixel ou overprint +@msg print settings: {e} configurações de impressão: {e} +@msg procedures nest too deeply os procedimentos estão aninhados em níveis demais +@msg procedures nested too deeply procedimentos aninhados em níveis demais +@msg quotes must be {double?, single?} quotes deve ser {double?, single?} +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} deve ser uma aspa de abertura e uma de fechamento, por exemplo "“”" +@msg range out of order in character class intervalo fora de ordem na classe de caracteres +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi deve ser 1–2400 (72 tela, 150 médio, 300 alto) +@msg rating must be a whole number from 0 to {MAX_RATING} rating deve ser um número inteiro de 0 a {MAX_RATING} +@msg rect and {k} contradict each other: give the area or the artboards rect e {k} se contradizem: informe a área ou as pranchetas +@msg rect applies to raster formats, not {_1} rect se aplica a formatos raster, não a {_1} +@msg rect is [x, y, width, height] in points rect é [x, y, width, height] em pontos +@msg rect must be [x, y, width, height] rect deve ser [x, y, width, height] +@msg rect must be [x, y, width, height] with a positive width and height rect deve ser [x, y, width, height] com largura e altura positivas +@msg rect needs finite numbers and a positive width and height rect precisa de números finitos e de largura e altura positivas +@msg redraw needs id redraw precisa de id +@msg redraw within must be a finite distance of 0 or more within de redraw deve ser uma distância finita igual ou maior que 0 +@msg reduce of an empty array with no initial value reduce de uma matriz vazia sem valor inicial +@msg registration must be 0..8 registration deve ser 0..8 +@msg regular expression flag '{_1}' given twice sinalizador de expressão regular '{_1}' informado duas vezes +@msg release a layer or a group libere uma camada ou um grupo +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace mantém a posição e a transformação do objeto substituído: remova template, at e rect +@msg replace: select the one object to replace replace: selecione o único objeto a substituir +@msg resolution must be a number or screen|medium|high resolution deve ser um número ou screen|medium|high +@msg resolution must be between 1 and 2400 ppi resolution deve estar entre 1 e 2400 ppi +@msg rotation must be a number of degrees rotation deve ser um número de graus +@msg rotation must be finite rotation deve ser finito +@msg rows and cols must be 1..50 rows e cols devem ser 1..50 +@msg rows and columns must be 1..500 rows e columns devem ser 1..500 +@msg rows of objects whose appearances differ can't be reordered together as linhas de objetos com aparências diferentes não podem ser reordenadas juntas +@msg sampled text would exceed the canvas (coordinates out of range) o texto com a amostra aplicada excederia a tela (coordenadas fora do intervalo) +@msg save the document first: Package collects a saved document salve o documento primeiro: Empacotar reúne um documento salvo +@msg save the document first: copy puts the file in its folder salve o documento primeiro: copy coloca o arquivo na pasta dele +@msg savedPreset: {e} savedPreset: {e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`: um fator como 2x, uma largura (100w), uma altura (100h) ou uma resolução (72ppi) +@msg scale must be a number above 0, at most 64 scale deve ser um número acima de 0, no máximo 64 +@msg scale must be non-zero scale deve ser diferente de zero +@msg scope must be selection or all (got {other}) scope deve ser selection ou all (recebido {other}) +@msg scribble is too long o rabisco é longo demais +@msg segment does not exist o segmento não existe +@msg select a Shaper group selecione um grupo do Formador +@msg select a blend to set easing or color shift selecione uma mesclagem para definir a suavização ou o deslocamento de cor +@msg select a compound shape selecione uma forma composta +@msg select a graph selecione um gráfico +@msg select a group or layer with visible, unlocked members selecione um grupo ou uma camada com membros visíveis e desbloqueados +@msg select a layer selecione uma camada +@msg select a text object selecione um objeto de texto +@msg select an SVG filter selecione um filtro SVG +@msg select an image (or an Image Trace group) selecione uma imagem (ou um grupo de Rastreamento de Imagem) +@msg select an image, or pass id selecione uma imagem, ou passe id +@msg select an object (or pass `id`) selecione um objeto (ou passe `id`) +@msg select an object with a 3D effect selecione um objeto com um efeito 3D +@msg select an object with a 3D effect, or give its id in ids selecione um objeto com um efeito 3D, ou informe o id dele em ids +@msg select an object with a freeform gradient selecione um objeto com um gradiente de forma livre +@msg select an object with an opacity mask selecione um objeto com uma máscara de opacidade +@msg select area type selecione texto de área +@msg select area type (text in a frame) selecione texto de área (texto em uma moldura) +@msg select area type and closed paths (at least one area type object) selecione texto de área e caminhos fechados (pelo menos um objeto de texto de área) +@msg select area type whose first line can fit its frame selecione texto de área cuja primeira linha caiba na moldura +@msg select artwork to add its colours selecione a arte para adicionar as cores dela +@msg select artwork to make a group of its colours selecione a arte para criar um grupo com as cores dela +@msg select artwork to recolour, or give a colour `group` selecione a arte a recolorir, ou informe um `group` de cores +@msg select exactly two path ends to join selecione exatamente duas extremidades de caminho para unir +@msg select members of a compound shape selecione membros de uma forma composta +@msg select objects or give ids selecione objetos ou informe ids +@msg select objects to align to the guide selecione objetos para alinhar à guia +@msg select one Intertwine group selecione um grupo de Entrelaçar +@msg select one image or pass id selecione uma imagem ou passe id +@msg select one layer, group or object to rename selecione uma camada, um grupo ou um objeto para renomear +@msg select paths selecione caminhos +@msg select paths or compound paths selecione caminhos ou caminhos compostos +@msg select rows or objects to collect selecione linhas ou objetos para coletar +@msg select something to export selecione algo para exportar +@msg select something to export (or turn off selectedOnly) selecione algo para exportar (ou desative selectedOnly) +@msg select text to redefine the style from selecione o texto a partir do qual redefinir o estilo +@msg select the art and, on top of it, the mask object selecione a arte e, sobre ela, o objeto de máscara +@msg select the art for the design selecione a arte para o projeto +@msg select the art for the pattern tile selecione a arte para o ladrilho do padrão +@msg select the art to add first selecione primeiro a arte a adicionar +@msg select the art to collect (or give its ids) selecione a arte a coletar (ou informe os ids dela) +@msg select the art to weed selecione a arte a descascar +@msg select the content and a path on top selecione o conteúdo e um caminho por cima +@msg select the images to relink, or pass ids selecione as imagens a revincular, ou passe ids +@msg select the images, or pass ids selecione as imagens, ou passe ids +@msg select the linked images or placed documents, or pass ids selecione as imagens vinculadas ou os documentos colocados, ou passe ids +@msg select the object to copy from selecione o objeto do qual copiar +@msg select the objects (or give their ids) selecione os objetos (ou informe os ids deles) +@msg select the objects to attach to the path selecione os objetos a anexar ao caminho +@msg select the objects to bind selecione os objetos a vincular +@msg select two or more objects to intertwine selecione dois ou mais objetos para entrelaçar +@msg select two or more slices to combine selecione duas ou mais fatias para combinar +@msg select type on a path selecione texto em um caminho +@msg select type on a path or area type selecione texto em um caminho ou texto de área +@msg selection `{m}`: one or each selection `{m}`: one ou each +@msg settings is an object of document.exportForScreens params settings é um objeto com parâmetros de document.exportForScreens +@msg settings must be an object settings deve ser um objeto +@msg show `{show}`: all, missing, modified or embedded show `{show}`: all, missing, modified ou embedded +@msg show must be true or false show deve ser true ou false +@msg side is left or right, not `{v}` side é left ou right, não `{v}` +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size deve ser "small", "medium", "large" ou um número de pontos acima de 0 até {_1} +@msg size must be positive size deve ser positivo +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode é "fixed" ou "pressure", não `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode é "fixed" ou "pressure", não {other} +@msg sizeVariation must be 0 or more sizeVariation deve ser 0 ou mais +@msg smallCaps must be true or false smallCaps deve ser true ou false +@msg sort `{x}`: name, kind or status sort `{x}`: name, kind ou status +@msg source paths are too large for Shaper os caminhos de origem são grandes demais para o Formador +@msg source text transform is collapsed or unrepresentable a transformação do texto de origem é degenerada ou não pode ser representada +@msg spot, mode and group apply to solid colours only spot, mode e group se aplicam somente a cores sólidas +@msg status `{s}`: linked, embedded, missing or modified status `{s}`: linked, embedded, missing ou modified +@msg steps must be 1..1000 steps deve ser 1..1000 +@msg steps must be 1..{MAX_STEPS} steps deve ser 1..{MAX_STEPS} +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) parada {i}: `plates` deve conter quatro números de 0 a 1 (C, M, Y, K) +@msg stopped before the archive was finished interrompido antes de o arquivo compactado ficar pronto +@msg strokeMode must be within|along|across, got {m} strokeMode deve ser within|along|across, recebido {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions deve ser um objeto com opções de stroke.set +@msg strokeWidth must be a positive number of pixels strokeWidth deve ser um número positivo de pixels +@msg subpath {si} is not an open subpath o subcaminho {si} não é um subcaminho aberto +@msg subpaths required when joining the same path subpaths é obrigatório ao unir o mesmo caminho +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict deve ser "merge" ou "add", não {v} +@msg table must be true or false table deve ser true ou false +@msg takes one name aceita um único nome +@msg target text transform is collapsed or unrepresentable a transformação do texto de destino é degenerada ou não pode ser representada +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text deve ser {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} +@msg text transform is collapsed or unrepresentable a transformação do texto é degenerada ou não pode ser representada +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces deve ser um número inteiro de 2 a 100 +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`: use "{_1}" ou "{_2}" +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: informe {path} ou {name, dataBase64} de um arquivo de imagem, ou uma textura como effect.list a fornece +@msg textureImage: {e} textureImage: {e} +@msg that makes more than {MAX_PIECES} slices isso gera mais de {MAX_PIECES} fatias +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer isso gera peças demais (no máximo 100000) ou etapas de tracejado demais: simplifique o padrão de tracejado ou aumente os traços +@msg that object is not in the overlap esse objeto não está na sobreposição +@msg the Affinity document has no page with a size o documento do Affinity não tem nenhuma página com tamanho +@msg the CMYK image couldn't be written não foi possível gravar a imagem CMYK +@msg the Layers panel is not shown o painel Camadas não está visível +@msg the Layers panel's search, filter or Show Layers Only hides its row a pesquisa, o filtro ou Mostrar Somente Camadas do painel Camadas oculta a linha dele +@msg the Objects on Path group is hidden or locked o grupo Objetos no Caminho está oculto ou bloqueado +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected as configurações de segurança do PDF foram salvas sem as senhas: informe openPassword ou permissionsPassword novamente, ou security sem passwordRequired para exportar sem proteção +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) o arquivo RTF tem parágrafos ou mudanças de formatação demais para ser colocado como texto (no máximo {MAX_PARAGRAPHS} parágrafos e {MAX_RUNS} trechos) +@msg the RTF file is too large to place as type (at most {_1} MB) o arquivo RTF é grande demais para ser colocado como texto (no máximo {_1} MB) +@msg the SVG expands to more than {0} nested SVG images o SVG se expande em mais de {0} imagens SVG aninhadas +@msg the SVG expands to more than {0} objects o SVG se expande em mais de {0} objetos +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes o arquivo SVG tem mais de {MAX_IMPORT_BYTES} bytes +@msg the SVG has no drawable objects o SVG não tem objetos desenháveis +@msg the SVGZ file unpacks to more than {_1} MB o arquivo SVGZ é descompactado em mais de {_1} MB +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place o documento do Word tem mais de {MAX_PAGES} páginas de propriedades, demais para colocar +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place o documento do Word tem mais de {MAX_RUNS} trechos de propriedades de caractere ou de parágrafo, demais para colocar +@msg the Word document has more than {_1} MB of formatting properties, too many to place o documento do Word tem mais de {_1} MB de propriedades de formatação, demais para colocar +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) o documento do Word tem texto ou formatação demais para ser colocado como texto (no máximo {MAX_PARAGRAPHS} parágrafos, {MAX_RUNS} trechos e {_1} MB de texto) +@msg the Word document has too much text to place as type (at most {_1} MB) o documento do Word tem texto demais para ser colocado como texto (no máximo {_1} MB) +@msg the Word document is encrypted or protected with a password; save it without one to place it o documento do Word está criptografado ou protegido por senha; salve-o sem senha para colocá-lo +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) o documento do Word é grande demais para colocar (o fluxo {name} dele contém mais de {_1} MB) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off o documento do Word foi salvo pela última vez com salvamento rápido, que o VectorCraft não lê; salve-o novamente com o salvamento rápido desativado +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document o documento do Word foi salvo pelo Word 6.0 ou pelo Word 95, que o VectorCraft não lê; salve-o como um documento do Word 97-2003 +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place o texto do documento do Word é formado por mais de {MAX_PIECES} partes, demais para colocar +@msg the XML operation needs an XML or XMLList value a operação XML precisa de um valor XML ou XMLList +@msg the XPath expression does not evaluate to a node-set a expressão XPath não resulta em um conjunto de nós +@msg the XPath expression ends early a expressão XPath termina antes da hora +@msg the XPath expression has an unmatched ) a expressão XPath tem um ) sem par +@msg the XPath expression has an unmatched ] a expressão XPath tem um ] sem par +@msg the XPath expression has an unmatched closing bracket a expressão XPath tem um colchete de fechamento sem par +@msg the XPath expression selects a namespace node, which is not an XML value a expressão XPath seleciona um nó de namespace, que não é um valor XML +@msg the XPath expression selects the root node, which is not an XML value a expressão XPath seleciona o nó raiz, que não é um valor XML +@msg the XPath function {name} needs a node-set a função XPath {name} precisa de um conjunto de nós +@msg the XPath function {name} takes the wrong number of arguments a função XPath {name} recebe o número errado de argumentos +@msg the XPath name functions need a node-set as funções de nome do XPath precisam de um conjunto de nós +@msg the XPath prefix {shown} is not bound to a namespace o prefixo XPath {shown} não está vinculado a um namespace +@msg the XPath variable ${shown} is not bound a variável XPath ${shown} não está vinculada +@msg the action set ids are not a list os ids de conjunto de ações não são uma lista +@msg the active paint is not a gradient a pintura ativa não é um gradiente +@msg the argument list is not an object a lista de argumentos não é um objeto +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size as pranchetas não cabem na tela: use menos pranchetas, menos espaçamento ou um tamanho menor +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out a arte é grande demais para EPS: o que vem depois de 256 MB de PostScript foi omitido +@msg the bitmaps hold more pixels than one file may os bitmaps contêm mais pixels do que um arquivo pode conter +@msg the character's size can't be measured não é possível medir o tamanho do caractere +@msg the code runs past its last instruction o código passa da última instrução +@msg the compound shape is too complex to expand a forma composta é complexa demais para expandir +@msg the container of the listed objects is gone o contêiner dos objetos listados não existe mais +@msg the copy `{user_id}` can't be read as a library a cópia `{user_id}` não pode ser lida como uma biblioteca +@msg the copy would leave the canvas a cópia sairia da tela +@msg the curve tolerance must be from 0.01 to 10 points a tolerância de curva deve estar entre 0.01 e 10 pontos +@msg the custom dictionary couldn't be saved to {_1}: {e} não foi possível salvar o dicionário personalizado em {_1}: {e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is o dicionário personalizado {_1} não pode ser lido ({why}), então fica como está +@msg the cut cannot be made on this art: {e} o corte não pode ser feito nesta arte: {e} +@msg the data isn't an EMF or WMF picture os dados não são uma figura EMF nem WMF +@msg the data isn't an EPS or PostScript picture os dados não são uma figura EPS nem PostScript +@msg the deferred paste is no longer available a colagem adiada não está mais disponível +@msg the design is too big to draw at every data point of this graph o projeto é grande demais para ser desenhado em cada ponto de dados deste gráfico +@msg the design is too big to draw in every column of this graph o projeto é grande demais para ser desenhado em cada coluna deste gráfico +@msg the dialog it was opened over is no longer open a caixa de diálogo sobre a qual foi aberto não está mais aberta +@msg the dictionary stack overflowed a pilha de dicionários estourou +@msg the document already has an SVG filter named {name} o documento já tem um filtro SVG chamado {name} +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it o documento tem outro filtro SVG chamado {name}: use svgFilter.apply, ou svgFilter.define com `replace` para editá-lo +@msg the document has no SVG filter named {name} o documento não tem nenhum filtro SVG chamado {name} +@msg the document has no SVG filter named {r} o documento não tem nenhum filtro SVG chamado {r} +@msg the document has no color swatches o documento não tem amostras de cor +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 as amostras do documento contêm {_1} cores; a redução do documento aceita no máximo 255 +@msg the editing data's hash has no place o hash dos dados de edição não tem lugar +@msg the entities the document declares expand to too much text as entidades que o documento declara se expandem em texto demais +@msg the envelope's content has no bounds o conteúdo do envelope não tem limites +@msg the envelope's mesh is malformed a malha do envelope está malformada +@msg the file holds no `variableSets` element o arquivo não contém nenhum elemento `variableSets` +@msg the file is larger than {_1} MB o arquivo tem mais de {_1} MB +@msg the file is too large o arquivo é grande demais +@msg the file isn't valid SVG: {e} o arquivo não é um SVG válido: {e} +@msg the file names no plate o arquivo não indica nenhuma placa +@msg the file was updated or encrypted already o arquivo já foi atualizado ou criptografado +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) o primeiro caractere de uma linha não tem caractere anterior para o kerning: ele só se move para cima e para baixo (dy) +@msg the font {_1} is not available a fonte {_1} não está disponível +@msg the format has no file extension o formato não tem extensão de arquivo +@msg the function's prototype is not an object o protótipo da função não é um objeto +@msg the graphic can't be read: {e} não é possível ler o gráfico: {e} +@msg the graphic is empty o gráfico está vazio +@msg the graphic's data is damaged os dados do gráfico estão danificados +@msg the graphic's document has no artboard o documento do gráfico não tem prancheta +@msg the guide is hidden or locked a guia está oculta ou bloqueada +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them o cabeçalho `{cell}` de `{name}` seria lido de volta como a variável `{other}`: renomeie uma delas +@msg the image `{path}` isn't followed: {r} a imagem `{path}` não é seguida: {r} +@msg the image has no embedded pixels a imagem não tem pixels incorporados +@msg the image is empty a imagem está vazia +@msg the image is fully transparent a imagem é totalmente transparente +@msg the image is not on an artboard; pass rect a imagem não está em uma prancheta; passe rect +@msg the image's pixels are missing faltam os pixels da imagem +@msg the image's pixels aren't in the document os pixels da imagem não estão no documento +@msg the image's pixels can't be read: {e} não é possível ler os pixels da imagem: {e} +@msg the image's transparency doesn't fit it a transparência da imagem não corresponde a ela +@msg the job would print more than {MAX_PAGES} pages o trabalho imprimiria mais de {MAX_PAGES} páginas +@msg the job would print more than {_1} pages o trabalho imprimiria mais de {_1} páginas +@msg the layers hold no art to mask as camadas não contêm arte para mascarar +@msg the layout moved o layout mudou de lugar +@msg the library has a group `{name}` already a biblioteca já tem um grupo `{name}` +@msg the library has no group `{name}` a biblioteca não tem nenhum grupo `{name}` +@msg the library has no item `{item}` a biblioteca não tem nenhum item `{item}` +@msg the library has no item `{other}` a biblioteca não tem nenhum item `{other}` +@msg the library holds {MAX_GROUPS} groups already a biblioteca já contém {MAX_GROUPS} grupos +@msg the library holds {MAX_ITEMS} items of this kind already a biblioteca já contém {MAX_ITEMS} itens deste tipo +@msg the library's file is outside the read roots o arquivo da biblioteca está fora das raízes de leitura +@msg the line is too long, or the paths it passes too many, to check a linha é longa demais, ou passa por caminhos demais, para verificar +@msg the method needs an XML value o método precisa de um valor XML +@msg the name is empty o nome está vazio +@msg the object has no fill, stroke or effect o objeto não tem preenchimento, traçado nem efeito +@msg the object has no graphic style (pass `name`) o objeto não tem estilo gráfico (passe `name`) +@msg the objects don't match the plan os objetos não correspondem ao plano +@msg the open and permissions passwords must differ a senha de abertura e a de permissões devem ser diferentes +@msg the operand stack overflowed a pilha de operandos estourou +@msg the page has no area a página não tem área +@msg the page's type in an unread object's box isn't in its page art o texto da página na caixa de um objeto não lido não está na arte da página +@msg the paint behind the proxy is not a gradient a pintura por trás do proxy não é um gradiente +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) a pintura não é um gradiente de forma livre (aplique um com paint.editGradient {kind}) +@msg the parameters or body do not form one function os parâmetros ou o corpo não formam uma única função +@msg the parent must be a layer or sublayer o pai deve ser uma camada ou subcamada +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it o filtro SVG colado {name} desenha de modo diferente do filtro do documento com esse nome, e nenhum nome livre serve para ele +@msg the path can't be extended o caminho não pode ser estendido +@msg the path can't be inside one of the objects to attach o caminho não pode estar dentro de um dos objetos a anexar +@msg the path has no segments to measure o caminho não tem segmentos para medir +@msg the path is empty o caminho está vazio +@msg the path is too short to shorten that much o caminho é curto demais para ser encurtado tanto assim +@msg the paths are too many or too tangled to find where they cross os caminhos são muitos ou emaranhados demais para encontrar onde se cruzam +@msg the pen table holds more than 255 pens a tabela de canetas contém mais de 255 canetas +@msg the picture has no objects a figura não tem objetos +@msg the plates can't be kept as placas não podem ser mantidas +@msg the position table does not match the instructions a tabela de posições não corresponde às instruções +@msg the program runs too long o programa é executado por tempo demais +@msg the program uses too much memory o programa usa memória demais +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes o texto do prompt tem mais de {MAX_PROMPT_BYTES} bytes +@msg the radix must be between 2 and 36 a base deve estar entre 2 e 36 +@msg the rectangle must have a width and a height o retângulo deve ter largura e altura +@msg the rectangle needs a size and must lie on the canvas o retângulo precisa de um tamanho e deve ficar na tela +@msg the rectangle or line reaches too many paths to check o retângulo ou a linha alcança caminhos demais para verificar +@msg the right side of 'in' is not an object o lado direito de 'in' não é um objeto +@msg the right side of 'instanceof' is not a function o lado direito de 'instanceof' não é uma função +@msg the selected art has no width a arte selecionada não tem largura +@msg the selected art is nested more than {MAX_DEPTH} levels deep a arte selecionada está aninhada em mais de {MAX_DEPTH} níveis +@msg the selected art is too large to measure a arte selecionada é grande demais para medir +@msg the selection draws nothing a seleção não desenha nada +@msg the selection has no area a seleção não tem área +@msg the selection has no art to export a seleção não tem arte para exportar +@msg the selection has no bounds a seleção não tem limites +@msg the selection has no graphic style a seleção não tem estilo gráfico +@msg the selection has no outlines to weed around a seleção não tem contornos em volta dos quais descascar +@msg the slice would leave the canvas a fatia sairia da tela +@msg the spot colour couldn't be added não foi possível adicionar a cor especial +@msg the subpath has no ends to extend (it is closed or a single point) o subcaminho não tem extremidades para estender (é fechado ou é um único ponto) +@msg the symbols are nested too deeply to expand their instances os símbolos estão aninhados em níveis demais para expandir as instâncias +@msg the text file is too large to place as type (at most {_1} MB) o arquivo de texto é grande demais para ser colocado como texto (no máximo {_1} MB) +@msg the text is empty o texto está vazio +@msg the text is over {_1} MB o texto tem mais de {_1} MB +@msg the top object must be a path o objeto superior deve ser um caminho +@msg the trace was cancelled o rastreamento foi cancelado +@msg the trailer has no size o trailer não tem tamanho +@msg the type object is flattened to nothing o objeto de texto é achatado até não sobrar nada +@msg the value bind was called on is not a function o valor no qual bind foi chamado não é uma função +@msg the value is not a constructor o valor não é um construtor +@msg the value is not a function o valor não é uma função +@msg the value is not a valid XML name o valor não é um nome XML válido +@msg the value is not an XML attribute name o valor não é um nome de atributo XML +@msg the value is not an XML name o valor não é um nome XML +@msg the value of axis `{tag}` must be a number or null o valor do eixo `{tag}` deve ser um número ou null +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets a biblioteca de variáveis declara mais de {MAX_LIBRARY_DATASETS} conjuntos de dados +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities a biblioteca de variáveis declara mais de {MAX_LIBRARY_ENTITIES} entidades +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables a biblioteca de variáveis declara mais de {MAX_LIBRARY_VARIABLES} variáveis +@msg the variable library declares no variables a biblioteca de variáveis não declara nenhuma variável +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes a biblioteca de variáveis tem mais de {MAX_LIBRARY_BYTES} bytes +@msg the variable library is not UTF-8 a biblioteca de variáveis não está em UTF-8 +@msg the variable library is not well-formed XML: {e} a biblioteca de variáveis não é XML bem formado: {e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep a biblioteca de variáveis aninha elementos em mais de {MAX_LIBRARY_NESTING} níveis +@msg the variable library's document type declaration holds something other than entities of plain text a declaração de tipo de documento da biblioteca de variáveis contém algo além de entidades de texto simples +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes as entidades da biblioteca de variáveis se expandem em mais de {MAX_LIBRARY_BYTES} bytes +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines as linhas de descasque cruzam os contornos da arte vezes demais para medir; use menos linhas +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap as linhas de descasque passam perto demais de segmentos de contorno demais para medir; use menos linhas ou um espaço menor +@msg the weeding lines would be more than {max_pieces} pieces as linhas de descasque teriam mais de {max_pieces} peças +@msg the window has no dock a janela não tem dock +@msg the written PDF has no catalog o PDF gravado não tem catálogo +@msg the written PDF has no cross-reference table o PDF gravado não tem tabela de referência cruzada +@msg the written PDF's metadata can't be edited não é possível editar os metadados do PDF gravado +@msg the {_1} plate lies outside the file a placa {_1} fica fora do arquivo +@msg there are no colour swatches to draw não há amostras de cor para desenhar +@msg there is already a design named `{name}` já existe um projeto chamado `{name}` +@msg there is already a design named `{to}` já existe um projeto chamado `{to}` +@msg there is already a toolbar named `{name}` já existe uma barra de ferramentas chamada `{name}` +@msg there is already a toolbar named `{to}` já existe uma barra de ferramentas chamada `{to}` +@msg there is no library: create one first não há biblioteca: crie uma primeiro +@msg these paths are too degenerate for Shaper estes caminhos são degenerados demais para o Formador +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that este é um arquivo DXF binário, que não pode ser lido: salve-o pelo aplicativo de CAD como DXF ASCII e abra esse arquivo +@msg this warp's points can't be edited: use Reset with Mesh first os pontos desta deformação não podem ser editados: use Redefinir com Malha primeiro +@msg thumbnail: no page {v} (the {what} has {count}) thumbnail: nenhuma página {v} ({what} tem {count}) +@msg tileRange of {what}: {e} tileRange de {what}: {e} +@msg title can't be empty title não pode ficar vazio +@msg to (the new name) is required to (o novo nome) é obrigatório +@msg to must be fill or stroke to deve ser fill ou stroke +@msg to must be smooth or corner to deve ser smooth ou corner +@msg toExponential() digits must be between 0 and 20 os dígitos de toExponential() devem estar entre 0 e 20 +@msg toFixed() digits must be between 0 and 20 os dígitos de toFixed() devem estar entre 0 e 20 +@msg toISOString is not a function toISOString não é uma função +@msg toPrecision() argument must be between 1 and 21 o argumento de toPrecision() deve estar entre 1 e 21 +@msg toSource: a getter cannot be written as source toSource: um getter não pode ser gravado como código-fonte +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource: uma matriz com propriedades além dos elementos, com uma propriedade não enumerável ou com outro protótipo não pode ser gravada como código-fonte +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource: um objeto com uma propriedade não enumerável, ou cujo protótipo não é Object.prototype, não pode ser gravado como código-fonte +@msg toSource: the source is too long toSource: o código-fonte é longo demais +@msg toSource: the value contains itself toSource: o valor contém a si mesmo +@msg toSource: the value has too many entries toSource: o valor tem entradas demais +@msg toSource: the value is nested too deep toSource: o valor está aninhado em níveis demais +@msg toString is not a function toString não é uma função +@msg too large for a zip archive: package to a folder grande demais para um arquivo zip: empacote em uma pasta +@msg too many Shaper edits edições demais do Formador +@msg too many `points` (at most 100000) `points` demais (no máximo 100000) +@msg too many arguments argumentos demais +@msg too many characters: at most {MAX_CHARS} caracteres demais: no máximo {MAX_CHARS} +@msg too many dots for this size: lower the frequency pontos demais para este tamanho: reduza a frequência +@msg too many filters over one file filtros demais sobre um único arquivo +@msg too many overlapping paths for Shaper caminhos sobrepostos demais para o Formador +@msg too many quantifiers quantificadores demais +@msg too many saved clips recortes salvos demais +@msg too many saved graphics states estados gráficos salvos demais +@msg toolbar (a toolbar's name) must be a string toolbar (o nome de uma barra de ferramentas) deve ser uma string +@msg toolbar name can't be empty o nome da barra de ferramentas não pode ficar vazio +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}: "flatten" (o padrão) ou "omit" +@msg two saved selections would be named `{_1}` duas seleções salvas se chamariam `{_1}` +@msg type dimension cannot be represented with this text transform a cota de texto não pode ser representada com esta transformação de texto +@msg type dimension must be finite a cota de texto deve ser finita +@msg unexpected resources recursos inesperados +@msg unknown CMYK profile `{name}` perfil CMYK desconhecido `{name}` +@msg unknown File Info field `{k}` campo de Informações do Arquivo desconhecido `{k}` +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) recurso OpenType desconhecido `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefixe com - para desativar; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom e aalt aceitam o número de uma alternativa como `tag=N` com N de 0 a {_1}, como `salt=2`) +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) recurso OpenType desconhecido `{t}` em featureChanges (as tags que `features` aceita) +@msg unknown PDF preset `{name}` (presets: {_1}) predefinição de PDF desconhecida `{name}` (predefinições: {_1}) +@msg unknown Warp style estilo de Deformação desconhecido +@msg unknown alignToPath `{a}` alignToPath desconhecido `{a}` +@msg unknown arrowhead {v} ponta de seta desconhecida {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) chave de artboardLayout desconhecida `{k}` (layout, columns, spacing, rightToLeft) +@msg unknown attribute `{attribute}` atributo desconhecido `{attribute}` +@msg unknown attribute `{k}` atributo desconhecido `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) eixo desconhecido `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` modo de mesclagem desconhecido `{b}` +@msg unknown blend mode {b} modo de mesclagem desconhecido {b} +@msg unknown brush type `{o}` tipo de pincel desconhecido `{o}` +@msg unknown category `{c}` categoria desconhecida `{c}` +@msg unknown category `{c}` ({_1}) categoria desconhecida `{c}` ({_1}) +@msg unknown character `{c}` caractere desconhecido `{c}` +@msg unknown character style number field campo numérico de estilo de caractere desconhecido +@msg unknown dither `{d}` (diffusion, pattern, noise or none) dither desconhecido `{d}` (diffusion, pattern, noise ou none) +@msg unknown effect `{e}` efeito desconhecido `{e}` +@msg unknown end {e} end desconhecido {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) flattenerPreset desconhecido `{name}` (High Resolution, Medium Resolution, Low Resolution ou uma salva: veja flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) formato desconhecido `{f}` (vcswatches, gpl, ase ou css) +@msg unknown from {f} from desconhecido {f} +@msg unknown graph type `{t}` tipo de gráfico desconhecido `{t}` +@msg unknown grid colours `{name}` (one of {_1}) cores de grade desconhecidas `{name}` (uma de {_1}) +@msg unknown gridType `{t}` gridType desconhecido `{t}` +@msg unknown highlight `{v}` highlight desconhecido `{v}` +@msg unknown intent `{i}` intent desconhecido `{i}` +@msg unknown issue kind {id} tipo de problema desconhecido {id} +@msg unknown issue kind {v} tipo de problema desconhecido {v} +@msg unknown knockout state {k} estado de knockout desconhecido {k} +@msg unknown language `{language}` (one of {_1}) idioma desconhecido `{language}` (um de {_1}) +@msg unknown language `{l}` (one of {_1}) idioma desconhecido `{l}` (um de {_1}) +@msg unknown method `{m}` ({_1}) método desconhecido `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) modo desconhecido `{other}` (gray, rgb, hsb, lab, cmyk ou web) +@msg unknown mode `{other}` (mono|cmyk) modo desconhecido `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) modo desconhecido `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) modo desconhecido `{other}` (set, add, subtract) +@msg unknown mode `{o}` modo desconhecido `{o}` +@msg unknown mode {m} modo desconhecido {m} +@msg unknown model `{other}` modelo desconhecido `{other}` +@msg unknown model `{other}` (keep, new or document) modelo desconhecido `{other}` (keep, new ou document) +@msg unknown op {o} op desconhecido {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back order desconhecido `{o}`: toggle, front, forward, backward ou back +@msg unknown overprints `{v}` (preserve, simulate or discard) overprints desconhecido `{v}` (preserve, simulate ou discard) +@msg unknown panel painel desconhecido +@msg unknown panel `{panel}` painel desconhecido `{panel}` +@msg unknown parameter `{key}` parâmetro desconhecido `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} parâmetro desconhecido `{within}settings.{format}`; {why} +@msg unknown parameter `{within}{key}`; {why} parâmetro desconhecido `{within}{key}`; {why} +@msg unknown pieLegend `{v}` (none, standard or wedges) pieLegend desconhecido `{v}` (none, standard ou wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) piePosition desconhecido `{v}` (even, ratio ou stacked) +@msg unknown pieSort `{v}` (none, all or first) pieSort desconhecido `{v}` (none, all ou first) +@msg unknown plate `{n}` placa desconhecida `{n}` +@msg unknown preference `{k}` preferência desconhecida `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) perfil desconhecido {id} (veja stroke.widthProfile.list) +@msg unknown proof target `{t}` destino de prova desconhecido `{t}` +@msg unknown quotes key `{k}` (double, single) chave de quotes desconhecida `{k}` (double, single) +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) redução desconhecida `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform ou document) +@msg unknown resolution `{v}` resolução desconhecida `{v}` +@msg unknown rule `{rule}` ({_1}) regra desconhecida `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) regra desconhecida `{r}` (veja color.harmony) +@msg unknown setting `{k}` configuração desconhecida `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) forma desconhecida `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) spacing desconhecido `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) tileType desconhecido `{t}` ({_1}) +@msg unknown units unidades desconhecidas +@msg unknown units `{u}` unidades desconhecidas `{u}` +@msg unknown value axis `{v}` (left, right or both) eixo de valores desconhecido `{v}` (left, right ou both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) variação desconhecida `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` estilo de deformação desconhecido `{s}` +@msg unknown {key} `{v}` (none, short or full) {key} desconhecido `{v}` (none, short ou full) +@msg unknown {key} profile `{n}` perfil de {key} desconhecido `{n}` +@msg unknown {k} key `{key}` (size, position) chave de {k} desconhecida `{key}` (size, position) +@msg unmatched ')' ')' sem par +@msg unreadable object objeto ilegível +@msg unsafe destination file name nome de arquivo de destino não seguro +@msg unterminated character class classe de caracteres não terminada +@msg unterminated group grupo não terminado +@msg use paths or compound paths use caminhos ou caminhos compostos +@msg useArtboards must be true or false, not {v} useArtboards deve ser true ou false, não {v} +@msg value for `{var}` must match its variable kind o valor de `{var}` deve corresponder ao tipo da variável +@msg variable `{_1}` is already defined a variável `{_1}` já está definida +@msg variable `{name}` has no `trait` a variável `{name}` não tem `trait` +@msg variable `{name}` is already defined a variável `{name}` já está definida +@msg variable `{name}` is declared twice a variável `{name}` é declarada duas vezes +@msg variable `{to}` already exists a variável `{to}` já existe +@msg view is required view é obrigatório +@msg view must be one of {_1} view deve ser um de {_1} +@msg warpStyle can only edit an applied Warp effect warpStyle só pode editar um efeito de Deformação aplicado +@msg width and height must be more than 0 and at most {limit} width e height devem ser maiores que 0 e no máximo {limit} +@msg width factors must be finite os fatores de largura devem ser finitos +@msg width must be positive width deve ser positivo +@msg width out of range largura fora do intervalo +@msg width/height must be > 0 width/height devem ser > 0 +@msg wordGap and lineGap need area type in the selection wordGap e lineGap precisam de texto de área na seleção +@msg x and y must be finite numbers x e y devem ser números finitos +@msg x and y must be given together x e y devem ser informados juntos +@msg xpath needs its variables as an object xpath precisa das variáveis como um objeto +@msg xpath needs the expression as a string xpath precisa da expressão como uma string +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone precisa de uma âncora: encaixe ao lado de um painel com anchor, ou em uma nova coluna do dock com side (Start ou End) +@msg zoom must be positive zoom deve ser positivo +@msg {C}: no effect at index {list} {C}: nenhum efeito no índice {list} +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} é protegida e permanece +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} é protegida: salve as configurações com outro nome +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED}, e as configurações e o cache dele não podem ser movidos de volta de {_1} para {_2}: {e} +@msg {LABEL} produced an empty result {LABEL} produziu um resultado vazio +@msg {_1} MB of document, at most {_2} MB {_1} MB de documento, no máximo {_2} MB +@msg {_1} MB of images, at most {_2} MB {_1} MB de imagens, no máximo {_2} MB +@msg {_1} bytes of RGBA data for a {width} × {height} image {_1} bytes de dados RGBA para uma imagem de {width} × {height} +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} não pode ser lido, ou mudou desde que foi lido: revincule-o ou atualize-o primeiro +@msg {_1} can't go into {into} {_1} não pode entrar em {into} +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} não pode limitar a impressão a baixa resolução: escolha PDF 1.4 ou posterior, ou permita a impressão em alta resolução +@msg {_1} files can't be opened yet os arquivos {_1} ainda não podem ser abertos +@msg {_1} files can't be password-protected: remove the passwords or choose no standard os arquivos {_1} não podem ser protegidos por senha: remova as senhas ou não escolha nenhum padrão +@msg {_1} files can't be {_2} os arquivos {_1} não podem ser {_2} +@msg {_1} files can't carry editing data: turn off preserveEditing os arquivos {_1} não podem conter dados de edição: desative preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers os arquivos {_1} não podem ter camadas de PDF: desative createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one os arquivos {_1} precisam de uma data de criação, e esta plataforma não tem relógio para fornecê-la +@msg {_1} is not a folder: {e} {_1} não é uma pasta: {e} +@msg {_1} isn't a path {_1} não é um caminho +@msg {_1} options: {e} opções de {_1}: {e} +@msg {_1} paints nothing to rasterize {_1} não pinta nada para rasterizar +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) as senhas de {_1} podem ter até {MAX_LEGACY_PASSWORD} caracteres ASCII (o PDF 1.7 e posteriores aceitam qualquer caractere) +@msg {_1} with linked images or an image map writes several files: use document.export {_1} com imagens vinculadas ou um mapa de imagem grava vários arquivos: use document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} grava aqui um arquivo por prancheta: use document.export, ou indique uma prancheta +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} pixels é grande demais para {_3} (no máximo {max} pixels de lado): reduza a escala +@msg {_1}: belongs to another account {_1}: pertence a outra conta +@msg {_1}: not a folder of its own {_1}: não é uma pasta própria +@msg {_1}: not a plain file {_1}: não é um arquivo comum +@msg {_1}: {_2} and {_3} are in the way {_1}: {_2} e {_3} estão no caminho +@msg {code}: {0} {code}: {0} +@msg {dest}: {e} {dest}: {e} +@msg {dir}: {e} {dir}: {e} +@msg {doc} has no folder {doc} não tem pasta +@msg {file} isn't linked {file} não está vinculado +@msg {file} names more than one linked JavaScript file {file} indica mais de um arquivo JavaScript vinculado +@msg {folder} is not a package folder {folder} não é uma pasta de pacote +@msg {folder}: more than {max} files and folders {folder}: mais de {max} arquivos e pastas +@msg {folder}: {e} {folder}: {e} +@msg {g} is not a Live Paint group {g} não é um grupo de Pintura ao Vivo +@msg {id} is not a slice {id} não é uma fatia +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} deve ser 0–{MAX_ADD_AROUND} pt +@msg {key} must be [x,y] {key} deve ser [x,y] +@msg {key} must be a percentage {min}..100 {key} deve ser uma porcentagem {min}..100 +@msg {key} must be a positive number, not {n} {key} deve ser um número positivo, não {n} +@msg {key} must be a positive number, not {v} {key} deve ser um número positivo, não {v} +@msg {key} must be one of {_1} {key} deve ser um de {_1} +@msg {key} must be {_1}, got {s} {key} deve ser {_1}, recebido {s} +@msg {key} must name a floating panel {key} deve indicar um painel flutuante +@msg {key} must name a panel in another group {key} deve indicar um painel de outro grupo +@msg {key} must name a panel that shows {key} deve indicar um painel visível +@msg {key} must name a registered panel {key} deve indicar um painel registrado +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) os arquivos {kind} não têm transparência, e o arquivo teria {what}: achate-a primeiro (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) as imagens {kind} não podem ser abertas (veja document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` não é uma das opções +@msg {k} has control characters{_1} {k} tem caracteres de controle{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} é longo demais (no máximo {MAX_TEXT} caracteres) +@msg {k} must be > 0 {k} deve ser > 0 +@msg {k} must be a number (pt) or a length such as "210 mm" {k} deve ser um número (pt) ou um comprimento como "210 mm" +@msg {k} must be a number from {min} to {max} {k} deve ser um número de {min} a {max} +@msg {k} must be a number or "auto" {k} deve ser um número ou "auto" +@msg {k} must be a string {k} deve ser uma string +@msg {k} must be text {k} deve ser texto +@msg {k} must be true or false {k} deve ser true ou false +@msg {k} must be {{size?, position?}} {k} deve ser {{size?, position?}} +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: o caminho de uma ponta de seta personalizada precisa de uma área para preencher e de no máximo {most} pontos de ancoragem e {most} subcaminhos, com cada ponto de ancoragem a até {far} unidades de ponta da extremidade dela +@msg {k}: bad custom arrowhead path: {e} {k}: caminho de ponta de seta personalizada inválido: {e} +@msg {k}: bad custom arrowhead: {e} {k}: ponta de seta personalizada inválida: {e} +@msg {k}: can't read `{s}` as a length {k}: não é possível ler `{s}` como um comprimento +@msg {k}: custom must be a name {k}: custom deve ser um nome +@msg {label} must be a number at most {max} {label} deve ser um número de no máximo {max} +@msg {label} must be a number from -1296 to 1296 {label} deve ser um número de -1296 a 1296 +@msg {label} must be a number from 1 to 10000 {label} deve ser um número de 1 a 10000 +@msg {name} `{s}`: {ids} {name} `{s}`: {ids} +@msg {name} is built in and can't be deleted {name} é integrado e não pode ser excluído +@msg {name} must be a number {name} deve ser um número +@msg {name} must be {lo}–{hi}{unit} (got {v}) {name} deve ser {lo}–{hi}{unit} (recebido {v}) +@msg {name}: CRC or size mismatch {name}: CRC ou tamanho não confere +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}: só foi possível ler a prévia incorporada deste arquivo do Affinity; use Arquivo › Abrir para vê-la com o aviso, ou exporte SVG ou PDF pelo Affinity antes de colocar +@msg {name}: {e} {name}: {e} +@msg {native}; its embedded preview can't be read either ({e}) {native}; a prévia incorporada também não pode ser lida ({e}) +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: copy só aceita um arquivo DCS quando as placas estão dentro dele; revincule-o no local onde estão os arquivos de placa +@msg {path}: larger than {_1} MB {path}: mais de {_1} MB +@msg {shown} is not an XPath 1.0 function {shown} não é uma função do XPath 1.0 +@msg {std} files are CMYK, and the file would have colours in {_1} os arquivos {std} são CMYK, e o arquivo teria cores em {_1} +@msg {std} files embed their fonts, and the file would have one that isn't os arquivos {std} incorporam as fontes, e o arquivo teria uma que não está incorporada +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} é o arquivo do qual este documento foi lido, e a leitura deixou coisas de fora ({what}): gravar por cima dele as perderia de vez. Grave outro arquivo, ou passe acknowledgeLoss: true para substituí-lo mesmo assim +@msg {ty} brushes need selected art (or params.{k}) os pincéis {ty} precisam de arte selecionada (ou de params.{k}) +@msg {v} is not an asset id {v} não é um id de ativo +@msg {v} names no layer or sublayer {v} não indica nenhuma camada ou subcamada +@msg {what} can't be intertwined {what} não pode ser entrelaçado +@msg {what} doesn't fit the paper with its bleed and marks {what} não cabe no papel com a sangria e as marcas +@msg {what} is not a function {what} não é uma função +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what} imprimiria mais de {MAX_COPIES_PER_SHEET} cópias em uma folha: aumente a escala +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what} seria impresso em mais de {MAX_TILES} ladrilhos: reduza a escala +@msg {what} {i} out of range {what} {i} fora do intervalo Banner Hems and Grommets… Bainhas e ilhoses de banner… Banner Hems and Grommets Bainhas e ilhoses de banner Hem Bainha Grommets Ilhoses Grommet Inset Distância dos ilhoses à borda Diameter Diâmetro +@msg `{key}` must be from 0.01 to 1000 pt `{key}` deve estar entre 0.01 e 1000 pt +@msg select the banner, or mark every artboard selecione o banner ou marque todas as pranchetas +@msg the banner has no width or height o banner não tem largura ou altura +@msg the grommet inset is too large for the banner a distância dos ilhoses à borda é grande demais para o banner +@msg the hem is too deep for the banner a bainha é profunda demais para o banner +@msg the spacing makes more than {MAX_GROMMETS} grommets o espaçamento gera mais de {MAX_GROMMETS} ilhoses +@msg there is no banner to mark não há banner para marcar +@msg turn on at least one side ative pelo menos um lado +@msg with no hem and no grommets there is nothing to mark sem bainha e sem ilhoses não há nada para marcar +@msg {saving} failed: {e} {saving} falhou: {e} diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..77f453df94 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -7,7 +7,7 @@ Per-language status of VectorCraft's interface. How the catalogs work and how to [target-app-parity.md](target-app-parity.md). **How it is measured:** rows with a translation in each `crates/ui-egui/src/i18n/.tsv`, counted on -origin/main `01e165af`, except the rows that give a count date: those were counted on the `main` commit named in the row (the other dated rows on `main` at `cb0a7d9`, 2026-10-21; the Portuguese row on `main` at `d26dd933a`, 2026-10-31). +origin/main `01e165af`, except the rows that give a count date: those were counted on the `main` commit named in the row (the rows dated 2026-10-21 on `main` at `cb0a7d9`). The reference is the French catalog, which was complete when counted: 3,416 interface strings (menus, command labels, `tl!` literals: panels, dialogs, tooltips, Preferences) plus 604 `@msg` status and error messages, 4,020 in all. A language's % is its rows over 4,020 (capped at 100% per part). A missing @@ -32,7 +32,7 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker | Hindi | `hi` | 8,782 rows (2026-11-13): 3,014 `@msg`, 47 `@msgplural`, 41 `@plural`; no gaps in the untranslated report | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; 2026-11-13 wording pass on messages and longer labels, with a glossary-term test; wider proofread welcome | complete catalog, machine-assisted draft | native review, layout and web font | | Arabic | `ar` | 0 | none | document text: bidi layout and Paragraph Direction; interface: no right-to-left layout, no Arabic shaping in egui | no | none | 25–40 | | French | `fr` | 4,020 / 4,020 (100%) | all | Latin | no | full | — (review) | -| Portuguese | `pt-br` | 6,106 catalog entries (counted 2026-10-31 on `main` at `d26dd933a`): menus, `tl!` literals and messages complete when counted; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) | +| Portuguese | `pt-br` | 9,260 rows, 3,445 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 39 missing messages for it; one/other plurals | all | Latin | contributor is a Brazilian Portuguese speaker; further native review welcome | full | — (review) | | Indonesian | `id` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one plural form | dialogs and tooltips all; 750 message rows (French has 796) | Latin | no | full | — (review) | | Japanese | `ja` | 4,004 / 4,020 (99.6%) | all | CJK UI font as above; Japanese IME; vertical type partial; Japanese crop marks and composition settings | no | full | — (review) | | German | `de` | 3,952 / 4,020 (98%; menus, literals and messages complete when counted) | all (602 of 604 message rows; landed 2026-10-10) | Latin | contributor-written, not reviewed | full | — (review) | From 64f80a5eb5d48d2bfd092c825401f2db4000e0a4 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:17:57 -0800 Subject: [PATCH 22/33] Tests: the Brushes panel lists the second brush of a repeated name as Twin 2 Reading a brush library now numbers the second and later brushes of a repeated name, so the panel lists Twin and Twin 2 rather than two Twin rows. The test expects those rows, and checks that rows of one name that reach the panel unnumbered still preview and drag the first. --- crates/ui-egui/src/panels/brushes/tests.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/ui-egui/src/panels/brushes/tests.rs b/crates/ui-egui/src/panels/brushes/tests.rs index 2d9305fc52..aeab927e23 100644 --- a/crates/ui-egui/src/panels/brushes/tests.rs +++ b/crates/ui-egui/src/panels/brushes/tests.rs @@ -822,26 +822,35 @@ fn previews_follow_the_swatches_images_and_pattern_options_their_art_uses() { } } -/// Of two brushes with one name, both rows preview and drag the first, the one `brush.get` gives -/// and a stroke that names it draws with. +/// Of two brushes with one name, the row of that name previews and drags the first, the one +/// `brush.get` gives and a stroke that names it draws with. Reading the library numbers the second +/// ("Twin 2"), so it gets a row of its own. #[test] fn rows_of_two_brushes_with_one_name_show_the_first() { let mut app = VectorcraftApp::new(Session::new(), Default::default()); app.run("file.new", json!({"width": 200, "height": 100})).unwrap(); + let mut twins: Vec = Vec::new(); app.session .edit("Brushes", |d, _| { let mut lib = vectorcraft_brush::library(d); let kinds: Vec<_> = ["Charcoal", "Dots"].iter().map(|n| lib.iter().find(|b| b.name == *n).unwrap().kind.clone()).collect(); - lib.extend(kinds.into_iter().map(|kind| Brush { name: "Twin".into(), kind })); + twins = kinds.into_iter().map(|kind| Brush { name: "Twin".into(), kind }).collect(); + lib.extend(twins.iter().cloned()); vectorcraft_brush::store(d, &lib); Ok(()) }) .unwrap(); - let twins: Vec = brushes(&mut app).0.into_iter().filter(|b| b.0 == "Twin").map(|b| b.1).collect(); - assert_eq!(twins, ["art", "scatter"], "the panel lists both"); + let rows: Vec<(String, String)> = brushes(&mut app).0.into_iter().filter(|b| b.0.starts_with("Twin")).map(|b| (b.0, b.1)).collect(); + assert_eq!(rows, [("Twin".to_string(), "art".to_string()), ("Twin 2".to_string(), "scatter".to_string())], "the panel lists both"); let got = app.run("brush.get", json!({"name": "Twin"})).unwrap(); let memo = document_brushes(app.session.active().unwrap()); let shown = by_name(&memo)["Twin"]; assert_eq!(shown.ty, "art"); assert_eq!(shown.def, got, "the row shows and drags what brush.get gives"); + // Rows of one name that reach the panel unnumbered still show the first. + let st = app.session.active().unwrap(); + let raw: Vec = twins.iter().map(|b| Shown::of(&st.doc, b)).collect(); + let shown = by_name(&raw)["Twin"]; + assert_eq!(shown.ty, "art"); + assert_eq!(shown.def, got, "the first of two rows of one name is the one shown"); } From c6211b35ab1fa8054226c7eb45ec766cd14c206f Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:17:34 -0800 Subject: [PATCH 23/33] Docs: the Linux title bar paragraphs describe the frame the desktop draws docs/control-protocol.md said the window has no OS decorations on Windows and Linux. On Linux and the BSDs the app bar is the title bar by default only on a Wayland session without server-side decorations, such as GNOME, in a window not opened through XWayland; elsewhere the desktop draws the frame unless VectorCraft Title Bar on Every Desktop or VECTORCRAFT_TITLEBAR=app turns the app frame on. The paragraph now says so. docs/development.md said the custom title bar is the Linux default. It now says where the app bar is the default title bar. The comment on CUSTOM_TITLEBAR in apps/vectorcraft/src/titlebar.rs made the same Windows and Linux claim and now names Windows only. --- apps/vectorcraft/src/titlebar.rs | 2 +- docs/control-protocol.md | 5 ++++- docs/development.md | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/vectorcraft/src/titlebar.rs b/apps/vectorcraft/src/titlebar.rs index f8007d612e..88dce26897 100644 --- a/apps/vectorcraft/src/titlebar.rs +++ b/apps/vectorcraft/src/titlebar.rs @@ -31,7 +31,7 @@ pub(crate) fn adapter_summary(info: &eframe::wgpu::AdapterInfo) -> String { format!("{} ({:?}, {:?})", info.name.trim(), info.backend, info.device_type) } -/// Windows and Linux: no OS title bar; the app bar is the title bar (`vectorcraft_ui_egui::titlebar`). +/// Windows: no OS title bar; the app bar is the title bar (`vectorcraft_ui_egui::titlebar`). /// macOS keeps its traffic lights over the integrated title strip. /// /// Used on macOS and Windows (where VectorCraft keeps today's frame regardless of the diff --git a/docs/control-protocol.md b/docs/control-protocol.md index 5cbf67f58a..0463351826 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -345,7 +345,10 @@ Tile Edge Color: Object → Pattern → Tile Edge Color… (`ui.tileEdgeColor`) `patternTileEdgeColor` (`prefs.set`), the colour pattern editing mode draws the tile edge and the swatch bounds in. A `color` with an alpha part (`#rrggbbaa` or `#rgba`) is an error, and the preference does not change. -Window title bar: on Windows and Linux the window has no OS decorations and the app bar is the title bar. Its +Window title bar: on Windows the window has no OS decorations and the app bar is the title bar. On Linux and the BSDs +the app bar is the title bar by default only on a Wayland session without server-side decorations, such as GNOME, in a +window not opened through XWayland. Elsewhere the desktop draws the frame and the app bar has no caption buttons, +unless VectorCraft Title Bar on Every Desktop or `VECTORCRAFT_TITLEBAR=app` turns the app frame on (see below). The caption buttons (Minimize, Maximize/Restore, Close) are 46 pt each. By default all three sit at the bar's right end. On Linux and the BSDs, when the desktop leaves the window's frame to the app (a Wayland session without server-side decorations, such as GNOME), the window is not opened through XWayland and `VECTORCRAFT_TITLEBAR` is unset, they diff --git a/docs/development.md b/docs/development.md index 05062e18b8..d3f0c7fe92 100644 --- a/docs/development.md +++ b/docs/development.md @@ -121,7 +121,7 @@ A file opened from the file manager (double-click, Open With, a drop on the exec ## Linux: Wayland and X11 -On GNOME/Wayland, enable **Preferences › User Interface › System Title Bar** and restart to use winit's font-free Adwaita window controls (`wayland-csd-adwaita-notitle`); the existing custom title bar remains the Linux default. On X11, on a Wayland compositor that draws server-side decorations such as KDE Plasma, and in a window opened through XWayland, enable **Preferences › User Interface › VectorCraft Title Bar on Every Desktop** and restart to use the app bar as the title bar instead of the desktop's frame. `VECTORCRAFT_TITLEBAR=app`, `=system` or `=desktop` chooses the frame for one run and takes precedence over System Title Bar and VectorCraft Title Bar on Every Desktop. The variable is case-sensitive and surrounding spaces are ignored; any other value, such as `unknown`, is ignored and does not override System Title Bar. +On GNOME/Wayland, enable **Preferences › User Interface › System Title Bar** and restart to use winit's font-free Adwaita window controls (`wayland-csd-adwaita-notitle`); the app bar is the title bar by default only on a Wayland compositor that leaves the window's frame to the app, such as GNOME's, in a window not opened through XWayland (see the drop and pen items below). On X11, on a Wayland compositor that draws server-side decorations such as KDE Plasma, and in a window opened through XWayland, enable **Preferences › User Interface › VectorCraft Title Bar on Every Desktop** and restart to use the app bar as the title bar instead of the desktop's frame. `VECTORCRAFT_TITLEBAR=app`, `=system` or `=desktop` chooses the frame for one run and takes precedence over System Title Bar and VectorCraft Title Bar on Every Desktop. The variable is case-sensitive and surrounding spaces are ignored; any other value, such as `unknown`, is ignored and does not override System Title Bar. The window runs natively on Wayland (eframe's `wayland` feature) and on X11. On machines with two GPUs it renders on the one the desktop runs on unless Preferences say otherwise; see [Desktop graphics processor](#desktop-graphics-processor) (#502). The system clipboard (`apps/vectorcraft/src/clipboard.rs`) is arboard with its `wayland-data-control` feature: From 09f5efad225294d87945b79ded169ae9d9c8c75f Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:25:25 -0800 Subject: [PATCH 24/33] i18n: Ukrainian translates 1,811 more status and error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit uk is in COMPLETE_MESSAGES, but the untranslated report listed status and error messages with no Ukrainian row, so with the interface in Ukrainian those messages showed in English. - uk.tsv gains 1,811 `@msg` rows. The translations are VectorCraft's own, written from the English meaning with the catalog's terms and typography (« » around interface names, ’ inside words). Placeholders, backticked names, command ids, double-quoted values and bare true, false and null stay as the English writes them. - The 41 rows that open #7211, #7214, #7116, #7330 and #817 add to uk.tsv are left out, so the catalog keeps one row per key when they merge (bundled_catalogs_are_consistent rejects a second row for a key). - The Ukrainian row of docs/localization-parity.md gives the catalog's counts. --- crates/ui-egui/src/i18n/uk.tsv | 1811 ++++++++++++++++++++++++++++++++ docs/localization-parity.md | 2 +- 2 files changed, 1812 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index a7b3ea2c2e..99e298d6b1 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -7098,3 +7098,1814 @@ breakText Glyphs Гліфи Grommets Люверси Grommet Inset Відступ люверсів від краю Diameter Діаметр +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions 'caller', 'callee' і 'arguments' не можна використовувати для функцій суворого режиму +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. Профіль підтримує один ефект «Обертання». Змініть натомість параметри наявного ефекту. +@msg Add Points at Crossings: a path went missing Додати точки на перетинах: контур зник +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} викликано для значення, яке не є Boolean +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB Зображення CMYK, відліки яких не вдалося прочитати в CMYK, імпортовано в RGB +@msg CSS options: {e} Параметри CSS: {e} +@msg CSV has a header but no data rows CSV має заголовок, але не має рядків даних +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark CSV містить байти NUL: збережіть його як UTF-8 або як UTF-16 з міткою порядку байтів +@msg CSV is empty CSV порожній +@msg CSV is larger than {MAX_CSV_BYTES} bytes CSV більший за {MAX_CSV_BYTES} байтів +@msg CSV is not UTF-16 CSV не є UTF-16 +@msg CSV is not UTF-8 CSV не є UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes CSV був би більшим за {MAX_CSV_BYTES} байтів +@msg Cannot write to the location of “{name}”; choose where to save it Не вдається записати в розташування «{name}»; виберіть, куди зберегти файл +@msg Choose original artwork, not a dimension's generated paths Виберіть вихідну графіку, а не створені розміром контури +@msg Contour: select paths or shapes Еквідистанта: виділіть контури або фігури +@msg Contour: the Offset Path effect is not available Еквідистанта: ефект «Зміщення контуру» недоступний +@msg Create needs fills, strokes or both Для створення потрібні заливки, обведення або те й інше +@msg DCS files can only be placed linked: use File › Place with Link on Файли DCS можна помістити лише як пов’язані: скористайтеся командою Файл › Помістити з увімкненим параметром «Зв’язати» +@msg DXF options: {e} Параметри DXF: {e} +@msg DXF scale must be a positive number up to {_1}, not {scale} Масштаб DXF має бути додатним числом не більше {_1}, а не {scale} +@msg DXF {field} `{s}`: {allowed} DXF {field} `{s}`: {allowed} +@msg Date.prototype method called on a value that is not a Date object Метод Date.prototype викликано для значення, яке не є об’єктом Date +@msg Dimension is locked or hidden Розмір заблоковано або приховано +@msg Dimension mode does not match its source Режим розміру не відповідає його джерелу +@msg Draw Inside needs exactly one selected path, compound path or text Для режиму «Малювати всередині» потрібен рівно один виділений контур, складений контур або текст +@msg EPS Save writes all artwork: use document.export for selected-only EPS Збереження EPS записує всю графіку: для EPS лише з виділеним скористайтеся document.export +@msg EPS flattener: {e} Зведення прозорості EPS: {e} +@msg EPS options: {e} Параметри EPS: {e} +@msg EPS {field} `{s}`: {allowed} EPS {field} `{s}`: {allowed} +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString викликано для значення, яке не є об’єктом +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft Експорт для екранів записує png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai або vectorcraft +@msg Flare click coordinates must be finite numbers Координати клацання для відблиску мають бути скінченними числами +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString викликано для значення, яке не є функцією +@msg HTML Text applies to object slices of type only Текст HTML застосовується лише до фрагментів на основі текстових об’єктів +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) Об’єкти ідентифікуються за XML ID: ім’я має бути припустимим XML ID (починатися з літери, підкреслення або двокрапки; без пробілів) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) Трасування зображення не вдалося: внутрішня помилка: його потік зупинився (будь ласка, повідомте про цю помилку) +@msg Image Trace failed: {e} Трасування зображення не вдалося: {e} +@msg Image Trace finished, but its document was closed Трасування зображення завершено, але його документ закрито +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again Трасування зображення завершено, але зображення або його параметри тим часом змінилися: виконайте трасування ще раз +@msg Import SVG Filter reads SVG files, not SVGZ Імпорт фільтра SVG читає файли SVG, а не SVGZ +@msg Invalid dimension offset Неприпустиме зміщення розміру +@msg Invalid point Неприпустима точка +@msg JSON.parse: {why} (at offset {at}) JSON.parse: {why} (зі зміщенням {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify не може записати структуру, яка містить саму себе +@msg Join: a subpath went missing З’єднання: вкладений контур зник +@msg Lock Workspace is not available in VectorCraft Блокування робочого середовища недоступне у VectorCraft +@msg Move Panel to New Window is not available: floating panels stay in the main window Переміщення панелі в нове вікно недоступне: плаваючі панелі залишаються в головному вікні +@msg Namespace.prototype.toString needs a Namespace Для Namespace.prototype.toString потрібен Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have Профілям нових документів потрібна папка налаштувань, якої в цьому запуску немає +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} викликано для значення, яке не є числом +@msg Object.create's prototype must be an object or null Прототип для Object.create має бути об’єктом або null +@msg Object.{function} was given a value that is not an object Object.{function} отримала значення, яке не є об’єктом +@msg Offset Path: the path is too complex to offset Зміщення контуру: контур занадто складний для зміщення +@msg Offset Path: {e} Зміщення контуру: {e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported Вміст PDF неповний або перевищує обмеження засобу читання; документ не імпортовано +@msg PDF flattener: {e} Зведення прозорості PDF: {e} +@msg PDF options: {e} Параметри PDF: {e} +@msg QName.prototype.toString needs a QName Для QName.prototype.toString потрібен QName +@msg Recolor Artwork is no longer open Вікно «Перефарбувати малюнок» уже закрито +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} викликано для значення, яке не є об’єктом RegExp +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 SVG не є UTF-8, UTF-16 або ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export Збереження записує одну монтажну область: укажіть одну або експортуйте кілька за допомогою document.export +@msg Select a dimension first Спочатку виділіть розмір +@msg Select dimensions to update Виділіть розміри для оновлення +@msg Select only dimensions Виділіть лише розміри +@msg Select two or more objects, or a group Виділіть два або більше об’єктів чи групу +@msg Shaper needs 1–64 source paths Для формоутворення потрібно 1–64 вихідних контурів +@msg Stencil Bridges: the selected art is too large or too detailed to bridge Містки трафарету: виділена графіка завелика або занадто детальна для створення містків +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString викликано для значення, яке не є рядком +@msg Switch x/y is for scatter graphs Команда «Поміняти x/y» призначена для точкових діаграм +@msg Text options: {e} Параметри тексту: {e} +@msg The {name} profile could not be read, so the new document has the default settings: {reason} Не вдалося прочитати профіль {name}, тому новий документ має параметри за замовчуванням: {reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time Трепінг: понад {_1} пар об’єктів розташовані достатньо близько для трепінгу або забагато об’єктів мають спільний горизонтальний діапазон; обробляйте менше об’єктів за раз +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off Рамка й лінії вибірки: жодна лінія вибірки не вміщується поза графікою, а рамку вибірки вимкнено +@msg \\ at end of pattern \\ наприкінці шаблону +@msg `alignOn` cannot be empty `alignOn` не може бути порожнім +@msg `alignOn` must be a string `alignOn` має бути рядком +@msg `align` is start, center or end `align` — це start, center або end +@msg `align` must be a string `align` має бути рядком +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` має бути масивом пар [subpath, anchor] +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` має містити індекси опорних точок (0, 1, 2…), а не {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` має містити індекси опорних точок (0, 1, 2…), а не {v} +@msg `answer` for this {kind} dialog must be one of: {_1} `answer` для цього діалогового вікна ({kind}) має бути одним з: {_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` містить понад {MAX_ANSWERS} відповідей +@msg `answers` is larger than 1 MiB `answers` більший за 1 МіБ +@msg `answers` must be a list of answers `answers` має бути списком відповідей +@msg `artboard` must be the index of an artboard of the document `artboard` має бути індексом монтажної області документа +@msg `artboardsAt` must be [x, y] `artboardsAt` має бути [x, y] +@msg `artboards` must be a list of artboard indices `artboards` має бути списком індексів монтажних областей +@msg `artboards` names an artboard the document does not have `artboards` указує монтажну область, якої немає в документі +@msg `artboards` names an artboard twice `artboards` указує монтажну область двічі +@msg `asset` is the asset id (see assets.list) `asset` — це ідентифікатор ресурсу (див. assets.list) +@msg `at` must be [x, y] (got {at}) `at` має бути [x, y] (отримано {at}) +@msg `at` must be a byte offset `at` має бути зміщенням у байтах +@msg `autoLeading` must be a number `autoLeading` має бути числом +@msg `background` must be "", "matte" or #rrggbb `background` має бути "", "matte" або #rrggbb +@msg `beside` needs `side` для `beside` потрібен `side` +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` має бути "none", "standard" або "forced" +@msg `center` must be [x, y] `center` має бути [x, y] +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` має бути "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" або "icfBottom" +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` має бути "default", "leftToRight" або "rightToLeft" +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` для `clearStroke` потрібні `stroke: false`, `ids: []` і відсутність `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` має бути списком ідентифікаторів текстових об’єктів, а не {v} +@msg `clear` must list type objects; {_1} is not one `clear` має містити текстові об’єкти; {_1} не є текстовим об’єктом +@msg `clear` must not list `id` `clear` не має містити `id` +@msg `code` must be a whole number `code` має бути цілим числом +@msg `colorMode` must be one of rgb, cmyk `colorMode` має бути одним з: rgb, cmyk +@msg `color` needs a `rule` to make the theme from для `color` потрібне `rule`, за яким створюється тема +@msg `colors` does not apply to the {name} reduction `colors` не застосовується до скорочення кольорів {name} +@msg `colors` is empty `colors` порожній +@msg `colors` must be a whole number from 2 to 256 `colors` має бути цілим числом від 2 до 256 +@msg `colors` must be at least 9 for the uniform reduction `colors` має бути не менше 9 для рівномірного скорочення кольорів +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` має бути від 1 до {MAX_THEME_COLORS} (отримано {n}) +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` — це vertical, uniform, repeating або sliding +@msg `columns` must be a whole number 1..50 `columns` має бути цілим числом 1..50 +@msg `commands` must not be empty `commands` не має бути порожнім +@msg `contents` goes with `to` `contents` використовується разом із `to` +@msg `corners` must be sharp, round or square, not `{other}` `corners` має бути sharp, round або square, а не `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` має містити індекси опорних точок (0, 1, 2…), а не {v} +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` має бути цілим числом від 0 до {MAX_COUNT} +@msg `count` must be a whole number from 1 to 100 `count` має бути цілим числом від 1 до 100 +@msg `dataBase64` is larger than the module limit `dataBase64` перевищує обмеження модуля +@msg `dataBase64` is not valid base64 `dataBase64` не є припустимим base64 +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` не можна поєднувати з `ids`, `reverse` або `append` +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` і `childLimit` обмежують зведення: передайте `summary: true` +@msg `design` is a design name, or null for plain columns `design` — це назва дизайну або null для звичайних стовпців +@msg `design` is a design name, or null for the default marker `design` — це назва дизайну або null для стандартного маркера +@msg `destinationFolder` is required when destination is `folder` `destinationFolder` обов’язковий, коли призначенням є `folder` +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` має бути `none`, `saveAndClose` або `folder` +@msg `diameter` must be from 0.1 to 2500 pt `diameter` має бути від 0.1 до 2500 pt +@msg `digits` must be one of {_1} `digits` має бути одним з: {_1} +@msg `dimImages` is a percentage or false `dimImages` — це відсоток або false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` має бути "auto", "leftToRight" або "rightToLeft" +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` має бути числом від 0 до 100 +@msg `each` is the value one design stands for, more than 0 `each` — це значення, якому відповідає один дизайн, більше 0 +@msg `end` must be "start" or "end" `end` має бути "start" або "end" +@msg `ends` applies to joining exactly two paths `ends` застосовується до з’єднання рівно двох контурів +@msg `event` must be one of {_1} `event` має бути одним з: {_1} +@msg `every` must be "all", "odd" or "even", not {v} `every` має бути "all", "odd" або "even", а не {v} +@msg `expand` must be true or false `expand` має бути true або false +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` має бути від {lo} до {hi} pt +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` має містити від 1 до 1000 шляхів файлів шрифтів, які знайшов останній пошук text.findFontFiles +@msg `focal` and `radius` cannot be used together `focal` і `radius` не можна використовувати разом +@msg `focal` must be an array of [subpath, anchor] pairs `focal` має бути масивом пар [subpath, anchor] +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` указує [{si}, {ai}], що не є опорною точкою контуру +@msg `folder` must be an absolute folder path `folder` має бути абсолютним шляхом до папки +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` має бути об’єктом тегів осей і значень, наприклад {"wght"} +@msg `format` must be a string `format` має бути рядком +@msg `format` must be one of {_1} `format` має бути одним з: {_1} +@msg `fractions` is chop or scale `fractions` — це chop або scale +@msg `from` and `to` must differ `from` і `to` мають відрізнятися +@msg `from` has an empty frame `from` має порожню рамку +@msg `from` must be a non-negative integer `from` має бути невід’ємним цілим числом +@msg `from` must be area type or type on a path `from` має бути текстом в області або текстом за контуром +@msg `gid` must be a glyph id, not {v} `gid` має бути ідентифікатором гліфа, а не {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` стосується одного символу, а не стилю символу (його задає text.setGlyphAlternate) +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` приймає лише null, що скасовує вибір гліфа (вибір робить text.setGlyphAlternate) +@msg `group` must be one of {_1} `group` має бути одним з: {_1} +@msg `group` must be the Objects on Path group of `member` and `target` `group` має бути групою «Об’єкти на контурі», до якої належать `member` і `target` +@msg `guide` must be a guide index (guide.list) `guide` має бути індексом напрямної (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` має бути default, left, center або right +@msg `id` is an object id, not {v} `id` — це ідентифікатор об’єкта, а не {v} +@msg `id` must be a node id `id` має бути ідентифікатором вузла +@msg `id` must be an object id, not {v} `id` має бути ідентифікатором об’єкта, а не {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` має вказувати групи «Об’єкти на контурі» або об’єкти в них +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` приймає не більше {MAX_INSPECT_IDS} ідентифікаторів +@msg `imageMap` must be none, rectangle or polygon `imageMap` має бути none, rectangle або polygon +@msg `includeArtboardFill` must be true or false `includeArtboardFill` має бути true або false +@msg `includeSubdirs` must be true or false `includeSubdirs` має бути true або false +@msg `index` must be a point index `index` має бути індексом точки +@msg `index` must be a whole number or null `index` має бути цілим числом або null +@msg `indices` must be a non-empty array of item indices `indices` має бути непорожнім масивом індексів елементів +@msg `item` needs `index` для `item` потрібен `index` +@msg `items` is a list of {kind, item} `items` — це список {kind, item} +@msg `items` is empty `items` порожній +@msg `items` must be a list of {id, points} `items` має бути списком {id, points} +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` має бути списком {{id, anchors?}}, а не {v} +@msg `items` must be an array of {id, anchors} `items` має бути масивом {id, anchors} +@msg `items` must be an array of {{id, corners?}}, not {v} `items` має бути масивом {{id, corners?}}, а не {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` має бути "none", "short", "medium", "long" або "stylistic" +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` стосується першого символу рядка, а не стилю символу (його задає text.setRangeStyle) +@msg `kind` is one of {_1} `kind` — це одне з: {_1} +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` має бути "round", "invertedRound" або "chamfer", а не {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` має бути `text`, `visibility`, `image` або `graph` +@msg `kind` must be a string `kind` має бути рядком +@msg `kind` must be image, noImage or htmlText `kind` має бути image, noImage або htmlText +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` має бути "pushInFirst", "pushOutFirst" або "pushOutOnly" +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` має бути "none", "hard" або "soft" +@msg `label` must be a non-empty name of at most 200 characters `label` має бути непорожнім іменем довжиною не більше 200 символів +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` має бути назвою або тегом мови, наприклад "Turkish" або "tr" +@msg `leader` must be a string `leader` має бути рядком +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` має бути "romanBaseline" або "emBoxTop" +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` має бути від 0.1 до 5000 або null для значення «Авто» +@msg `length` must be a number of at most 1000000 pt either way `length` має бути числом не більше 1000000 pt у будь-який бік +@msg `line` must be true or false `line` має бути true або false +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` має бути від 1 до {MAX_SUGGESTIONS} +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) не може бути більшим за `maxAngle` ({max_angle}°) +@msg `mode` must be "handles", "straight" or "smooth" `mode` має бути "handles", "straight" або "smooth" +@msg `mode` must be "straight", "bezier" or "arc" `mode` має бути "straight", "bezier" або "arc" +@msg `mode` must be one of {_1} `mode` має бути одним з: {_1} +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` має бути "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" або "lineEndHalf" +@msg `name` must be a name that is not empty `name` має бути непорожнім іменем +@msg `name` must not be empty `name` не має бути порожнім +@msg `names` is a list of preset names `names` — це список імен наборів +@msg `names` lists preset names `names` містить імена наборів +@msg `names` must be a list of names `names` має бути списком імен +@msg `names` must be a list of swatch names `names` має бути списком імен зразків +@msg `names` must be an array of style names `names` має бути масивом імен стилів +@msg `new` is a design name, or true `new` — це назва дизайну або true +@msg `origin` must be [x, y] in points `origin` має бути [x, y] у пунктах +@msg `path` must be a folder `path` має бути папкою +@msg `path` must be a path object `path` має бути об’єктом-контуром +@msg `picks` must be an array (got {v}) `picks` має бути масивом (отримано {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` має містити від 1 до {MAX_PICKS} виборів (отримано {_1}) +@msg `pivot` must be a whole number from 0 to 8 `pivot` має бути цілим числом від 0 до 8 +@msg `points` must be a list of [x, y] points `points` має бути списком точок [x, y] +@msg `points` must be a list of point indexes `points` має бути списком індексів точок +@msg `points` must be an array of point indices `points` має бути масивом індексів точок +@msg `points` must be point indexes `points` має містити індекси точок +@msg `port` must be "in" or "out" `port` має бути "in" або "out" +@msg `position` is above or below `position` — це above або below +@msg `preset` must be a string `preset` має бути рядком +@msg `radius` must be 0 or more `radius` має бути 0 або більше +@msg `ratio` must be from 0.1 to 0.5 `ratio` має бути від 0.1 до 0.5 +@msg `rect` must be [x, y, width, height] with a positive size `rect` має бути [x, y, width, height] з додатним розміром +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` має бути [x0, y0, x1, y1] (отримано {r}) +@msg `relation` must be overlap, inside or outside `relation` має бути overlap, inside або outside +@msg `replace` must be a string `replace` має бути рядком +@msg `reversed` must be true or false `reversed` має бути true або false +@msg `rotate` must be a finite number `rotate` має бути скінченним числом +@msg `rows` must be lists of numbers or nulls, not {r} `rows` має містити списки чисел або значень null, а не {r} +@msg `rule` must be nonZero or evenOdd `rule` має бути nonZero або evenOdd +@msg `scale` must be a number from 0.001 to 1000 `scale` має бути числом від 0.001 до 1000 +@msg `scale` must be a number in (0, 100] `scale` має бути числом у діапазоні (0, 100] +@msg `scope.{key}` must be a non-empty array `scope.{key}` має бути непорожнім масивом +@msg `scope` must be "document", {artboards} or {layers} `scope` має бути "document", {artboards} або {layers} +@msg `scope` must be "selection" or "document", not {other} `scope` має бути "selection" або "document", а не {other} +@msg `script` must be a string `script` має бути рядком +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` — це непорожній список індексів рядів +@msg `seriesIndexes` needs a `type` or a `valueAxis` для `seriesIndexes` потрібен `type` або `valueAxis` +@msg `shift` must be a number of points `shift` має бути числом пунктів +@msg `showCenter` must be true or false `showCenter` має бути true або false +@msg `side` must be "left", "right", "top" or "bottom" `side` має бути "left", "right", "top" або "bottom" +@msg `side` places the artboard: leave out x, y, width and height `side` розміщує монтажну область: не вказуйте x, y, width і height +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` має бути "fullJustify", "alignLeft", "alignCenter" або "alignRight" +@msg `size` is a percentage of the base text's size, 1 to 100 `size` — це відсоток від розміру основного тексту, від 1 до 100 +@msg `source` and `target` are the same object `source` і `target` — той самий об’єкт +@msg `source` is the only path in series group `target` `source` — єдиний контур у групі серії `target` +@msg `source` must be `folder` or `datasets` `source` має бути `folder` або `datasets` +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` створює понад {MAX_COUNT} ліній упоперек рамки +@msg `spacing` must be a number `spacing` має бути числом +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` має бути числом пунктів, 0 або більше, чи "auto" +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` має бути більше 0 і не більше {MAX_DISTANCE} pt +@msg `start` must be a finite number `start` має бути скінченним числом +@msg `starts` must be an array of anchor indices (or nulls) `starts` має бути масивом індексів опорних точок (або значень null) +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` має бути linked, embedded, missing або modified чи списком цих значень +@msg `stop` must be a stop index `stop` має бути індексом точки переходу +@msg `story` styles the whole story: give no `start` or `end` `story` оформлює весь матеріал: не вказуйте `start` або `end` +@msg `story` takes the whole story: give no `start` or `end` `story` охоплює весь матеріал: не вказуйте `start` або `end` +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` застосовується до обведень (stroke: true) +@msg `style` must be roman or japanese `style` має бути roman або japanese +@msg `subpaths` must be two integer indices `subpaths` має містити два цілочислові індекси +@msg `symbol` must be a symbol name `symbol` має бути іменем символу +@msg `target` must be "object" or "contents", not {v} `target` має бути "object" або "contents", а не {v} +@msg `target` must be an object of the same Objects on Path group as `member` `target` має бути об’єктом тієї самої групи «Об’єкти на контурі», що й `member` +@msg `tint` applies to global and spot colours `tint` застосовується до глобальних і плашкових кольорів +@msg `tint` goes with a global or spot `swatch` `tint` використовується разом із глобальним або плашковим `swatch` +@msg `tint` must be a number (%) `tint` має бути числом (%) +@msg `tint` needs a global or spot colour для `tint` потрібен глобальний або плашковий колір +@msg `to` is already threaded `to` уже пов’язано +@msg `to` is the design's new name `to` — це нова назва дизайну +@msg `to` must be [x, y] `to` має бути [x, y] +@msg `to` must be a closed path, area type or type on a path other than `from` `to` має бути замкненим контуром, текстом в області або текстом за контуром, відмінним від `from` +@msg `to` must be a whole number `to` має бути цілим числом +@msg `to` takes the rows of one object's stack, not aligned rows `to` приймає рядки стосу одного об’єкта, а не вирівняні рядки +@msg `useLab` must be true or false `useLab` має бути true або false +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` має бути default, top, middle, baseline або bottom +@msg `values` must be a list `values` має бути списком +@msg `values` must be an object `values` має бути об’єктом +@msg `values` must name hex, rgb, cmyk, lab or gray `values` має вказувати hex, rgb, cmyk, lab або gray +@msg `values` names a kind twice `values` указує один вид двічі +@msg `version` must be 0 or {_1} `version` має бути 0 або {_1} +@msg `version` must be 0 or {newest} `version` має бути 0 або {newest} +@msg `weight` must be from 0.01 to 1000 pt `weight` має бути від 0.01 до 1000 pt +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` має бути списком із {_1} чисел, по одному на рядок (отримано {v}) +@msg `xml` must be a string `xml` має бути рядком +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it `{KEY}`, запис {n}: абзац із байта {start} об’єкта {_1} отримує інші відступи, ніж у попередньому записі +@msg `{KEY}` entry {n} must be an object `{KEY}`, запис {n}: має бути об’єктом +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` `{KEY}`, запис {n}: не задає ні `leftIndent`, ні `firstLineIndent` +@msg `{KEY}` entry {n}: `id` must be an object id `{KEY}`, запис {n}: `id` має бути ідентифікатором об’єкта +@msg `{KEY}` entry {n}: `start` must be a byte offset `{KEY}`, запис {n}: `start` має бути зміщенням у байтах +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt `{KEY}`, запис {n}: `{k}` має бути числом від -{MAX_PARA} до {MAX_PARA} pt +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} `{KEY}`, запис {n}: жоден абзац об’єкта {_1} не починається з байта {start} +@msg `{KEY}` entry {n}: unknown key `{k}` `{KEY}`, запис {n}: невідомий ключ `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object `{KEY}`, запис {n}: {_1} не є текстовим об’єктом +@msg `{KEY}` is empty `{KEY}` порожній +@msg `{KEY}` must be a list `{KEY}` має бути списком +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` приймає не більше {MAX_PARAGRAPH_INDENTS} записів (отримано {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` не допускає `{other}` поруч із собою +@msg `{_1}` can't be edited or removed `{_1}` не можна змінити або вилучити +@msg `{_1}` is built in and always listed `{_1}` є вбудованим і завжди присутній у списку +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` не є файлом шрифту, який знайшов останній пошук text.findFontFiles +@msg `{_1}` would be overwritten by the package: choose a different folder or name `{_1}` буде перезаписано пакетом: виберіть іншу папку або ім’я +@msg `{base}` is larger than {_1} MB `{base}` більший за {_1} МБ +@msg `{base}` is not in the DCS file's folder `{base}` немає в папці файлу DCS +@msg `{file}` has no art to place `{file}` не містить графіки для розміщення +@msg `{file}` is over {_1} MB `{file}` перевищує {_1} МБ +@msg `{f}` belongs to a VectorCraft that is running `{f}` належить запущеному екземпляру VectorCraft +@msg `{f}` is the copy of a document open now `{f}` — це копія документа, відкритого зараз +@msg `{id}` is no writable format (see document.formats) `{id}` не є форматом, доступним для запису (див. document.formats) +@msg `{id}` isn't a loaded library `{id}` не є завантаженою бібліотекою +@msg `{key}` has no colours `{key}` не містить кольорів +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` містить понад {MAX_EDIT_WORDS} слів +@msg `{key}` is a design name `{key}` — це назва дизайну +@msg `{key}` is a design name or a list of them `{key}` — це назва дизайну або список таких назв +@msg `{key}` is longer than {MAX_NAME} characters `{key}` довший за {MAX_NAME} символів +@msg `{key}` is out of range `{key}` поза межами діапазону +@msg `{key}` must be 0 or more `{key}` має бути 0 або більше +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` має бути [min, desired, max] або {{min?, desired?, max?}}, у відсотках +@msg `{key}` must be [x, y] numbers `{key}` має бути числами [x, y] +@msg `{key}` must be a list of words `{key}` має бути списком слів +@msg `{key}` must be a list, not {v} `{key}` має бути списком, а не {v} +@msg `{key}` must be a non-negative integer `{key}` має бути невід’ємним цілим числом +@msg `{key}` must be a number or null, not {v} `{key}` має бути числом або null, а не {v} +@msg `{key}` must be a number {_1}..{_2} `{key}` має бути числом {_1}..{_2} +@msg `{key}` must be a whole number of 0 or more `{key}` має бути цілим числом, 0 або більше +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` має бути цілим числом не менше 0, а не {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` має бути індексом елемента оформлення (порядок зафарбовування) або null +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` має бути масивом ідентифікаторів ресурсів (див. assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` має бути від 0 до 180 градусів, а не {a} +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` має бути від 0 до {MAX_DISTANCE} pt +@msg `{key}` must be from {_1} to {shift_max} `{key}` має бути від {_1} до {shift_max} +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` має бути від {lo} до {hi}, а не {v} +@msg `{key}` must be one of auto, always, never `{key}` має бути одним з: auto, always, never +@msg `{key}` must be one of miter, round, bevel `{key}` має бути одним з: miter, round, bevel +@msg `{key}` must be one word of at most {_1} characters `{key}` має бути одним словом довжиною не більше {_1} символів +@msg `{key}` must hold single words of at most {_1} characters `{key}` має містити окремі слова довжиною не більше {_1} символів +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` має вказувати об’єкт, прикріплений до контуру групи «Об’єкти на контурі» +@msg `{key}` names an object that is hidden or locked `{key}` указує об’єкт, який приховано або заблоковано +@msg `{key}`: not a colour: {v} `{key}`: не колір: {v} +@msg `{k}.family` is required `{k}.family` є обов’язковим +@msg `{k}` is a number 0..1 `{k}` — це число 0..1 +@msg `{k}` is true or false `{k}` — це true або false +@msg `{k}` isn't an export setting: {_1} `{k}` не є параметром експорту: {_1} +@msg `{k}` must be a byte offset `{k}` має бути зміщенням у байтах +@msg `{k}` must be a number `{k}` має бути числом +@msg `{k}` must be a number of points `{k}` має бути числом пунктів +@msg `{k}` must be a string `{k}` має бути рядком +@msg `{k}` must be a whole number `{k}` має бути цілим числом +@msg `{k}` must be from {lo} to {hi} for {key} `{k}` має бути від {lo} до {hi} для {key} +@msg `{k}` must be {what}, not {v} `{k}`: потрібно {what}, а не {v} +@msg `{l}` isn't a global or spot colour `{l}` не є глобальним або плашковим кольором +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` не є растровим зображенням: скористайтеся clipboard.importSvg, importPdf або importText +@msg `{name}` can't be deleted `{name}` не можна видалити +@msg `{name}` can't be duplicated `{name}` не можна дублювати +@msg `{name}` can't be edited `{name}` не можна редагувати +@msg `{name}` can't be edited this way `{name}` не можна редагувати в такий спосіб +@msg `{name}` can't be renamed `{name}` не можна перейменувати +@msg `{name}` can't name a folder `{name}` не може вказувати папку +@msg `{name}` has no art to place `{name}` не містить графіки для розміщення +@msg `{name}` has no text to place `{name}` не містить тексту для розміщення +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text `{name}` у документі Word не є текстом UTF-8 або UTF-16 +@msg `{name}` in the Word document is not well-formed XML: {e} `{name}` у документі Word не є правильно сформованим XML: {e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep `{name}` у документі Word має вкладеність елементів понад {MAX_NESTING} рівнів +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` — це вбудований набір, доступний лише для читання: збережіть його під іншим іменем +@msg `{name}` is a built-in preset: save under another name `{name}` — це вбудований набір: збережіть під іншим іменем +@msg `{name}` is a built-in profile and stays `{name}` — це вбудований профіль, він залишається +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` — це окремий файл, який можна знайти лише поруч зі шляхом файлу DCS +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` — це відтінок `{base}`: змініть `{base}` (зразку відтінку можна лише дати нове ім’я) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` — це робочий простір: спочатку виберіть інший за допомогою edit.colorSettings +@msg `{name}` is built in and stays `{name}` є вбудованим і залишається +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` приймає фарбу лише свого виду (колір, градієнт або візерунок) +@msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} +@msg `{n}` can't be deleted `{n}` не можна видалити +@msg `{n}` can't be merged `{n}` не можна об’єднати +@msg `{n}` can't be moved `{n}` не можна перемістити +@msg `{n}` is not a colour swatch `{n}` не є зразком кольору +@msg `{n}` isn't a solid colour `{n}` не є суцільним кольором +@msg `{n}`: colour groups hold solid colours only `{n}`: групи кольорів містять лише суцільні кольори +@msg `{other}` doesn't trace an image `{other}` не трасує зображення +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`: посилання кроку копіюють понад {_1} МБ значень +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` не є тегом осі: тег складається з чотирьох символів, наприклад "wght" +@msg `{to}` is empty or already used `{to}` порожній або вже використовується +@msg `{u}` is no Unicode character `{u}` не є символом Unicode +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` довший за {MAX_TEXT} байтів +@msg `{what}` must be text `{what}` має бути текстом +@msg `{word}` isn't in the custom dictionary `{word}` немає у словнику користувача +@msg a CMap has too many mappings CMap містить забагато відображень +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard креслення DXF має одну точку початку координат: передайте useArtboards: true, щоб отримати одне креслення на монтажну область +@msg a PDF presets file with over {_1} presets файл наборів PDF, що містить понад {_1} наборів +@msg a \\u escape needs four hexadecimal digits для екранування \\u потрібні чотири шістнадцяткові цифри +@msg a batch preview cannot switch documents попередній перегляд пакета не може перемикати документи +@msg a brush named `{nn}` exists пензель з іменем `{nn}` уже існує +@msg a comma in an XPath expression must separate function arguments кома у виразі XPath має розділяти аргументи функції +@msg a container of the listed objects is gone контейнера перелічених об’єктів більше немає +@msg a content stream can't be read не вдалося прочитати потік вмісту +@msg a content stream isn't a stream потік вмісту не є потоком +@msg a design holds at most {_1} objects дизайн містить не більше {_1} об’єктів +@msg a design needs a name дизайну потрібна назва +@msg a dictionary grew too large словник став завеликим +@msg a document holds at most {_1} SVG filters документ містить не більше {_1} фільтрів SVG +@msg a document keeps at most {_1} graph designs документ зберігає не більше {_1} дизайнів діаграм +@msg a document keeps at most {_1} saved selections документ зберігає не більше {_1} збережених виділень +@msg a document keeps at most {_1} views документ зберігає не більше {_1} виглядів +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files документ може посилатися не більше ніж на {MAX_SVG_SCRIPTS} файлів JavaScript +@msg a document with unsaved changes has no recovery copy документ із незбереженими змінами не має копії для відновлення +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes порядок фільтра feConvolveMatrix дорівнює {x}×{y}, що переповнює множення розміру ядра, яке виконує usvg +@msg a font's data is too large дані шрифту завеликі +@msg a font's subroutines are too large підпрограми шрифту завеликі +@msg a function declaration without a name оголошення функції без імені +@msg a gradient preset is already called {n} набір градієнта з іменем {n} уже існує +@msg a graph has at most {_1} series, so it transposes with at most that many categories діаграма має не більше {_1} рядів, тому її можна транспонувати лише з такою самою найбільшою кількістю категорій +@msg a host value has too many entries значення хоста містить забагато записів +@msg a host value is nested too deep значення хоста має завелику вкладеність +@msg a layer can't be merged into its own sublayer шар не можна об’єднати з його власним підшаром +@msg a layer's form box isn't an array рамка форми шару не є масивом +@msg a layer's form can't be read не вдалося прочитати форму шару +@msg a layer's form has no box форма шару не має рамки +@msg a layer's form has no dictionary форма шару не має словника +@msg a legend draws at most {MAX_CHIPS} swatches легенда малює не більше {MAX_CHIPS} зразків +@msg a name names one asset: give multiple: false, or one object ім’я вказує один ресурс: задайте multiple: false або один об’єкт +@msg a namespace declaration uses a reserved prefix or URI оголошення простору імен використовує зарезервований префікс або URI +@msg a namespace qualifier is undefined кваліфікатор простору імен не визначено +@msg a namespace with an empty URI cannot have a prefix простір імен із порожнім URI не може мати префікса +@msg a number has no digits число не має цифр +@msg a number's exponent has no digits показник степеня числа не має цифр +@msg a number's fraction has no digits дробова частина числа не має цифр +@msg a page can't be read не вдалося прочитати сторінку +@msg a page object is used by something else об’єкт сторінки використовується чимось іншим +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) мініатюра сторінки має складатися з пікселів RGB, від 1 до {THUMBNAIL_SIZE} по кожній стороні (отримано {w} × {h}) +@msg a pattern layer doesn't end what it starts шар візерунка не завершує те, що починає +@msg a pattern layer's colors hold something else кольори шару візерунка містять щось інше +@msg a pattern named `{n}` exists візерунок з іменем `{n}` уже існує +@msg a plug-in with id {_1} is already installed модуль з ідентифікатором {_1} уже встановлено +@msg a preset named `{n}` exists набір з іменем `{n}` уже існує +@msg a print presets file with over {_1} presets файл наборів друку, що містить понад {_1} наборів +@msg a profile is already called {n} профіль з іменем {n} уже існує +@msg a property descriptor cannot have both a value or writable and a get or set дескриптор властивості не може мати одночасно value або writable і get або set +@msg a property descriptor must be an object дескриптор властивості має бути об’єктом +@msg a property's {name} must be a function or undefined {name} властивості має бути функцією або undefined +@msg a row can't go into itself рядок не можна помістити в самого себе +@msg a series goes on the left or the right value axis ряд розміщується на лівій або правій осі значень +@msg a start must be an anchor index ≥ 0 or null початок має бути індексом опорної точки ≥ 0 або null +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied потік у ньому після декодування перевищив би 256 МБ або його параметри декодування не можна застосувати +@msg a string holds a control character рядок містить керівний символ +@msg a string holds an escape sequence JSON does not have рядок містить екрановану послідовність, якої немає в JSON +@msg a string is not closed рядок не закрито +@msg a style named `{name}` already exists стиль з іменем `{name}` уже існує +@msg a style named `{to}` already exists стиль з іменем `{to}` уже існує +@msg a swatch needs a colour, gradient or pattern зразку потрібен колір, градієнт або візерунок +@msg a symbol named `{n}` already exists символ з іменем `{n}` уже існує +@msg a theme has 1 to {THEME_SIZE} colours тема містить від 1 до {THEME_SIZE} кольорів +@msg a theme is already called {name} (replace: "{name}" overwrites it) тема з іменем {name} уже існує (replace: "{name}" перезаписує її) +@msg a toolbar name has at most {MAX_NAME} characters ім’я панелі інструментів містить не більше {MAX_NAME} символів +@msg a view named `{_1}` exists вигляд з іменем `{_1}` уже існує +@msg a view named `{name}` exists вигляд з іменем `{name}` уже існує +@msg a workspace keeps at most {MAX} toolbars робоче середовище зберігає не більше {MAX} панелей інструментів +@msg a {_1} series can't go in a {_2} graph ряд типу {_1} не можна помістити в діаграму типу {_2} +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}: "pdf" (сумісний із PDF, за замовчуванням) або "7" (застарілий .ai, версія 7) +@msg already editing an opacity mask маска непрозорості вже редагується +@msg already editing another object's opacity mask уже редагується маска непрозорості іншого об’єкта +@msg already in pattern editing mode режим редагування візерунка вже ввімкнено +@msg an SVG filter is named by its id: give `xml` with `name` фільтр SVG іменується своїм ідентифікатором: укажіть `xml` разом із `name` +@msg an XML element has two attributes with the same name елемент XML має два атрибути з однаковим іменем +@msg an XML value cannot be inserted into itself or its descendants значення XML не можна вставити в самого себе або у своїх нащадків +@msg an XML value's children cannot be assigned by position дочірні елементи значення XML не можна призначати за позицією +@msg an XML value's children cannot be deleted by position дочірні елементи значення XML не можна видаляти за позицією +@msg an XMLList converts to XML only when it has one item XMLList перетворюється на XML, лише коли містить один елемент +@msg an XPath node test is missing відсутня перевірка вузла XPath +@msg an XPath operand is missing відсутній операнд XPath +@msg an XPath operator is missing відсутній оператор XPath +@msg an XPath predicate or step cannot follow here предикат або крок XPath не може йти тут +@msg an XPath predicate or step follows a value that is not a node-set предикат або крок XPath іде після значення, яке не є набором вузлів +@msg an XPath step is missing відсутній крок XPath +@msg an artboard is listed twice монтажну область указано двічі +@msg an empty artboard has no width or height to scale to порожня монтажна область не має ширини або висоти, до якої можна масштабувати +@msg an image couldn't be decoded and is missing from the picture не вдалося декодувати зображення, тому його немає на малюнку +@msg an image is too large for DXF and was left out зображення завелике для DXF, тому його пропущено +@msg an image of {_1} MB, at most {_2} MB зображення розміром {_1} МБ, дозволено не більше {_2} МБ +@msg an image of {declared_w} × {declared_h} pixels is too large to draw зображення розміром {declared_w} × {declared_h} пікселів завелике для малювання +@msg an image of {width} × {height} pixels is too large to draw зображення розміром {width} × {height} пікселів завелике для малювання +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) зображення розміром {w} × {h} пікселів завелике для перетворення (не більше {_1} пікселів) +@msg an index must be a whole number індекс має бути цілим числом +@msg an inline graphic has no glyphs вбудована в рядок графіка не має гліфів +@msg an invisible text form can't be read не вдалося прочитати форму невидимого тексту +@msg an object is missing відсутній об’єкт +@msg an object's header is missing відсутній заголовок об’єкта +@msg an overprinting form can't be read не вдалося прочитати форму з накладенням +@msg an unread object has no readable bounds непрочитаний об’єкт не має меж, які можна прочитати +@msg an unread object holds a frame of threaded type непрочитаний об’єкт містить рамку пов’язаного тексту +@msg an unread object is missing from its layer непрочитаного об’єкта немає в його шарі +@msg anchor `smooth` must be true or false `smooth` опорної точки має бути true або false +@msg anchor `{key}` must be [x, y] `{key}` опорної точки має бути [x, y] +@msg anchor `{key}` must be a number `{key}` опорної точки має бути числом +@msg angle and roundness must be finite angle і roundness мають бути скінченними +@msg angles must be a list as long as pins (degrees or null) angles має бути списком такої самої довжини, як pins (градуси або null) +@msg appearance must be flat|center|edge appearance має бути flat|center|edge +@msg apply is "fill" or "stroke", not `{other}` apply — це "fill" або "stroke", а не `{other}` +@msg apply was called on a value that is not a function apply викликано для значення, яке не є функцією +@msg area must be a closed polygon of 3 to 100000 [x, y] points area має бути замкненим багатокутником із 3–100000 точок [x, y] +@msg area type in a thread cannot use Dynamic Text: {frames} текст в області у пов’язаному тексті не може використовувати динамічний текст: {frames} +@msg area type needs a path that encloses an area для тексту в області потрібен контур, який охоплює область +@msg area type {_1} is named twice текст в області {_1} указано двічі +@msg array literal too long літерал масиву задовгий +@msg arrowAlign must be extend|tip, got {o} arrowAlign має бути extend|tip, отримано {o} +@msg arrowScale must be [start, end] arrowScale має бути [start, end] +@msg artboard is a 0-based index artboard — це індекс, що починається з 0 +@msg artboard must be a 0-based index artboard має бути індексом, що починається з 0 +@msg artboard {i} doesn't print монтажна область {i} не друкується +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout має бути {layout?, columns?, spacing?, rightToLeft?} +@msg artboardLayout.columns must be 1 or more artboardLayout.columns має бути 1 або більше +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout має бути gridByRow, gridByColumn, row або column +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft має бути true або false +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing має бути 0–{MAX_SPACING} pt +@msg artboards must be a non-empty list artboards має бути непорожнім списком +@msg artboards must be a number artboards має бути числом +@msg asset `{_1}` has no art to export ресурс `{_1}` не містить графіки для експорту +@msg assignment to a constant binding присвоєння константній прив’язці +@msg at least one row that isn't excluded needs a weight above 0 принаймні один невиключений рядок повинен мати вагу понад 0 +@msg at most {MAX_KEYWORDS} keywords не більше {MAX_KEYWORDS} ключових слів +@msg at most {MAX_MATCHES} matches are kept зберігається не більше {MAX_MATCHES} збігів +@msg at most {MAX_POINTS} points не більше {MAX_POINTS} точок +@msg at most {MAX_QUEUE} files не більше {MAX_QUEUE} файлів +@msg at must be [x, y] at має бути [x, y] +@msg at/rect lie outside the canvas at/rect лежать поза полотном +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute має бути size|leading|tracking|kerning|baselineShift +@msg attrs must be an object attrs має бути об’єктом +@msg automation path rejected: the folder's name isn't valid text шлях автоматизації відхилено: ім’я папки не є припустимим текстом +@msg automation path rejected: {_1} isn't valid text шлях автоматизації відхилено: {_1} не є припустимим текстом +@msg automation path rejected: {why}: {path} шлях автоматизації відхилено: {why}: {path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis має бути "vertical", "horizontal" або кутом у градусах, а не `{given}` +@msg backgroundContents must be transparent or white backgroundContents має бути transparent або white +@msg bad AES key неправильний ключ AES +@msg bad `runs`: {e} неправильний `runs`: {e} +@msg bad arrowScale неправильний arrowScale +@msg bad base64 неправильний base64 +@msg bad color {c} неправильний колір {c} +@msg bad colour неправильний колір +@msg bad colour in `colors` неправильний колір у `colors` +@msg bad colour {v} неправильний колір {v} +@msg bad colour {v} in groupColors неправильний колір {v} у groupColors +@msg bad cross-reference entry неправильний запис перехресних посилань +@msg bad cross-reference table неправильна таблиця перехресних посилань +@msg bad current colour {v} неправильний поточний колір {v} +@msg bad dataBase64 неправильний dataBase64 +@msg bad edit неправильна правка +@msg bad fill {v} неправильна заливка {v} +@msg bad fit: {e} неправильне припасування: {e} +@msg bad model {other} (keep, new or document) неправильна модель {other} (keep, new або document) +@msg bad new colour {v} неправильний новий колір {v} +@msg bad object неправильний об’єкт +@msg bad object number неправильний номер об’єкта +@msg bad offset неправильне зміщення +@msg bad path data: {e} неправильні дані контуру: {e} +@msg bad point {v}: expected [x, y] or [x, y, pressure] неправильна точка {v}: очікується [x, y] або [x, y, pressure] +@msg bad reference неправильне посилання +@msg bad trailer size неправильний розмір трейлера +@msg bad {k} colour неправильний колір {k} +@msg batches cannot nest пакети не можна вкладати один в один +@msg bind something to a variable first спочатку прив’яжіть що-небудь до змінної +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed має бути 0–{MAX_BLEED} pt: число, [top, bottom, left, right] або {{top, bottom, left, right}} +@msg blend must be a mode name blend має бути назвою режиму +@msg both sides of an XPath | must select nodes обидві сторони оператора | в XPath мають вибирати вузли +@msg brush must be a name or null brush має бути іменем або null +@msg by must be a number by має бути числом +@msg can't add page thumbnails: {why} не вдалося додати мініатюри сторінок: {why} +@msg can't add the editing data for other apps: {why} не вдалося додати дані редагування для інших програм: {why} +@msg can't edit the written PDF: {why} не вдалося змінити записаний PDF: {why} +@msg can't encrypt the PDF: {why} не вдалося зашифрувати PDF: {why} +@msg can't linearise the PDF: {why} не вдалося лінеаризувати PDF: {why} +@msg can't open `{_1}`: {_2} не вдалося відкрити `{_1}`: {_2} +@msg can't open `{_1}`: {e} не вдалося відкрити `{_1}`: {e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) не вдалося відкрити `{name}`: VectorCraft не читає цей формат (див. document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) не вдалося помістити `{name}`: VectorCraft не читає цей формат (див. document.formats) +@msg can't read back the written PDF: {e} не вдалося прочитати записаний PDF: {e} +@msg can't read this Word document: {why} не вдалося прочитати цей документ Word: {why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp не можна записувати зображення .{e}: скористайтеся .png, .jpg, .gif, .webp, .tif або .bmp +@msg can't write PDF layers, overprint or invisible text: {why} не вдалося записати шари PDF, накладення або невидимий текст: {why} +@msg can't write the file's copyright: {why} не вдалося записати відомості про авторські права файлу: {why} +@msg can't write the file's modification date: {why} не вдалося записати дату змінення файлу: {why} +@msg can't write the pages' user units: {why} не вдалося записати користувацькі одиниці сторінок: {why} +@msg canceled скасовано +@msg cannot convert the object to a primitive value не вдається перетворити об’єкт на примітивне значення +@msg cannot convert undefined or null to an object не вдається перетворити undefined або null на об’єкт +@msg cannot distort a zero-size or non-finite bounding box не можна спотворити обмежувальну рамку нульового або нескінченного розміру +@msg cannot move an object into itself не можна перемістити об’єкт у самого себе +@msg case must be upper|lower|title|sentence case має бути upper|lower|title|sentence +@msg category `{c}`: one of {_1} category `{c}`: одне з: {_1} +@msg center must be [x, y] center має бути [x, y] +@msg character dimensions cannot be represented with the source text transform розміри символів не можна передати з трансформацією вихідного тексту +@msg character dimensions cannot be represented with the target text transform розміри символів не можна передати з трансформацією цільового тексту +@msg choose at least one artboard виберіть принаймні одну монтажну область +@msg choose at least one status виберіть принаймні один стан +@msg clip must be true or false clip має бути true або false +@msg clippingMask must be true or false clippingMask має бути true або false +@msg closed must be a boolean closed має бути логічним значенням +@msg collapsed must be true or false collapsed має бути true або false +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: немає профілю RGB або CMYK з іменем «{profile}» +@msg colorMode must be rgb or cmyk, not `{m}` colorMode має бути rgb або cmyk, а не `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel має бути {own} (режим документа), grayscale або bitmap +@msg colour groups can't go into colour groups групи кольорів не можна поміщати в групи кольорів +@msg colour, mode and spot apply to solid-colour swatches only colour, mode і spot застосовуються лише до зразків суцільного кольору +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read стовпець {column}: `{cell}` — це стовпець `%` файлів даних діаграм, які не читаються +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable стовпець {column}: `{cell}` указує `{name}`, а це змінна типу {_1} +@msg column {column}: `{cell}` names no variable стовпець {column}: `{cell}` не вказує жодної змінної +@msg column {column}: the variable name is longer than {MAX_NAME} characters стовпець {column}: ім’я змінної довше за {MAX_NAME} символів +@msg column: true floats the whole column and takes no onto, above or below column: true робить плаваючим увесь стовпець і не приймає onto, above або below +@msg command must be a shape.* command command має бути командою shape.* +@msg commands[{i}]: missing `command` commands[{i}]: відсутній `command` +@msg composer must be "singleLine" or "everyLine" composer має бути "singleLine" або "everyLine" +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes складені фігури приймають контури, складені контури, групи, складені фігури, текст, переходи й оболонки +@msg compound {_1} must contain exactly one open path to join складений об’єкт {_1} має містити рівно один відкритий контур для з’єднання +@msg connect needs a path other than the one extended для connect потрібен контур, відмінний від того, що продовжується +@msg connect needs id для connect потрібен id +@msg connect path has no open end контур для connect не має відкритого кінця +@msg continuation must be an array of path chunks continuation має бути масивом частин контуру +@msg continuation requires points для continuation потрібні points +@msg copy needs path: the file to copy into the document's folder для copy потрібен path: файл, який треба скопіювати в папку документа +@msg copy takes images only: relink placed documents where their files are copy приймає лише зображення: поміщені документи зв’яжіть повторно там, де розташовані їхні файли +@msg copying a file isn't available on the web копіювання файлу недоступне у вебверсії +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus має бути unknown, copyrighted або publicDomain +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl має бути URL-адресою (без пробілів, не більше 2048 символів) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners має містити 4 точки [x, y] (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`: item або file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`: лише сторінки PDF мають рамки art, trim, bleed і media; використайте "crop" або "bounding" +@msg crop rectangle doesn't overlap the image прямокутник обрізання не перетинається із зображенням +@msg cross-reference and object streams aren't supported потоки перехресних посилань і потоки об’єктів не підтримуються +@msg damaged compressed data: {e} пошкоджені стиснуті дані: {e} +@msg dash lengths must be numbers, got {v} довжини штрихів мають бути числами, отримано {v} +@msg dash must be a list of lengths or null, got {v} dash має бути списком довжин або null, отримано {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) ключ даних `{k}`: літери, цифри, -, _ і . (не name і не vc-…) +@msg data must be an object of key: value, not {v} data має бути об’єктом із пар ключ: значення, а не {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare набір даних `{name}` містить значення для `{var}`, яку бібліотека не оголошує +@msg data set `{name}` holds two values for `{var}` набір даних `{name}` містить два значення для `{var}` +@msg data set `{name}` is declared twice набір даних `{name}` оголошено двічі +@msg data takes at most {MAX_DATA} keys at once data приймає не більше {MAX_DATA} ключів за раз +@msg dataset `{name}` already exists набір даних `{name}` уже існує +@msg dataset `{to}` already exists набір даних `{to}` уже існує +@msg date `{d}`: YYYY-MM-DD date `{d}`: YYYY-MM-DD +@msg decoded data is too large декодовані дані завеликі +@msg define a variable first спочатку визначте змінну +@msg detect must be true or false detect має бути true або false +@msg direct-select anchor points of a path виділіть опорні точки контуру прямим виділенням +@msg distance must be > 0 distance має бути > 0 +@msg dx and dy give the arrow's direction dx і dy задають напрямок стрілки +@msg dx and dy must be numbers dx і dy мають бути числами +@msg dxf fitTo must be [width, height], positive dxf fitTo має бути [width, height] з додатними значеннями +@msg dxf must be an object of DXF import options dxf має бути об’єктом параметрів імпорту DXF +@msg dxf scale must be a positive number up to {_1}, not {s} dxf scale має бути додатним числом не більше {_1}, а не {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt dxf unit `{u}`: одиниця на зразок mm, cm, in або pt +@msg dxf: {e} dxf: {e} +@msg each format is an object {format, scale?, suffix?} кожен формат — це об’єкт {format, scale?, suffix?} +@msg each item needs an id кожному елементу потрібен id +@msg each match needs `kind` кожному збігу потрібен `kind` +@msg each match needs a `name` кожному збігу потрібен `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them кожен елемент `fonts` — це {family, style?}; їх має бути від 1 до 1000 +@msg each of `items` is {kind, item} кожен елемент `items` — це {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} кожен елемент `items` — це {{id, corners?}} з ідентифікатором об’єкта, а не {it} +@msg each path chunk must contain 1 to 4096 points кожна частина контуру має містити від 1 до 4096 точок +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) кожен вибір — це {{at: [x, y]}} або {{rect: [x0, y0, x1, y1]}} (отримано {v}) +@msg each row needs `from` кожному рядку потрібен `from` +@msg each stop needs a position кожній точці переходу потрібна позиція +@msg each subpath needs an anchors array кожному вкладеному контуру потрібен масив anchors +@msg each weight must be a number of 0 or more (got {v}) кожна вага має бути числом, 0 або більше (отримано {v}) +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType має бути easeIn, easeOut, easeInOut або easeOutIn +@msg editingData must be true or false editingData має бути true або false +@msg editingData must be true, false or "always" editingData має бути true, false або "always" +@msg edits overlap правки перекриваються +@msg effects of objects whose appearances differ can't be moved together ефекти об’єктів із різним оформленням не можна переміщувати разом +@msg empty header in column {_1} порожній заголовок у стовпці {_1} +@msg encoding `{_1}`: utf8 or utf16 encoding `{_1}`: utf8 або utf16 +@msg ends must be two of "first" and "last" ends має містити два значення з "first" і "last" +@msg every numbered name for the result is taken усі нумеровані імена для результату вже зайняті +@msg expanded SVG styles exceed the import size limit розгорнуті стилі SVG перевищують обмеження розміру імпорту +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes файл наборів експорту більший за {MAX_PRESET_FILE_BYTES} байтів +@msg exportText must be editable or appearance exportText має бути editable або appearance +@msg exportType must be movieClip or graphic, not `{s}` exportType має бути movieClip або graphic, а не `{s}` +@msg extend needs id для extend потрібен id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges містить не більше {MAX_FEATURE_CHANGES} тегів +@msg featureChanges must be a list of tags featureChanges має бути списком тегів +@msg features must be a list of tags features має бути списком тегів +@msg fidelity must be 0..100 fidelity має бути 0..100 +@msg files must be {name, dataBase64} files має бути {name, dataBase64} +@msg finally table {t} out of range таблиця finally {t} поза межами діапазону +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit має бути none|autoHeight|shrinkText|block, отримано {id} +@msg flags cannot be given with a RegExp object прапорці не можна задавати разом з об’єктом RegExp +@msg flattener: {e} зведення прозорості: {e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`: high, medium, low або збережений набір (див. flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile має бути along|across, отримано {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`: none, rows, format, item або itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels вкладеність папок понад {MAX_BATCH_DEPTH} рівнів +@msg folders deeper than {MAX_DEPTH} levels вкладеність папок понад {MAX_DEPTH} рівнів +@msg font family `{family}` is not available гарнітура `{family}` недоступна +@msg font family `{tf}` is not available гарнітура `{tf}` недоступна +@msg format `{f}`: pdf or postscript format `{f}`: pdf або postscript +@msg format `{f}`: text or json format `{f}`: text або json +@msg format is `{format}` формат: `{format}` +@msg formats must be an array of rows, not {v} formats має бути масивом рядків, а не {v} +@msg frequency must be between 1 and 300 lines per inch frequency має бути від 1 до 300 ліній на дюйм +@msg from and to must be finite from і to мають бути скінченними +@msg from must be [x0, y0, x1, y1] from має бути [x0, y0, x1, y1] +@msg function declaration index out of range індекс оголошення функції поза межами діапазону +@msg give `before` or `after`, not both укажіть `before` або `after`, але не обидва +@msg give `char`, `text`, `code` or `unicode` укажіть `char`, `text`, `code` або `unicode` +@msg give `colors`, or `color` and `rule` укажіть `colors` або `color` і `rule` +@msg give `count` or `spacing`, not both укажіть `count` або `spacing`, але не обидва +@msg give `event`, such as onclick укажіть `event`, наприклад onclick +@msg give `file`, the JavaScript file's path or URL укажіть `file` — шлях або URL-адресу файлу JavaScript +@msg give `fill` and/or `stroke` укажіть `fill` і/або `stroke` +@msg give `fill`, `stroke` or both укажіть `fill`, `stroke` або обидва +@msg give `folder` укажіть `folder` +@msg give `fonts`, or open a document укажіть `fonts` або відкрийте документ +@msg give `from` or `indices`, not both укажіть `from` або `indices`, але не обидва +@msg give `id` (no text is being edited) укажіть `id` (жоден текст зараз не редагується) +@msg give `id` and `anchors`, or `items`, not both укажіть `id` і `anchors` або `items`, але не те й інше +@msg give `id` or `guide`, not both укажіть `id` або `guide`, але не обидва +@msg give `length` or `to` укажіть `length` або `to` +@msg give `matches`: [{kind, name, target}] укажіть `matches`: [{kind, name, target}] +@msg give `name` (or edit a pattern) укажіть `name` (або редагуйте візерунок) +@msg give `name` or `names` укажіть `name` або `names` +@msg give `name`, an SVG filter's name укажіть `name` — ім’я фільтра SVG +@msg give `path` (a .wasm file) or `dataBase64` (the module) укажіть `path` (файл .wasm) або `dataBase64` (модуль) +@msg give `path`, `data` or `dataBase64` укажіть `path`, `data` або `dataBase64` +@msg give `path`, `data`, `dataBase64` or `csv` укажіть `path`, `data`, `dataBase64` або `csv` +@msg give `path`, `xml` or `dataBase64` укажіть `path`, `xml` або `dataBase64` +@msg give `path`, an SVG file, or `svg`, SVG text укажіть `path` (файл SVG) або `svg` (текст SVG) +@msg give `script`, the JavaScript the event runs ("" removes the event) укажіть `script` — код JavaScript, який виконує подія ("" вилучає подію) +@msg give `text` (the reading) or `remove` укажіть `text` (читання) або `remove` +@msg give `x` and `y` (and a `tolerance` of 0 or more) укажіть `x` і `y` (а також `tolerance`, 0 або більше) +@msg give `xml`, one SVG element укажіть `xml` — один елемент SVG +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments укажіть mode або корекції r/g/b, c/m/y/k, gray чи tint +@msg give a name, or paint the active proxy with a saved preset укажіть ім’я або зафарбуйте активний індикатор збереженим набором +@msg give a name, or select a stroke with a saved profile укажіть ім’я або виділіть обведення зі збереженим профілем +@msg give a pattern name or select an object painted with a pattern укажіть ім’я візерунка або виділіть об’єкт, зафарбований візерунком +@msg give at least one of clip, invert, disabled, linked укажіть принаймні одне з: clip, invert, disabled, linked +@msg give color, none, swatch or gradient укажіть color, none, swatch або gradient +@msg give dataBase64 укажіть dataBase64 +@msg give either at (a click) or area (a path drawn around the overlaps) укажіть at (клацання) або area (контур, намальований навколо перекриттів) +@msg give exactly one of `path`, `data` and `dataBase64` укажіть рівно одне з: `path`, `data`, `dataBase64` +@msg give formats or a preset, not both укажіть formats або набір, але не те й інше +@msg give index or indexes, not both укажіть index або indexes, але не обидва +@msg give newName or delete укажіть newName або delete +@msg give offset or by укажіть offset або by +@msg give one element укажіть один елемент +@msg give one of `to`, `rect` or `at` укажіть одне з: `to`, `rect` або `at` +@msg give one of assets, layers, selection or fullDocument укажіть одне з: assets, layers, selection або fullDocument +@msg give only one of onto, above, below, icon and a column place укажіть лише одне з: onto, above, below, icon або місце у стовпці +@msg give opacity or blend укажіть opacity або blend +@msg give origin: [x, y], or reset: true укажіть origin: [x, y] або reset: true +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note укажіть overprintFill, overprintStroke, showCenter, imageMap, url або note +@msg give path (a file) or folder укажіть path (файл) або folder +@msg give path, or name and dataBase64 укажіть path або name і dataBase64 +@msg give paths or files укажіть paths або files +@msg give rows, rowHeight, columns or columnWidth укажіть rows, rowHeight, columns або columnWidth +@msg give svg, or dataBase64 of an SVG or SVGZ file укажіть svg або dataBase64 файлу SVG чи SVGZ +@msg give text укажіть text +@msg give the asset a name дайте ресурсу ім’я +@msg give the preset a `name` дайте набору `name` +@msg give the preset a name дайте набору ім’я +@msg give the preset a name (1–64 characters) дайте набору ім’я (1–64 символи) +@msg give the settings to change, e.g. {"formats"}]} укажіть параметри, які треба змінити, наприклад {"formats"}]} +@msg give the style a name дайте стилю ім’я +@msg give two or more style `names` укажіть два або більше стилів у `names` +@msg give two or more swatches укажіть два або більше зразків +@msg give x, y, or a handle's length or angle укажіть x, y або довжину чи кут маркера +@msg give {key, value} or {values} укажіть {key, value} або {values} +@msg global must be true or false global має бути true або false +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) гліф {_1} не малюється для {ch} зі стилем цього символу (як-от «Усі великі», «Капітель» або система числення) +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} гліф {g} не є альтернативою для {ch} у {_1} {_2} +@msg gradient must be "objects" or "mesh" gradient має бути "objects" або "mesh" +@msg grays must be "profile" or "black", not `{g}` grays має бути "profile" або "black", а не `{g}` +@msg gridColors must be a preset name or two colours gridColors має бути іменем набору або двома кольорами +@msg gridSize must be small, medium or large gridSize має бути small, medium або large +@msg guide {g} is not selected напрямну {g} не виділено +@msg handle must be 0..3 handle має бути 0..3 +@msg handler at {_1} expects {_2} values but its range starts with {d} обробник за адресою {_1} очікує значень: {_2}, але його діапазон починається з {d} +@msg handler depth too large глибина обробника завелика +@msg handler range {_1}..{_2} out of range діапазон обробника {_1}..{_2} поза допустимими межами +@msg hiddenLayers must be true or false hiddenLayers має бути true або false +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}: "layer" (за замовчуванням), "omit" або "visible" +@msg highlight a layer to merge into виділіть шар, з яким треба об’єднати +@msg highlight two or more rows in one layer or group виділіть два або більше рядків в одному шарі чи групі +@msg highlight two or more rows to merge виділіть два або більше рядків для об’єднання +@msg hvcC hvcC +@msg icon docks a panel in the icon column: use window.panel.dock icon закріплює панель у стовпці значків: скористайтеся window.panel.dock +@msg id must be a dimension id id має бути ідентифікатором розміру +@msg image has no bounds зображення не має меж +@msg image {_1} is linked already зображення {_1} уже пов’язане +@msg imageMap `{m}`: none, client or server imageMap `{m}`: none, client або server +@msg images in a spot ink whose samples couldn't be read were imported in RGB зображення у плашковій фарбі, відліки яких не вдалося прочитати, імпортовано в RGB +@msg images past the most pixels one file may hold were left out зображення понад найбільшу кількість пікселів, дозволену для одного файлу, пропущено +@msg images too large to read were left out зображення, завеликі для читання, пропущено +@msg index out of range індекс поза межами діапазону +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes може містити не більше {MAX_GUIDE_INDEXES} записів +@msg indexes must be an array of guide indexes indexes має бути масивом індексів напрямних +@msg instances must be 1..1000 instances має бути 1..1000 +@msg instruction out of range інструкція поза межами діапазону +@msg instruction {pc} ({op}) pops below the stack інструкція {pc} ({op}) знімає значення нижче дна стека +@msg instruction {pc} reached with stack depths {seen} and {d} до інструкції {pc} можна дійти з глибинами стека {seen} і {d} +@msg invalid `color` неприпустимий `color` +@msg invalid anchor index {v}: expected a non-negative integer неприпустимий індекс опорної точки {v}: очікується невід’ємне ціле число +@msg invalid anchor reference {v}: expected [subpath, anchor] неприпустиме посилання на опорну точку {v}: очікується [subpath, anchor] +@msg invalid angle неприпустимий кут +@msg invalid array length неприпустима довжина масиву +@msg invalid brush: {e} неприпустимий пензель: {e} +@msg invalid color неприпустимий колір +@msg invalid colour {c} неприпустимий колір {c} +@msg invalid delta неприпустима дельта +@msg invalid group неприпустима група +@msg invalid object id {v}: expected a non-negative integer неприпустимий ідентифікатор об’єкта {v}: очікується невід’ємне ціле число +@msg invalid point неприпустима точка +@msg invalid regular expression flag '{_1}' неприпустимий прапорець регулярного виразу '{_1}' +@msg invalid regular expression: {m} неприпустимий регулярний вираз: {m} +@msg invalid size неприпустимий розмір +@msg invalid t неприпустиме значення t +@msg invalid time value неприпустиме значення часу +@msg invalid tolerance неприпустимий допуск +@msg is a confirm's answer, whose `value` must be true or false є відповіддю на confirm, чиє `value` має бути true або false +@msg is a prompt's answer, whose `value` must be a string or null є відповіддю на prompt, чиє `value` має бути рядком або null +@msg is an alert's answer, whose `value` must be "ok" є відповіддю на alert, чиє `value` має бути "ok" +@msg is longer than 16 KiB довший за 16 КіБ +@msg it draws its image through a clipping path він малює своє зображення через відсічний контур +@msg it draws no image він не малює жодного зображення +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes він більший за {MAX_DICTIONARY_FILE_BYTES} байтів +@msg it isn't a file це не файл +@msg it names {_1} plates, and at most {MAX_PLATES} are read він називає форм: {_1}, а читається не більше {MAX_PLATES} +@msg its art is too long or too detailed for a head його графіка задовга або занадто детальна для наконечника +@msg its art paints nothing його графіка нічого не зафарбовує +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) усі його монтажні області лежать поза полотном (далі ніж {_1} pt від початку координат) +@msg its clipping masks are nested too deeply його відсічні маски мають завелику вкладеність +@msg its image is too large to read його зображення завелике для читання +@msg its metadata has no description його метадані не мають опису +@msg its metadata has no direct length його метадані не мають прямо заданої довжини +@msg its plates are too large: more than {MAX_SAMPLES} pixels together його форми завеликі: разом понад {MAX_SAMPLES} пікселів +@msg its plates are too large: {_1} plates of {w} × {h} pixels його форми завеликі: форм {_1}, кожна {w} × {h} пікселів +@msg its plates can't be kept together його форми не можна зберегти разом +@msg its shapes could not be combined into one його фігури не вдалося об’єднати в одну +@msg join two different paths з’єднуйте два різні контури +@msg jump to {pc}, past the end перехід до {pc}, за межі кінця +@msg justify must be auto|left|center|right|justifyAll justify має бути auto|left|center|right|justifyAll +@msg kbps must be a positive number, not {kbps} kbps має бути додатним числом, а не {kbps} +@msg keywords must be a list of words or a comma-separated string keywords має бути списком слів або рядком зі словами через кому +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters кожне значення keywords має містити не більше {MAX_KEYWORD} символів і не мати керівних символів +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind має бути 1, 2 або 3 чи вкажіть `name` набору (див. perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind має бути graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle або paraStyle +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout має бути "on", "off", "neutral" або логічним значенням, а не {v} +@msg layer `{name}` has no art to export шар `{name}` не містить графіки для експорту +@msg layer must name a layer or sublayer layer має вказувати шар або підшар +@msg layers can't be intertwined: select the objects in them шари не можна переплітати: виділіть об’єкти в них +@msg layers go only at the top level or in layers, objects in layers and groups шари розміщуються лише на верхньому рівні або в шарах, об’єкти — у шарах і групах +@msg layers must be true or false layers має бути true або false +@msg layout must be gridByRow, gridByColumn, row or column layout має бути gridByRow, gridByColumn, row або column +@msg leading must be a number or "auto" leading має бути числом або "auto" +@msg left/right must be non-negative widths left/right мають бути невід’ємними ширинами +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding має бути одним з: {_1} +@msg level `{t}`: 2 or 3 level `{t}`: 2 або 3 +@msg line {line}: more than {MAX_CSV_COLS} columns рядок {line}: понад {MAX_CSV_COLS} стовпців +@msg line {line}: unclosed quote рядок {line}: незакриті лапки +@msg line {line}: value for `{var}` must match its variable kind рядок {line}: значення `{var}` має відповідати типу її змінної +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`: lf або crlf +@msg loading profiles from disk isn't available in the browser завантаження профілів із диска недоступне у браузері +@msg localize: the first argument must be a localization object or a string localize: перший аргумент має бути об’єктом локалізації або рядком +@msg margin: a {m} pt margin leaves no imageable area on the paper поле: поле {m} pt не залишає на папері області друку +@msg matrix must be finite and invertible, depth finite matrix має бути скінченною й оборотною, depth — скінченною +@msg matte: {e} підкладка: {e} +@msg member is for order front, forward, backward or back member призначений для order front, forward, backward або back +@msg missing `artboards` відсутній `artboards` +@msg missing `at` [x, y] відсутній `at` [x, y] +@msg missing `attrs` відсутній `attrs` +@msg missing `color` or `opacity` відсутній `color` або `opacity` +@msg missing `colors` відсутній `colors` +@msg missing `commands` відсутній `commands` +@msg missing `destinationFolder` відсутній `destinationFolder` +@msg missing `effect` id відсутній ідентифікатор `effect` +@msg missing `family` відсутній `family` +@msg missing `find` відсутній `find` +@msg missing `gid` (a glyph id, or null for the default glyph) відсутній `gid` (ідентифікатор гліфа або null для стандартного гліфа) +@msg missing `group` відсутній `group` +@msg missing `id` відсутній `id` +@msg missing `id`: give one, or select one type object відсутній `id`: укажіть його або виділіть один текстовий об’єкт +@msg missing `index` (a point index, or null to clear) відсутній `index` (індекс точки або null для очищення) +@msg missing `index` (a stop index, or null to clear) відсутній `index` (індекс точки переходу або null для очищення) +@msg missing `index` (an item index or null) відсутній `index` (індекс елемента або null) +@msg missing `item` відсутній `item` +@msg missing `library` відсутній `library` +@msg missing `library` (see graphicStyle.libraries) відсутній `library` (див. graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) відсутній `library` (див. swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) відсутній `map` (рядки [{from, to}] або {current}) +@msg missing `matrix` [a,b,c,d,e,f] відсутній `matrix` [a,b,c,d,e,f] +@msg missing `name` (see gradient.presets.list) відсутній `name` (див. gradient.presets.list) +@msg missing `offset` (a distance in points) відсутній `offset` (відстань у пунктах) +@msg missing `path` відсутній `path` +@msg missing `path` (a path object id) відсутній `path` (ідентифікатор об’єкта-контуру) +@msg missing `picks` відсутній `picks` +@msg missing `rule` відсутній `rule` +@msg missing `source` id відсутній ідентифікатор `source` +@msg missing `start` (the byte offset of a character) відсутній `start` (зміщення символу в байтах) +@msg missing `status`: linked, embedded, missing or modified відсутній `status`: linked, embedded, missing або modified +@msg missing `target` id відсутній ідентифікатор `target` +@msg missing `text` відсутній `text` +@msg missing `to` відсутній `to` +@msg missing `to` (an index) відсутній `to` (індекс) +@msg missing `to`: a non-negative integer відсутній `to`: невід’ємне ціле число +@msg missing `type` відсутній `type` +@msg missing `value` відсутній `value` +@msg missing `word` відсутній `word` +@msg missing `{key}` відсутній `{key}` +@msg missing `{k}` відсутній `{k}` +@msg missing anchor відсутня опорна точка +@msg missing anchors відсутні опорні точки +@msg missing anchors [[subpath, anchor]…] відсутні anchors [[subpath, anchor]…] +@msg missing array `{key}` відсутній масив `{key}` +@msg missing case відсутній case +@msg missing color відсутній колір +@msg missing commands відсутні команди +@msg missing destination folder відсутня папка призначення +@msg missing from відсутній from +@msg missing group відсутня група +@msg missing id or ids відсутній id або ids +@msg missing ids відсутній ids +@msg missing indexes відсутній indexes +@msg missing matrix [a,b,c,d,e,f] відсутня matrix [a,b,c,d,e,f] +@msg missing name відсутнє ім’я +@msg missing op відсутній op +@msg missing or invalid `color` відсутній або неприпустимий `color` +@msg missing or unknown `tool` відсутній або невідомий `tool` +@msg missing point відсутня точка +@msg missing point [x,y] відсутня точка [x,y] +@msg missing point `{key}` ([x, y]) відсутня точка `{key}` ([x, y]) +@msg missing points відсутні точки +@msg missing points [[x,y]…] відсутні точки [[x,y]…] +@msg missing segment відсутній сегмент +@msg missing stops відсутні точки переходу +@msg missing subpaths відсутні вкладені контури +@msg missing target відсутній target +@msg missing to відсутній to +@msg missing x, y відсутні x, y +@msg mode is set, add or toggle, not `{other}` mode — це set, add або toggle, а не `{other}` +@msg mode must be "cmyk" or "rgb" mode має бути "cmyk" або "rgb" +@msg mode must be area|onPath, got {o} mode має бути area|onPath, отримано {o} +@msg more than 10000 linked files: package dependencies in smaller parts понад 10000 пов’язаних файлів: упаковуйте залежності меншими частинами +@msg more than 256 commands понад 256 команд +@msg more than {MAX_BATCH_FILES} data sets понад {MAX_BATCH_FILES} наборів даних +@msg more than {MAX_BATCH_FILES} files понад {MAX_BATCH_FILES} файлів +@msg more than {MAX_CSV_ROWS} data rows понад {MAX_CSV_ROWS} рядків даних +@msg more than {MAX_FILES} images понад {MAX_FILES} зображень +@msg more than {MAX_IMPORT} presets понад {MAX_IMPORT} наборів +@msg moved must be [[x,y]…] moved має бути [[x,y]…] +@msg must be an object with `kind` and `value` має бути об’єктом із `kind` і `value` +@msg must have `kind` alert, confirm or prompt повинен мати `kind` зі значенням alert, confirm або prompt +@msg must have exactly `kind` and `value` повинен мати рівно `kind` і `value` +@msg name (a toolbar's name) is required name (ім’я панелі інструментів) є обов’язковим +@msg name (a toolbar's name) must be a string name (ім’я панелі інструментів) має бути рядком +@msg name must be a toolbar's name name має бути іменем панелі інструментів +@msg names is empty names порожній +@msg names must be an array of symbol names names має бути масивом імен символів +@msg need `anchor` or `segment` + `t` потрібен `anchor` або `segment` + `t` +@msg need at least one anchor потрібна принаймні одна опорна точка +@msg need at least one point потрібна принаймні одна точка +@msg need at least two points потрібні принаймні дві точки +@msg needs a closing quote after the name потрібні закривні лапки після імені +@msg needs a name потрібне ім’я +@msg needs a name of at most 64 bytes потрібне ім’я довжиною не більше 64 байтів +@msg needs a name of letters, digits and _, or a quoted name потрібне ім’я з літер, цифр і _ або ім’я в лапках +@msg needs a name without control characters потрібне ім’я без керівних символів +@msg needs points потрібні точки +@msg nesting too deep завелика вкладеність +@msg nineSlice must be true or false nineSlice має бути true або false +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides має бути [left, top, right, bottom], кожне значення -16..16, причому left + right < 1 і top + bottom < 1 +@msg no HP-GL format немає формату HP-GL +@msg no New Document profile called `{shown}` ({_1}) немає профілю нового документа з іменем `{shown}` ({_1}) +@msg no PDF preset named `{n}` немає набору PDF з іменем `{n}` +@msg no SVG filter at index {k} немає фільтра SVG з індексом {k} +@msg no action set is current немає поточного набору дій +@msg no active data set немає активного набору даних +@msg no appearance item {index} немає елемента оформлення {index} +@msg no arrowheads file here тут немає файлу стрілок +@msg no artboard {_1} немає монтажної області {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) немає монтажної області {artboard} (нумерація з 0; у документі їх {_1}) +@msg no asset {id} немає ресурсу {id} +@msg no asset {id} (see assets.list) немає ресурсу {id} (див. assets.list) +@msg no assets to export немає ресурсів для експорту +@msg no assets yet: collect art for export first (assets.add) ресурсів ще немає: спочатку зберіть графіку для експорту (assets.add) +@msg no brush named `{n}` немає пензля з іменем `{n}` +@msg no character at byte {start} немає символу в байті {start} +@msg no character has code {code} жоден символ не має коду {code} +@msg no colour немає кольору +@msg no colour group `{group}` немає групи кольорів `{group}` +@msg no colour group `{name}` немає групи кольорів `{name}` +@msg no custom arrowhead {name} (see stroke.arrowheads.list) немає користувацької стрілки {name} (див. stroke.arrowheads.list) +@msg no data sets немає наборів даних +@msg no dataset named `{from}` немає набору даних з іменем `{from}` +@msg no dataset named `{name}` немає набору даних з іменем `{name}` +@msg no datasets немає наборів даних +@msg no design named `{name}` немає дизайну з назвою `{name}` +@msg no design named `{n}` немає дизайну з назвою `{n}` +@msg no design named `{old}` немає дизайну з назвою `{old}` +@msg no encoder for {_1} yet кодувальника для {_1} ще немає +@msg no export preset `{name}` (see document.exportPresets.list) немає набору експорту `{name}` (див. document.exportPresets.list) +@msg no file to open немає файлу для відкриття +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder немає папки: передайте `path` або задайте Налаштування › Модулі та робочі диски › Додаткова папка модулів +@msg no fonts to look for: they are all available немає шрифтів для пошуку: усі вони доступні +@msg no formats to export немає форматів для експорту +@msg no gradient preset `{name}` (see gradient.presets.list) немає набору градієнта `{name}` (див. gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` немає графічного стилю `{n}` у `{_1}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) немає бібліотеки графічних стилів `{key}` (див. graphicStyle.libraries) +@msg no guide selected не виділено жодної напрямної +@msg no guide selected (or give an index) не виділено жодної напрямної (або вкажіть індекс) +@msg no guide {i} немає напрямної {i} +@msg no images in the folder у папці немає зображень +@msg no instructions немає інструкцій +@msg no item at index {_1} немає елемента з індексом {_1} +@msg no item at index {from} немає елемента з індексом {from} +@msg no item at one of the indices {idx} немає елемента з одним з індексів {idx} +@msg no layer немає шару +@msg no layer to rename немає шару для перейменування +@msg no layer {id} (see layer.list) немає шару {id} (див. layer.list) +@msg no layers to export немає шарів для експорту +@msg no library `{key}` немає бібліотеки `{key}` +@msg no metafile format немає формату метафайлу +@msg no object {_1} немає об’єкта {_1} +@msg no objects немає об’єктів +@msg no parent layer немає батьківського шару +@msg no perspective grid preset named `{b}` (see perspective.presets.list) немає набору сітки перспективи з іменем `{b}` (див. perspective.presets.list) +@msg no perspective grid preset named `{n}` немає набору сітки перспективи з іменем `{n}` +@msg no point selected (give `index`) не виділено жодної точки (укажіть `index`) +@msg no preset called `{from}` (see file.newPresets) немає набору з іменем `{from}` (див. file.newPresets) +@msg no preset called `{name}` (see file.newPresets) немає набору з іменем `{name}` (див. file.newPresets) +@msg no preset named `{n}` немає набору з іменем `{n}` +@msg no print preset named `{b}` (see print.presets.list) немає набору друку з іменем `{b}` (див. print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) немає набору друку з іменем `{name}` (набори: {_1}) +@msg no print preset named `{n}` немає набору друку з іменем `{n}` +@msg no profile named `{name}` немає профілю з іменем `{name}` +@msg no random numbers ({e}) немає випадкових чисел ({e}) +@msg no readable files in the folder у папці немає файлів, які можна прочитати +@msg no recovery copy `{f}` (see file.recovery.list) немає копії для відновлення `{f}` (див. file.recovery.list) +@msg no rows немає рядків +@msg no saved PDF preset named `{name}` немає збереженого набору PDF з іменем `{name}` +@msg no saved export preset `{name}` немає збереженого набору експорту `{name}` +@msg no saved gradient preset called {name} немає збереженого набору градієнта з іменем {name} +@msg no saved gradient presets немає збережених наборів градієнта +@msg no saved preset called `{name}` немає збереженого набору з іменем `{name}` +@msg no saved presets to export (name [Default] in `names` to export it) немає збережених наборів для експорту (укажіть [Default] у `names`, щоб експортувати його) +@msg no saved presets to export (name built-in ones in `names`) немає збережених наборів для експорту (вбудовані вкажіть у `names`) +@msg no saved print preset named `{name}` немає збереженого набору друку з іменем `{name}` +@msg no saved profile called {name} немає збереженого профілю з іменем {name} +@msg no saved selection `{name}` немає збереженого виділення `{name}` +@msg no saved theme called {name} (see colorTheme.list) немає збереженої теми з іменем {name} (див. colorTheme.list) +@msg no saved views немає збережених виглядів +@msg no selected or given object has a visible 3D effect жоден виділений або вказаний об’єкт не має видимого 3D-ефекту +@msg no series {i} немає ряду {i} +@msg no slice {n} in the image (see slice.list) у зображенні немає фрагмента {n} (див. slice.list) +@msg no source art немає вихідної графіки +@msg no stop {i} (the gradient has {n}) немає точки переходу {i} (у градієнті їх {n}) +@msg no style `{name}` немає стилю `{name}` +@msg no such artboard {v} такої монтажної області немає: {v} +@msg no such assets (see assets.list) таких ресурсів немає (див. assets.list) +@msg no such frame anchor, or the frame would reach past the canvas такої опорної точки рамки немає або рамка вийшла б за межі полотна +@msg no such objects таких об’єктів немає +@msg no such path anchor, or the path would reach past the canvas такої опорної точки контуру немає або контур вийшов би за межі полотна +@msg no such rows таких рядків немає +@msg no swatch `{keep}` немає зразка `{keep}` +@msg no swatch `{n}` немає зразка `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) немає зразка `{n}` (зразки та групи кольорів переміщуються окремо) +@msg no swatch library `{key}` (see swatch.library.list) немає бібліотеки зразків `{key}` (див. swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) немає бібліотеки зразків `{key}` (див. swatch.library.list; "document": зразки документа) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) немає бібліотеки зразків `{key}` (див. swatch.library.list; "{DOCUMENT_SWATCHES}": зразки документа) +@msg no swatch named `{n}` немає зразка з іменем `{n}` +@msg no swatch or colour group `{n}` немає зразка або групи кольорів `{n}` +@msg no swatch or colour group `{n}` in `{_1}` немає зразка або групи кольорів `{n}` у `{_1}` +@msg no symbol named `{_1}` немає символу з іменем `{_1}` +@msg no symbol named `{name}` немає символу з іменем `{name}` +@msg no text objects selected не виділено жодного текстового об’єкта +@msg no toolbar named `{_1}` немає панелі інструментів з іменем `{_1}` +@msg no toolbar named `{name}` немає панелі інструментів з іменем `{name}` +@msg no tracing preset `{preset}` (see imageTrace.presets) немає набору трасування `{preset}` (див. imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) тут немає папки користувацьких бібліотек (збережіть зі шляхом або без нього, щоб отримати дані) +@msg no variable named `{from}` немає змінної з іменем `{from}` +@msg no variable named `{name}` немає змінної з іменем `{name}` +@msg no variable named `{var}` немає змінної з іменем `{var}` +@msg no variable named `{v}` немає змінної з іменем `{v}` +@msg no view named `{name}` немає вигляду з іменем `{name}` +@msg node {_1} already has the text variable `{_2}` вузол {_1} уже має текстову змінну `{_2}` +@msg node {_1} is not a graph вузол {_1} не є діаграмою +@msg node {_1} is not an image вузол {_1} не є зображенням +@msg node {_1} is not type вузол {_1} не є текстом +@msg not a DCS file це не файл DCS +@msg not a PDF presets file (format `{_1}`) це не файл наборів PDF (формат `{_1}`) +@msg not a PDF presets file: {e} це не файл наборів PDF: {e} +@msg not a RegExp object це не об’єкт RegExp +@msg not a Word document: its WordDocument stream doesn't start with a file information block це не документ Word: його потік WordDocument не починається з блоку відомостей про файл +@msg not a Word document: its compound file has no WordDocument stream це не документ Word: його складений файл не має потоку WordDocument +@msg not a Word document: its main part is not a WordprocessingML document це не документ Word: його основна частина не є документом WordprocessingML +@msg not a Word document: its package has no main document part це не документ Word: його пакет не має основної частини документа +@msg not a closed path це не замкнений контур +@msg not a flattener presets file (format `{_1}`) це не файл наборів зведення прозорості (формат `{_1}`) +@msg not a flattener presets file: {e} це не файл наборів зведення прозорості: {e} +@msg not a layer or group це не шар і не група +@msg not a library file: {e} це не файл бібліотеки: {e} +@msg not a mesh envelope point це не точка сітки оболонки +@msg not a perspective grid presets file (format `{_1}`) це не файл наборів сітки перспективи (формат `{_1}`) +@msg not a perspective grid presets file: {e} це не файл наборів сітки перспективи: {e} +@msg not a print presets file (format `{_1}`) це не файл наборів друку (формат `{_1}`) +@msg not a print presets file: {e} це не файл наборів друку: {e} +@msg not a regular expression це не регулярний вираз +@msg not a stored or deflated central record це не збережений і не стиснутий методом deflate центральний запис +@msg not a valid SVGZ file: {e} це не припустимий файл SVGZ: {e} +@msg not an RTF file: it doesn't start with {\\rtf це не файл RTF: він не починається з {\\rtf +@msg not an action set: the text doesn't start with /version and a number це не набір дій: текст не починається з /version і числа +@msg not an image VectorCraft reads (see document.formats) це не зображення, яке читає VectorCraft (див. document.formats) +@msg not area type (text in a frame) outside perspective це не текст в області (текст у рамці) поза перспективою +@msg not editing an opacity mask маска непрозорості не редагується +@msg not type on a path outside perspective це не текст за контуром поза перспективою +@msg nothing prints нічого не друкується +@msg nothing to bind немає чого прив’язувати +@msg nothing to change немає чого змінювати +@msg nothing to change: give dx, dy, hScale, vScale or rotation немає чого змінювати: укажіть dx, dy, hScale, vScale або rotation +@msg nothing to crop немає чого обрізати +@msg nothing to export немає чого експортувати +@msg nothing to export: the document has no visible art немає чого експортувати: документ не містить видимої графіки +@msg nothing to mark немає чого позначати +@msg nothing to print: every artboard picked is blank немає чого друкувати: усі вибрані монтажні області порожні +@msg nothing to print: the document has no art that prints немає чого друкувати: документ не містить графіки, яка друкується +@msg nothing to repeat немає чого повторювати +@msg numbers out of order in {_1} quantifier числа в неправильному порядку у квантифікаторі {_1} +@msg object is not a path об’єкт не є контуром +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object прозорість об’єкта не можна перенести на виділені символи; вимкніть «Прозорість» у параметрах піпетки або виділіть увесь об’єкт +@msg object {_1} can't be set as point type об’єкт {_1} не можна зробити текстом із точки +@msg object {_1} has lost its file's contents: relink it об’єкт {_1} втратив вміст свого файлу: зв’яжіть його повторно +@msg object {_1} has no path об’єкт {_1} не має контуру +@msg object {_1} has no size об’єкт {_1} не має розміру +@msg object {_1} is hidden or locked, or on a locked artboard об’єкт {_1} приховано чи заблоковано або він розташований на заблокованій монтажній області +@msg object {_1} is not a placed document об’єкт {_1} не є поміщеним документом +@msg object {_1} is not an Intertwine group об’єкт {_1} не є групою переплетення +@msg object {_1} is not an image об’єкт {_1} не є зображенням +@msg object {_1} is not an image or a linked file об’єкт {_1} не є зображенням або пов’язаним файлом +@msg object {_1} is not an image or a placed document об’єкт {_1} не є зображенням або поміщеним документом +@msg object {_1} is not in this Intertwine group об’єкт {_1} не входить до цієї групи переплетення +@msg object {_1} needs a 3D effect об’єкту {_1} потрібен 3D-ефект +@msg object {_1} needs a 3D effect at index {index} об’єкту {_1} потрібен 3D-ефект з індексом {index} +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic об’єкт {_1}: порожнисте «Видавлювання та фаска» не має торців: увімкніть «Торець», щоб накласти графіку +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front об’єкт {_1}: за глибини 0 об’єкт має одну грань: накладіть його графіку на передню грань +@msg object {_1}: {_2} об’єкт {_1}: {_2} +@msg object {_1}: {e} об’єкт {_1}: {e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams об’єкт {_1}: {name} з індексом {index} не має наборів освітлення; задайте його джерела світла за допомогою effect.setParams +@msg object {id} has anchors 0–{_1} for `corners`, not {past} об’єкт {id} має для `corners` опорні точки 0–{_1}, а не {past} +@msg object {id} has {n} anchors, so no anchor {past} об’єкт {id} має опорних точок: {n}, тому точки {past} немає +@msg objects must be none, move or copy objects має бути none, move або copy +@msg objects were left out об’єкти пропущено +@msg offset out of range зміщення поза межами діапазону +@msg on must be true or false on має бути true або false +@msg only groups, layers, type and images have a Contents (Characters) row рядок «Вміст (символи)» мають лише групи, шари, текст і зображення +@msg only interior mesh lines can be deleted видаляти можна лише внутрішні лінії сітки +@msg only solid colours can be added (not gradients or patterns) додавати можна лише суцільні кольори (не градієнти й не візерунки) +@msg only solid-colour swatches can be spot colours плашковими кольорами можуть бути лише зразки суцільного кольору +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first вдалося прочитати лише вбудований попередній перегляд цього файлу Affinity: скористайтеся командою Файл › Відкрити, щоб побачити його з попередженням, або спочатку експортуйте SVG чи PDF з Affinity +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning вдалося прочитати лише вбудований попередній перегляд цього файлу Affinity; скористайтеся командою Файл › Відкрити, щоб побачити його з попередженням +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon і місце у стовпці належать до window.panel.float і window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto, above, below, icon і місце у стовпці не приймають anchor, side, zone або before +@msg onto, above, below, icon and a column place take no x, y, width or height onto, above, below, icon і місце у стовпці не приймають x, y, width або height +@msg opacity must be a number opacity має бути числом +@msg open a document to look for its linked files відкрийте документ, щоб шукати його пов’язані файли +@msg operand too large операнд завеликий +@msg options must be an object (see file.formatOptions) options має бути об’єктом (див. file.formatOptions) +@msg order is a whole number from 0 to {others}, not `{v}` order — це ціле число від 0 до {others}, а не `{v}` +@msg order must be leftToRight or rightToLeft order має бути leftToRight або rightToLeft +@msg orientation must be portrait or landscape, not `{o}` orientation має бути portrait або landscape, а не `{o}` +@msg outlineText {v}: true or false outlineText {v}: true або false +@msg output intent profile: {e} профіль призначення виведення: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: немає профілю RGB або CMYK з іменем «{name}» +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: файли {_1} мають модель CMYK, а «{name}» — це профіль RGB +@msg output.destination: {e} output.destination: {e} +@msg output.inks: no ink is set to print output.inks: жодну фарбу не призначено для друку +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: немає профілю з іменем «{name}», а призначення виведення {std} вбудовує свій профіль +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent: немає профілю з іменем «{name}»: призначення виведення {std} вбудовує профіль або вказує зареєстровану умову друку (outputConditionId і registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: файли {std} друкуються в CMYK, а «{name}» — це профіль RGB +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: «{name}» — це профіль відтінків сірого, який не може бути призначенням виведення {std} +@msg overlap: {o} pt is half a tile or more перекриття: {o} pt — це половина плитки або більше +@msg overlapping Shaper paths must be in the same layer or group контури формоутворення, що перекриваються, мають бути в одному шарі або групі +@msg overlapping edits правки перекриваються +@msg page art conflicts with image data for {key} графіка сторінки конфліктує з даними зображення для {key} +@msg page art conflicts with pattern {name} графіка сторінки конфліктує з візерунком {name} +@msg page art conflicts with swatch {name} графіка сторінки конфліктує зі зразком {name} +@msg page art is missing image data for {key} у графіці сторінки бракує даних зображення для {key} +@msg page art is missing pattern {name} у графіці сторінки бракує візерунка {name} +@msg page art is missing swatch {name} у графіці сторінки бракує зразка {name} +@msg page must be a whole number from 1 page має бути цілим числом від 1 +@msg page thumbnail: {e} мініатюра сторінки: {e} +@msg page {page} of the PDF has no objects сторінка {page} файлу PDF не містить об’єктів +@msg page {page}: `{file}` has {n} {artboards} сторінка {page}: `{file}` має {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} сторінка {page}: `{name}` має {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages має бути діапазоном на зразок "2-3" або номером сторінки, а не {v} +@msg pages: {e} pages: {e} +@msg panel position must be finite положення панелі має бути скінченним +@msg parameters must be an object parameters має бути об’єктом +@msg params must be an object params має бути об’єктом +@msg passwords can have up to {MAX_PASSWORD} bytes паролі можуть містити до {MAX_PASSWORD} байтів +@msg path chunks must share their boundary point частини контуру повинні мати спільну граничну точку +@msg path has no open end контур не має відкритого кінця +@msg paths must be strings paths має містити рядки +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false потребує всіх монтажних областей, щоб зберегти редаговану графіку +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false потребує монтажних областей, щоб зберегти редаговану графіку, коли useArtboards дорівнює false +@msg pdfs `{m}`: single or multiple pdfs `{m}`: single або multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom — це "artwork" або "swatches", а не {v} +@msg pins and moved must be non-empty lists of the same length pins і moved мають бути непорожніми списками однакової довжини +@msg pins must be [[x,y]…] pins має бути [[x,y]…] +@msg pins must be finite pins мають бути скінченними +@msg place is above, below or inside, not `{other}` place — це above, below або inside, а не `{other}` +@msg plane must be left|right|ground plane має бути left|right|ground +@msg plane must be left|right|ground|none plane має бути left|right|ground|none +@msg point is outside the mesh точка лежить поза сіткою +@msg point x must be a number x точки має бути числом +@msg point y must be a number y точки має бути числом +@msg points must be [t, left, right] with non-negative widths points має бути [t, left, right] з невід’ємними ширинами +@msg points must be [x,y] pairs points має містити пари [x,y] +@msg points must be a non-empty [[x,y,pressure?]…] list points має бути непорожнім списком [[x,y,pressure?]…] +@msg points must be a non-empty list of finite [x,y] points має бути непорожнім списком скінченних [x,y] +@msg points must be an array or null points має бути масивом або null +@msg points must be finite points мають бути скінченними +@msg points must be finite [x,y] pairs points має містити скінченні пари [x,y] +@msg points must be finite canvas points points має містити скінченні точки полотна +@msg points must contain 1–4096 coordinates points має містити 1–4096 координат +@msg position must be normal, superscript or subscript position має бути normal, superscript або subscript +@msg position must be topLeft, topRight, bottomLeft or bottomRight position має бути topLeft, topRight, bottomLeft або bottomRight +@msg positions must be between 0 and 100000 pt positions мають бути від 0 до 100000 pt +@msg ppi must be a positive number, not {ppi} ppi має бути додатним числом, а не {ppi} +@msg ppi must be between 1 and 2400 ppi має бути від 1 до 2400 +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` не є transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape має бути clippingMask|transparency +@msg preset `{id}`: mobile or density preset `{id}`: mobile або density +@msg preset must be standard, diffuse, topLeft or right preset має бути standard, diffuse, topLeft або right +@msg preset must be the name of a print preset preset має бути іменем набору друку +@msg previewMode must be default, pixel or overprint previewMode має бути default, pixel або overprint +@msg print settings: {e} параметри друку: {e} +@msg procedures nest too deeply процедури мають завелику вкладеність +@msg procedures nested too deeply вкладеність процедур завелика +@msg quotes must be {double?, single?} quotes має бути {double?, single?} +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} має складатися з відкривних і закривних лапок, наприклад "“”" +@msg range out of order in character class діапазон у неправильному порядку в класі символів +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi має бути 1–2400 (72 — екран, 150 — середня, 300 — висока) +@msg rating must be a whole number from 0 to {MAX_RATING} rating має бути цілим числом від 0 до {MAX_RATING} +@msg rect and {k} contradict each other: give the area or the artboards rect і {k} суперечать один одному: укажіть область або монтажні області +@msg rect applies to raster formats, not {_1} rect застосовується до растрових форматів, а не до {_1} +@msg rect is [x, y, width, height] in points rect — це [x, y, width, height] у пунктах +@msg rect must be [x, y, width, height] rect має бути [x, y, width, height] +@msg rect must be [x, y, width, height] with a positive width and height rect має бути [x, y, width, height] з додатними шириною та висотою +@msg rect needs finite numbers and a positive width and height для rect потрібні скінченні числа й додатні ширина та висота +@msg redraw needs id для redraw потрібен id +@msg redraw within must be a finite distance of 0 or more within для redraw має бути скінченною відстанню, 0 або більше +@msg reduce of an empty array with no initial value reduce для порожнього масиву без початкового значення +@msg registration must be 0..8 registration має бути 0..8 +@msg regular expression flag '{_1}' given twice прапорець регулярного виразу '{_1}' указано двічі +@msg release a layer or a group звільніть шар або групу +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace зберігає місце й трансформацію заміненого об’єкта: не вказуйте template, at і rect +@msg replace: select the one object to replace replace: виділіть один об’єкт, який треба замінити +@msg resolution must be a number or screen|medium|high resolution має бути числом або screen|medium|high +@msg resolution must be between 1 and 2400 ppi resolution має бути від 1 до 2400 ppi +@msg rotation must be a number of degrees rotation має бути числом градусів +@msg rotation must be finite rotation має бути скінченним +@msg rows and cols must be 1..50 rows і cols мають бути 1..50 +@msg rows and columns must be 1..500 rows і columns мають бути 1..500 +@msg rows of objects whose appearances differ can't be reordered together рядки об’єктів із різним оформленням не можна перевпорядковувати разом +@msg sampled text would exceed the canvas (coordinates out of range) текст, з якого взято зразок, вийшов би за межі полотна (координати поза діапазоном) +@msg save the document first: Package collects a saved document спочатку збережіть документ: команда «Упакувати» збирає збережений документ +@msg save the document first: copy puts the file in its folder спочатку збережіть документ: copy поміщає файл у його папку +@msg savedPreset: {e} savedPreset: {e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`: коефіцієнт на зразок 2x, ширина (100w), висота (100h) або роздільна здатність (72ppi) +@msg scale must be a number above 0, at most 64 scale має бути числом більше 0 і не більше 64 +@msg scale must be non-zero scale не має дорівнювати нулю +@msg scope must be selection or all (got {other}) scope має бути selection або all (отримано {other}) +@msg scribble is too long розчерк задовгий +@msg segment does not exist сегмент не існує +@msg select a Shaper group виділіть групу формоутворення +@msg select a blend to set easing or color shift виділіть перехід, щоб задати плавність або зсув кольору +@msg select a graph виділіть діаграму +@msg select a group or layer with visible, unlocked members виділіть групу або шар із видимими незаблокованими елементами +@msg select a layer виділіть шар +@msg select a text object виділіть текстовий об’єкт +@msg select an SVG filter виділіть фільтр SVG +@msg select an image (or an Image Trace group) виділіть зображення (або групу трасування зображення) +@msg select an image, or pass id виділіть зображення або передайте id +@msg select an object (or pass `id`) виділіть об’єкт (або передайте `id`) +@msg select an object with a 3D effect виділіть об’єкт із 3D-ефектом +@msg select an object with a 3D effect, or give its id in ids виділіть об’єкт із 3D-ефектом або вкажіть його ідентифікатор в ids +@msg select an object with a freeform gradient виділіть об’єкт із довільним градієнтом +@msg select an object with an opacity mask виділіть об’єкт із маскою непрозорості +@msg select area type виділіть текст в області +@msg select area type (text in a frame) виділіть текст в області (текст у рамці) +@msg select area type and closed paths (at least one area type object) виділіть текст в області й замкнені контури (принаймні один об’єкт тексту в області) +@msg select area type whose first line can fit its frame виділіть текст в області, перший рядок якого вміщується в його рамку +@msg select artwork to add its colours виділіть графіку, щоб додати її кольори +@msg select artwork to make a group of its colours виділіть графіку, щоб створити групу з її кольорів +@msg select artwork to recolour, or give a colour `group` виділіть графіку для перефарбування або вкажіть `group` кольорів +@msg select exactly two path ends to join виділіть рівно два кінці контурів для з’єднання +@msg select objects or give ids виділіть об’єкти або вкажіть ids +@msg select objects to align to the guide виділіть об’єкти, які треба вирівняти за напрямною +@msg select one Intertwine group виділіть одну групу переплетення +@msg select one image or pass id виділіть одне зображення або передайте id +@msg select one layer, group or object to rename виділіть один шар, групу або об’єкт для перейменування +@msg select paths виділіть контури +@msg select paths or compound paths виділіть контури або складені контури +@msg select rows or objects to collect виділіть рядки або об’єкти, які треба зібрати +@msg select something to export виділіть що-небудь для експорту +@msg select something to export (or turn off selectedOnly) виділіть що-небудь для експорту (або вимкніть selectedOnly) +@msg select text to redefine the style from виділіть текст, за яким треба перевизначити стиль +@msg select the art and, on top of it, the mask object виділіть графіку і розташований поверх неї об’єкт маски +@msg select the art for the design виділіть графіку для дизайну +@msg select the art for the pattern tile виділіть графіку для плитки візерунка +@msg select the art to add first спочатку виділіть графіку, яку треба додати +@msg select the art to collect (or give its ids) виділіть графіку, яку треба зібрати (або вкажіть її ідентифікатори) +@msg select the art to weed виділіть графіку для вибірки +@msg select the content and a path on top виділіть вміст і контур поверх нього +@msg select the images to relink, or pass ids виділіть зображення для повторного зв’язування або передайте ids +@msg select the images, or pass ids виділіть зображення або передайте ids +@msg select the linked images or placed documents, or pass ids виділіть пов’язані зображення чи поміщені документи або передайте ids +@msg select the object to copy from виділіть об’єкт, з якого треба копіювати +@msg select the objects (or give their ids) виділіть об’єкти (або вкажіть їхні ідентифікатори) +@msg select the objects to attach to the path виділіть об’єкти, які треба прикріпити до контуру +@msg select the objects to bind виділіть об’єкти, які треба прив’язати +@msg select two or more objects to intertwine виділіть два або більше об’єктів для переплетення +@msg select two or more slices to combine виділіть два або більше фрагментів для об’єднання +@msg select type on a path виділіть текст за контуром +@msg select type on a path or area type виділіть текст за контуром або текст в області +@msg selection `{m}`: one or each selection `{m}`: one або each +@msg settings is an object of document.exportForScreens params settings — це об’єкт параметрів document.exportForScreens +@msg settings must be an object settings має бути об’єктом +@msg show `{show}`: all, missing, modified or embedded show `{show}`: all, missing, modified або embedded +@msg show must be true or false show має бути true або false +@msg side is left or right, not `{v}` side — це left або right, а не `{v}` +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size має бути "small", "medium", "large" або числом пунктів більше 0 і не більше {_1} +@msg size must be positive size має бути додатним +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode — це "fixed" або "pressure", а не `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode — це "fixed" або "pressure", а не {other} +@msg sizeVariation must be 0 or more sizeVariation має бути 0 або більше +@msg smallCaps must be true or false smallCaps має бути true або false +@msg sort `{x}`: name, kind or status sort `{x}`: name, kind або status +@msg source paths are too large for Shaper вихідні контури завеликі для формоутворення +@msg source text transform is collapsed or unrepresentable трансформація вихідного тексту вироджена або її не можна передати +@msg spot, mode and group apply to solid colours only spot, mode і group застосовуються лише до суцільних кольорів +@msg status `{s}`: linked, embedded, missing or modified status `{s}`: linked, embedded, missing або modified +@msg steps must be 1..1000 steps має бути 1..1000 +@msg steps must be 1..{MAX_STEPS} steps має бути 1..{MAX_STEPS} +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) точка переходу {i}: `plates` має містити чотири числа від 0 до 1 (C, M, Y, K) +@msg stopped before the archive was finished зупинено до завершення архіву +@msg strokeMode must be within|along|across, got {m} strokeMode має бути within|along|across, отримано {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions має бути об’єктом параметрів stroke.set +@msg strokeWidth must be a positive number of pixels strokeWidth має бути додатним числом пікселів +@msg subpath {si} is not an open subpath вкладений контур {si} не є відкритим +@msg subpaths required when joining the same path для з’єднання того самого контуру потрібен subpaths +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict має бути "merge" або "add", а не {v} +@msg table must be true or false table має бути true або false +@msg takes one name приймає одне ім’я +@msg target text transform is collapsed or unrepresentable трансформація цільового тексту вироджена або її не можна передати +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text має бути {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} +@msg text transform is collapsed or unrepresentable трансформація тексту вироджена або її не можна передати +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces має бути цілим числом від 2 до 100 +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`: використайте "{_1}" або "{_2}" +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: укажіть {path} або {name, dataBase64} файлу зображення чи текстуру в тому вигляді, у якому її подає effect.list +@msg textureImage: {e} textureImage: {e} +@msg that makes more than {MAX_PIECES} slices це створює понад {MAX_PIECES} фрагментів +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer це створює забагато частин (не більше 100000) або кроків штриха: спростіть шаблон штрихів або зробіть штрихи довшими +@msg that object is not in the overlap цей об’єкт не входить до перекриття +@msg the Affinity document has no page with a size документ Affinity не має сторінки з розміром +@msg the CMYK image couldn't be written не вдалося записати зображення CMYK +@msg the Layers panel is not shown панель «Шари» не показано +@msg the Layers panel's search, filter or Show Layers Only hides its row пошук, фільтр або режим «Показати лише шари» на панелі «Шари» приховує його рядок +@msg the Objects on Path group is hidden or locked групу «Об’єкти на контурі» приховано або заблоковано +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected параметри захисту PDF збережено без паролів: укажіть openPassword або permissionsPassword ще раз чи задайте security без passwordRequired, щоб експортувати без захисту +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) файл RTF містить забагато абзаців або змін форматування, щоб помістити його як текст (не більше {MAX_PARAGRAPHS} абзаців і {MAX_RUNS} фрагментів форматування) +@msg the RTF file is too large to place as type (at most {_1} MB) файл RTF завеликий, щоб помістити його як текст (не більше {_1} МБ) +@msg the SVG expands to more than {0} nested SVG images SVG розгортається у понад {0} вкладених зображень SVG +@msg the SVG expands to more than {0} objects SVG розгортається у понад {0} об’єктів +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes файл SVG більший за {MAX_IMPORT_BYTES} байтів +@msg the SVG has no drawable objects SVG не містить об’єктів, які можна намалювати +@msg the SVGZ file unpacks to more than {_1} MB файл SVGZ розпаковується у понад {_1} МБ +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place документ Word містить понад {MAX_PAGES} сторінок властивостей, це забагато для розміщення +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place документ Word містить понад {MAX_RUNS} фрагментів властивостей символів або абзаців, це забагато для розміщення +@msg the Word document has more than {_1} MB of formatting properties, too many to place документ Word містить понад {_1} МБ властивостей форматування, це забагато для розміщення +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) документ Word містить забагато тексту або форматування, щоб помістити його як текст (не більше {MAX_PARAGRAPHS} абзаців, {MAX_RUNS} фрагментів форматування і {_1} МБ тексту) +@msg the Word document has too much text to place as type (at most {_1} MB) документ Word містить забагато тексту, щоб помістити його як текст (не більше {_1} МБ) +@msg the Word document is encrypted or protected with a password; save it without one to place it документ Word зашифровано або захищено паролем; збережіть його без пароля, щоб помістити +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) документ Word завеликий для розміщення (його потік {name} містить понад {_1} МБ) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off документ Word востаннє збережено зі швидким збереженням, яке VectorCraft не читає; збережіть його ще раз, вимкнувши швидке збереження +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document документ Word збережено у Word 6.0 або Word 95, а такі файли VectorCraft не читає; збережіть його як документ Word 97-2003 +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place текст документа Word складається з понад {MAX_PIECES} частин, це забагато для розміщення +@msg the XML operation needs an XML or XMLList value для операції XML потрібне значення XML або XMLList +@msg the XPath expression does not evaluate to a node-set результат виразу XPath не є набором вузлів +@msg the XPath expression ends early вираз XPath закінчується передчасно +@msg the XPath expression has an unmatched ) вираз XPath містить непарну дужку ) +@msg the XPath expression has an unmatched ] вираз XPath містить непарну дужку ] +@msg the XPath expression has an unmatched closing bracket вираз XPath містить непарну закривну дужку +@msg the XPath expression selects a namespace node, which is not an XML value вираз XPath вибирає вузол простору імен, який не є значенням XML +@msg the XPath expression selects the root node, which is not an XML value вираз XPath вибирає кореневий вузол, який не є значенням XML +@msg the XPath function {name} needs a node-set функції XPath {name} потрібен набір вузлів +@msg the XPath function {name} takes the wrong number of arguments функція XPath {name} отримала неправильну кількість аргументів +@msg the XPath name functions need a node-set функціям імен XPath потрібен набір вузлів +@msg the XPath prefix {shown} is not bound to a namespace префікс XPath {shown} не прив’язано до простору імен +@msg the XPath variable ${shown} is not bound змінну XPath ${shown} не прив’язано +@msg the action set ids are not a list ідентифікатори наборів дій не є списком +@msg the active paint is not a gradient активна фарба не є градієнтом +@msg the argument list is not an object список аргументів не є об’єктом +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size монтажні області не вміщуються на полотні: зменште їх кількість, інтервал або розмір +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out графіка завелика для EPS: усе, що йде після 256 МБ коду PostScript, пропущено +@msg the bitmaps hold more pixels than one file may растрові зображення містять більше пікселів, ніж дозволено для одного файлу +@msg the character's size can't be measured розмір символу не можна виміряти +@msg the code runs past its last instruction код виконується далі своєї останньої інструкції +@msg the compound shape is too complex to expand складена фігура занадто складна для розбирання +@msg the container of the listed objects is gone контейнера перелічених об’єктів більше немає +@msg the copy `{user_id}` can't be read as a library копію `{user_id}` не вдалося прочитати як бібліотеку +@msg the copy would leave the canvas копія вийшла б за межі полотна +@msg the curve tolerance must be from 0.01 to 10 points допуск кривої має бути від 0.01 до 10 пунктів +@msg the custom dictionary couldn't be saved to {_1}: {e} не вдалося зберегти словник користувача в {_1}: {e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is не вдалося прочитати словник користувача {_1} ({why}), тому його залишено без змін +@msg the cut cannot be made on this art: {e} на цій графіці не можна зробити розріз: {e} +@msg the data isn't an EMF or WMF picture дані не є малюнком EMF або WMF +@msg the data isn't an EPS or PostScript picture дані не є малюнком EPS або PostScript +@msg the deferred paste is no longer available відкладена вставка вже недоступна +@msg the design is too big to draw at every data point of this graph дизайн завеликий, щоб малювати його в кожній точці даних цієї діаграми +@msg the design is too big to draw in every column of this graph дизайн завеликий, щоб малювати його в кожному стовпці цієї діаграми +@msg the dialog it was opened over is no longer open діалогове вікно, поверх якого його відкрито, уже закрито +@msg the dictionary stack overflowed стек словників переповнено +@msg the document already has an SVG filter named {name} документ уже має фільтр SVG з іменем {name} +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it документ має інший фільтр SVG з іменем {name}: скористайтеся svgFilter.apply або svgFilter.define із `replace`, щоб змінити його +@msg the document has no SVG filter named {name} документ не має фільтра SVG з іменем {name} +@msg the document has no SVG filter named {r} документ не має фільтра SVG з іменем {r} +@msg the document has no color swatches документ не має зразків кольору +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 зразки документа містять кольорів: {_1}; скорочення кольорів document приймає не більше 255 +@msg the editing data's hash has no place для хешу даних редагування немає місця +@msg the entities the document declares expand to too much text сутності, які оголошує документ, розгортаються в завеликий текст +@msg the envelope's content has no bounds вміст оболонки не має меж +@msg the envelope's mesh is malformed сітка оболонки має неправильну структуру +@msg the file holds no `variableSets` element файл не містить елемента `variableSets` +@msg the file is larger than {_1} MB файл більший за {_1} МБ +@msg the file is too large файл завеликий +@msg the file isn't valid SVG: {e} файл не є припустимим SVG: {e} +@msg the file names no plate файл не вказує жодної форми +@msg the file was updated or encrypted already файл уже оновлено або зашифровано +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) перед першим символом рядка немає символу для кернінгу: він зміщується лише вгору й униз (dy) +@msg the font {_1} is not available шрифт {_1} недоступний +@msg the format has no file extension формат не має розширення файлу +@msg the function's prototype is not an object прототип функції не є об’єктом +@msg the graphic can't be read: {e} не вдалося прочитати графіку: {e} +@msg the graphic is empty графіка порожня +@msg the graphic's data is damaged дані графіки пошкоджено +@msg the graphic's document has no artboard документ графіки не має монтажної області +@msg the guide is hidden or locked напрямну приховано або заблоковано +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them заголовок `{cell}` для `{name}` під час читання дав би змінну `{other}`: перейменуйте одну з них +@msg the image `{path}` isn't followed: {r} перехід до зображення `{path}` не виконується: {r} +@msg the image has no embedded pixels зображення не має вбудованих пікселів +@msg the image is empty зображення порожнє +@msg the image is fully transparent зображення повністю прозоре +@msg the image is not on an artboard; pass rect зображення не розташоване на монтажній області; передайте rect +@msg the image's pixels are missing пікселі зображення відсутні +@msg the image's pixels aren't in the document пікселів зображення немає в документі +@msg the image's pixels can't be read: {e} не вдалося прочитати пікселі зображення: {e} +@msg the image's transparency doesn't fit it прозорість зображення не відповідає йому +@msg the job would print more than {MAX_PAGES} pages завдання надрукувало б понад {MAX_PAGES} сторінок +@msg the job would print more than {_1} pages завдання надрукувало б понад {_1} сторінок +@msg the layers hold no art to mask шари не містять графіки для маскування +@msg the layout moved макет змістився +@msg the library has a group `{name}` already бібліотека вже має групу `{name}` +@msg the library has no group `{name}` бібліотека не має групи `{name}` +@msg the library has no item `{item}` бібліотека не має елемента `{item}` +@msg the library has no item `{other}` бібліотека не має елемента `{other}` +@msg the library holds {MAX_GROUPS} groups already бібліотека вже містить груп: {MAX_GROUPS} +@msg the library holds {MAX_ITEMS} items of this kind already бібліотека вже містить елементів цього виду: {MAX_ITEMS} +@msg the library's file is outside the read roots файл бібліотеки розташований поза кореневими папками читання +@msg the method needs an XML value методу потрібне значення XML +@msg the name is empty ім’я порожнє +@msg the object has no fill, stroke or effect об’єкт не має заливки, обведення або ефекту +@msg the object has no graphic style (pass `name`) об’єкт не має графічного стилю (передайте `name`) +@msg the objects don't match the plan об’єкти не відповідають плану +@msg the open and permissions passwords must differ пароль для відкриття і пароль прав доступу мають відрізнятися +@msg the operand stack overflowed стек операндів переповнено +@msg the page has no area сторінка не має площі +@msg the page's type in an unread object's box isn't in its page art тексту сторінки в рамці непрочитаного об’єкта немає в графіці цієї сторінки +@msg the paint behind the proxy is not a gradient фарба за індикатором не є градієнтом +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) фарба не є довільним градієнтом (застосуйте його за допомогою paint.editGradient {kind}) +@msg the parameters or body do not form one function параметри або тіло не утворюють одну функцію +@msg the parent must be a layer or sublayer батьківським елементом має бути шар або підшар +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it вставлений фільтр SVG {name} малює інакше, ніж фільтр документа з таким іменем, а вільного імені для нього немає +@msg the path can't be extended контур не можна продовжити +@msg the path can't be inside one of the objects to attach контур не може бути всередині одного з об’єктів, які прикріплюються +@msg the path has no segments to measure контур не має сегментів для вимірювання +@msg the path is empty контур порожній +@msg the path is too short to shorten that much контур закороткий, щоб укоротити його настільки +@msg the pen table holds more than 255 pens таблиця пер містить понад 255 пер +@msg the picture has no objects малюнок не містить об’єктів +@msg the plates can't be kept форми не можна зберегти +@msg the position table does not match the instructions таблиця позицій не відповідає інструкціям +@msg the program runs too long програма виконується задовго +@msg the program uses too much memory програма використовує забагато пам’яті +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes текст запиту довший за {MAX_PROMPT_BYTES} байтів +@msg the radix must be between 2 and 36 основа системи числення має бути від 2 до 36 +@msg the rectangle must have a width and a height прямокутник повинен мати ширину й висоту +@msg the rectangle needs a size and must lie on the canvas прямокутнику потрібен розмір, і він має лежати на полотні +@msg the right side of 'in' is not an object права частина 'in' не є об’єктом +@msg the right side of 'instanceof' is not a function права частина 'instanceof' не є функцією +@msg the selected art has no width виділена графіка не має ширини +@msg the selected art is nested more than {MAX_DEPTH} levels deep виділена графіка має вкладеність понад {MAX_DEPTH} рівнів +@msg the selected art is too large to measure виділена графіка завелика для вимірювання +@msg the selection draws nothing виділення нічого не малює +@msg the selection has no area виділення не має площі +@msg the selection has no art to export виділення не містить графіки для експорту +@msg the selection has no bounds виділення не має меж +@msg the selection has no graphic style виділення не має графічного стилю +@msg the selection has no outlines to weed around виділення не має обрисів, навколо яких можна робити вибірку +@msg the slice would leave the canvas фрагмент вийшов би за межі полотна +@msg the spot colour couldn't be added не вдалося додати плашковий колір +@msg the subpath has no ends to extend (it is closed or a single point) вкладений контур не має кінців для продовження (він замкнений або є однією точкою) +@msg the symbols are nested too deeply to expand their instances символи мають завелику вкладеність, щоб розібрати їхні екземпляри +@msg the text file is too large to place as type (at most {_1} MB) текстовий файл завеликий, щоб помістити його як текст (не більше {_1} МБ) +@msg the text is empty текст порожній +@msg the text is over {_1} MB текст перевищує {_1} МБ +@msg the top object must be a path верхній об’єкт має бути контуром +@msg the trace was cancelled трасування скасовано +@msg the trailer has no size трейлер не має розміру +@msg the type object is flattened to nothing після зведення від текстового об’єкта нічого не залишається +@msg the value bind was called on is not a function значення, для якого викликано bind, не є функцією +@msg the value is not a constructor значення не є конструктором +@msg the value is not a function значення не є функцією +@msg the value is not a valid XML name значення не є припустимим іменем XML +@msg the value is not an XML attribute name значення не є іменем атрибута XML +@msg the value is not an XML name значення не є іменем XML +@msg the value of axis `{tag}` must be a number or null значення осі `{tag}` має бути числом або null +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets бібліотека змінних оголошує понад {MAX_LIBRARY_DATASETS} наборів даних +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities бібліотека змінних оголошує понад {MAX_LIBRARY_ENTITIES} сутностей +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables бібліотека змінних оголошує понад {MAX_LIBRARY_VARIABLES} змінних +@msg the variable library declares no variables бібліотека змінних не оголошує жодної змінної +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes бібліотека змінних більша за {MAX_LIBRARY_BYTES} байтів +@msg the variable library is not UTF-8 бібліотека змінних не є UTF-8 +@msg the variable library is not well-formed XML: {e} бібліотека змінних не є правильно сформованим XML: {e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep бібліотека змінних має вкладеність елементів понад {MAX_LIBRARY_NESTING} рівнів +@msg the variable library's document type declaration holds something other than entities of plain text оголошення типу документа бібліотеки змінних містить щось інше, крім сутностей зі звичайним текстом +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes сутності бібліотеки змінних розгортаються у понад {MAX_LIBRARY_BYTES} байтів +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines лінії вибірки перетинають обриси графіки забагато разів для вимірювання; використайте менше ліній +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap лінії вибірки проходять заблизько до завеликої кількості сегментів обрисів для вимірювання; використайте менше ліній або менший проміжок +@msg the weeding lines would be more than {max_pieces} pieces ліній вибірки вийшло б понад {max_pieces} частин +@msg the window has no dock вікно не має області закріплення +@msg the written PDF has no catalog записаний PDF не має каталогу +@msg the written PDF has no cross-reference table записаний PDF не має таблиці перехресних посилань +@msg the written PDF's metadata can't be edited метадані записаного PDF не можна змінити +@msg the {_1} plate lies outside the file форма {_1} лежить поза файлом +@msg there are no colour swatches to draw немає зразків кольору для малювання +@msg there is already a design named `{name}` дизайн з назвою `{name}` уже існує +@msg there is already a design named `{to}` дизайн з назвою `{to}` уже існує +@msg there is already a toolbar named `{name}` панель інструментів з іменем `{name}` уже існує +@msg there is already a toolbar named `{to}` панель інструментів з іменем `{to}` уже існує +@msg there is no library: create one first бібліотеки немає: спочатку створіть її +@msg these paths are too degenerate for Shaper ці контури занадто вироджені для формоутворення +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that це двійковий файл DXF, який не можна прочитати: збережіть його з програми САПР як ASCII DXF і відкрийте отриманий файл +@msg this warp's points can't be edited: use Reset with Mesh first точки цієї деформації не можна редагувати: спочатку скористайтеся командою «Скинути із сіткою» +@msg thumbnail: no page {v} (the {what} has {count}) thumbnail: немає сторінки {v} ({what} має сторінок: {count}) +@msg tileRange of {what}: {e} tileRange для {what}: {e} +@msg title can't be empty title не може бути порожнім +@msg to (the new name) is required to (нове ім’я) є обов’язковим +@msg to must be fill or stroke to має бути fill або stroke +@msg to must be smooth or corner to має бути smooth або corner +@msg toExponential() digits must be between 0 and 20 кількість цифр для toExponential() має бути від 0 до 20 +@msg toFixed() digits must be between 0 and 20 кількість цифр для toFixed() має бути від 0 до 20 +@msg toISOString is not a function toISOString не є функцією +@msg toPrecision() argument must be between 1 and 21 аргумент toPrecision() має бути від 1 до 21 +@msg toSource: a getter cannot be written as source toSource: гетер не можна записати як вихідний код +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource: масив із властивостями, крім його елементів, з неперелічуваною властивістю або з іншим прототипом не можна записати як вихідний код +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource: об’єкт із неперелічуваною властивістю або з прототипом, відмінним від Object.prototype, не можна записати як вихідний код +@msg toSource: the source is too long toSource: вихідний код задовгий +@msg toSource: the value contains itself toSource: значення містить саме себе +@msg toSource: the value has too many entries toSource: значення містить забагато записів +@msg toSource: the value is nested too deep toSource: значення має завелику вкладеність +@msg toString is not a function toString не є функцією +@msg too large for a zip archive: package to a folder завелике для архіву zip: упакуйте в папку +@msg too many Shaper edits забагато правок формоутворення +@msg too many `points` (at most 100000) забагато `points` (не більше 100000) +@msg too many arguments забагато аргументів +@msg too many characters: at most {MAX_CHARS} забагато символів: не більше {MAX_CHARS} +@msg too many dots for this size: lower the frequency забагато точок для цього розміру: зменште частоту +@msg too many filters over one file забагато фільтрів для одного файлу +@msg too many overlapping paths for Shaper забагато контурів, що перекриваються, для формоутворення +@msg too many quantifiers забагато квантифікаторів +@msg too many saved clips забагато збережених відсікань +@msg too many saved graphics states забагато збережених графічних станів +@msg toolbar (a toolbar's name) must be a string toolbar (ім’я панелі інструментів) має бути рядком +@msg toolbar name can't be empty ім’я панелі інструментів не може бути порожнім +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}: "flatten" (за замовчуванням) або "omit" +@msg two saved selections would be named `{_1}` два збережені виділення отримали б ім’я `{_1}` +@msg type dimension cannot be represented with this text transform розмір тексту не можна передати з цією трансформацією тексту +@msg type dimension must be finite розмір тексту має бути скінченним +@msg unexpected resources неочікувані ресурси +@msg unknown CMYK profile `{name}` невідомий профіль CMYK `{name}` +@msg unknown File Info field `{k}` невідоме поле відомостей про файл `{k}` +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) невідома функція OpenType `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; префікс - вимикає функцію; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom і aalt приймають номер альтернативи у вигляді `tag=N`, де N від 0 до {_1}, наприклад `salt=2`) +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) невідома функція OpenType `{t}` у featureChanges (допустимі теги, які приймає `features`) +@msg unknown PDF preset `{name}` (presets: {_1}) невідомий набір PDF `{name}` (набори: {_1}) +@msg unknown Warp style невідомий стиль деформації +@msg unknown alignToPath `{a}` невідомий alignToPath `{a}` +@msg unknown arrowhead {v} невідома стрілка {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) невідомий ключ artboardLayout `{k}` (layout, columns, spacing, rightToLeft) +@msg unknown attribute `{attribute}` невідомий атрибут `{attribute}` +@msg unknown attribute `{k}` невідомий атрибут `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) невідома вісь `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` невідомий режим накладання `{b}` +@msg unknown blend mode {b} невідомий режим накладання {b} +@msg unknown brush type `{o}` невідомий тип пензля `{o}` +@msg unknown category `{c}` невідома категорія `{c}` +@msg unknown category `{c}` ({_1}) невідома категорія `{c}` ({_1}) +@msg unknown character `{c}` невідомий символ `{c}` +@msg unknown character style number field невідоме числове поле стилю символу +@msg unknown dither `{d}` (diffusion, pattern, noise or none) невідомий dither `{d}` (diffusion, pattern, noise або none) +@msg unknown effect `{e}` невідомий ефект `{e}` +@msg unknown end {e} невідомий кінець {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) невідомий flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution або збережений набір: див. flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) невідомий формат `{f}` (vcswatches, gpl, ase або css) +@msg unknown from {f} невідомий from {f} +@msg unknown graph type `{t}` невідомий тип діаграми `{t}` +@msg unknown grid colours `{name}` (one of {_1}) невідомі кольори сітки `{name}` (одне з: {_1}) +@msg unknown gridType `{t}` невідомий gridType `{t}` +@msg unknown highlight `{v}` невідомий highlight `{v}` +@msg unknown intent `{i}` невідомий intent `{i}` +@msg unknown issue kind {id} невідомий вид проблеми {id} +@msg unknown issue kind {v} невідомий вид проблеми {v} +@msg unknown knockout state {k} невідомий стан вирізування {k} +@msg unknown language `{language}` (one of {_1}) невідома мова `{language}` (одна з: {_1}) +@msg unknown language `{l}` (one of {_1}) невідома мова `{l}` (одна з: {_1}) +@msg unknown method `{m}` ({_1}) невідомий метод `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) невідомий режим `{other}` (gray, rgb, hsb, lab, cmyk або web) +@msg unknown mode `{other}` (mono|cmyk) невідомий режим `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) невідомий режим `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) невідомий режим `{other}` (set, add, subtract) +@msg unknown mode `{o}` невідомий режим `{o}` +@msg unknown mode {m} невідомий режим {m} +@msg unknown model `{other}` невідома модель `{other}` +@msg unknown model `{other}` (keep, new or document) невідома модель `{other}` (keep, new або document) +@msg unknown op {o} невідомий op {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back невідомий order `{o}`: toggle, front, forward, backward або back +@msg unknown overprints `{v}` (preserve, simulate or discard) невідомий overprints `{v}` (preserve, simulate або discard) +@msg unknown panel невідома панель +@msg unknown panel `{panel}` невідома панель `{panel}` +@msg unknown parameter `{key}` невідомий параметр `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} невідомий параметр `{within}settings.{format}`; {why} +@msg unknown parameter `{within}{key}`; {why} невідомий параметр `{within}{key}`; {why} +@msg unknown pieLegend `{v}` (none, standard or wedges) невідомий pieLegend `{v}` (none, standard або wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) невідомий piePosition `{v}` (even, ratio або stacked) +@msg unknown pieSort `{v}` (none, all or first) невідомий pieSort `{v}` (none, all або first) +@msg unknown plate `{n}` невідома форма `{n}` +@msg unknown preference `{k}` невідоме налаштування `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) невідомий профіль {id} (див. stroke.widthProfile.list) +@msg unknown proof target `{t}` невідома ціль проби `{t}` +@msg unknown quotes key `{k}` (double, single) невідомий ключ quotes `{k}` (double, single) +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) невідоме скорочення кольорів `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform або document) +@msg unknown resolution `{v}` невідома роздільна здатність `{v}` +@msg unknown rule `{rule}` ({_1}) невідоме правило `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) невідоме правило `{r}` (див. color.harmony) +@msg unknown setting `{k}` невідомий параметр `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) невідома фігура `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) невідомий spacing `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) невідомий tileType `{t}` ({_1}) +@msg unknown units невідомі одиниці +@msg unknown units `{u}` невідомі одиниці `{u}` +@msg unknown value axis `{v}` (left, right or both) невідома вісь значень `{v}` (left, right або both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) невідома варіація `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` невідомий стиль деформації `{s}` +@msg unknown {key} `{v}` (none, short or full) невідомий {key} `{v}` (none, short або full) +@msg unknown {key} profile `{n}` невідомий профіль {key} `{n}` +@msg unknown {k} key `{key}` (size, position) невідомий ключ {k} `{key}` (size, position) +@msg unmatched ')' непарна дужка ')' +@msg unreadable object об’єкт не вдалося прочитати +@msg unsafe destination file name небезпечне ім’я файлу призначення +@msg unterminated character class незавершений клас символів +@msg unterminated group незавершена група +@msg use paths or compound paths використовуйте контури або складені контури +@msg useArtboards must be true or false, not {v} useArtboards має бути true або false, а не {v} +@msg value for `{var}` must match its variable kind значення `{var}` має відповідати типу її змінної +@msg variable `{_1}` is already defined змінну `{_1}` уже визначено +@msg variable `{name}` has no `trait` змінна `{name}` не має `trait` +@msg variable `{name}` is already defined змінну `{name}` уже визначено +@msg variable `{name}` is declared twice змінну `{name}` оголошено двічі +@msg variable `{to}` already exists змінна `{to}` уже існує +@msg view is required view є обов’язковим +@msg view must be one of {_1} view має бути одним з: {_1} +@msg warpStyle can only edit an applied Warp effect warpStyle може змінювати лише застосований ефект деформації +@msg width and height must be more than 0 and at most {limit} width і height мають бути більше 0 і не більше {limit} +@msg width factors must be finite коефіцієнти ширини мають бути скінченними +@msg width must be positive width має бути додатним +@msg width out of range ширина поза межами діапазону +@msg width/height must be > 0 width/height мають бути > 0 +@msg wordGap and lineGap need area type in the selection для wordGap і lineGap у виділенні потрібен текст в області +@msg x and y must be finite numbers x і y мають бути скінченними числами +@msg x and y must be given together x і y треба вказувати разом +@msg xpath needs its variables as an object для xpath змінні мають бути об’єктом +@msg xpath needs the expression as a string для xpath вираз має бути рядком +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) для zone потрібен anchor: закріпіть поруч із панеллю за допомогою anchor або в новому стовпці закріплення за допомогою side (Start або End) +@msg zoom must be positive zoom має бути додатним +@msg {C}: no effect at index {list} {C}: немає ефекту з індексом {list} +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} захищено, він залишається +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} захищено: збережіть параметри під іншим іменем +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED}, а його параметри й кеш не вдалося перемістити назад із {_1} до {_2}: {e} +@msg {LABEL} produced an empty result {LABEL}: отримано порожній результат +@msg {_1} MB of document, at most {_2} MB обсяг документа {_1} МБ, дозволено не більше {_2} МБ +@msg {_1} MB of images, at most {_2} MB обсяг зображень {_1} МБ, дозволено не більше {_2} МБ +@msg {_1} bytes of RGBA data for a {width} × {height} image {_1} байтів даних RGBA для зображення {width} × {height} +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} не вдалося прочитати або його змінено після читання: спочатку зв’яжіть його повторно чи оновіть +@msg {_1} can't go into {into} {_1} не можна помістити в {into} +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} не може обмежити друк низькою роздільною здатністю: виберіть PDF 1.4 або новіший чи дозвольте друк із високою роздільною здатністю +@msg {_1} files can't be opened yet файли {_1} поки не можна відкривати +@msg {_1} files can't be password-protected: remove the passwords or choose no standard файли {_1} не можна захищати паролем: приберіть паролі або не вибирайте стандарт +@msg {_1} files can't be {_2} файли {_1} не можуть бути файлами {_2} +@msg {_1} files can't carry editing data: turn off preserveEditing файли {_1} не можуть містити дані редагування: вимкніть preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers файли {_1} не можуть мати шари PDF: вимкніть createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one файлам {_1} потрібна дата створення, а ця платформа не має годинника, щоб її надати +@msg {_1} is not a folder: {e} {_1} не є папкою: {e} +@msg {_1} isn't a path {_1} не є контуром +@msg {_1} options: {e} параметри {_1}: {e} +@msg {_1} paints nothing to rasterize {_1} не малює нічого, що можна растеризувати +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) паролі {_1} можуть містити до {MAX_LEGACY_PASSWORD} символів ASCII (PDF 1.7 і новіші приймають будь-які символи) +@msg {_1} with linked images or an image map writes several files: use document.export {_1} з пов’язаними зображеннями або картою зображення записує кілька файлів: скористайтеся document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} записує тут по одному файлу на монтажну область: скористайтеся document.export або вкажіть одну монтажну область +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} пікселів — це забагато для {_3} (не більше {max} пікселів по кожній стороні): зменште масштаб +@msg {_1}: belongs to another account {_1}: належить іншому обліковому запису +@msg {_1}: not a folder of its own {_1}: не є власною папкою +@msg {_1}: not a plain file {_1}: не є звичайним файлом +@msg {_1}: {_2} and {_3} are in the way {_1}: заважають {_2} і {_3} +@msg {code}: {0} {code}: {0} +@msg {dest}: {e} {dest}: {e} +@msg {dir}: {e} {dir}: {e} +@msg {doc} has no folder {doc} не має папки +@msg {file} isn't linked {file} не пов’язано +@msg {file} names more than one linked JavaScript file {file} указує більше одного пов’язаного файлу JavaScript +@msg {folder} is not a package folder {folder} не є папкою пакета +@msg {folder}: more than {max} files and folders {folder}: понад {max} файлів і папок +@msg {folder}: {e} {folder}: {e} +@msg {g} is not a Live Paint group {g} не є групою швидкої заливки +@msg {id} is not a slice {id} не є фрагментом +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} має бути 0–{MAX_ADD_AROUND} pt +@msg {key} must be [x,y] {key} має бути [x,y] +@msg {key} must be a percentage {min}..100 {key} має бути відсотком {min}..100 +@msg {key} must be a positive number, not {n} {key} має бути додатним числом, а не {n} +@msg {key} must be a positive number, not {v} {key} має бути додатним числом, а не {v} +@msg {key} must be one of {_1} {key} має бути одним з: {_1} +@msg {key} must be {_1}, got {s} {key} має бути {_1}, отримано {s} +@msg {key} must name a floating panel {key} має вказувати плаваючу панель +@msg {key} must name a panel in another group {key} має вказувати панель в іншій групі +@msg {key} must name a panel that shows {key} має вказувати панель, яку показано +@msg {key} must name a registered panel {key} має вказувати зареєстровану панель +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) файли {kind} не мають прозорості, а файл містив би таке: {what}; спочатку зведіть прозорість (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) зображення {kind} не можна відкрити (див. document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` не є одним із варіантів +@msg {k} has control characters{_1} {k} містить керівні символи{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} задовгий (не більше {MAX_TEXT} символів) +@msg {k} must be > 0 {k} має бути > 0 +@msg {k} must be a number (pt) or a length such as "210 mm" {k} має бути числом (pt) або довжиною на зразок "210 mm" +@msg {k} must be a number from {min} to {max} {k} має бути числом від {min} до {max} +@msg {k} must be a number or "auto" {k} має бути числом або "auto" +@msg {k} must be a string {k} має бути рядком +@msg {k} must be text {k} має бути текстом +@msg {k} must be true or false {k} має бути true або false +@msg {k} must be {{size?, position?}} {k} має бути {{size?, position?}} +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: контуру користувацької стрілки потрібна площа для заливки і не більше {most} опорних точок та {most} вкладених контурів, причому кожна опорна точка має бути не далі ніж {far} одиниць наконечника від його вістря +@msg {k}: bad custom arrowhead path: {e} {k}: неправильний контур користувацької стрілки: {e} +@msg {k}: bad custom arrowhead: {e} {k}: неправильна користувацька стрілка: {e} +@msg {k}: can't read `{s}` as a length {k}: не вдалося прочитати `{s}` як довжину +@msg {k}: custom must be a name {k}: custom має бути іменем +@msg {label} must be a number at most {max} {label} має бути числом не більше {max} +@msg {label} must be a number from -1296 to 1296 {label} має бути числом від -1296 до 1296 +@msg {label} must be a number from 1 to 10000 {label} має бути числом від 1 до 10000 +@msg {name} `{s}`: {ids} {name} `{s}`: {ids} +@msg {name} is built in and can't be deleted {name} є вбудованим, його не можна видалити +@msg {name} must be {lo}–{hi}{unit} (got {v}) {name} має бути {lo}–{hi}{unit} (отримано {v}) +@msg {name}: CRC or size mismatch {name}: невідповідність CRC або розміру +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}: вдалося прочитати лише вбудований попередній перегляд цього файлу Affinity; скористайтеся командою Файл › Відкрити, щоб побачити його з попередженням, або перед розміщенням експортуйте SVG чи PDF з Affinity +@msg {name}: {e} {name}: {e} +@msg {native}; its embedded preview can't be read either ({e}) {native}; його вбудований попередній перегляд теж не вдалося прочитати ({e}) +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: copy приймає файл DCS, лише коли його форми містяться в ньому самому; зв’яжіть його повторно там, де розташовані файли його форм +@msg {path}: larger than {_1} MB {path}: більший за {_1} МБ +@msg {shown} is not an XPath 1.0 function {shown} не є функцією XPath 1.0 +@msg {std} files are CMYK, and the file would have colours in {_1} файли {std} мають модель CMYK, а файл містив би кольори в {_1} +@msg {std} files embed their fonts, and the file would have one that isn't файли {std} вбудовують свої шрифти, а файл містив би невбудований шрифт +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} — це файл, з якого прочитано цей документ, і під час читання дещо було пропущено ({what}): запис поверх нього призвів би до остаточної втрати пропущеного. Запишіть інший файл або передайте acknowledgeLoss: true, щоб усе одно замінити його +@msg {ty} brushes need selected art (or params.{k}) пензлям типу {ty} потрібна виділена графіка (або params.{k}) +@msg {v} is not an asset id {v} не є ідентифікатором ресурсу +@msg {v} names no layer or sublayer {v} не вказує жодного шару або підшару +@msg {what} can't be intertwined {what}: переплетення неможливе +@msg {what} doesn't fit the paper with its bleed and marks {what}: не вміщується на папері разом із вильотом і мітками +@msg {what} is not a function {what} не є функцією +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what}: на аркуші було б надруковано понад {MAX_COPIES_PER_SHEET} копій: збільште масштаб +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what}: друк зайняв би понад {MAX_TILES} плиток: зменште масштаб +@msg {what} {i} out of range {what} {i} поза межами діапазону +@msg Alignment must be one of the choices Вирівнювання має бути одним із варіантів +@msg Line Gap must be a number Проміжок між рядками має бути числом +@msg `at` must be a point [x, y] `at` має бути точкою [x, y] +@msg `points` must hold from 2 to 100000 points `points` має містити від 2 до 100000 точок +@msg `rect` must be [x, y, width, height] `rect` має бути [x, y, width, height] +@msg `tolerance` must be from 0 to 100 pt `tolerance` має бути від 0 до 100 pt +@msg `warichuAlign` must be one of {_1} `warichuAlign` має бути одним з: {_1} +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` має бути цілим числом від {_1} до {_2} +@msg bad point {v}: expected [x, y] неправильна точка {v}: очікується [x, y] +@msg give exactly one of `at`, `rect` and `points` укажіть рівно одне з: `at`, `rect`, `points` +@msg the line is too long, or the paths it passes too many, to check лінія задовга або проходить через забагато контурів, щоб її можна було перевірити +@msg the paths are too many or too tangled to find where they cross контурів забагато або вони занадто заплутані, щоб знайти місця їх перетину +@msg the rectangle or line reaches too many paths to check прямокутник або лінія охоплює забагато контурів для перевірки +@msg {name} must be a number {name} має бути числом +@msg `{key}` must be from 0.01 to 1000 pt `{key}` має бути від 0.01 до 1000 pt +@msg select the banner, or mark every artboard виділіть банер або розмітьте всі монтажні області +@msg the banner has no width or height банер не має ширини або висоти +@msg the grommet inset is too large for the banner відступ люверсів від краю завеликий для банера +@msg the hem is too deep for the banner підгин заглибокий для банера +@msg the spacing makes more than {MAX_GROMMETS} grommets за такого інтервалу виходить понад {MAX_GROMMETS} люверсів +@msg there is no banner to mark немає банера для розмітки +@msg turn on at least one side увімкніть принаймні одну сторону +@msg with no hem and no grommets there is nothing to mark без підгину й люверсів немає чого розмічати +@msg {saving} failed: {e} {saving}: не вдалося: {e} diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..ea91758a60 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -54,7 +54,7 @@ plus review. Native-speaker review is human work and isn't in the hours. | Dutch | `nl` | 7,299 rows, 1,609 of them `@msg` and 47 `@msgplural` (counted 2026-11-13 on `main` at `9a1a158a3`): in `COMPLETE_MESSAGES`, and the untranslated report lists the same missing messages for it as for German; one/other plurals | full | | Danish | `da` | 5,279 rows, 750 of them `@msg` (counted 2026-10-21): menus and `tl!` literals complete when counted; its messages are partial; one/other plurals | full | | Russian | `ru` | 4,009 / 4,020 | full | -| Ukrainian | `uk` | 4,009 / 4,020 (one/few/many plurals) | full | +| Ukrainian | `uk` | 8,802 rows, 3,439 of them `@msg` and 48 `@msgplural` (counted 2026-11-13); one/few/many plurals | full | | Hungarian | `hu` | 5,253 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | full | | Polish | `pl` | 5,130 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/few/many plurals | full; native review pending | | Finnish | `fi` | 5,318 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/other plurals, 797 `@msg` rows | full; AI-assisted, native proofreading pending | From 40f69a12045f7515ecac89a7043c0d119cad3526 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:27:27 -0800 Subject: [PATCH 25/33] Docs: the Brushes panel lists the second brush of a repeated name in its own numbered row Reading a brush library numbers the second brush of a repeated name, so the panel's row of that name previews and drags the first brush, and the second brush has a numbered row of its own. --- docs/target-app-parity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index c227ddec4b..10084f87e2 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1829,7 +1829,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **Brush art of copies:** symbol instances, inline graphics and repeat copies of a brushed path keep their own brush art between frames, as the same path on the page does. The renderer also keeps the stroke outlines of brush art and the evaluation of a live object in it, such as an envelope that a scatter brush places. The brush art is made again when the path, its stroke or the brush changes, and when the art of a symbol or pattern that the brush art can show changes. On one thread at 1000 × 1000 pixels (the median frame, with the median fastest frame in brackets), 2,000 paths on the page whose scatter brush art is an envelope take 94 ms (80 ms) a frame instead of 400 ms (306 ms), and 2,000 instances of a symbol whose art is a brushed path take 13.4 ms (12.0 ms) instead of 19.3 ms (15.6 ms), with the same pixels. -- **Two brushes with one name:** when a document's Brushes panel holds two brushes with the same name, a stroke that names it draws on the canvas with the first of them, as `brush.get`, Brush Options, expanding, outlining and the SVG, PDF, EPS and `.ai` exports take it. Document Info gives that brush's type, and both rows of the Brushes panel preview and drag the first brush. +- **Two brushes with one name:** when a document stores two brushes with the same name, a stroke that names it draws on the canvas with the first of them, as `brush.get`, Brush Options, expanding, outlining and the SVG, PDF, EPS and `.ai` exports take it. Document Info gives that brush's type, and the Brushes panel's row of that name previews and drags the first brush. Reading the library numbers the second brush ("Name 2"), so the panel lists it in a row of its own that shows it. - **Live objects in symbols and masks:** symbol instances and inline graphics whose symbol is a blend, an envelope, a repeat, an Intertwine object or a gradient mesh, or a group that holds one, draw the evaluation the renderer keeps for that object between frames, as the same object on the page does. So do opacity masks whose art is such an object, and such objects in clip groups in Outline view. The object is evaluated again when the symbols, patterns, fonts or color settings it shows change. Symbol art with effects of its own, or that holds a symbol instance, an image, a placed file, type with an inline graphic or a raster effect, is still placed and evaluated again in every frame. At 1000 × 1000 pixels, a page of 2,000 instances of a symbol that holds an envelope takes 28 ms a frame instead of 126 ms, with the same pixels. From ead3cd1ebdfda89ce450c3abfa4971a22da8a3a3 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 15:38:12 -0800 Subject: [PATCH 26/33] Variables: Import Data Sets reads UTF-16 CSV files without a byte-order mark A CSV file that holds NUL bytes and has no byte-order mark was refused. It is now read as UTF-16 when at least half of its two-byte units hold a NUL byte, in the byte order that reads a line break. A file with fewer NUL bytes (UTF-8 or Windows-1252 text with stray NUL bytes, or UTF-16 text made mostly of characters above U+00FF) is still refused, and so is a file that reads a line break in both byte orders, has an odd length, isn't valid UTF-16 in that order or holds a NUL character. Refused files get the same message as before. --- crates/engine/src/cmd/variablecmds.rs | 2 +- crates/engine/src/cmd/variablecmds/csv.rs | 49 ++++++++++++----- crates/engine/src/tests/variables.rs | 64 +++++++++++++++++++++-- docs/mcp.md | 9 +++- docs/target-app-parity.md | 1 + 5 files changed, 107 insertions(+), 18 deletions(-) diff --git a/crates/engine/src/cmd/variablecmds.rs b/crates/engine/src/cmd/variablecmds.rs index 74c89d1089..9acd31e664 100644 --- a/crates/engine/src/cmd/variablecmds.rs +++ b/crates/engine/src/cmd/variablecmds.rs @@ -867,7 +867,7 @@ pub fn specs() -> Vec { "Import Data Sets…", ["Window", "Variables"], None, - "{path? | data? | dataBase64? | csv?} replace every dataset with the rows of a CSV, comma- or tab-separated, in UTF-16 with a byte-order mark, UTF-8 or else Windows-1252 (header: one variable name per column, whatever its case; one row per dataset, named Data Set 1, 2… in file order). The document's variables and their bindings stay. A header that names no variable defines one, unbound: `#Name` defines a visibility variable, `@Name` defines an image variable, and a header without a prefix defines a text variable. A prefixed header whose name is a variable of another kind, and a `%` header that isn't a variable's exact name (graph data files), are refused, and so are two headers that name one variable and a header that would define a variable while the variables are locked. An import that leaves the variables and data sets as they were adds no undo step → {imported, names, defined, deletedDataSets}", + "{path? | data? | dataBase64? | csv?} replace every dataset with the rows of a CSV, comma- or tab-separated, in UTF-16 (with a byte-order mark, or without one when at least half of its two-byte units hold a NUL byte and only one byte order reads a line break; any other file with NUL bytes is refused), UTF-8 or else Windows-1252 (header: one variable name per column, whatever its case; one row per dataset, named Data Set 1, 2… in file order). The document's variables and their bindings stay. A header that names no variable defines one, unbound: `#Name` defines a visibility variable, `@Name` defines an image variable, and a header without a prefix defines a text variable. A prefixed header whose name is a variable of another kind, and a `%` header that isn't a variable's exact name (graph data files), are refused, and so are two headers that name one variable and a header that would define a variable while the variables are locked. An import that leaves the variables and data sets as they were adds no undo step → {imported, names, defined, deletedDataSets}", has_doc, dataset_import ), diff --git a/crates/engine/src/cmd/variablecmds/csv.rs b/crates/engine/src/cmd/variablecmds/csv.rs index 31e46787f2..38dd42d922 100644 --- a/crates/engine/src/cmd/variablecmds/csv.rs +++ b/crates/engine/src/cmd/variablecmds/csv.rs @@ -32,20 +32,17 @@ fn datasets_csv_bytes(p: &Value, cmd: &str) -> Result<(Vec, bool)> { } /// The text of a datasets CSV given as a file or as `dataBase64`: UTF-16 after a UTF-16 -/// byte-order mark, else UTF-8, with or without a byte-order mark, else Windows-1252. A file -/// that starts with a byte-order mark must be valid in its encoding, and a file without one must -/// not hold NUL bytes (UTF-16 without a byte-order mark). +/// byte-order mark, else UTF-16 without one when the file holds NUL bytes (see +/// [`utf16_without_mark`]), else UTF-8, with or without a byte-order mark, else Windows-1252. A +/// file that starts with a byte-order mark must be valid in its encoding, and a file with NUL +/// bytes and no byte-order mark must read as UTF-16 by [`utf16_without_mark`]'s rule. fn datasets_csv_text(bytes: &[u8], cmd: &str) -> Result { - let utf16 = |rest: &[u8], le: bool| { - let (units, odd) = rest.as_chunks::<2>(); - let units = units.iter().map(|&c| if le { u16::from_le_bytes(c) } else { u16::from_be_bytes(c) }); - let text = char::decode_utf16(units).collect::>(); - text.ok().filter(|_| odd.is_empty()).ok_or_else(|| bad(cmd, "CSV is not UTF-16")) - }; match bytes { - [0xFF, 0xFE, rest @ ..] => return utf16(rest, true), - [0xFE, 0xFF, rest @ ..] => return utf16(rest, false), - _ if bytes.contains(&0) => return Err(bad(cmd, "CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark")), + [0xFF, 0xFE, rest @ ..] => return utf16(rest, true).ok_or_else(|| bad(cmd, "CSV is not UTF-16")), + [0xFE, 0xFF, rest @ ..] => return utf16(rest, false).ok_or_else(|| bad(cmd, "CSV is not UTF-16")), + _ if bytes.contains(&0) => { + return utf16_without_mark(bytes).ok_or_else(|| bad(cmd, "CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark")); + } _ => {} } match std::str::from_utf8(bytes) { @@ -55,6 +52,34 @@ fn datasets_csv_text(bytes: &[u8], cmd: &str) -> Result { } } +/// `bytes` as UTF-16, little-endian when `le` is set, or `None` when they aren't valid UTF-16. +fn utf16(bytes: &[u8], le: bool) -> Option { + let (units, odd) = bytes.as_chunks::<2>(); + let units = units.iter().map(|&c| if le { u16::from_le_bytes(c) } else { u16::from_be_bytes(c) }); + char::decode_utf16(units).collect::>().ok().filter(|_| odd.is_empty()) +} + +/// `bytes`, which hold NUL bytes and no byte-order mark, as UTF-16, or `None`. At least half of +/// the two-byte units must hold a NUL byte, as characters from U+0001 to U+00FF do; a file with +/// fewer, such as UTF-8 or Windows-1252 text with stray NUL bytes, is refused. The byte order is +/// the one that reads a line break (`\n` or `\r`). The other order reads one only from U+0A00 or +/// U+0D00, so a file that reads one in both orders is refused. A file that reads none in either +/// order has no data rows, and is read little-endian so that the import reports that. The text +/// must be valid UTF-16 in that order and hold no NUL character. +fn utf16_without_mark(bytes: &[u8]) -> Option { + let (units, odd) = bytes.as_chunks::<2>(); + if !odd.is_empty() || units.iter().filter(|c| c.contains(&0)).count() * 2 < units.len() { + return None; + } + let is_line_break = |u: u16| matches!(u, 0x0A | 0x0D); + let lines_le = units.iter().any(|&c| is_line_break(u16::from_le_bytes(c))); + let lines_be = units.iter().any(|&c| is_line_break(u16::from_be_bytes(c))); + if lines_le && lines_be { + return None; + } + utf16(bytes, !lines_be).filter(|text| !text.contains('\0')) +} + /// A row of a datasets CSV: the file line it starts on, its cells, and for each cell whether it was quoted. type CsvRow = (usize, Vec, Vec); diff --git a/crates/engine/src/tests/variables.rs b/crates/engine/src/tests/variables.rs index 08ba426ef1..eea8ae4986 100644 --- a/crates/engine/src/tests/variables.rs +++ b/crates/engine/src/tests/variables.rs @@ -2137,8 +2137,9 @@ fn datasets_csv_reads_windows_1252_files() { } /// A CSV that starts with a UTF-16 byte-order mark is read as UTF-16, and -/// one that isn't valid UTF-16 is refused. A file with NUL bytes and no byte-order mark is refused. -/// A text value holding NUL imports back from the `csv` that `dataset.export` returns. +/// one that isn't valid UTF-16 is refused. A file with NUL bytes and no byte-order mark that isn't +/// UTF-16 is refused. A text value holding NUL imports back from the `csv` that `dataset.export` +/// returns. #[test] fn datasets_csv_reads_utf16_files() { let mut s = session(); @@ -2160,7 +2161,7 @@ fn datasets_csv_reads_utf16_files() { assert!(e.contains("CSV is not UTF-16"), "{e}"); let e = import(&mut s, &[b"\xff\xfe".as_slice(), "City\nAB,".as_bytes(), b"\x00\xd8"].concat()).unwrap_err().to_string(); assert!(e.contains("CSV is not UTF-16"), "{e}"); - let e = import(&mut s, &utf16(b"", "City\nx\n", true)).unwrap_err().to_string(); + let e = import(&mut s, b"City\nx\0\n").unwrap_err().to_string(); assert!(e.contains("CSV holds NUL bytes"), "{e}"); assert_eq!(value(&s, "Data Set 1"), text_value("José"), "a refused file changes nothing"); s.execute("dataset.set", &json!({"name": "Data Set 1", "values": {"City": "a\u{0}b"}})).unwrap(); @@ -2170,6 +2171,63 @@ fn datasets_csv_reads_utf16_files() { assert_eq!(value(&s, "Data Set 1"), text_value("a\u{0}b")); } +/// A CSV in UTF-16 without a byte-order mark, with a NUL byte in at least half of its units, is +/// read in the byte order that reads a line break: in either order, with characters whose bytes +/// read as a line break, a quote or NUL, and when its bytes are also valid UTF-8. Files with fewer +/// NUL bytes, that read a line break in both orders, that aren't valid UTF-16 in it, that hold a +/// NUL character or that have an odd length are refused. +#[test] +fn datasets_csv_reads_utf16_files_without_a_byte_order_mark() { + let mut s = session(); + s.execute("variable.define", &json!({"name": "City", "kind": "text"})).unwrap(); + let import = |s: &mut Session, bytes: &[u8]| s.execute("dataset.import", &json!({"dataBase64": vectorcraft_format::base64_encode(bytes)})); + let value = |s: &Session, name: &str| s.doc().unwrap().doc.variables.dataset(name).and_then(|d| d.values.get("City")).cloned(); + let text_value = |v: &str| Some(vectorcraft_doc::DataValue::Text(v.into())); + let utf16 = |text: &str, le: bool| -> Vec { text.encode_utf16().flat_map(|u| if le { u.to_le_bytes() } else { u.to_be_bytes() }).collect() }; + import(&mut s, &utf16("City\r\nParis\r\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("Paris")); + import(&mut s, &utf16("City\nZoë 😀\n", false)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("Zoë 😀")); + // U+4E00's low byte is NUL, U+010A holds a `\n` byte and U+2200 a `"` byte; all three files + // are valid UTF-8 too. + import(&mut s, &utf16("City\n一\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("一")); + import(&mut s, &utf16("City\nĊ\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("Ċ")); + import(&mut s, &utf16("City\n∀∀∀\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("∀∀∀")); + // A file with no line break has no data rows in either byte order. + for bytes in [utf16("City", true), utf16("City", false), b"\0aa\0".to_vec()] { + let e = import(&mut s, &bytes).unwrap_err().to_string(); + assert!(e.contains("CSV has a header but no data rows"), "{e}"); + } + import(&mut s, &utf16("City\tName\n一二三丁目\tKai\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("一二三丁目")); + // Most units hold their NUL byte where a big-endian file would (U+4E00); the line breaks + // give the byte order. + import(&mut s, &utf16("City\n一一一一一一一一一一二\n", true)).unwrap(); + assert_eq!(value(&s, "Data Set 1"), text_value("一一一一一一一一一一二")); + // Windows-1252 and UTF-8 text with stray NUL bytes (the first with `\n\0` units), UTF-16 with + // fewer than half of its units holding a NUL byte, and, last, two files that read a line break + // in both byte orders (U+0D00 reads as `\r` and U+0A00 as `\n` big-endian). + let refused: [&[u8]; 9] = [ + b"Caf\xe9\n\0Lyon\n\0", + b"Cit\0\n", + b"City\nx\0\n", + &utf16("City\n丁丁丁丁丁丁丁丁丁丁\n", true), + &utf16("City\na\u{0}\n", true), + &[utf16("City\n", true), vec![0x00, 0xD8], utf16("\n", true)].concat(), + &[utf16("City\n", false), vec![0xDC, 0x00], utf16("\n", false)].concat(), + &utf16("City\n\u{0D00}\u{0D00}\u{0D00}\n", true), + &utf16("City\u{0A00}\nParis\n", true), + ]; + for bytes in refused { + let e = import(&mut s, bytes).unwrap_err().to_string(); + assert!(e.contains("CSV holds NUL bytes"), "{bytes:?}: {e}"); + } + assert_eq!(value(&s, "Data Set 1"), text_value("一一一一一一一一一一二"), "a refused file changes nothing"); +} + /// A data set of [`library`]: its name, and each variable's name with its paragraphs. type LibrarySet<'a> = (&'a str, &'a [(&'a str, &'a [&'a str])]); diff --git a/docs/mcp.md b/docs/mcp.md index 707a414d0c..7b9798a6d0 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -3115,8 +3115,13 @@ already walks Image Trace. Short agent entry points: reply's `defined` lists the variables the import defined. While the variables are locked, an import whose header would define a variable fails with "variables are locked". A `dataset.import` file that isn't UTF-8 is read as Windows-1252, unless it starts with the UTF-8 byte-order mark. - A file that starts with a UTF-16 byte-order mark is read as UTF-16, and a file without one that holds NUL bytes is - refused. `csv` and `data` are read as the text they hold. + A file that starts with a UTF-16 byte-order mark is read as UTF-16. A file without one that holds NUL bytes is read + as UTF-16 when at least half of its two-byte units hold a NUL byte, as characters from U+0001 to U+00FF do, in the + byte order that reads a line break (`\n` or `\r`). It is refused when fewer of its units hold a NUL byte (UTF-8 or + Windows-1252 text with stray NUL bytes, or UTF-16 text made mostly of other characters, which needs a byte-order + mark), when both byte orders read a line break (the text holds U+0A00 or U+0D00), when it has an odd length, and + when it isn't valid UTF-16 in that order or holds a NUL character. A file with no line break has no data rows and is + refused for that. `csv` and `data` are read as the text they hold. - Variable libraries: `variable.loadLibrary {path|xml|dataBase64}` (the Variables panel menu's Load Variable Library…) replaces the document's variables and data sets with a variable library's in one undo step. A load, like a `dataset.import`, adds no undo step when it leaves the variables and data sets as they were. The library diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e7c1b28726..ab8526a209 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -779,6 +779,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Variables (data merge): text, visibility and image variables bound to objects (an object may carry several), dataset rows applied in one undo step (`variable.*`, `dataset.*`) from a Variables panel and Window › Variables, with dialogs to define, bind and edit, Make Text/Visibility/Image Dynamic, Capture/Update Data Set, CSV import/export (`dataset.import` / `dataset.export`), and a row in italics when an edit leaves the art not matching it; an image dataset value is a file path and apply keeps the object's bounds; bindings let go of deleted art and locked art is left alone except by visibility values; every command, like the rest, is drivable over MCP and the CLI. - Export for Screens numbers distinct row files whose names collide, compared without case, as `-2`, `-3`… instead of leaving them out; identical rows still write one file. It puts row folders that differ only in case into one folder (`document.exportForScreens`). - Variables: Import Data Sets… (`dataset.import`) reads tab-separated files as well as comma-separated ones. Every header cell is a variable, and the rows replace the document's data sets as Data Set 1, Data Set 2 and so on. An import error about a row's values gives the file line the row starts on. + - Variables: Import Data Sets… (`dataset.import`) reads a UTF-16 file without a byte-order mark, instead of refusing it, when at least half of its two-byte units hold a NUL byte; it uses the byte order that reads its line breaks. - Variables: Import Data Sets… (`dataset.import`) defines the variables a file's header names that the document lacks, unbound. `#Shown` defines a visibility variable, `@Photo` defines an image variable, and a header without a prefix defines a text variable. A header that is a variable's exact name keeps naming it, and any other `%` header (graph data files) is refused. - Variables: Load Variable Library… in the Variables panel menu (`variable.loadLibrary`) replaces the document's variables and data sets with a variable library's, read from its XML file, in one undo step: every data set is deleted, and every variable is deleted with its bindings and values, except a text variable the library also declares, which stays bound so the library's data sets change its objects (#4136); then the library's other text variables are defined, unbound, and its data sets take the text of each value's `p` elements, one paragraph each. Variables of other kinds are left out with a warning that names their kinds. - Variables: Save Variable Library… in the Variables panel menu (`variable.saveLibrary`) writes the document's text variables and data sets as a variable library's XML file, which Load Variable Library reads back with the same text variables, data set names and text values. Variables of other kinds are left out with a warning that names their kinds. From be8d614b0c91617a21631a83fab0f50a32a29221 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:20:00 -0800 Subject: [PATCH 27/33] .ai import: straight compound paths whose guides reach past them keep the drawn look under Twist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A straight compound path with Twist was read live up to 180° whatever guides it held, and the live Twist was measured from bounds that take in the guide members. A straight compound path with Twist whose guide members reach past its other members now opens as its drawn look instead of live, and so does a compound path made only of guides. Compound paths whose guides lie inside their other members, a Twist of 0°, and single paths keep opening live. The `.ai` writer keeps a graphic-style link only where the reader reads the style live, so such an object is now written without the link, with the existing style-link warning. --- crates/eps/src/ai_editing/tests.rs | 20 ++++++++++-- crates/eps/src/import/ai/read/ops.rs | 19 +++++++++-- crates/eps/src/import/ai/tests.rs | 48 +++++++++++++++++++++++++++- docs/file-format-parity.md | 2 +- docs/target-app-parity.md | 2 +- 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/crates/eps/src/ai_editing/tests.rs b/crates/eps/src/ai_editing/tests.rs index 7a15ff2563..16b1aa532c 100644 --- a/crates/eps/src/ai_editing/tests.rs +++ b/crates/eps/src/ai_editing/tests.rs @@ -7582,9 +7582,10 @@ fn a_linked_object_past_the_object_limit_warns_that_its_link_is_left_out() { } /// A link to a graphic style with Twist is kept where the reader reads Twist: on a curve, a curved -/// compound path or a group with nothing stroked up to a quarter turn, and on a path with straight -/// segments at any angle. A stroked curve, a group with a stroked member or a guide, or a curve, -/// compound path or group twisted further, is written without the link and warns. +/// compound path or a group with nothing stroked up to a quarter turn, and on a path or compound path +/// with straight segments up to half a turn. A stroked curve, a group with a stroked member or a +/// guide, a straight compound path with a guide member that reaches past its other members, or an +/// object twisted further, is written without the link and warns. #[test] fn a_link_to_a_twist_style_is_kept_where_the_reader_reads_it() { // A document with the object `make` builds from its appearance, `ap` with Twist at `angle`, @@ -7619,6 +7620,17 @@ fn a_link_to_a_twist_style_is_kept_where_the_reader_reads_it() { n.appearance = ap.into(); n }; + // A compound path of a square and a guide, a rectangle with corners `a` and `b`. + let guided = |a: (f64, f64), b: (f64, f64)| { + move |doc: &mut Document, ap: Appearance| { + let outer = Node::path(doc.alloc_id(), square(), Appearance::default()); + let path = shapes::rectangle(Rect::new(a.0, a.1, b.0, b.1)); + let guide = Node::new(doc.alloc_id(), NodeKind::Path { path, rule: FillRule::NonZero, live: None, clipping: false, guide: true }); + let mut n = Node::new(doc.alloc_id(), NodeKind::Compound { children: vec![Arc::new(outer), Arc::new(guide)], rule: FillRule::NonZero }); + n.appearance = ap.into(); + n + } + }; // A group of a filled square and `second`. let group = |second: fn(&mut Document) -> Node| { move |doc: &mut Document, ap: Appearance| { @@ -7649,6 +7661,8 @@ fn a_link_to_a_twist_style_is_kept_where_the_reader_reads_it() { ("a square twisted three quarters of a turn back", twisted(-270.0, filled(), path(square())), false), ("a straight compound path twisted half a turn", twisted(180.0, filled(), frame), true), ("a straight compound path twisted a full turn", twisted(360.0, filled(), frame), false), + ("a straight compound path with a guide inside it", twisted(45.0, filled(), guided((30.0, 20.0), (70.0, 50.0))), true), + ("a straight compound path with a guide reaching past it", twisted(45.0, filled(), guided((30.0, 20.0), (120.0, 50.0))), false), ("a curve twisted past a quarter turn", twisted(120.0, filled(), path(oval())), false), ("a stroked curve", twisted(45.0, Appearance::basic(black_fill(), red.clone(), 2.0), path(oval())), false), ("a curved compound path", twisted(-60.0, filled(), ring), true), diff --git a/crates/eps/src/import/ai/read/ops.rs b/crates/eps/src/import/ai/read/ops.rs index 83d0f71d40..5a934fd4a0 100644 --- a/crates/eps/src/import/ai/read/ops.rs +++ b/crates/eps/src/import/ai/read/ops.rs @@ -1102,6 +1102,15 @@ fn no_guides(n: &Node, depth: usize) -> bool { depth <= MAX_DEPTH && !n.is_guide() && n.children().into_iter().flatten().all(|c| no_guides(c, depth + 1)) } +/// Is `n` a compound path with a guide member whose bounds reach past the bounds of its members that +/// aren't guides, or with guide members only? Any other object counts as no. +fn guides_reach_past(n: &Node) -> bool { + let NodeKind::Compound { children, .. } = &n.kind else { return false }; + let bounds = + |guides: bool| children.iter().filter(|c| c.is_guide() == guides).fold(None, |acc, c| vectorcraft_geom::union_opt(acc, c.geometric_bounds())); + bounds(true).is_some_and(|g| bounds(false).is_none_or(|o| o.union(g) != o)) +} + /// The largest Twist angle, in degrees either way, read as a live effect on an object that isn't a /// path or compound path with straight segments: VectorCraft's own limit. An object with a larger /// Twist keeps the file's drawn look. @@ -1115,7 +1124,8 @@ const MAX_STRAIGHT_TWIST: f64 = 180.0; /// Is `object` one that style `st`, whose effect moves the outline, is read on? It and everything /// in it [`moves_alike`], and the style paints no gradient. Twist is read only when nothing is /// stroked and its angle is a number: up to [`MAX_STRAIGHT_TWIST`] degrees either way on a path or -/// compound path with straight segments, or up to [`MAX_TWIST`] degrees either way on another object +/// compound path with straight segments (only at 0° when its guide members reach past its other +/// members, see [`guides_reach_past`]), or up to [`MAX_TWIST`] degrees either way on another object /// with no guide in it. Transform that scales, turns or reflects is read only when nothing is stroked /// (its reference point lies on the bounds without strokes). fn moves_with(object: &Node, st: &crate::import::ai::style::ArtStyle) -> bool { @@ -1128,7 +1138,12 @@ fn moves_with(object: &Node, st: &crate::import::ai::style::ArtStyle) -> bool { && st.effects.iter().all(|e| match e.id.as_str() { "distort.twist" => { let within = |limit: f64| e.params["angle"].as_f64().is_some_and(|a| a.abs() <= limit); - !stroked && if straight(object) { within(MAX_STRAIGHT_TWIST) } else { no_guides(object, 0) && within(MAX_TWIST) } + !stroked + && if straight(object) { + within(MAX_STRAIGHT_TWIST) && (e.params["angle"].as_f64() == Some(0.0) || !guides_reach_past(object)) + } else { + no_guides(object, 0) && within(MAX_TWIST) + } } "distort.transform" => { let p = &e.params; diff --git a/crates/eps/src/import/ai/tests.rs b/crates/eps/src/import/ai/tests.rs index 23d2536adc..578c488855 100644 --- a/crates/eps/src/import/ai/tests.rs +++ b/crates/eps/src/import/ai/tests.rs @@ -7183,7 +7183,11 @@ fn twist_is_read_on_curves_compound_paths_and_groups_with_nothing_stroked_up_to_ ("a twisted group", read(&twist(90.0, None), &distort_group()), 2), ("a group with a hidden member", read(&twist(45.0, None), &hidden_member), 2), ("a curve twisted a quarter turn back", read(&twist(-90.0, BLUE_FILL), &curved), 0), - ("a straight compound path with a guide, as before", read(&twist(45.0, BLUE_FILL), &with_guide(&rect_path(20.0, 20.0, 60.0, 50.0))), 2), + ( + "a straight compound path with a guide inside it, as before", + read(&twist(45.0, BLUE_FILL), &with_guide(&rect_path(0.0, 0.0, 60.0, 50.0))), + 2, + ), ] { assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{what}: {:?}", r.warnings); let a = art(&r.doc); @@ -7250,6 +7254,48 @@ fn twist_is_read_on_straight_paths_and_compound_paths_up_to_half_a_turn() { } } +#[test] +fn twist_on_a_straight_compound_path_keeps_the_drawn_look_where_a_guide_reaches_past_its_other_members() { + // GUARDS: a straight compound path whose guide members reach past its other members was read + // with a live Twist measured from bounds that take in those guides, which draws differently from + // the art saved with it. + // The object, `hidden` painted with its style, under its drawn look. + let object = |hidden: &str| { + format!("0 Ae\nu\n{RED}{}f\nU\n%_Bb\n%_{}0 BB\n1 (Bent) XW\n", rect_path(16.0, 16.0, 64.0, 54.0), hidden.replace('\n', "\n%_")) + }; + // A compound path of a square and the guide from `from` to `to`. + let with_guide = |from: (f64, f64), to: (f64, f64)| { + object(&format!("*u\n{}f\n{} {} m\n{} {} L\n(N) *\n*U\n", rect_path(20.0, 20.0, 60.0, 50.0), from.0, from.1, to.0, to.1)) + }; + let twist = |angle: f64| distort_styles("Adobe Twirl", &format!("{angle} /Real (angle) ,\n"), BLUE_FILL); + let read = |angle: f64, art_: &str| read_ok(&format!("{}{}", twist(angle), layer(art_))); + for (what, r) in [ + ("a guide inside the square", read(45.0, &with_guide((25.0, 25.0), (35.0, 35.0)))), + ("a guide on the square's corners", read(180.0, &with_guide((20.0, 20.0), (60.0, 50.0)))), + ("a guide inside the square, half a turn back", read(-180.0, &with_guide((30.0, 40.0), (50.0, 30.0)))), + ("a guide outside the square, not turned", read(0.0, &with_guide((5.0, 0.0), (15.0, 10.0)))), + ] { + assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{what}: {:?}", r.warnings); + let a = art(&r.doc); + assert_eq!(a.len(), 1, "{what}: {a:#?}"); + assert_eq!(a[0].children().map_or(0, |c| c.len()), 2, "{what}: the object, not its look: {a:#?}"); + let [e] = a[0].appearance.effects.as_slice() else { panic!("{what}: {:?}", a[0].appearance) }; + assert_eq!(e.id, "distort.twist", "{what}"); + } + for (what, r) in [ + ("a guide outside the square", read(45.0, &with_guide((5.0, 0.0), (15.0, 10.0)))), + ("a guide reaching past the square's right side", read(45.0, &with_guide((30.0, 30.0), (70.0, 40.0)))), + ("a guide reaching past the square's top, half a turn", read(180.0, &with_guide((30.0, 30.0), (40.0, 51.0)))), + ("a guide around the square, a quarter turn back", read(-90.0, &with_guide((0.0, 0.0), (80.0, 70.0)))), + ("a compound path of guides only", read(45.0, &object("*u\n25 25 m\n35 35 L\n(N) *\n40 25 m\n50 35 L\n(N) *\n*U\n"))), + ] { + let a = art(&r.doc); + assert_eq!(a.len(), 1, "{what}: {a:#?}"); + assert!(a[0].appearance.effects.is_empty(), "{what}: the drawn look: {a:#?}"); + assert!(r.warnings.iter().any(|w| w.contains("drawn look")), "{what}: {:?}", r.warnings); + } +} + #[test] fn a_bare_comment_line_in_a_prolog_leaves_its_end_alone() { // GUARDS: a bare `%` line's empty marker matched the empty begin marker of a section that has none. diff --git a/docs/file-format-parity.md b/docs/file-format-parity.md index c5cb2a78c1..01dc7448cf 100644 --- a/docs/file-format-parity.md +++ b/docs/file-format-parity.md @@ -252,7 +252,7 @@ private editing data. VectorCraft: ### Illustrator's private editing data -The app's own editing copy of a file (an EPS after its `%%EOF`, a `.ai`'s `AIPrivateData` streams, a PDF's `AIPDFPrivateData` streams when it was saved with the editing data (#868), Zstandard or zlib, the EPS's in ASCII85) is not documented as a whole. VectorCraft reads its structure: layers and sublayers (names, order, visibility, lock, printing, preview, dimming, colour), groups as they were nested, compound paths (painted by their last path, or the last painted one, with the paths of groups inside them kept as their paths: #1410), clipping groups and layer clipping (several clipping paths in a clipping group, a layer or an opacity mask's art clip to their intersection, and a compound path of clipping paths stays one clip; a filled clipping path keeps its fill, which paints under the art it clips, except the path of a mask that covers several layers, whose fill is read as a path of its own in the layer that holds the mask's path, and a compound clipping path whose subpaths paint differently, whose filled subpaths are read as a compound path of their own under the art it clips; clipping paths that would nest the art past the document's 32 levels stop the editing data from being read: #5789), object names, hidden and locked objects, fills and strokes (grey, CMYK, RGB, named and spot colours, spot colours defined in Lab with their Lab values (#1032), global process colours as global swatches at their tint; a stroke of width 0, `0 w`, on a path, a gradient-filled path, legacy type or in an art style comes in as a 0.25 pt hairline, as from PDF: #1671), linear and radial gradients, pattern fills with their patterns as pattern swatches (#1025), gradient and pattern names in the file up to their first newline (a name that another swatch, or a gradient or pattern defined earlier, already has is numbered "Sky 2"; a name without a newline is kept, and when another swatch has it, the pattern's swatch, or a named color sharing a gradient or pattern swatch's name, is numbered the same way), opacity, blend modes, isolation and knockout, embedded images where their matrix puts them, rotated ones too (#2269), with their alpha channel (CMYK images keep their ink amounts, as they do when a `.ai` opens from its PDF part, and an alpha channel that hides part of a CMYK image becomes a grayscale opacity mask), placed (linked) files as linked images in their place, each showing the low-resolution preview the file keeps of it until its file is found or relinked in the Links panel (#1264), with the folder the file keeps relative to the document's, where the link is looked for when its path is gone, placed PDF and `.ai` pages as placed documents of the [page and Crop to box](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-other-file-types/adobe-pdf-placement-options.html) their dictionary names (`AI11 PlacedPDFPageIndex`, `AI11 PlacedPDFCropTo`; a value VectorCraft doesn't read, or a binding to an image variable, leaves a linked image), guides, and every artboard where it is, with its center mark, cross hairs, video safe areas and video ruler pixel aspect ratio. VectorCraft reads `CropAreaActive`, an `/Int` in the dictionary that holds the artboard array, as the index of the active artboard in that array, and the file opens with that artboard active; without it, or when it names no artboard that opens, the first artboard is active. It also reads the Document Raster Effects Settings (resolution, color model, background, anti-alias, clipping mask, Add Around Object and Preserve Spot Colors), the author from the header's `%%For` (an EPS's own header when the editing data's has none), swatches for the header's `%%CMYKCustomColor` and `%%RGBCustomColor` colors that the document has none of, and `%%TemplateBox` in older files; a setting with a value of another type or out of range keeps its default. Objects on Path groups open live, with their path, their objects in order along it, Pivot and Rotate; one with a dragged spacing or other options opens as a plain group. A gradient with stops in the middle runs its whole ramp across its shape, and a gradient object does not pass its fill to the next path (#1390). An object whose art style is one fill and one stroke at most (solid colours, its own gradient, a variable-width profile on a Stroke Offset stroke or written as each side's widths) with Drop Shadow, Inner Glow, Outer Glow, Feather, Round Corners, Scribble, Offset Path, Outline Object, Outline Stroke, Convert to Shape, Tweak (only when it moves nothing), Pucker & Bloat at 0 or Bas Relief (read from its settings block: [raster effects reference](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/raster-effects-reference.html), [Photoshop File Formats Specification](https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/), [scripting the filter gallery](https://community.adobe.com/questions-712/scripting-support-for-the-filter-gallery-1064646)) comes in as itself with them and their saved settings. A path, compound path or group (not a clipping group) whose only effect is Transform (not with copies or Random) or a Free Distort whose corners make a parallelogram comes in as itself with it too, when neither it nor anything in it paints with a gradient or pattern and everything in it is a path, compound path or group with no opacity mask and no effects of its own; Transform that scales, rotates or reflects needs nothing in it to be stroked. Twist as the only effect is read the same way when nothing in the object is stroked: up to 180° either way (a limit VectorCraft sets) on a path or compound path whose segments are all straight, and up to 90° either way (a limit VectorCraft sets) on another path, compound path or group with no guide in it. One with more, other effects (Zig Zag, a Pucker & Bloat of another amount, a Free Distort with other corners), an effect setting that is not read (an unknown key, a missing or out-of-range value, a Scribble with a color of its own), a variable-width stroke with one centered width array, dashes aligned to corners, a brush other than a Calligraphic or Art one, a Calligraphic brush with settings of its own or an angle that follows the path, or an Art brush other than the ones described next comes in as its drawn look (a group named after it). Objects in a symbol defined before the file's art styles section, as newer files write it, take the same styles as the layers' objects. A path with a Calligraphic brush otherwise comes in as the path, the brush on its stroke at the stroke's weight, which scales the brush (#372), and so does a path with an Art brush, whose weight scales the art across the path, when all of these hold: the brush stretches its art along the path, unflipped, at 100% in Tints; the path's settings are the brush's own, with no entry that isn't read, no variable width and no effects; its stroke isn't a swatch; and the art is paths, compound paths and groups in solid fills, with no strokes, masks or effects, and has some length along the path. A stroke takes the brush the Brushes panel keeps under its name, and one whose name the panel doesn't keep comes in as its drawn look. Type with an Art brush keeps its drawn look under the type. A Calligraphic brush's Pressure, Tilt, Bearing and Rotation come in as those channels (a 6D Art Pen's brushes vary with the pressure, the bearing and the barrel's rotation); its Stylus Wheel varies at random. The file's Calligraphic, Scatter, Art and Bristle brushes replace the default ones in the Brushes panel, in the file's order, also when the file comes in as its PDF part (a brush library has no layers); pattern brushes and brushes it can't read are left out with a note. When the file opens from its layers, the spot colors in the art of each brush that comes in become swatches, and that art's colors count toward the document's color mode, as a pattern's do. The swatches take at most 20,000 color names in all across the brush art, and each brush-art section keeps at most 20,000 names. Each name keeps its first definition, with the color operators' names before the gradient stops' names. A spot color past those limits makes no swatch, and its color still counts toward the color mode. A spot color that the page's art, a pattern or a symbol also defines keeps the value defined there. Type keeps its art style as a live appearance whatever its stack (an Art brush aside), so it can be retyped: each fill and stroke of the style's stack (solid colours, gradients and pattern fills, placed as the drawn look places them) with its own opacity, blend mode and effects, the effects on the whole object, and where the characters paint among them; the effects read are Warp, Offset Path, Outline Object, Outline Stroke, Transform (with copies, Transform Objects, Transform Patterns and Scale Strokes & Effects; not with Random), Free Distort, Pucker & Bloat, Twist, Zig Zag, Tweak (only when it moves nothing, the file holding none of its random outline), Drop Shadow, Inner Glow, Outer Glow, Feather, Round Corners, Scribble, Gaussian Blur, Bas Relief (its detail, smoothness and light direction from its settings block, or the effect's defaults when the style holds no settings block; a settings block that isn't read keeps the drawn look) and Convert to Shape, and a style with others, or with an effect setting that isn't read, keeps the type's drawn look. Convert to Shape keeps its Rectangle, Rounded Rectangle or Ellipse, its Absolute or Relative size (a relative size in the file is added to each side) and its corner radius; a relative Ellipse, or a setting that isn't read, keeps the drawn look. The page's drawing of such a style isn't held to look like VectorCraft's where its drawn look lay. Gradients take their matrix (`Xm`, radials and skewed ones too), focal point and the order of stops sharing a ramp point; gradient meshes, a clipping path's own fill and its stroke, and the artboards' names are read too. `editingData: "always"` (`convert --editing-data`) opens a `.ai` or an Illustrator EPS from its editing data even where it draws differently from its page, which can be older, with a note. Type is made from the file's text document, whole and editable, shown or not: point type (its frame's matrix, which newer files leave out when it is neither turned nor scaled, read as none), area type (a story in several frames as threaded type) and type on a path, with its characters, fonts, size, leading, tracking, scaling, baseline shift, fill and stroke (grey, RGB or CMYK), alignment, indents and paragraph spacing (a character attribute a run doesn't set comes from its character style, then the style that one is based on, then the document's defaults); area type keeps its frame's Area Type Options rows and columns, the gutter between rows and the gutter between columns, and its first baseline (Fixed, Cap Height, Leading or x Height) with its minimum; type on a path keeps its frame's Type on a Path effect, Align to Path and Flip (a frame whose options set key 6 to true keeps their defaults), and the first frame's Spacing is fitted from the file's layout, not read from the dialog's value; the distance from the path under Align to Path is measured for each character at its size and vertical scale (in vertical type, its horizontal scale), from the top of its font's em box (Ascender), half the height of the top of its font's H (Center) or the bottom of its font's bounding box (Descender); the fit under Gravity does not reproduce the file's layout, and its glyphs can lie up to 7.4 pt from the positions the file keeps; the page's drawing of it (often in pieces) is left out. Vertical type keeps its orientation, its columns from right to left, and a run set to Tate-chu-yoko keeps that setting. When it makes the type of every text object that shows, the page's type is only compared with it, each text object keeps its own group, frame and layer, and a line break inside a paragraph stays one. Where it can't make a text object's type (a frame of another kind), that text object takes the page's type where the text document puts its lines, or else what is left of the page's type in painting order (no group around it); the page's type where made type draws stays that type's, and hidden type is left out with a note. Text wrap is read too: an object whose dictionary has the wrap options (offset and Invert Wrap), or any graphic of a legacy wraparound group (`*w` … `*W`, before or after its type, with no offset), is a wrap object, and area type below it in its layer flows around it when the file opens, threaded type included (#2763). Kerning, underline and the other character and paragraph options aren't read yet. A `.ai` saved without PDF compatibility (a placeholder page) opens from its editing data alone, its point type, area type and type on a path made from the text document. A `.ai` whose PDF part shows only text isn't taken for that placeholder when its page states an art, trim or bleed box: it opens from its editing data alone where that reads, else from its PDF part (#1387). A `.ai` whose PDF part draws nothing at all (an empty page, with or without those boxes) opens from its editing data alone where that reads too: there is no page art to check its layers against, and the empty page would lose all of it. Text objects with an appearance keep their own text and frame, type of hidden layers stays hidden, and type on later artboards lands on its text objects (#1391); area type whose frame is too short for a line of the fallback font shows its first line (the frame is made taller, with a warning in the open result) (#1392). In a PDF saved with some of its artboards, the type on each page after the first goes to an artboard of that page's size after the previous page's artboard, and only to one that leaves an artboard for each later page; the first page's artboard also leaves one for each later page. When several artboards fit a later page, VectorCraft takes the one whose art draws most like the page, and compares at most 16 artboards for all the later pages together. Symbols are read into the Symbols panel, in the file's symbol list order, with their art, their registration point and the instances in their art, and their instances come in live with their matrix, name, hide, lock, opacity, blending mode, isolation, knockout and opacity mask; a symbol whose art isn't read whole (type, placed files, an image it leaves out or anything else it doesn't read) isn't kept. A file that comes in as its page or PDF part because its editing data has no layers (a symbol library), or because its layers can't be read or used (any of the cases below), gets the symbols its editing data defines before its first layer, in the symbol list's order, while its page art stays as the page draws it. Instances of symbols it can't read, instances with appearance changes of their own (a dynamic symbol's), pattern fills whose pattern it can't read, placed files the data keeps no preview of and anything else it doesn't read make the file come in as its page (on a layer that doesn't show, they are left out of that layer), with a warning saying what. An unread object on a shown layer whose box can be found instead comes in as a group of the page's objects inside that box, named after it, with the rest of the layers and groups kept and a warning listing what came in as the page's art; the file still comes in as its page when live art or another unread object shares that page art, when the object spans two artboards or lies on an artboard that has no page, when no page art lies inside its box, when that page art is inside a page group with a clip, mask, transparency or appearance, when the object holds a frame of threaded type, when the page art uses a pattern, image or swatch that the page lacks or that differs from the editing data's, or when the page art doesn't carry its transparency (#4806). The layers' art is also compared with the page (by lightness, so colours written as their CMYK equivalents compare alike, and by whether a pixel is inked at all); the page is used when they differ by more than 5% (away from type, when all the type is made from the text document), where one draws an object the other doesn't (away from type, whose lines may be laid out afresh), and when an EPS whose page is its art's box has art printed outside it (#735). A `.ai` is not compared with its page, and its PDF part is not imported, when its text document makes the type of every text object (at least one shows, and none is drawn by the appearance of the layer or group it is in), no object is unread, no placed file has a second placement, no live envelope keeps its drawn art, it declares no large canvas, and its artboards are its pages' trim boxes one for one, in points or in the first page's `UserUnit`: it opens from its layers, with the bleed of its pages' bleed boxes. When they differ because some objects draw differently (an object in another color, or one that only one of them draws), each top-level object of the layers that holds such a difference comes in as a group of the page's objects inside its box and the layers stay, with a warning that names the layer or sublayer of each such object as the Layers panel shows it (`` for one without a name) and how many came in from it; an export or Save As to the file's own path then needs `acknowledgeLoss: true`, and a wrap object replaced this way keeps its wrap options. The file still comes in as its page when only edges or type differ, when a difference lies outside every top-level object, when more than half of the top-level objects or more than 64 hold one, when such an object is or holds type or holds a wrap object inside a group, when the page's type of a text object that stays lies inside such an object's box, when the page's art can't replace such an object for one of the reasons above for an unread object, when matching the differences to the objects would take too much work, or when the result still differs from the page. `editingData: "always"` keeps the objects as read, and placed files at the other point their data gives are tried as well. An EPS whose page is its art's box keeps that page when it comes in through its layers, not the editing data's artboard, and a text object counts as drawn by the page by its colours, not only its lightness, so a red stroke stacked over a dark red one stays (#1102). `textAs: "outlines"` opens a `.ai` whose type shows from its PDF part, which has the outlines; `editingData: false` opens any of them as its page or PDF part only, for print pipelines (#735). A file whose import left something out (hidden text, art or layers) is never written over by an export or Save As to its own path unless `acknowledgeLoss: true` (`convert --replace-lossy`), so the original keeps what the document lacks. Where a `.ai` can't be read that way (or its layers are turned off in the import options), we read its PDF-compatible part, so Illustrator-only live objects arrive as appearance, and plain groups arrive ungrouped: that part draws each object on its own and marks only layers, clipping groups and groups with opacity, blending or a mask (those come back). Art outside the artboards isn't in that part at all (Illustrator's PDF part leaves it out), so it doesn't open; the import says so in a note (#472). Its pages still go where the editing data puts the artboards (a grid stays a grid), with the art on each, when it has as many artboards as pages, of the same sizes (#1068); the page of the artboard `CropAreaActive` names is then the active artboard. A `.ai` whose editing data has artboards but no layers also opens on that page under the same check, and its pages stay where they are. A `.ai` whose PDF part's pages are cropped to its art (each page the box of all the art, which is the editing data's bounding box) instead of its artboards opens from its layers, with its own artboards, where they draw as that page does. Its pages are laid out so that art one of them draws past its artboard (notes beside a design, which the part doesn't clip) doesn't lie over the page before it in the row, where it was taken for that page's art: the check against the editing data then found art its layers don't have there and opened the file as its PDF part, without the art outside the artboards. That part also has no paragraphs: area type is written line by line, and comes back as area type only where its font is installed and lays the lines out with the same breaks, else as a point type object per line (#508). Art off the page that a PDF does draw opens on the pasteboard. +The app's own editing copy of a file (an EPS after its `%%EOF`, a `.ai`'s `AIPrivateData` streams, a PDF's `AIPDFPrivateData` streams when it was saved with the editing data (#868), Zstandard or zlib, the EPS's in ASCII85) is not documented as a whole. VectorCraft reads its structure: layers and sublayers (names, order, visibility, lock, printing, preview, dimming, colour), groups as they were nested, compound paths (painted by their last path, or the last painted one, with the paths of groups inside them kept as their paths: #1410), clipping groups and layer clipping (several clipping paths in a clipping group, a layer or an opacity mask's art clip to their intersection, and a compound path of clipping paths stays one clip; a filled clipping path keeps its fill, which paints under the art it clips, except the path of a mask that covers several layers, whose fill is read as a path of its own in the layer that holds the mask's path, and a compound clipping path whose subpaths paint differently, whose filled subpaths are read as a compound path of their own under the art it clips; clipping paths that would nest the art past the document's 32 levels stop the editing data from being read: #5789), object names, hidden and locked objects, fills and strokes (grey, CMYK, RGB, named and spot colours, spot colours defined in Lab with their Lab values (#1032), global process colours as global swatches at their tint; a stroke of width 0, `0 w`, on a path, a gradient-filled path, legacy type or in an art style comes in as a 0.25 pt hairline, as from PDF: #1671), linear and radial gradients, pattern fills with their patterns as pattern swatches (#1025), gradient and pattern names in the file up to their first newline (a name that another swatch, or a gradient or pattern defined earlier, already has is numbered "Sky 2"; a name without a newline is kept, and when another swatch has it, the pattern's swatch, or a named color sharing a gradient or pattern swatch's name, is numbered the same way), opacity, blend modes, isolation and knockout, embedded images where their matrix puts them, rotated ones too (#2269), with their alpha channel (CMYK images keep their ink amounts, as they do when a `.ai` opens from its PDF part, and an alpha channel that hides part of a CMYK image becomes a grayscale opacity mask), placed (linked) files as linked images in their place, each showing the low-resolution preview the file keeps of it until its file is found or relinked in the Links panel (#1264), with the folder the file keeps relative to the document's, where the link is looked for when its path is gone, placed PDF and `.ai` pages as placed documents of the [page and Crop to box](https://helpx.adobe.com/illustrator/desktop/add-and-import-files/import-other-file-types/adobe-pdf-placement-options.html) their dictionary names (`AI11 PlacedPDFPageIndex`, `AI11 PlacedPDFCropTo`; a value VectorCraft doesn't read, or a binding to an image variable, leaves a linked image), guides, and every artboard where it is, with its center mark, cross hairs, video safe areas and video ruler pixel aspect ratio. VectorCraft reads `CropAreaActive`, an `/Int` in the dictionary that holds the artboard array, as the index of the active artboard in that array, and the file opens with that artboard active; without it, or when it names no artboard that opens, the first artboard is active. It also reads the Document Raster Effects Settings (resolution, color model, background, anti-alias, clipping mask, Add Around Object and Preserve Spot Colors), the author from the header's `%%For` (an EPS's own header when the editing data's has none), swatches for the header's `%%CMYKCustomColor` and `%%RGBCustomColor` colors that the document has none of, and `%%TemplateBox` in older files; a setting with a value of another type or out of range keeps its default. Objects on Path groups open live, with their path, their objects in order along it, Pivot and Rotate; one with a dragged spacing or other options opens as a plain group. A gradient with stops in the middle runs its whole ramp across its shape, and a gradient object does not pass its fill to the next path (#1390). An object whose art style is one fill and one stroke at most (solid colours, its own gradient, a variable-width profile on a Stroke Offset stroke or written as each side's widths) with Drop Shadow, Inner Glow, Outer Glow, Feather, Round Corners, Scribble, Offset Path, Outline Object, Outline Stroke, Convert to Shape, Tweak (only when it moves nothing), Pucker & Bloat at 0 or Bas Relief (read from its settings block: [raster effects reference](https://helpx.adobe.com/illustrator/desktop/special-effects-styles/apply-filter-effects/raster-effects-reference.html), [Photoshop File Formats Specification](https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/), [scripting the filter gallery](https://community.adobe.com/questions-712/scripting-support-for-the-filter-gallery-1064646)) comes in as itself with them and their saved settings. A path, compound path or group (not a clipping group) whose only effect is Transform (not with copies or Random) or a Free Distort whose corners make a parallelogram comes in as itself with it too, when neither it nor anything in it paints with a gradient or pattern and everything in it is a path, compound path or group with no opacity mask and no effects of its own; Transform that scales, rotates or reflects needs nothing in it to be stroked. Twist as the only effect is read the same way when nothing in the object is stroked: up to 180° either way (a limit VectorCraft sets) on a path or compound path whose segments are all straight, except a compound path with a guide member that reaches past its other members at an angle other than 0°, and up to 90° either way (a limit VectorCraft sets) on another path, compound path or group with no guide in it. One with more, other effects (Zig Zag, a Pucker & Bloat of another amount, a Free Distort with other corners), an effect setting that is not read (an unknown key, a missing or out-of-range value, a Scribble with a color of its own), a variable-width stroke with one centered width array, dashes aligned to corners, a brush other than a Calligraphic or Art one, a Calligraphic brush with settings of its own or an angle that follows the path, or an Art brush other than the ones described next comes in as its drawn look (a group named after it). Objects in a symbol defined before the file's art styles section, as newer files write it, take the same styles as the layers' objects. A path with a Calligraphic brush otherwise comes in as the path, the brush on its stroke at the stroke's weight, which scales the brush (#372), and so does a path with an Art brush, whose weight scales the art across the path, when all of these hold: the brush stretches its art along the path, unflipped, at 100% in Tints; the path's settings are the brush's own, with no entry that isn't read, no variable width and no effects; its stroke isn't a swatch; and the art is paths, compound paths and groups in solid fills, with no strokes, masks or effects, and has some length along the path. A stroke takes the brush the Brushes panel keeps under its name, and one whose name the panel doesn't keep comes in as its drawn look. Type with an Art brush keeps its drawn look under the type. A Calligraphic brush's Pressure, Tilt, Bearing and Rotation come in as those channels (a 6D Art Pen's brushes vary with the pressure, the bearing and the barrel's rotation); its Stylus Wheel varies at random. The file's Calligraphic, Scatter, Art and Bristle brushes replace the default ones in the Brushes panel, in the file's order, also when the file comes in as its PDF part (a brush library has no layers); pattern brushes and brushes it can't read are left out with a note. When the file opens from its layers, the spot colors in the art of each brush that comes in become swatches, and that art's colors count toward the document's color mode, as a pattern's do. The swatches take at most 20,000 color names in all across the brush art, and each brush-art section keeps at most 20,000 names. Each name keeps its first definition, with the color operators' names before the gradient stops' names. A spot color past those limits makes no swatch, and its color still counts toward the color mode. A spot color that the page's art, a pattern or a symbol also defines keeps the value defined there. Type keeps its art style as a live appearance whatever its stack (an Art brush aside), so it can be retyped: each fill and stroke of the style's stack (solid colours, gradients and pattern fills, placed as the drawn look places them) with its own opacity, blend mode and effects, the effects on the whole object, and where the characters paint among them; the effects read are Warp, Offset Path, Outline Object, Outline Stroke, Transform (with copies, Transform Objects, Transform Patterns and Scale Strokes & Effects; not with Random), Free Distort, Pucker & Bloat, Twist, Zig Zag, Tweak (only when it moves nothing, the file holding none of its random outline), Drop Shadow, Inner Glow, Outer Glow, Feather, Round Corners, Scribble, Gaussian Blur, Bas Relief (its detail, smoothness and light direction from its settings block, or the effect's defaults when the style holds no settings block; a settings block that isn't read keeps the drawn look) and Convert to Shape, and a style with others, or with an effect setting that isn't read, keeps the type's drawn look. Convert to Shape keeps its Rectangle, Rounded Rectangle or Ellipse, its Absolute or Relative size (a relative size in the file is added to each side) and its corner radius; a relative Ellipse, or a setting that isn't read, keeps the drawn look. The page's drawing of such a style isn't held to look like VectorCraft's where its drawn look lay. Gradients take their matrix (`Xm`, radials and skewed ones too), focal point and the order of stops sharing a ramp point; gradient meshes, a clipping path's own fill and its stroke, and the artboards' names are read too. `editingData: "always"` (`convert --editing-data`) opens a `.ai` or an Illustrator EPS from its editing data even where it draws differently from its page, which can be older, with a note. Type is made from the file's text document, whole and editable, shown or not: point type (its frame's matrix, which newer files leave out when it is neither turned nor scaled, read as none), area type (a story in several frames as threaded type) and type on a path, with its characters, fonts, size, leading, tracking, scaling, baseline shift, fill and stroke (grey, RGB or CMYK), alignment, indents and paragraph spacing (a character attribute a run doesn't set comes from its character style, then the style that one is based on, then the document's defaults); area type keeps its frame's Area Type Options rows and columns, the gutter between rows and the gutter between columns, and its first baseline (Fixed, Cap Height, Leading or x Height) with its minimum; type on a path keeps its frame's Type on a Path effect, Align to Path and Flip (a frame whose options set key 6 to true keeps their defaults), and the first frame's Spacing is fitted from the file's layout, not read from the dialog's value; the distance from the path under Align to Path is measured for each character at its size and vertical scale (in vertical type, its horizontal scale), from the top of its font's em box (Ascender), half the height of the top of its font's H (Center) or the bottom of its font's bounding box (Descender); the fit under Gravity does not reproduce the file's layout, and its glyphs can lie up to 7.4 pt from the positions the file keeps; the page's drawing of it (often in pieces) is left out. Vertical type keeps its orientation, its columns from right to left, and a run set to Tate-chu-yoko keeps that setting. When it makes the type of every text object that shows, the page's type is only compared with it, each text object keeps its own group, frame and layer, and a line break inside a paragraph stays one. Where it can't make a text object's type (a frame of another kind), that text object takes the page's type where the text document puts its lines, or else what is left of the page's type in painting order (no group around it); the page's type where made type draws stays that type's, and hidden type is left out with a note. Text wrap is read too: an object whose dictionary has the wrap options (offset and Invert Wrap), or any graphic of a legacy wraparound group (`*w` … `*W`, before or after its type, with no offset), is a wrap object, and area type below it in its layer flows around it when the file opens, threaded type included (#2763). Kerning, underline and the other character and paragraph options aren't read yet. A `.ai` saved without PDF compatibility (a placeholder page) opens from its editing data alone, its point type, area type and type on a path made from the text document. A `.ai` whose PDF part shows only text isn't taken for that placeholder when its page states an art, trim or bleed box: it opens from its editing data alone where that reads, else from its PDF part (#1387). A `.ai` whose PDF part draws nothing at all (an empty page, with or without those boxes) opens from its editing data alone where that reads too: there is no page art to check its layers against, and the empty page would lose all of it. Text objects with an appearance keep their own text and frame, type of hidden layers stays hidden, and type on later artboards lands on its text objects (#1391); area type whose frame is too short for a line of the fallback font shows its first line (the frame is made taller, with a warning in the open result) (#1392). In a PDF saved with some of its artboards, the type on each page after the first goes to an artboard of that page's size after the previous page's artboard, and only to one that leaves an artboard for each later page; the first page's artboard also leaves one for each later page. When several artboards fit a later page, VectorCraft takes the one whose art draws most like the page, and compares at most 16 artboards for all the later pages together. Symbols are read into the Symbols panel, in the file's symbol list order, with their art, their registration point and the instances in their art, and their instances come in live with their matrix, name, hide, lock, opacity, blending mode, isolation, knockout and opacity mask; a symbol whose art isn't read whole (type, placed files, an image it leaves out or anything else it doesn't read) isn't kept. A file that comes in as its page or PDF part because its editing data has no layers (a symbol library), or because its layers can't be read or used (any of the cases below), gets the symbols its editing data defines before its first layer, in the symbol list's order, while its page art stays as the page draws it. Instances of symbols it can't read, instances with appearance changes of their own (a dynamic symbol's), pattern fills whose pattern it can't read, placed files the data keeps no preview of and anything else it doesn't read make the file come in as its page (on a layer that doesn't show, they are left out of that layer), with a warning saying what. An unread object on a shown layer whose box can be found instead comes in as a group of the page's objects inside that box, named after it, with the rest of the layers and groups kept and a warning listing what came in as the page's art; the file still comes in as its page when live art or another unread object shares that page art, when the object spans two artboards or lies on an artboard that has no page, when no page art lies inside its box, when that page art is inside a page group with a clip, mask, transparency or appearance, when the object holds a frame of threaded type, when the page art uses a pattern, image or swatch that the page lacks or that differs from the editing data's, or when the page art doesn't carry its transparency (#4806). The layers' art is also compared with the page (by lightness, so colours written as their CMYK equivalents compare alike, and by whether a pixel is inked at all); the page is used when they differ by more than 5% (away from type, when all the type is made from the text document), where one draws an object the other doesn't (away from type, whose lines may be laid out afresh), and when an EPS whose page is its art's box has art printed outside it (#735). A `.ai` is not compared with its page, and its PDF part is not imported, when its text document makes the type of every text object (at least one shows, and none is drawn by the appearance of the layer or group it is in), no object is unread, no placed file has a second placement, no live envelope keeps its drawn art, it declares no large canvas, and its artboards are its pages' trim boxes one for one, in points or in the first page's `UserUnit`: it opens from its layers, with the bleed of its pages' bleed boxes. When they differ because some objects draw differently (an object in another color, or one that only one of them draws), each top-level object of the layers that holds such a difference comes in as a group of the page's objects inside its box and the layers stay, with a warning that names the layer or sublayer of each such object as the Layers panel shows it (`` for one without a name) and how many came in from it; an export or Save As to the file's own path then needs `acknowledgeLoss: true`, and a wrap object replaced this way keeps its wrap options. The file still comes in as its page when only edges or type differ, when a difference lies outside every top-level object, when more than half of the top-level objects or more than 64 hold one, when such an object is or holds type or holds a wrap object inside a group, when the page's type of a text object that stays lies inside such an object's box, when the page's art can't replace such an object for one of the reasons above for an unread object, when matching the differences to the objects would take too much work, or when the result still differs from the page. `editingData: "always"` keeps the objects as read, and placed files at the other point their data gives are tried as well. An EPS whose page is its art's box keeps that page when it comes in through its layers, not the editing data's artboard, and a text object counts as drawn by the page by its colours, not only its lightness, so a red stroke stacked over a dark red one stays (#1102). `textAs: "outlines"` opens a `.ai` whose type shows from its PDF part, which has the outlines; `editingData: false` opens any of them as its page or PDF part only, for print pipelines (#735). A file whose import left something out (hidden text, art or layers) is never written over by an export or Save As to its own path unless `acknowledgeLoss: true` (`convert --replace-lossy`), so the original keeps what the document lacks. Where a `.ai` can't be read that way (or its layers are turned off in the import options), we read its PDF-compatible part, so Illustrator-only live objects arrive as appearance, and plain groups arrive ungrouped: that part draws each object on its own and marks only layers, clipping groups and groups with opacity, blending or a mask (those come back). Art outside the artboards isn't in that part at all (Illustrator's PDF part leaves it out), so it doesn't open; the import says so in a note (#472). Its pages still go where the editing data puts the artboards (a grid stays a grid), with the art on each, when it has as many artboards as pages, of the same sizes (#1068); the page of the artboard `CropAreaActive` names is then the active artboard. A `.ai` whose editing data has artboards but no layers also opens on that page under the same check, and its pages stay where they are. A `.ai` whose PDF part's pages are cropped to its art (each page the box of all the art, which is the editing data's bounding box) instead of its artboards opens from its layers, with its own artboards, where they draw as that page does. Its pages are laid out so that art one of them draws past its artboard (notes beside a design, which the part doesn't clip) doesn't lie over the page before it in the row, where it was taken for that page's art: the check against the editing data then found art its layers don't have there and opened the file as its PDF part, without the art outside the artboards. That part also has no paragraphs: area type is written line by line, and comes back as area type only where its font is installed and lays the lines out with the same breaks, else as a point type object per line (#508). Art off the page that a PDF does draw opens on the pasteboard. A run of type in the editing data whose baseline direction is 1 opens with Standard Vertical Roman Alignment (https://helpx.adobe.com/illustrator/using/formatting-asian-characters.html) on, which is VectorCraft's own mapping: its half-width characters stand upright in vertical type, and horizontal type keeps the mark with no change to its layout. A run whose baseline direction is 2 or 3 (Tate-chu-yoko), or that names none in the run, its character style or the document's defaults, opens with it off. The scripting reference lists 1, 2 and 3 as the Standard, vertical rotated and Tate-chu-yoko baseline directions (https://web.archive.org/web/20170621180804/https://wwwimages.adobe.com/content/dam/Adobe/en/devnet/illustrator/sdk/CC2015_3/Illustrator%20VBScript%20Reference.pdf, page 256). A Scatter brush in the editing data whose last 15 numbers start with 1 comes into the Brushes panel in Tints. Each brush art section is read without its frame: an unnamed, unpainted rectangle with no settings of its own at the art's bounds (within 1/1000 pt), closed, or open with its first corner again at the end. A wider frame stays in the art, and an art group with a state or look of its own (hidden, locked, isolated, knockout, an opacity, a blend mode, a name, a mask, a note or an appearance) stays a group. diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 26681e5b7f..dd1a1db997 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1644,7 +1644,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur In a `.ai` opened from its editing data, a path or type object with Offset Path, Outline Object or Outline Stroke on the whole object opens as itself with those effects live, in their order and shown or hidden; on a fill or stroke of type they stay on that fill or stroke. Offset Path keeps its Offset, Joins and Miter Limit, and Outline Stroke takes its width from the stroke. An Offset Path with a setting missing, out of range or not read keeps the drawn art, as does a path with a Path effect on one fill or stroke (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/tests_native.rs`). In a `.ai` or EPS opened from its editing data, a Scatter brush whose last 15 numbers start with 1 comes into the Brushes panel in Tints, and each brush art section is read without its frame, an unnamed, unpainted rectangle with no settings of its own at the art's bounds (within 1/1000 pt). A wider frame stays in the art, and an art group with a state or look of its own (hidden, locked, isolated, knockout, an opacity, a blend mode, a name, a mask, a note or an appearance) stays a group (`crates/eps/src/import/ai/tests.rs`). In a `.ai` opened from its editing data, type with Warp on the object or on one of its fills or strokes opens as type with the effect live when the live warp draws as the art saved with it. When it draws differently, or when the file's limit of checks has no check left for it, it opens as that art, and one note says how many did. Type stays live without a comparison when the saved art paints nothing or draws type of its own, in frames of threaded type, when a text variable binds it, and when its text object doesn't come in as one text object (`crates/eps/src/import/ai/envelope.rs`, `crates/eps/src/import/tests_native.rs`). - In a `.ai` opened from its editing data, an object with a Tweak that moves nothing or Pucker & Bloat at 0 opens as itself with the effect live. A path, compound path or group whose only effect is Transform without copies, or a Free Distort whose corners make a parallelogram, opens as itself with it live, and so does an object with nothing stroked whose only effect is Twist: a path or compound path with straight segments at up to 180° either way, and another path, compound path or group with no guide in it at up to 90° either way (limits VectorCraft sets); one twisted further keeps the drawn art. A gradient or pattern, an image, type, a clipping group, an opacity mask or effects inside the object, or a stroke with a Transform that scales, rotates or reflects, keep the drawn art (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/ai/read/ops.rs`). + In a `.ai` opened from its editing data, an object with a Tweak that moves nothing or Pucker & Bloat at 0 opens as itself with the effect live. A path, compound path or group whose only effect is Transform without copies, or a Free Distort whose corners make a parallelogram, opens as itself with it live, and so does an object with nothing stroked whose only effect is Twist: a path or compound path with straight segments at up to 180° either way, unless it is a compound path with a guide member that reaches past its other members and the angle is not 0°, and another path, compound path or group with no guide in it at up to 90° either way (limits VectorCraft sets); one twisted further keeps the drawn art. A gradient or pattern, an image, type, a clipping group, an opacity mask or effects inside the object, or a stroke with a Transform that scales, rotates or reflects, keep the drawn art (`crates/eps/src/import/ai/style/tests.rs`, `crates/eps/src/import/ai/tests.rs`, `crates/eps/src/import/ai/read/ops.rs`). A PDF page whose `UserUnit` (PDF 1.6) is above 1 opens at its size in points, its numbers times the `UserUnit` (one above 75,000 counts as 75,000), and `document.pdfInfo` gives its size and boxes in points too. A `.ai` whose editing data has its artboards in the units of such a page opens from that data at the page's size, with its layers, and when the layers can't be read its pages still take the artboard layout of the data, scaled the same way (part of #1482). `document.open`, `file.place` and `document.pdfInfo` reject a `cropTo`, `crop`, `password`, `colorMode` or `textAs` that is not a string, such as `"textAs": true`, and `document.pdfInfo` rejects a `thumbnailSize` that is not a number; the error names the param, and nothing opens or is placed. A param left out or given as null keeps its default (`crates/engine/src/cmd/fileio/tests_pdfimport.rs`). When the PDF content of a `.ai` has a page for each artboard, the layers are compared with every page on its artboard (until the pages compared take 8,388,608 pixels at the comparison's scale, about 67 letter-size pages, or number 128): art that only the layers or only a later page draws is handled as art that differs on the first page is, and a text object that a later page doesn't draw is left out unless its layer doesn't print (`crates/eps/src/import/tests_native.rs`). From aef3acd51bdf65032f0aee27d914aeb6244e05da Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:44:37 -0800 Subject: [PATCH 28/33] Text Wrap: Offset in picas reads bare numbers as the other length fields do MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a document in picas, a bare number typed in Text Wrap Options › Offset follows Preferences › Units › Numbers Without Units Are Points, as every length field drawn by the shared unit field does: points while it is on (the default), picas while it is off. A test covers both settings, and the evidence line in docs/target-app-parity.md states the rule. --- crates/ui-egui/src/dialogs/command.rs | 37 +++++++++++++++++++++++++++ docs/target-app-parity.md | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/dialogs/command.rs b/crates/ui-egui/src/dialogs/command.rs index 3f591ccd30..6656b45bf5 100644 --- a/crates/ui-egui/src/dialogs/command.rs +++ b/crates/ui-egui/src/dialogs/command.rs @@ -629,6 +629,43 @@ mod tests { assert_eq!(offset(&mut h), -5.0); } + /// In a document in picas, Text Wrap Options › Offset reads a bare number as the other length + /// fields in picas do: in points with Preferences › Units › Numbers Without Units Are Points on + /// (the default), in picas with it off. A typed unit is read as typed either way. + #[test] + fn text_wrap_offset_in_picas_follows_numbers_without_units_are_points() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.run("file.new", json!({"width": 400, "height": 400})).unwrap(); + app.run("document.setUnits", json!({"units": "picas"})).unwrap(); + let id = app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap()["id"].as_u64().unwrap(); + app.run("select.set", json!({"ids": [id]})).unwrap(); + app.run("object.textWrap.make", json!({})).unwrap(); + let mut fonts = false; + let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1100.0, 900.0)).build_ui_state( + move |ui, app: &mut VectorcraftApp| { + if !std::mem::replace(&mut fonts, true) { + crate::theme::install_fonts(ui.ctx()); + return; + } + crate::dialogs::show(app, ui.ctx()); + }, + app, + ); + let apply = |h: &mut egui_kittest::Harness<'_, VectorcraftApp>, text: &str| { + crate::menus::invoke(h.state_mut(), "object.textWrap.options", json!({})); + h.run_steps(3); + enter_offset(h, text); + assert!(h.state().ui.dialog.is_none(), "{text}: OK applied it"); + h.state_mut().session.execute("object.textWrap.options", &json!({})).unwrap()["offset"].as_f64().unwrap() + }; + assert_eq!(apply(&mut h, "10"), 10.0, "on by default: a bare number is in points"); + assert_eq!(apply(&mut h, "2p6"), 30.0, "picas and points as typed"); + assert_eq!(apply(&mut h, "-1in"), -72.0, "a typed unit wins"); + crate::widgets::set_bare_numbers_are_points(&h.ctx, false); + assert_eq!(apply(&mut h, "10"), 120.0, "off: a bare number is in picas"); + assert_eq!(apply(&mut h, "-12 pt"), -12.0, "a typed unit wins"); + } + /// Clicks the dialog's OK button with the pointer, the press and the release in frames of /// their own. fn click_ok(h: &mut egui_kittest::Harness<'_, VectorcraftApp>) { diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 5ae03e1093..6cd911988c 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1286,7 +1286,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur A text wrap object's Offset may be negative as well as positive ([the help page](https://help.adobe.com/archive/en_US/illustrator/cs4/illustrator_cs4_help.pdf#page=307)). A negative Offset (to -1000 pt) shrinks the wrap object by that distance, measured from each edge itself and from the edges of its holes, so lines beside it, above it and below it run that far into it, and an object narrower than twice the distance no longer moves text aside. With Invert Wrap, a negative Offset keeps the text that far inside the object. These are VectorCraft's own rules for the shape of the shrunk object. Text Wrap Options, `object.textWrap.make` and `object.textWrap.options` and the MCP `text_wrap` tool take the negative value, and layout uses it (`crates/text/src/layout/region.rs` tests, `cmd::textwrap::tests::text_wrap_options_negative_offset_reflows_and_round_trips`). In area type of a shape other than a rectangle, each line's whole band, ascent to descent, stays inside the frame: where an edge pinches in within the band, the line ends at the narrowest point, and a line runs beside a hole that starts and ends within its band, not across it. In area type of any shape, a line also stays clear of a text wrap object that reaches into any part of its band, and inside an Invert Wrap object over its whole band (`crates/text/src/layout/region.rs` tests). With Invert Wrap, the spans a line keeps inside a wrap shape of many parts (a comb of 500 teeth) are found by stepping through the line's spans and the shape's spans once for each piece of the band, so the work grows with the number of spans, not with its square (`crates/text/src/layout/region.rs` tests). - Text Wrap Options › Offset is a distance field in the document's units: a bare number is in those units, a typed unit ("12 pt") is read as typed, and OK sends points. An Offset typed outside -1000 to 1000 pt, VectorCraft's own range for the command, or text that doesn't read, is refused: the field flashes and the dialog stays open, whether Enter or a click on OK ends the entry; in every dialog drawn by the shared dialog window, a click on OK no longer confirms it when the distance field it takes the focus from refuses its text (`dialogs::command::tests::text_wrap_offset_field_takes_document_units_and_refuses_values_outside_its_range`). Behavior reference: [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/enter-values-in-panels-and-dialog-boxes.html). + Text Wrap Options › Offset is a distance field in the document's units: a bare number is in those units, a typed unit ("12 pt") is read as typed, and OK sends points. In a document in picas, a bare number follows Preferences › Units › Numbers Without Units Are Points as the other length fields do: it is in points while that preference is on (the default) and in picas while it is off (`dialogs::command::tests::text_wrap_offset_in_picas_follows_numbers_without_units_are_points`). An Offset typed outside -1000 to 1000 pt, VectorCraft's own range for the command, or text that doesn't read, is refused: the field flashes and the dialog stays open, whether Enter or a click on OK ends the entry; in every dialog drawn by the shared dialog window, a click on OK no longer confirms it when the distance field it takes the focus from refuses its text (`dialogs::command::tests::text_wrap_offset_field_takes_document_units_and_refuses_values_outside_its_range`). Behavior reference: [the help page](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/enter-values-in-panels-and-dialog-boxes.html). The Character panel's Small Caps, Superscript and Subscript buttons and the panel menu's items of the same names turn small caps and the superscript or subscript position on and off for the selected type, or for the text the Type tool has selected (`text.setFormat` or `text.setRangeStyle` `{smallCaps, position}`), in Document Setup's proportions. Turning Superscript on turns Subscript off, and turning Subscript on turns Superscript off. A button or item shows as on when the first selected character (at a caret, the character before it) has its attribute. The Character panel's Anti-aliasing Method dropdown, under Character Rotation, sets how the edges of the selected type objects are drawn as pixels (`text.setFormat {antiAlias}`), for the whole object, with each frame of a thread keeping its own method and graph labels keeping it when the graph is drawn again, on the canvas, in raster exports and when the type is rasterized. VectorCraft defines the methods: Sharp, the default, draws edges as before; None paints a pixel only where the type covers at least half of it; Crisp shows a pixel covered by a share c at `2c - 0.5`, and Strong shows it at `2c`, each kept between 0 and 1. SVG, PDF and EPS export are unchanged. Scores and hours unchanged. The OpenType panel's Stylistic Alternates and Titling Alternates checkboxes turn `salt` and `titl` on and off for the selected type, and its Figure menu lists Default Figure, Tabular Lining, Proportional Lining, Proportional Oldstyle and Tabular Oldstyle; Proportional Lining sets `pnum` and `lnum` and turns `tnum` and `onum` off, Tabular Lining turns `pnum` off and keeps `lnum`, Proportional Oldstyle turns `lnum` off and keeps `pnum`, and Default Figure and Tabular Oldstyle turn `pnum` and `lnum` off. Behavior reference: [Format text with OpenType features](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/format-text-with-opentype-features.html) and [Use swashes, titling alternates, or stylistic alternates](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/use-swashes-titling-alternates-or-stylistic-alternates.html) (public documentation). Tests: `panels::opentype::tests`. From cc790703f86e18db27b2c008d48a82003524147e Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:35:12 -0800 Subject: [PATCH 29/33] i18n: Polish has a row for 1,808 of the messages the untranslated report lists The Polish catalog lacked a row for 1,854 of the status and error messages that the untranslated report finds in the sources, so the status bar showed those messages in English. - pl.tsv gains 1,808 `@msg` rows: the errors of commands and their parameter checks, scripts, file import and export, printing and plug-ins. The translations are VectorCraft's own, written from the English meaning with the catalog's conventions and its existing feature names. Placeholders, parameter names, command ids and literal values stay as the English writes them. - 44 of the others get their Polish rows from open #7211, #7214, #7116, #7330 and #1931, so this change leaves them out. 2 are messages that no catalog has a row for yet. Menu strings, `tl!` literals, undo labels and swatch names are unchanged. - Two of the messages have two placeholders in a row. The catalog does not load such a row, so those two are still shown in English. - The Polish row of docs/localization-parity.md gives the catalog's current counts. Docs: docs/target-app-parity.md, docs/localization-parity.md. --- crates/ui-egui/src/i18n/pl.tsv | 1810 ++++++++++++++++++++++++++++++++ docs/localization-parity.md | 2 +- docs/target-app-parity.md | 1 + 3 files changed, 1812 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index fa7803a28b..d7e49bb232 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -7347,3 +7347,1813 @@ breakText Glyphs Glify Grommets Oczka Grommet Inset Odległość oczek od krawędzi Diameter Średnica + +# Status and error messages of commands, scripts, file import and export, and plug-ins. +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions w funkcjach trybu ścisłego nie można używać 'caller', 'callee' ani 'arguments' +@msg Alignment must be one of the choices Wyrównanie musi być jedną z dostępnych opcji +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} wywołano na wartości, która nie jest wartością Boolean +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB Obrazy CMYK, których próbek nie udało się odczytać w CMYK, zaimportowano w RGB +@msg Cannot write to the location of “{name}”; choose where to save it Nie można zapisać w lokalizacji pliku „{name}”; wybierz miejsce zapisu +@msg DCS files can only be placed linked: use File › Place with Link on Pliki DCS można umieszczać tylko jako połączone: użyj polecenia Plik › Umieść z włączoną opcją Łącze +@msg Date.prototype method called on a value that is not a Date object Metodę Date.prototype wywołano na wartości, która nie jest obiektem Date +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString wywołano na wartości, która nie jest obiektem +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString wywołano na wartości, która nie jest funkcją +@msg Import SVG Filter reads SVG files, not SVGZ Importowanie filtra SVG odczytuje pliki SVG, a nie SVGZ +@msg JSON.parse: {why} (at offset {at}) JSON.parse: {why} (przesunięcie {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify nie może zapisać struktury, która zawiera samą siebie +@msg Line Gap must be a number Odstęp między wierszami musi być liczbą +@msg Namespace.prototype.toString needs a Namespace Namespace.prototype.toString wymaga obiektu Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have Profile nowego dokumentu wymagają folderu preferencji, którego to uruchomienie nie ma +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} wywołano na wartości, która nie jest liczbą +@msg Object.create's prototype must be an object or null Object.create: prototyp musi być obiektem lub null +@msg Object.{function} was given a value that is not an object Object.{function} otrzymało wartość, która nie jest obiektem +@msg QName.prototype.toString needs a QName QName.prototype.toString wymaga obiektu QName +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} wywołano na wartości, która nie jest obiektem RegExp +@msg Stencil Bridges: the selected art is too large or too detailed to bridge Mostki szablonu: zaznaczona grafika jest zbyt duża lub zbyt szczegółowa, aby utworzyć mostki +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString wywołano na wartości, która nie jest ciągiem znaków +@msg The {name} profile could not be read, so the new document has the default settings: {reason} Nie udało się odczytać profilu {name}, więc nowy dokument ma ustawienia domyślne: {reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time Zalewka: ponad {_1} par obiektów leży na tyle blisko siebie, że wymaga zalewki, albo zbyt wiele obiektów ma wspólny zakres poziomy; stosuj zalewkę do mniejszej liczby obiektów naraz +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off Ramka i linie do wybierania: żadna linia do wybierania nie mieści się poza grafiką, a ramka do wybierania jest wyłączona +@msg \\ at end of pattern \\ na końcu wzorca +@msg `alignOn` cannot be empty `alignOn` nie może być puste +@msg `alignOn` must be a string `alignOn` musi być ciągiem znaków +@msg `align` must be a string `align` musi być ciągiem znaków +@msg `answer` for this {kind} dialog must be one of: {_1} `answer` dla tego okna dialogowego {kind} musi mieć jedną z wartości: {_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` zawiera więcej niż {MAX_ANSWERS} odpowiedzi +@msg `answers` is larger than 1 MiB `answers` ma rozmiar większy niż 1 MiB +@msg `answers` must be a list of answers `answers` musi być listą odpowiedzi +@msg `artboard` must be the index of an artboard of the document `artboard` musi być indeksem obszaru kompozycji dokumentu +@msg `artboardsAt` must be [x, y] `artboardsAt` musi mieć postać [x, y] +@msg `artboards` names an artboard the document does not have `artboards` wskazuje obszar kompozycji, którego dokument nie ma +@msg `artboards` names an artboard twice `artboards` wskazuje ten sam obszar kompozycji dwa razy +@msg `at` must be [x, y] (got {at}) `at` musi mieć postać [x, y] (podano {at}) +@msg `at` must be a point [x, y] `at` musi być punktem [x, y] +@msg `beside` needs `side` `beside` wymaga `side` +@msg `code` must be a whole number `code` musi być liczbą całkowitą +@msg `colorMode` must be one of rgb, cmyk `colorMode` musi mieć jedną z wartości: rgb, cmyk +@msg `colors` does not apply to the {name} reduction `colors` nie ma zastosowania do redukcji {name} +@msg `colors` must be a whole number from 2 to 256 `colors` musi być liczbą całkowitą od 2 do 256 +@msg `colors` must be at least 9 for the uniform reduction `colors` musi wynosić co najmniej 9 dla redukcji uniform +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` musi mieścić się w zakresie od 1 do {MAX_THEME_COLORS} (podano {n}) +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` musi być liczbą całkowitą od 0 do {MAX_COUNT} +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` musi być liczbą od 0 do 100 +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` musi mieścić się w zakresie od {lo} do {hi} pt +@msg `focal` and `radius` cannot be used together `focal` i `radius` nie mogą być używane razem +@msg `focal` must be an array of [subpath, anchor] pairs `focal` musi być tablicą par [subpath, anchor] +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` wskazuje [{si}, {ai}], co nie jest punktem kontrolnym ścieżki +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` przyjmuje tylko null, co usuwa wybór glifu (wybór ustawia text.setGlyphAlternate) +@msg `group` must be one of {_1} `group` musi mieć jedną z wartości: {_1} +@msg `group` must be the Objects on Path group of `member` and `target` `group` musi być grupą Obiekty na ścieżce, do której należą `member` i `target` +@msg `id` is an object id, not {v} `id` to identyfikator obiektu, a nie {v} +@msg `item` needs `index` `item` wymaga `index` +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` należy do pierwszego znaku wiersza, a nie do stylu znakowego (ustawia go text.setRangeStyle) +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` musi mieć wartość "pushInFirst", "pushOutFirst" lub "pushOutOnly" +@msg `leader` must be a string `leader` musi być ciągiem znaków +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` musi wynosić od 1 do {MAX_SUGGESTIONS} +@msg `name` must be a name that is not empty `name` musi być niepustą nazwą +@msg `name` must not be empty `name` nie może być puste +@msg `origin` must be [x, y] in points `origin` musi mieć postać [x, y] w punktach +@msg `picks` must be an array (got {v}) `picks` musi być tablicą (podano {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` musi zawierać od 1 do {MAX_PICKS} wskazań (podano {_1}) +@msg `points` must hold from 2 to 100000 points `points` musi zawierać od 2 do 100000 punktów +@msg `radius` must be 0 or more `radius` musi wynosić 0 lub więcej +@msg `rect` must be [x, y, width, height] `rect` musi mieć postać [x, y, width, height] +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` musi mieć postać [x0, y0, x1, y1] (podano {r}) +@msg `replace` must be a string `replace` musi być ciągiem znaków +@msg `rows` must be lists of numbers or nulls, not {r} `rows` musi zawierać listy liczb lub wartości null, a nie {r} +@msg `scale` must be a number from 0.001 to 1000 `scale` musi być liczbą od 0,001 do 1000 +@msg `scope.{key}` must be a non-empty array `scope.{key}` musi być niepustą tablicą +@msg `scope` must be "document", {artboards} or {layers} `scope` musi mieć wartość "document", {artboards} lub {layers} +@msg `side` must be "left", "right", "top" or "bottom" `side` musi mieć wartość "left", "right", "top" lub "bottom" +@msg `side` places the artboard: leave out x, y, width and height `side` wyznacza położenie obszaru kompozycji: pomiń x, y, width i height +@msg `source` is the only path in series group `target` `source` jest jedyną ścieżką w grupie serii `target` +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` daje więcej niż {MAX_COUNT} linii w poprzek ramki +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` musi być większe od 0 i wynosić najwyżej {MAX_DISTANCE} pt +@msg `target` must be an object of the same Objects on Path group as `member` `target` musi być obiektem tej samej grupy Obiekty na ścieżce co `member` +@msg `tolerance` must be from 0 to 100 pt `tolerance` musi mieścić się w zakresie od 0 do 100 pt +@msg `version` must be 0 or {_1} `version` musi wynosić 0 lub {_1} +@msg `version` must be 0 or {newest} `version` musi wynosić 0 lub {newest} +@msg `warichuAlign` must be one of {_1} `warichuAlign` musi mieć jedną z wartości: {_1} +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` musi być liczbą całkowitą od {_1} do {_2} +@msg `weight` must be from 0.01 to 1000 pt `weight` musi mieścić się w zakresie od 0,01 do 1000 pt +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` musi być listą {_1} liczb, po jednej na wiersz (podano {v}) +@msg `xml` must be a string `xml` musi być ciągiem znaków +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it `{KEY}`: wpis {n} nadaje akapitowi od bajtu {start} obiektu {_1} inne wcięcia niż wcześniejszy wpis +@msg `{KEY}` entry {n} must be an object `{KEY}`: wpis {n} musi być obiektem +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` `{KEY}`: wpis {n} nie ustawia ani `leftIndent`, ani `firstLineIndent` +@msg `{KEY}` entry {n}: `id` must be an object id `{KEY}`: wpis {n}: `id` musi być identyfikatorem obiektu +@msg `{KEY}` entry {n}: `start` must be a byte offset `{KEY}`: wpis {n}: `start` musi być przesunięciem w bajtach +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt `{KEY}`: wpis {n}: `{k}` musi być liczbą od -{MAX_PARA} do {MAX_PARA} pt +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} `{KEY}`: wpis {n}: żaden akapit obiektu {_1} nie zaczyna się od bajtu {start} +@msg `{KEY}` entry {n}: unknown key `{k}` `{KEY}`: wpis {n}: nieznany klucz `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object `{KEY}`: wpis {n}: {_1} nie jest obiektem tekstowym +@msg `{KEY}` is empty `{KEY}` jest puste +@msg `{KEY}` must be a list `{KEY}` musi być listą +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` przyjmuje najwyżej {MAX_PARAGRAPH_INDENTS} wpisów (podano {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` nie przyjmuje obok siebie `{other}` +@msg `{base}` is larger than {_1} MB `{base}` ma rozmiar większy niż {_1} MB +@msg `{base}` is not in the DCS file's folder `{base}` nie znajduje się w folderze pliku DCS +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` zawiera więcej niż {MAX_EDIT_WORDS} słów +@msg `{key}` must be 0 or more `{key}` musi wynosić 0 lub więcej +@msg `{key}` must be [x, y] numbers `{key}` musi zawierać liczby [x, y] +@msg `{key}` must be a list of words `{key}` musi być listą słów +@msg `{key}` must be a list, not {v} `{key}` musi być listą, a nie {v} +@msg `{key}` must be a number or null, not {v} `{key}` musi być liczbą lub null, a nie {v} +@msg `{key}` must be a whole number of 0 or more `{key}` musi być liczbą całkowitą równą 0 lub większą +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` musi mieścić się w zakresie od 0 do {MAX_DISTANCE} pt +@msg `{key}` must be from 0.01 to 1000 pt `{key}` musi mieścić się w zakresie od 0,01 do 1000 pt +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` musi mieścić się w zakresie od {lo} do {hi}, a nie {v} +@msg `{key}` must be one of auto, always, never `{key}` musi mieć jedną z wartości: auto, always, never +@msg `{key}` must be one of miter, round, bevel `{key}` musi mieć jedną z wartości: miter, round, bevel +@msg `{key}` must be one word of at most {_1} characters `{key}` musi być jednym słowem o długości najwyżej {_1} znaków +@msg `{key}` must hold single words of at most {_1} characters `{key}` musi zawierać pojedyncze słowa o długości najwyżej {_1} znaków +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` musi wskazywać obiekt dołączony do ścieżki grupy Obiekty na ścieżce +@msg `{key}` names an object that is hidden or locked `{key}` wskazuje obiekt, który jest ukryty lub zablokowany +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` to osobny plik, który można znaleźć tylko obok ścieżki pliku DCS +@msg `{name}`: {_1}: {e} `{name}`: {_1}: {e} +@msg `{u}` is no Unicode character `{u}` nie jest znakiem Unicode +@msg `{word}` isn't in the custom dictionary słowa `{word}` nie ma w słowniku użytkownika +@msg a CMap has too many mappings mapa CMap ma zbyt wiele mapowań +@msg a \\u escape needs four hexadecimal digits sekwencja \\u wymaga czterech cyfr szesnastkowych +@msg a comma in an XPath expression must separate function arguments przecinek w wyrażeniu XPath musi rozdzielać argumenty funkcji +@msg a document holds at most {_1} SVG filters dokument może zawierać najwyżej {_1} filtrów SVG +@msg a document with unsaved changes has no recovery copy dokument z niezapisanymi zmianami nie ma kopii odzyskiwania +@msg a font's data is too large dane czcionki są zbyt duże +@msg a font's subroutines are too large podprogramy czcionki są zbyt duże +@msg a function declaration without a name deklaracja funkcji bez nazwy +@msg a host value has too many entries wartość hosta ma zbyt wiele wpisów +@msg a host value is nested too deep wartość hosta jest zbyt głęboko zagnieżdżona +@msg a namespace declaration uses a reserved prefix or URI deklaracja przestrzeni nazw używa zastrzeżonego prefiksu lub identyfikatora URI +@msg a namespace qualifier is undefined kwalifikator przestrzeni nazw jest niezdefiniowany +@msg a namespace with an empty URI cannot have a prefix przestrzeń nazw z pustym identyfikatorem URI nie może mieć prefiksu +@msg a number has no digits liczba nie ma cyfr +@msg a number's exponent has no digits wykładnik liczby nie ma cyfr +@msg a number's fraction has no digits część ułamkowa liczby nie ma cyfr +@msg a property descriptor cannot have both a value or writable and a get or set deskryptor właściwości nie może mieć jednocześnie value lub writable oraz get lub set +@msg a property descriptor must be an object deskryptor właściwości musi być obiektem +@msg a property's {name} must be a function or undefined {name} właściwości musi być funkcją lub undefined +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied zawarty w nim strumień po zdekodowaniu przekroczyłby 256 MB albo nie można zastosować jego parametrów dekodowania +@msg a string holds a control character ciąg znaków zawiera znak sterujący +@msg a string holds an escape sequence JSON does not have ciąg znaków zawiera sekwencję ucieczki, której nie ma w JSON +@msg a string is not closed ciąg znaków nie jest zamknięty +@msg an SVG filter is named by its id: give `xml` with `name` nazwą filtra SVG jest jego identyfikator: podaj `xml` razem z `name` +@msg an XML element has two attributes with the same name element XML ma dwa atrybuty o tej samej nazwie +@msg an XML value cannot be inserted into itself or its descendants wartości XML nie można wstawić do niej samej ani do jej elementów potomnych +@msg an XML value's children cannot be assigned by position elementów potomnych wartości XML nie można przypisywać według pozycji +@msg an XML value's children cannot be deleted by position elementów potomnych wartości XML nie można usuwać według pozycji +@msg an XMLList converts to XML only when it has one item XMLList można przekształcić w XML tylko wtedy, gdy ma jeden element +@msg an XPath node test is missing brak testu węzła XPath +@msg an XPath operand is missing brak operandu XPath +@msg an XPath operator is missing brak operatora XPath +@msg an XPath predicate or step cannot follow here w tym miejscu nie może wystąpić predykat ani krok XPath +@msg an XPath predicate or step follows a value that is not a node-set predykat lub krok XPath następuje po wartości, która nie jest zbiorem węzłów +@msg an XPath step is missing brak kroku XPath +@msg an image couldn't be decoded and is missing from the picture nie udało się zdekodować obrazu, więc brakuje go na ilustracji +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) obraz o wymiarach {w} × {h} pikseli jest zbyt duży, aby go przekonwertować (najwyżej {_1} pikseli) +@msg apply was called on a value that is not a function apply wywołano na wartości, która nie jest funkcją +@msg area type in a thread cannot use Dynamic Text: {frames} tekst akapitowy w tekście wątkowym nie może używać tekstu dynamicznego: {frames} +@msg array literal too long literał tablicowy jest zbyt długi +@msg assignment to a constant binding przypisanie do stałego powiązania +@msg at least one row that isn't excluded needs a weight above 0 co najmniej jeden niewykluczony wiersz musi mieć wagę większą od 0 +@msg bad point {v}: expected [x, y] nieprawidłowy punkt {v}: oczekiwano [x, y] +@msg blend must be a mode name blend musi być nazwą trybu +@msg both sides of an XPath | must select nodes obie strony operatora | w XPath muszą wybierać węzły +@msg canceled anulowano +@msg cannot convert the object to a primitive value nie można przekonwertować obiektu na wartość prostą +@msg cannot convert undefined or null to an object nie można przekonwertować undefined ani null na obiekt +@msg character dimensions cannot be represented with the source text transform wymiarów znaków nie można przedstawić przy przekształceniu tekstu źródłowego +@msg character dimensions cannot be represented with the target text transform wymiarów znaków nie można przedstawić przy przekształceniu tekstu docelowego +@msg continuation must be an array of path chunks continuation musi być tablicą fragmentów ścieżki +@msg continuation requires points continuation wymaga points +@msg each path chunk must contain 1 to 4096 points każdy fragment ścieżki musi zawierać od 1 do 4096 punktów +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) każde wskazanie ma postać {{at: [x, y]}} lub {{rect: [x0, y0, x1, y1]}} (podano {v}) +@msg each weight must be a number of 0 or more (got {v}) każda waga musi być liczbą równą 0 lub większą (podano {v}) +@msg finally table {t} out of range tabela finally {t} poza zakresem +@msg flags cannot be given with a RegExp object flag nie można podawać razem z obiektem RegExp +@msg function declaration index out of range indeks deklaracji funkcji poza zakresem +@msg give `char`, `text`, `code` or `unicode` podaj `char`, `text`, `code` lub `unicode` +@msg give `count` or `spacing`, not both podaj `count` albo `spacing`, nie oba naraz +@msg give `name`, an SVG filter's name podaj `name`, nazwę filtra SVG +@msg give `path`, an SVG file, or `svg`, SVG text podaj `path`, plik SVG, albo `svg`, tekst SVG +@msg give `xml`, one SVG element podaj `xml`, jeden element SVG +@msg give exactly one of `at`, `rect` and `points` podaj dokładnie jedno z: `at`, `rect` i `points` +@msg give exactly one of `path`, `data` and `dataBase64` podaj dokładnie jedno z: `path`, `data` i `dataBase64` +@msg give index or indexes, not both podaj index albo indexes, nie oba naraz +@msg give one element podaj jeden element +@msg give opacity or blend podaj opacity lub blend +@msg handler at {_1} expects {_2} values but its range starts with {d} procedura obsługi w {_1} oczekuje {_2} wartości, ale jej zakres zaczyna się od {d} +@msg handler depth too large zbyt duża głębokość procedur obsługi +@msg handler range {_1}..{_2} out of range zakres procedury obsługi {_1}..{_2} poza zakresem +@msg hiddenLayers must be true or false hiddenLayers musi mieć wartość true lub false +@msg hvcC hvcC +@msg images in a spot ink whose samples couldn't be read were imported in RGB obrazy w farbie dodatkowej, których próbek nie udało się odczytać, zaimportowano w RGB +@msg images past the most pixels one file may hold were left out pominięto obrazy przekraczające największą liczbę pikseli dozwoloną w jednym pliku +@msg images too large to read were left out pominięto obrazy zbyt duże do odczytania +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes może zawierać najwyżej {MAX_GUIDE_INDEXES} wpisów +@msg indexes must be an array of guide indexes indexes musi być tablicą indeksów linii pomocniczych +@msg instruction out of range instrukcja poza zakresem +@msg instruction {pc} ({op}) pops below the stack instrukcja {pc} ({op}) zdejmuje ze stosu więcej, niż on zawiera +@msg instruction {pc} reached with stack depths {seen} and {d} instrukcja {pc} osiągnięta przy głębokościach stosu {seen} i {d} +@msg invalid array length nieprawidłowa długość tablicy +@msg invalid group nieprawidłowa grupa +@msg invalid object id {v}: expected a non-negative integer nieprawidłowy identyfikator obiektu {v}: oczekiwano nieujemnej liczby całkowitej +@msg invalid regular expression flag '{_1}' nieprawidłowa flaga wyrażenia regularnego '{_1}' +@msg invalid regular expression: {m} nieprawidłowe wyrażenie regularne: {m} +@msg invalid time value nieprawidłowa wartość czasu +@msg is a confirm's answer, whose `value` must be true or false to odpowiedź na okno confirm, której `value` musi mieć wartość true lub false +@msg is a prompt's answer, whose `value` must be a string or null to odpowiedź na okno prompt, której `value` musi być ciągiem znaków lub null +@msg is an alert's answer, whose `value` must be "ok" to odpowiedź na okno alert, której `value` musi mieć wartość "ok" +@msg is longer than 16 KiB ma długość większą niż 16 KiB +@msg it draws its image through a clipping path rysuje swój obraz przez ścieżkę przycinającą +@msg it draws no image nie rysuje żadnego obrazu +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes ma rozmiar większy niż {MAX_DICTIONARY_FILE_BYTES} bajtów +@msg it isn't a file to nie jest plik +@msg it names {_1} plates, and at most {MAX_PLATES} are read wymienia {_1} płyt, a odczytywanych jest najwyżej {MAX_PLATES} +@msg its art is too long or too detailed for a head jego grafika jest zbyt długa lub zbyt szczegółowa na grot +@msg its art paints nothing jego grafika niczego nie maluje +@msg its image is too large to read jego obraz jest zbyt duży do odczytania +@msg its plates are too large: more than {MAX_SAMPLES} pixels together jego płyty są zbyt duże: łącznie ponad {MAX_SAMPLES} pikseli +@msg its plates are too large: {_1} plates of {w} × {h} pixels jego płyty są zbyt duże: {_1} płyt o wymiarach {w} × {h} pikseli +@msg its plates can't be kept together jego płyt nie można zachować razem +@msg its shapes could not be combined into one jego kształtów nie udało się połączyć w jeden +@msg jump to {pc}, past the end skok do {pc}, poza koniec +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding musi mieć jedną z wartości: {_1} +@msg localize: the first argument must be a localization object or a string localize: pierwszy argument musi być obiektem lokalizacji lub ciągiem znaków +@msg missing `picks` brak `picks` +@msg missing `word` brak `word` +@msg must be an object with `kind` and `value` musi być obiektem z `kind` i `value` +@msg must have `kind` alert, confirm or prompt `kind` musi mieć wartość alert, confirm lub prompt +@msg must have exactly `kind` and `value` musi mieć dokładnie `kind` i `value` +@msg needs a closing quote after the name wymaga zamykającego cudzysłowu po nazwie +@msg needs a name wymaga nazwy +@msg needs a name of at most 64 bytes wymaga nazwy o długości najwyżej 64 bajtów +@msg needs a name of letters, digits and _, or a quoted name wymaga nazwy złożonej z liter, cyfr i znaku _ albo nazwy w cudzysłowie +@msg needs a name without control characters wymaga nazwy bez znaków sterujących +@msg nesting too deep zbyt głębokie zagnieżdżenie +@msg no HP-GL format brak formatu HP-GL +@msg no New Document profile called `{shown}` ({_1}) brak profilu nowego dokumentu o nazwie `{shown}` ({_1}) +@msg no SVG filter at index {k} brak filtra SVG o indeksie {k} +@msg no arrowheads file here tu nie ma pliku grotów strzałek +@msg no character has code {code} żaden znak nie ma kodu {code} +@msg no custom arrowhead {name} (see stroke.arrowheads.list) brak niestandardowego grotu strzałki {name} (patrz stroke.arrowheads.list) +@msg no file to open brak pliku do otwarcia +@msg no instructions brak instrukcji +@msg no layer to rename brak warstwy do zmiany nazwy +@msg no random numbers ({e}) brak liczb losowych ({e}) +@msg no selected or given object has a visible 3D effect żaden zaznaczony ani podany obiekt nie ma widocznego efektu 3D +@msg no such artboard {v} brak takiego obszaru kompozycji {v} +@msg no symbol named `{_1}` brak symbolu o nazwie `{_1}` +@msg node {_1} already has the text variable `{_2}` węzeł {_1} ma już zmienną tekstową `{_2}` +@msg not a DCS file to nie jest plik DCS +@msg not a RegExp object to nie jest obiekt RegExp +@msg not a regular expression to nie jest wyrażenie regularne +@msg not an action set: the text doesn't start with /version and a number to nie jest zestaw operacji: tekst nie zaczyna się od /version i liczby +@msg nothing to repeat brak elementu do powtórzenia +@msg numbers out of order in {_1} quantifier liczby w niewłaściwej kolejności w kwantyfikatorze {_1} +@msg object {_1} needs a 3D effect obiekt {_1} wymaga efektu 3D +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic obiekt {_1}: puste w środku Wyciągnięcie i fazowanie nie ma zakończeń: włącz opcję Zakończenie, aby odwzorować grafikę +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front obiekt {_1}: przy głębokości 0 obiekt ma jedną ścianę: odwzoruj jego grafiki na przodzie +@msg object {_1}: {_2} obiekt {_1}: {_2} +@msg object {_1}: {e} obiekt {_1}: {e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams obiekt {_1}: {name} o indeksie {index} nie ma ustawień oświetlenia; ustaw jego światła poleceniem effect.setParams +@msg opacity must be a number opacity musi być liczbą +@msg operand too large operand jest zbyt duży +@msg order is a whole number from 0 to {others}, not `{v}` order to liczba całkowita od 0 do {others}, a nie `{v}` +@msg path chunks must share their boundary point fragmenty ścieżki muszą mieć wspólny punkt graniczny +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false wymaga obszarów kompozycji, aby zachować edytowalną grafikę, gdy useArtboards ma wartość false +@msg point x must be a number współrzędna x punktu musi być liczbą +@msg point y must be a number współrzędna y punktu musi być liczbą +@msg points must be [x,y] pairs points musi zawierać pary [x,y] +@msg points must be finite canvas points points musi zawierać skończone punkty płótna +@msg range out of order in character class zakres w niewłaściwej kolejności w klasie znaków +@msg reduce of an empty array with no initial value reduce na pustej tablicy bez wartości początkowej +@msg regular expression flag '{_1}' given twice flagę wyrażenia regularnego '{_1}' podano dwa razy +@msg sampled text would exceed the canvas (coordinates out of range) próbkowany tekst wykroczyłby poza płótno (współrzędne poza zakresem) +@msg select an SVG filter zaznacz filtr SVG +@msg select an object with a 3D effect zaznacz obiekt z efektem 3D +@msg select an object with a 3D effect, or give its id in ids zaznacz obiekt z efektem 3D albo podaj jego identyfikator w ids +@msg select one layer, group or object to rename zaznacz jedną warstwę, grupę lub obiekt, aby zmienić nazwę +@msg select the art to weed zaznacz grafikę do wybierania +@msg select the banner, or mark every artboard zaznacz baner albo oznacz każdy obszar kompozycji +@msg select type on a path or area type zaznacz tekst na ścieżce lub tekst akapitowy +@msg side is left or right, not `{v}` side ma wartość left lub right, a nie `{v}` +@msg source text transform is collapsed or unrepresentable przekształcenie tekstu źródłowego jest zdegenerowane lub nie da się go przedstawić +@msg table must be true or false table musi mieć wartość true lub false +@msg takes one name przyjmuje jedną nazwę +@msg target text transform is collapsed or unrepresentable przekształcenie tekstu docelowego jest zdegenerowane lub nie da się go przedstawić +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer to daje za dużo części (najwyżej 100000) lub kroków kresek: uprość wzór kresek albo wydłuż kreski +@msg the Layers panel is not shown panel Warstwy nie jest wyświetlany +@msg the Layers panel's search, filter or Show Layers Only hides its row wyszukiwanie, filtr lub opcja Pokaż tylko warstwy w panelu Warstwy ukrywa jego wiersz +@msg the Objects on Path group is hidden or locked grupa Obiekty na ścieżce jest ukryta lub zablokowana +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes plik SVG jest większy niż {MAX_IMPORT_BYTES} bajtów +@msg the XML operation needs an XML or XMLList value operacja XML wymaga wartości XML lub XMLList +@msg the XPath expression does not evaluate to a node-set wyrażenie XPath nie daje w wyniku zbioru węzłów +@msg the XPath expression ends early wyrażenie XPath kończy się przedwcześnie +@msg the XPath expression has an unmatched ) wyrażenie XPath zawiera niesparowany znak ) +@msg the XPath expression has an unmatched ] wyrażenie XPath zawiera niesparowany znak ] +@msg the XPath expression has an unmatched closing bracket wyrażenie XPath zawiera niesparowany nawias zamykający +@msg the XPath expression selects a namespace node, which is not an XML value wyrażenie XPath wybiera węzeł przestrzeni nazw, który nie jest wartością XML +@msg the XPath expression selects the root node, which is not an XML value wyrażenie XPath wybiera węzeł główny, który nie jest wartością XML +@msg the XPath function {name} needs a node-set funkcja XPath {name} wymaga zbioru węzłów +@msg the XPath function {name} takes the wrong number of arguments funkcja XPath {name} przyjmuje inną liczbę argumentów +@msg the XPath name functions need a node-set funkcje nazw XPath wymagają zbioru węzłów +@msg the XPath prefix {shown} is not bound to a namespace prefiks XPath {shown} nie jest powiązany z przestrzenią nazw +@msg the XPath variable ${shown} is not bound zmienna XPath ${shown} nie jest powiązana +@msg the argument list is not an object lista argumentów nie jest obiektem +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out grafika jest zbyt duża dla formatu EPS: pominięto wszystko po 256 MB kodu PostScript +@msg the banner has no width or height baner nie ma szerokości lub wysokości +@msg the bitmaps hold more pixels than one file may bitmapy zawierają więcej pikseli, niż może mieścić jeden plik +@msg the code runs past its last instruction kod wykracza poza swoją ostatnią instrukcję +@msg the curve tolerance must be from 0.01 to 10 points tolerancja krzywych musi mieścić się w zakresie od 0,01 do 10 punktów +@msg the custom dictionary couldn't be saved to {_1}: {e} nie udało się zapisać słownika użytkownika w {_1}: {e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is nie można odczytać słownika użytkownika {_1} ({why}), więc pozostaje on bez zmian +@msg the document already has an SVG filter named {name} dokument ma już filtr SVG o nazwie {name} +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it dokument ma inny filtr SVG o nazwie {name}: użyj svgFilter.apply albo svgFilter.define z `replace`, aby go edytować +@msg the document has no SVG filter named {name} dokument nie ma filtra SVG o nazwie {name} +@msg the document has no SVG filter named {r} dokument nie ma filtra SVG o nazwie {r} +@msg the document has no color swatches dokument nie ma próbek kolorów +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 próbki dokumentu zawierają {_1} kolorów; redukcja document przyjmuje najwyżej 255 +@msg the file isn't valid SVG: {e} plik nie jest prawidłowym plikiem SVG: {e} +@msg the file names no plate plik nie wymienia żadnej płyty +@msg the function's prototype is not an object prototyp funkcji nie jest obiektem +@msg the grommet inset is too large for the banner odległość oczek od krawędzi jest zbyt duża dla banera +@msg the guide is hidden or locked linia pomocnicza jest ukryta lub zablokowana +@msg the hem is too deep for the banner obszycie jest zbyt głębokie dla banera +@msg the library's file is outside the read roots plik biblioteki znajduje się poza katalogami głównymi odczytu +@msg the line is too long, or the paths it passes too many, to check linia jest zbyt długa albo mija zbyt wiele ścieżek, aby ją sprawdzić +@msg the method needs an XML value metoda wymaga wartości XML +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) malowanie obiektu nie jest gradientem dowolnym (zastosuj go poleceniem paint.editGradient {kind}) +@msg the parameters or body do not form one function parametry lub treść nie tworzą jednej funkcji +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it wklejony filtr SVG {name} rysuje inaczej niż filtr dokumentu o tej nazwie, a nie znaleziono dla niego wolnej nazwy +@msg the paths are too many or too tangled to find where they cross ścieżek jest zbyt wiele lub są zbyt splątane, aby znaleźć miejsca ich przecięć +@msg the pen table holds more than 255 pens tabela pisaków zawiera więcej niż 255 pisaków +@msg the plates can't be kept nie można zachować płyt +@msg the position table does not match the instructions tabela pozycji nie odpowiada instrukcjom +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes tekst okna prompt jest dłuższy niż {MAX_PROMPT_BYTES} bajtów +@msg the radix must be between 2 and 36 podstawa systemu liczbowego musi mieścić się w zakresie od 2 do 36 +@msg the rectangle or line reaches too many paths to check prostokąt lub linia sięga zbyt wielu ścieżek, aby je sprawdzić +@msg the right side of 'in' is not an object prawa strona operatora 'in' nie jest obiektem +@msg the right side of 'instanceof' is not a function prawa strona operatora 'instanceof' nie jest funkcją +@msg the selected art is nested more than {MAX_DEPTH} levels deep zaznaczona grafika jest zagnieżdżona głębiej niż na {MAX_DEPTH} poziomów +@msg the selected art is too large to measure zaznaczona grafika jest zbyt duża, aby ją zmierzyć +@msg the selection has no outlines to weed around zaznaczenie nie ma konturów, wokół których można wybierać +@msg the spacing makes more than {MAX_GROMMETS} grommets odstęp daje więcej niż {MAX_GROMMETS} oczek +@msg the value bind was called on is not a function wartość, na której wywołano bind, nie jest funkcją +@msg the value is not a constructor wartość nie jest konstruktorem +@msg the value is not a function wartość nie jest funkcją +@msg the value is not a valid XML name wartość nie jest prawidłową nazwą XML +@msg the value is not an XML attribute name wartość nie jest nazwą atrybutu XML +@msg the value is not an XML name wartość nie jest nazwą XML +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines linie do wybierania przecinają kontury grafiki zbyt wiele razy, aby to zmierzyć; użyj mniejszej liczby linii +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap linie do wybierania przebiegają zbyt blisko zbyt wielu segmentów konturu, aby to zmierzyć; użyj mniejszej liczby linii lub mniejszego odstępu +@msg the weeding lines would be more than {max_pieces} pieces linie do wybierania składałyby się z więcej niż {max_pieces} części +@msg the {_1} plate lies outside the file płyta {_1} leży poza plikiem +@msg there is no banner to mark nie ma banera do oznaczenia +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that to binarny plik DXF, którego nie można odczytać: zapisz go w programie CAD jako ASCII DXF i otwórz ten plik +@msg this warp's points can't be edited: use Reset with Mesh first punktów tego wypaczenia nie można edytować: najpierw użyj polecenia Resetuj z siatką +@msg toExponential() digits must be between 0 and 20 liczba cyfr w toExponential() musi mieścić się w zakresie od 0 do 20 +@msg toFixed() digits must be between 0 and 20 liczba cyfr w toFixed() musi mieścić się w zakresie od 0 do 20 +@msg toISOString is not a function toISOString nie jest funkcją +@msg toPrecision() argument must be between 1 and 21 argument toPrecision() musi mieścić się w zakresie od 1 do 21 +@msg toSource: a getter cannot be written as source toSource: gettera nie można zapisać jako kodu źródłowego +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource: tablicy z właściwościami innymi niż jej elementy, z właściwością niewyliczalną lub z innym prototypem nie można zapisać jako kodu źródłowego +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource: obiektu z właściwością niewyliczalną lub z prototypem innym niż Object.prototype nie można zapisać jako kodu źródłowego +@msg toSource: the source is too long toSource: kod źródłowy jest zbyt długi +@msg toSource: the value contains itself toSource: wartość zawiera samą siebie +@msg toSource: the value has too many entries toSource: wartość ma zbyt wiele wpisów +@msg toSource: the value is nested too deep toSource: wartość jest zbyt głęboko zagnieżdżona +@msg toString is not a function toString nie jest funkcją +@msg too many arguments zbyt wiele argumentów +@msg too many quantifiers zbyt wiele kwantyfikatorów +@msg turn on at least one side włącz co najmniej jedną stronę +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) nieznana funkcja OpenType `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefiks - wyłącza funkcję; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom i aalt przyjmują numer wariantu w postaci `tag=N`, gdzie N to liczba od 0 do {_1}, na przykład `salt=2`) +@msg unknown dither `{d}` (diffusion, pattern, noise or none) nieznane rozpraszanie `{d}` (diffusion, pattern, noise lub none) +@msg unknown panel `{panel}` nieznany panel `{panel}` +@msg unknown parameter `{key}` nieznany parametr `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} nieznany parametr `{within}settings.{format}`; {why} +@msg unknown parameter `{within}{key}`; {why} nieznany parametr `{within}{key}`; {why} +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) nieznana redukcja `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform lub document) +@msg unmatched ')' niesparowany znak ')' +@msg unterminated character class niezakończona klasa znaków +@msg unterminated group niezakończona grupa +@msg with no hem and no grommets there is nothing to mark bez obszycia i bez oczek nie ma czego oznaczać +@msg wordGap and lineGap need area type in the selection wordGap i lineGap wymagają tekstu akapitowego w zaznaczeniu +@msg xpath needs its variables as an object xpath wymaga zmiennych w postaci obiektu +@msg xpath needs the expression as a string xpath wymaga wyrażenia w postaci ciągu znaków +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED}, a jej ustawień i pamięci podręcznej nie można przenieść z powrotem z {_1} do {_2}: {e} +@msg {_1} is not a folder: {e} {_1} nie jest folderem: {e} +@msg {_1}: {_2} and {_3} are in the way {_1}: przeszkadzają {_2} i {_3} +@msg {code}: {0} {code}: {0} +@msg {dir}: {e} {dir}: {e} +@msg {file} names more than one linked JavaScript file {file} wskazuje więcej niż jeden połączony plik JavaScript +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}: ścieżka niestandardowego grotu strzałki wymaga obszaru do wypełnienia oraz najwyżej {most} punktów kontrolnych i {most} ścieżek podrzędnych, a każdy punkt kontrolny musi leżeć najwyżej {far} jednostek grotu od jego wierzchołka +@msg {k}: bad custom arrowhead path: {e} {k}: nieprawidłowa ścieżka niestandardowego grotu strzałki: {e} +@msg {k}: bad custom arrowhead: {e} {k}: nieprawidłowy niestandardowy grot strzałki: {e} +@msg {k}: custom must be a name {k}: custom musi być nazwą +@msg {name} must be a number {name} musi być liczbą +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}: kopiowanie przyjmuje plik DCS tylko wtedy, gdy zawiera on swoje płyty; połącz go ponownie w miejscu, w którym znajdują się pliki jego płyt +@msg {path}: larger than {_1} MB {path}: rozmiar większy niż {_1} MB +@msg {saving} failed: {e} {saving}: niepowodzenie: {e} +@msg {shown} is not an XPath 1.0 function {shown} nie jest funkcją XPath 1.0 +@msg {v} names no layer or sublayer {v} nie wskazuje żadnej warstwy ani podwarstwy +@msg {what} is not a function {what} nie jest funkcją +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what}: na arkuszu zostałoby wydrukowanych ponad {MAX_COPIES_PER_SHEET} kopii: zwiększ skalę +@msg {what} {i} out of range {what} {i} poza zakresem +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. Profil obsługuje jeden efekt Obrót. Zamiast tego edytuj opcje istniejącego efektu. +@msg Add Points at Crossings: a path went missing Dodaj punkty na skrzyżowaniach: brakuje ścieżki +@msg CSS options: {e} Opcje CSS: {e} +@msg CSV has a header but no data rows Plik CSV ma nagłówek, ale nie ma wierszy danych +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark Plik CSV zawiera bajty NUL: zapisz go jako UTF-8 albo jako UTF-16 ze znacznikiem kolejności bajtów +@msg CSV is empty Plik CSV jest pusty +@msg CSV is larger than {MAX_CSV_BYTES} bytes Plik CSV jest większy niż {MAX_CSV_BYTES} bajtów +@msg CSV is not UTF-16 Plik CSV nie jest w kodowaniu UTF-16 +@msg CSV is not UTF-8 Plik CSV nie jest w kodowaniu UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes Plik CSV byłby większy niż {MAX_CSV_BYTES} bajtów +@msg Contour: select paths or shapes Kontur: zaznacz ścieżki lub kształty +@msg Contour: the Offset Path effect is not available Kontur: efekt Odsuń ścieżkę nie jest dostępny +@msg Create needs fills, strokes or both Tworzenie wymaga wypełnień, obrysów lub obu +@msg DXF options: {e} Opcje DXF: {e} +@msg DXF scale must be a positive number up to {_1}, not {scale} Skala DXF musi być liczbą dodatnią nie większą niż {_1}, a nie {scale} +@msg DXF {field} `{s}`: {allowed} DXF {field} `{s}`: {allowed} +@msg Draw Inside needs exactly one selected path, compound path or text Rysuj wewnątrz wymaga dokładnie jednej zaznaczonej ścieżki, ścieżki złożonej lub tekstu +@msg EPS Save writes all artwork: use document.export for selected-only EPS Zapis EPS obejmuje całą grafikę: użyj document.export, aby zapisać w EPS tylko zaznaczenie +@msg EPS flattener: {e} Spłaszczanie EPS: {e} +@msg EPS options: {e} Opcje EPS: {e} +@msg EPS {field} `{s}`: {allowed} EPS {field} `{s}`: {allowed} +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft Eksportuj na ekrany zapisuje formaty png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai lub vectorcraft +@msg Flare click coordinates must be finite numbers Współrzędne kliknięcia rozbłysku muszą być liczbami skończonymi +@msg HTML Text applies to object slices of type only Tekst HTML dotyczy tylko plasterków obiektów tekstowych +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) Opcja Identyfikuj obiekty według ma wartość ID XML: nazwa musi być prawidłowym ID XML (zaczynać się od litery, podkreślenia lub dwukropka; bez spacji) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) Obrys obrazu nie powiódł się: błąd wewnętrzny: jego wątek został zatrzymany (zgłoś ten błąd) +@msg Image Trace failed: {e} Obrys obrazu nie powiódł się: {e} +@msg Image Trace finished, but its document was closed Obrys obrazu został ukończony, ale jego dokument został zamknięty +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again Obrys obrazu został ukończony, ale obraz lub jego ustawienia zmieniły się w międzyczasie: obrysuj go ponownie +@msg Join: a subpath went missing Połącz: brakuje ścieżki podrzędnej +@msg Lock Workspace is not available in VectorCraft Polecenie Zablokuj obszar roboczy nie jest dostępne w programie VectorCraft +@msg Move Panel to New Window is not available: floating panels stay in the main window Polecenie Przenieś panel do nowego okna nie jest dostępne: panele pływające pozostają w oknie głównym +@msg Offset Path: the path is too complex to offset Odsuń ścieżkę: ścieżka jest zbyt złożona, aby ją odsunąć +@msg Offset Path: {e} Odsuń ścieżkę: {e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported Zawartość PDF jest niekompletna lub przekracza limity czytnika; nie zaimportowano żadnego dokumentu +@msg PDF flattener: {e} Spłaszczanie PDF: {e} +@msg PDF options: {e} Opcje PDF: {e} +@msg Recolor Artwork is no longer open Okno Zmień kolory kompozycji nie jest już otwarte +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 SVG nie jest w kodowaniu UTF-8, UTF-16 ani ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export Zapis obejmuje jeden obszar kompozycji: wskaż jeden albo wyeksportuj kilka poleceniem document.export +@msg Select a dimension first Najpierw zaznacz wymiar +@msg Select dimensions to update Zaznacz wymiary do zaktualizowania +@msg Select only dimensions Zaznacz tylko wymiary +@msg Select two or more objects, or a group Zaznacz co najmniej dwa obiekty albo grupę +@msg Shaper needs 1–64 source paths Kształtowanie wymaga od 1 do 64 ścieżek źródłowych +@msg Switch x/y is for scatter graphs Zamień x/y dotyczy wykresów punktowych +@msg Text options: {e} Opcje tekstu: {e} +@msg `align` is start, center or end `align` ma wartość start, center lub end +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` musi być tablicą par [subpath, anchor] +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` musi zawierać indeksy punktów kontrolnych (0, 1, 2…), a nie {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` musi zawierać indeksy punktów kontrolnych (0, 1, 2…), a nie {v} +@msg `artboards` must be a list of artboard indices `artboards` musi być listą indeksów obszarów kompozycji +@msg `asset` is the asset id (see assets.list) `asset` to identyfikator zasobu (patrz assets.list) +@msg `at` must be a byte offset `at` musi być przesunięciem w bajtach +@msg `autoLeading` must be a number `autoLeading` musi być liczbą +@msg `background` must be "", "matte" or #rrggbb `background` musi mieć wartość "", "matte" lub #rrggbb +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` musi mieć wartość "none", "standard" lub "forced" +@msg `center` must be [x, y] `center` musi mieć postać [x, y] +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` musi mieć wartość "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" lub "icfBottom" +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` musi mieć wartość "default", "leftToRight" lub "rightToLeft" +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` `clearStroke` wymaga `stroke: false`, `ids: []` i braku `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` musi być listą identyfikatorów obiektów tekstowych, a nie {v} +@msg `clear` must list type objects; {_1} is not one `clear` musi zawierać obiekty tekstowe; {_1} nim nie jest +@msg `clear` must not list `id` `clear` nie może zawierać `id` +@msg `color` needs a `rule` to make the theme from `color` wymaga `rule`, na podstawie której powstaje motyw +@msg `colors` is empty `colors` jest puste +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` ma wartość vertical, uniform, repeating lub sliding +@msg `columns` must be a whole number 1..50 `columns` musi być liczbą całkowitą z zakresu 1..50 +@msg `commands` must not be empty `commands` nie może być puste +@msg `contents` goes with `to` `contents` występuje razem z `to` +@msg `corners` must be sharp, round or square, not `{other}` `corners` musi mieć wartość sharp, round lub square, a nie `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` musi zawierać indeksy punktów kontrolnych (0, 1, 2…), a nie {v} +@msg `count` must be a whole number from 1 to 100 `count` musi być liczbą całkowitą od 1 do 100 +@msg `dataBase64` is larger than the module limit `dataBase64` przekracza limit rozmiaru modułu +@msg `dataBase64` is not valid base64 `dataBase64` nie jest prawidłowym kodem base64 +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` nie można łączyć z `ids`, `reverse` ani `append` +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` i `childLimit` ograniczają podsumowanie: przekaż `summary: true` +@msg `design` is a design name, or null for plain columns `design` to nazwa wzoru albo null dla zwykłych kolumn +@msg `design` is a design name, or null for the default marker `design` to nazwa wzoru albo null dla domyślnego znacznika +@msg `destinationFolder` is required when destination is `folder` `destinationFolder` jest wymagane, gdy destination ma wartość `folder` +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` musi mieć wartość `none`, `saveAndClose` lub `folder` +@msg `diameter` must be from 0.1 to 2500 pt `diameter` musi mieścić się w zakresie od 0,1 do 2500 pt +@msg `digits` must be one of {_1} `digits` musi mieć jedną z wartości: {_1} +@msg `dimImages` is a percentage or false `dimImages` to wartość procentowa albo false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` musi mieć wartość "auto", "leftToRight" lub "rightToLeft" +@msg `each` is the value one design stands for, more than 0 `each` to wartość, którą reprezentuje jeden wzór, większa od 0 +@msg `end` must be "start" or "end" `end` musi mieć wartość "start" lub "end" +@msg `ends` applies to joining exactly two paths `ends` dotyczy łączenia dokładnie dwóch ścieżek +@msg `event` must be one of {_1} `event` musi mieć jedną z wartości: {_1} +@msg `every` must be "all", "odd" or "even", not {v} `every` musi mieć wartość "all", "odd" lub "even", a nie {v} +@msg `expand` must be true or false `expand` musi mieć wartość true lub false +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` musi zawierać od 1 do 1000 ścieżek plików czcionek znalezionych przez ostatnie wyszukiwanie text.findFontFiles +@msg `folder` must be an absolute folder path `folder` musi być bezwzględną ścieżką folderu +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` musi być obiektem ze znacznikami osi i wartościami, na przykład {"wght"} +@msg `format` must be a string `format` musi być ciągiem znaków +@msg `format` must be one of {_1} `format` musi mieć jedną z wartości: {_1} +@msg `fractions` is chop or scale `fractions` ma wartość chop lub scale +@msg `from` and `to` must differ `from` i `to` muszą się różnić +@msg `from` has an empty frame `from` ma pustą ramkę +@msg `from` must be a non-negative integer `from` musi być nieujemną liczbą całkowitą +@msg `from` must be area type or type on a path `from` musi być tekstem akapitowym lub tekstem na ścieżce +@msg `gid` must be a glyph id, not {v} `gid` musi być identyfikatorem glifu, a nie {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` należy do jednego znaku, a nie do stylu znakowego (ustawia go text.setGlyphAlternate) +@msg `guide` must be a guide index (guide.list) `guide` musi być indeksem linii pomocniczej (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` musi mieć wartość default, left, center lub right +@msg `id` must be a node id `id` musi być identyfikatorem węzła +@msg `id` must be an object id, not {v} `id` musi być identyfikatorem obiektu, a nie {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` musi wskazywać grupy Obiekty na ścieżce lub należące do nich obiekty +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` przyjmuje najwyżej {MAX_INSPECT_IDS} identyfikatorów +@msg `imageMap` must be none, rectangle or polygon `imageMap` musi mieć wartość none, rectangle lub polygon +@msg `includeArtboardFill` must be true or false `includeArtboardFill` musi mieć wartość true lub false +@msg `includeSubdirs` must be true or false `includeSubdirs` musi mieć wartość true lub false +@msg `index` must be a point index `index` musi być indeksem punktu +@msg `index` must be a whole number or null `index` musi być liczbą całkowitą lub null +@msg `indices` must be a non-empty array of item indices `indices` musi być niepustą tablicą indeksów elementów +@msg `items` is a list of {kind, item} `items` to lista elementów {kind, item} +@msg `items` is empty `items` jest puste +@msg `items` must be a list of {id, points} `items` musi być listą elementów {id, points} +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` musi być listą elementów {{id, anchors?}}, a nie {v} +@msg `items` must be an array of {id, anchors} `items` musi być tablicą elementów {id, anchors} +@msg `items` must be an array of {{id, corners?}}, not {v} `items` musi być tablicą elementów {{id, corners?}}, a nie {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` musi mieć wartość "none", "short", "medium", "long" lub "stylistic" +@msg `kind` is one of {_1} `kind` ma jedną z wartości: {_1} +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` musi mieć wartość "round", "invertedRound" lub "chamfer", a nie {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` musi mieć wartość `text`, `visibility`, `image` lub `graph` +@msg `kind` must be a string `kind` musi być ciągiem znaków +@msg `kind` must be image, noImage or htmlText `kind` musi mieć wartość image, noImage lub htmlText +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` musi mieć wartość "none", "hard" lub "soft" +@msg `label` must be a non-empty name of at most 200 characters `label` musi być niepustą nazwą o długości najwyżej 200 znaków +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` musi być nazwą lub znacznikiem języka, na przykład "Turkish" lub "tr" +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` musi mieć wartość "romanBaseline" lub "emBoxTop" +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` musi mieścić się w zakresie od 0,1 do 5000 albo mieć wartość null dla interlinii automatycznej +@msg `length` must be a number of at most 1000000 pt either way `length` musi być liczbą nie większą niż 1000000 pt w każdą stronę +@msg `line` must be true or false `line` musi mieć wartość true lub false +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) nie może być większe niż `maxAngle` ({max_angle}°) +@msg `mode` must be "handles", "straight" or "smooth" `mode` musi mieć wartość "handles", "straight" lub "smooth" +@msg `mode` must be "straight", "bezier" or "arc" `mode` musi mieć wartość "straight", "bezier" lub "arc" +@msg `mode` must be one of {_1} `mode` musi mieć jedną z wartości: {_1} +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` musi mieć wartość "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" lub "lineEndHalf" +@msg `names` is a list of preset names `names` to lista nazw ustawień +@msg `names` lists preset names `names` zawiera nazwy ustawień +@msg `names` must be a list of names `names` musi być listą nazw +@msg `names` must be a list of swatch names `names` musi być listą nazw próbek +@msg `names` must be an array of style names `names` musi być tablicą nazw stylów +@msg `new` is a design name, or true `new` to nazwa wzoru albo true +@msg `path` must be a folder `path` musi być folderem +@msg `path` must be a path object `path` musi być obiektem ścieżki +@msg `pivot` must be a whole number from 0 to 8 `pivot` musi być liczbą całkowitą od 0 do 8 +@msg `points` must be a list of [x, y] points `points` musi być listą punktów [x, y] +@msg `points` must be a list of point indexes `points` musi być listą indeksów punktów +@msg `points` must be an array of point indices `points` musi być tablicą indeksów punktów +@msg `points` must be point indexes `points` musi zawierać indeksy punktów +@msg `port` must be "in" or "out" `port` musi mieć wartość "in" lub "out" +@msg `position` is above or below `position` ma wartość above lub below +@msg `preset` must be a string `preset` musi być ciągiem znaków +@msg `ratio` must be from 0.1 to 0.5 `ratio` musi mieścić się w zakresie od 0,1 do 0,5 +@msg `rect` must be [x, y, width, height] with a positive size `rect` musi mieć postać [x, y, width, height] i dodatni rozmiar +@msg `relation` must be overlap, inside or outside `relation` musi mieć wartość overlap, inside lub outside +@msg `reversed` must be true or false `reversed` musi mieć wartość true lub false +@msg `rotate` must be a finite number `rotate` musi być liczbą skończoną +@msg `rule` must be nonZero or evenOdd `rule` musi mieć wartość nonZero lub evenOdd +@msg `scale` must be a number in (0, 100] `scale` musi być liczbą z przedziału (0, 100] +@msg `scope` must be "selection" or "document", not {other} `scope` musi mieć wartość "selection" lub "document", a nie {other} +@msg `script` must be a string `script` musi być ciągiem znaków +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` to niepusta lista indeksów serii +@msg `seriesIndexes` needs a `type` or a `valueAxis` `seriesIndexes` wymaga `type` lub `valueAxis` +@msg `shift` must be a number of points `shift` musi być liczbą punktów +@msg `showCenter` must be true or false `showCenter` musi mieć wartość true lub false +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` musi mieć wartość "fullJustify", "alignLeft", "alignCenter" lub "alignRight" +@msg `size` is a percentage of the base text's size, 1 to 100 `size` to wartość procentowa rozmiaru tekstu podstawowego, od 1 do 100 +@msg `source` and `target` are the same object `source` i `target` to ten sam obiekt +@msg `source` must be `folder` or `datasets` `source` musi mieć wartość `folder` lub `datasets` +@msg `spacing` must be a number `spacing` musi być liczbą +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` musi być liczbą punktów równą 0 lub większą albo wartością "auto" +@msg `start` must be a finite number `start` musi być liczbą skończoną +@msg `starts` must be an array of anchor indices (or nulls) `starts` musi być tablicą indeksów punktów kontrolnych (lub wartości null) +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` musi mieć wartość linked, embedded, missing lub modified albo być ich listą +@msg `stop` must be a stop index `stop` musi być indeksem punktu koloru +@msg `story` styles the whole story: give no `start` or `end` `story` nadaje styl całemu tekstowi wątkowemu: nie podawaj `start` ani `end` +@msg `story` takes the whole story: give no `start` or `end` `story` obejmuje cały tekst wątkowy: nie podawaj `start` ani `end` +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` dotyczy obrysów (stroke: true) +@msg `style` must be roman or japanese `style` musi mieć wartość roman lub japanese +@msg `subpaths` must be two integer indices `subpaths` musi zawierać dwa indeksy całkowite +@msg `symbol` must be a symbol name `symbol` musi być nazwą symbolu +@msg `target` must be "object" or "contents", not {v} `target` musi mieć wartość "object" lub "contents", a nie {v} +@msg `tint` applies to global and spot colours `tint` dotyczy kolorów globalnych i dodatkowych +@msg `tint` goes with a global or spot `swatch` `tint` występuje razem z globalną lub dodatkową próbką `swatch` +@msg `tint` must be a number (%) `tint` musi być liczbą (%) +@msg `tint` needs a global or spot colour `tint` wymaga koloru globalnego lub dodatkowego +@msg `to` is already threaded `to` jest już połączone w wątek +@msg `to` is the design's new name `to` to nowa nazwa wzoru +@msg `to` must be [x, y] `to` musi mieć postać [x, y] +@msg `to` must be a closed path, area type or type on a path other than `from` `to` musi być ścieżką zamkniętą, tekstem akapitowym lub tekstem na ścieżce innym niż `from` +@msg `to` must be a whole number `to` musi być liczbą całkowitą +@msg `to` takes the rows of one object's stack, not aligned rows `to` przyjmuje wiersze stosu jednego obiektu, a nie wiersze wyrównane +@msg `useLab` must be true or false `useLab` musi mieć wartość true lub false +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` musi mieć wartość default, top, middle, baseline lub bottom +@msg `values` must be a list `values` musi być listą +@msg `values` must be an object `values` musi być obiektem +@msg `values` must name hex, rgb, cmyk, lab or gray `values` musi wskazywać hex, rgb, cmyk, lab lub gray +@msg `values` names a kind twice `values` wskazuje ten sam rodzaj dwa razy +@msg `{_1}` can't be edited or removed `{_1}` nie można edytować ani usunąć +@msg `{_1}` is built in and always listed `{_1}` jest wbudowane i zawsze widoczne na liście +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` nie jest plikiem czcionki znalezionym przez ostatnie wyszukiwanie text.findFontFiles +@msg `{_1}` would be overwritten by the package: choose a different folder or name `{_1}` zostałoby nadpisane przez pakiet: wybierz inny folder lub inną nazwę +@msg `{file}` has no art to place `{file}` nie zawiera grafiki do umieszczenia +@msg `{file}` is over {_1} MB `{file}` ma ponad {_1} MB +@msg `{f}` belongs to a VectorCraft that is running `{f}` należy do uruchomionego programu VectorCraft +@msg `{f}` is the copy of a document open now `{f}` to kopia otwartego teraz dokumentu +@msg `{id}` is no writable format (see document.formats) `{id}` nie jest formatem z możliwością zapisu (patrz document.formats) +@msg `{id}` isn't a loaded library `{id}` nie jest wczytaną biblioteką +@msg `{key}` has no colours `{key}` nie zawiera kolorów +@msg `{key}` is a design name `{key}` to nazwa wzoru +@msg `{key}` is a design name or a list of them `{key}` to nazwa wzoru lub lista takich nazw +@msg `{key}` is longer than {MAX_NAME} characters `{key}` ma więcej niż {MAX_NAME} znaków +@msg `{key}` is out of range `{key}` jest poza zakresem +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` musi mieć postać [min, desired, max] lub {{min?, desired?, max?}}, w procentach +@msg `{key}` must be a non-negative integer `{key}` musi być nieujemną liczbą całkowitą +@msg `{key}` must be a number {_1}..{_2} `{key}` musi być liczbą z zakresu {_1}..{_2} +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` musi być liczbą całkowitą równą co najmniej 0, a nie {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` musi być indeksem elementu wyglądu (kolejność malowania) lub null +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` musi być tablicą identyfikatorów zasobów (patrz assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` musi mieścić się w zakresie od 0 do 180 stopni, a nie {a} +@msg `{key}` must be from {_1} to {shift_max} `{key}` musi mieścić się w zakresie od {_1} do {shift_max} +@msg `{key}`: not a colour: {v} `{key}`: to nie jest kolor: {v} +@msg `{k}.family` is required `{k}.family` jest wymagane +@msg `{k}` is a number 0..1 `{k}` to liczba z zakresu 0..1 +@msg `{k}` is true or false `{k}` ma wartość true lub false +@msg `{k}` isn't an export setting: {_1} `{k}` nie jest ustawieniem eksportu: {_1} +@msg `{k}` must be a byte offset `{k}` musi być przesunięciem w bajtach +@msg `{k}` must be a number `{k}` musi być liczbą +@msg `{k}` must be a number of points `{k}` musi być liczbą punktów +@msg `{k}` must be a string `{k}` musi być ciągiem znaków +@msg `{k}` must be a whole number `{k}` musi być liczbą całkowitą +@msg `{k}` must be from {lo} to {hi} for {key} `{k}` musi mieścić się w zakresie od {lo} do {hi} dla {key} +@msg `{k}` must be {what}, not {v} `{k}` musi mieć wartość {what}, a nie {v} +@msg `{l}` isn't a global or spot colour `{l}` nie jest kolorem globalnym ani dodatkowym +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` nie jest bitmapą: użyj clipboard.importSvg, importPdf lub importText +@msg `{name}` can't be deleted `{name}` nie można usunąć +@msg `{name}` can't be duplicated `{name}` nie można powielić +@msg `{name}` can't be edited `{name}` nie można edytować +@msg `{name}` can't be edited this way `{name}` nie można edytować w ten sposób +@msg `{name}` can't be renamed nazwy `{name}` nie można zmienić +@msg `{name}` can't name a folder `{name}` nie może być nazwą folderu +@msg `{name}` has no art to place `{name}` nie zawiera grafiki do umieszczenia +@msg `{name}` has no text to place `{name}` nie zawiera tekstu do umieszczenia +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text `{name}` w dokumencie Word nie jest tekstem UTF-8 ani UTF-16 +@msg `{name}` in the Word document is not well-formed XML: {e} `{name}` w dokumencie Word nie jest poprawnie sformułowanym kodem XML: {e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep `{name}` w dokumencie Word zagnieżdża elementy głębiej niż na {MAX_NESTING} poziomów +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` to wbudowane ustawienie tylko do odczytu: zapisz je pod inną nazwą +@msg `{name}` is a built-in preset: save under another name `{name}` to wbudowane ustawienie: zapisz pod inną nazwą +@msg `{name}` is a built-in profile and stays `{name}` to wbudowany profil i pozostaje +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` to tinta koloru `{base}`: edytuj `{base}` (próbka tinty przyjmuje tylko nową nazwę) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` to przestrzeń robocza: najpierw wybierz inną poleceniem edit.colorSettings +@msg `{name}` is built in and stays `{name}` jest wbudowane i pozostaje +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` przyjmuje malowanie własnego rodzaju (kolor, gradient lub wzorek) +@msg `{n}` can't be deleted `{n}` nie można usunąć +@msg `{n}` can't be merged `{n}` nie można scalić +@msg `{n}` can't be moved `{n}` nie można przenieść +@msg `{n}` is not a colour swatch `{n}` nie jest próbką koloru +@msg `{n}` isn't a solid colour `{n}` nie jest kolorem jednolitym +@msg `{n}`: colour groups hold solid colours only `{n}`: grupy kolorów zawierają tylko kolory jednolite +@msg `{other}` doesn't trace an image `{other}` nie obrysowuje obrazu +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`: odwołania kroku kopiują ponad {_1} MB wartości +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` nie jest znacznikiem osi: znacznik ma cztery znaki, na przykład "wght" +@msg `{to}` is empty or already used `{to}` jest puste lub już używane +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` ma więcej niż {MAX_TEXT} bajtów +@msg `{what}` must be text `{what}` musi być tekstem +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard rysunek DXF ma jeden punkt początkowy: przekaż useArtboards: true, aby uzyskać jeden rysunek na obszar kompozycji +@msg a PDF presets file with over {_1} presets plik ustawień PDF zawierający ponad {_1} ustawień +@msg a batch preview cannot switch documents podgląd przetwarzania wsadowego nie może przełączać dokumentów +@msg a brush named `{nn}` exists istnieje już pędzel o nazwie `{nn}` +@msg a container of the listed objects is gone kontener wymienionych obiektów już nie istnieje +@msg a content stream can't be read nie można odczytać strumienia zawartości +@msg a content stream isn't a stream strumień zawartości nie jest strumieniem +@msg a design holds at most {_1} objects wzór zawiera najwyżej {_1} obiektów +@msg a design needs a name wzór wymaga nazwy +@msg a dictionary grew too large słownik stał się zbyt duży +@msg a document keeps at most {_1} graph designs dokument przechowuje najwyżej {_1} wzorów wykresów +@msg a document keeps at most {_1} saved selections dokument przechowuje najwyżej {_1} zapisanych zaznaczeń +@msg a document keeps at most {_1} views dokument przechowuje najwyżej {_1} widoków +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files dokument łączy najwyżej {MAX_SVG_SCRIPTS} plików JavaScript +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes rząd filtra feConvolveMatrix wynosi {x}×{y}, co powoduje przepełnienie przy mnożeniu rozmiaru jądra wykonywanym przez usvg +@msg a gradient preset is already called {n} ustawienie gradientu o nazwie {n} już istnieje +@msg a graph has at most {_1} series, so it transposes with at most that many categories wykres ma najwyżej {_1} serii, więc można go transponować przy najwyżej tylu kategoriach +@msg a layer can't be merged into its own sublayer warstwy nie można scalić z jej własną podwarstwą +@msg a layer's form box isn't an array pole ograniczające formularza warstwy nie jest tablicą +@msg a layer's form can't be read nie można odczytać formularza warstwy +@msg a layer's form has no box formularz warstwy nie ma pola ograniczającego +@msg a layer's form has no dictionary formularz warstwy nie ma słownika +@msg a legend draws at most {MAX_CHIPS} swatches legenda rysuje najwyżej {MAX_CHIPS} próbek +@msg a name names one asset: give multiple: false, or one object nazwa wskazuje jeden zasób: podaj multiple: false albo jeden obiekt +@msg a page can't be read nie można odczytać strony +@msg a page object is used by something else obiekt strony jest używany przez coś innego +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) miniatura strony musi składać się z pikseli RGB, od 1 do {THUMBNAIL_SIZE} na bok (podano {w} × {h}) +@msg a pattern layer doesn't end what it starts warstwa wzorka nie kończy tego, co zaczyna +@msg a pattern layer's colors hold something else kolory warstwy wzorka zawierają coś innego +@msg a pattern named `{n}` exists istnieje już wzorek o nazwie `{n}` +@msg a plug-in with id {_1} is already installed wtyczka o ID {_1} jest już zainstalowana +@msg a preset named `{n}` exists istnieje już ustawienie o nazwie `{n}` +@msg a print presets file with over {_1} presets plik ustawień drukowania zawierający ponad {_1} ustawień +@msg a profile is already called {n} profil o nazwie {n} już istnieje +@msg a row can't go into itself wiersza nie można przenieść do niego samego +@msg a series goes on the left or the right value axis seria trafia na lewą lub prawą oś wartości +@msg a start must be an anchor index ≥ 0 or null początek musi być indeksem punktu kontrolnego ≥ 0 lub null +@msg a style named `{name}` already exists styl o nazwie `{name}` już istnieje +@msg a style named `{to}` already exists styl o nazwie `{to}` już istnieje +@msg a swatch needs a colour, gradient or pattern próbka wymaga koloru, gradientu lub wzorka +@msg a symbol named `{n}` already exists symbol o nazwie `{n}` już istnieje +@msg a theme has 1 to {THEME_SIZE} colours motyw ma od 1 do {THEME_SIZE} kolorów +@msg a theme is already called {name} (replace: "{name}" overwrites it) motyw o nazwie {name} już istnieje (replace: "{name}" go nadpisze) +@msg a toolbar name has at most {MAX_NAME} characters nazwa paska narzędzi ma najwyżej {MAX_NAME} znaków +@msg a view named `{_1}` exists istnieje już widok o nazwie `{_1}` +@msg a view named `{name}` exists istnieje już widok o nazwie `{name}` +@msg a workspace keeps at most {MAX} toolbars obszar roboczy przechowuje najwyżej {MAX} pasków narzędzi +@msg a {_1} series can't go in a {_2} graph serii {_1} nie można umieścić na wykresie {_2} +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}: "pdf" (zgodny z PDF, domyślny) lub "7" (starszy format .ai, wersja 7) +@msg already editing an opacity mask trwa już edycja maski krycia +@msg already editing another object's opacity mask trwa już edycja maski krycia innego obiektu +@msg already in pattern editing mode tryb edycji wzorka jest już włączony +@msg an artboard is listed twice obszar kompozycji jest wymieniony dwa razy +@msg an empty artboard has no width or height to scale to pusty obszar kompozycji nie ma szerokości ani wysokości, do których można skalować +@msg an image is too large for DXF and was left out obraz jest zbyt duży dla formatu DXF i został pominięty +@msg an image of {_1} MB, at most {_2} MB obraz o rozmiarze {_1} MB, dozwolone najwyżej {_2} MB +@msg an image of {declared_w} × {declared_h} pixels is too large to draw obraz o wymiarach {declared_w} × {declared_h} pikseli jest zbyt duży, aby go narysować +@msg an image of {width} × {height} pixels is too large to draw obraz o wymiarach {width} × {height} pikseli jest zbyt duży, aby go narysować +@msg an index must be a whole number indeks musi być liczbą całkowitą +@msg an inline graphic has no glyphs grafika w wierszu nie ma glifów +@msg an invisible text form can't be read nie można odczytać formularza niewidocznego tekstu +@msg an object is missing brakuje obiektu +@msg an object's header is missing brakuje nagłówka obiektu +@msg an overprinting form can't be read nie można odczytać formularza z nadrukiem +@msg an unread object has no readable bounds nieodczytany obiekt nie ma odczytywalnych granic +@msg an unread object holds a frame of threaded type nieodczytany obiekt zawiera ramkę tekstu wątkowego +@msg an unread object is missing from its layer nieodczytanego obiektu brakuje w jego warstwie +@msg anchor `smooth` must be true or false `smooth` punktu kontrolnego musi mieć wartość true lub false +@msg anchor `{key}` must be [x, y] `{key}` punktu kontrolnego musi mieć postać [x, y] +@msg anchor `{key}` must be a number `{key}` punktu kontrolnego musi być liczbą +@msg angle and roundness must be finite kąt i okrągłość muszą być skończone +@msg angles must be a list as long as pins (degrees or null) angles musi być listą tej samej długości co pins (stopnie lub null) +@msg appearance must be flat|center|edge appearance musi mieć wartość flat|center|edge +@msg apply is "fill" or "stroke", not `{other}` apply ma wartość "fill" lub "stroke", a nie `{other}` +@msg area must be a closed polygon of 3 to 100000 [x, y] points area musi być zamkniętym wielokątem złożonym z 3 do 100000 punktów [x, y] +@msg area type needs a path that encloses an area tekst akapitowy wymaga ścieżki, która otacza obszar +@msg area type {_1} is named twice tekst akapitowy {_1} wymieniono dwa razy +@msg arrowAlign must be extend|tip, got {o} arrowAlign musi mieć wartość extend|tip, podano {o} +@msg arrowScale must be [start, end] arrowScale musi mieć postać [start, end] +@msg artboard is a 0-based index artboard to indeks liczony od 0 +@msg artboard must be a 0-based index artboard musi być indeksem liczonym od 0 +@msg artboard {i} doesn't print obszar kompozycji {i} nie jest drukowany +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout musi mieć postać {layout?, columns?, spacing?, rightToLeft?} +@msg artboardLayout.columns must be 1 or more artboardLayout.columns musi wynosić 1 lub więcej +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout musi mieć wartość gridByRow, gridByColumn, row lub column +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft musi mieć wartość true lub false +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing musi wynosić 0–{MAX_SPACING} pt +@msg artboards must be a non-empty list artboards musi być niepustą listą +@msg artboards must be a number artboards musi być liczbą +@msg asset `{_1}` has no art to export zasób `{_1}` nie zawiera grafiki do wyeksportowania +@msg at most {MAX_KEYWORDS} keywords najwyżej {MAX_KEYWORDS} słów kluczowych +@msg at most {MAX_MATCHES} matches are kept zachowywanych jest najwyżej {MAX_MATCHES} dopasowań +@msg at most {MAX_POINTS} points najwyżej {MAX_POINTS} punktów +@msg at most {MAX_QUEUE} files najwyżej {MAX_QUEUE} plików +@msg at must be [x, y] at musi mieć postać [x, y] +@msg at/rect lie outside the canvas at/rect leżą poza płótnem +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute musi mieć wartość size|leading|tracking|kerning|baselineShift +@msg attrs must be an object attrs musi być obiektem +@msg automation path rejected: the folder's name isn't valid text odrzucono ścieżkę automatyzacji: nazwa folderu nie jest prawidłowym tekstem +@msg automation path rejected: {_1} isn't valid text odrzucono ścieżkę automatyzacji: {_1} nie jest prawidłowym tekstem +@msg automation path rejected: {why}: {path} odrzucono ścieżkę automatyzacji: {why}: {path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis musi mieć wartość "vertical", "horizontal" lub być kątem w stopniach, a nie `{given}` +@msg backgroundContents must be transparent or white backgroundContents musi mieć wartość transparent lub white +@msg bad AES key nieprawidłowy klucz AES +@msg bad `runs`: {e} nieprawidłowe `runs`: {e} +@msg bad arrowScale nieprawidłowe arrowScale +@msg bad base64 nieprawidłowy kod base64 +@msg bad color {c} nieprawidłowy kolor {c} +@msg bad colour nieprawidłowy kolor +@msg bad colour in `colors` nieprawidłowy kolor w `colors` +@msg bad colour {v} nieprawidłowy kolor {v} +@msg bad colour {v} in groupColors nieprawidłowy kolor {v} w groupColors +@msg bad cross-reference entry nieprawidłowy wpis tabeli odsyłaczy +@msg bad cross-reference table nieprawidłowa tabela odsyłaczy +@msg bad current colour {v} nieprawidłowy bieżący kolor {v} +@msg bad dataBase64 nieprawidłowe dataBase64 +@msg bad edit nieprawidłowa edycja +@msg bad fill {v} nieprawidłowe wypełnienie {v} +@msg bad fit: {e} nieprawidłowe dopasowanie: {e} +@msg bad model {other} (keep, new or document) nieprawidłowy model {other} (keep, new lub document) +@msg bad new colour {v} nieprawidłowy nowy kolor {v} +@msg bad object nieprawidłowy obiekt +@msg bad object number nieprawidłowy numer obiektu +@msg bad offset nieprawidłowe przesunięcie +@msg bad path data: {e} nieprawidłowe dane ścieżki: {e} +@msg bad point {v}: expected [x, y] or [x, y, pressure] nieprawidłowy punkt {v}: oczekiwano [x, y] lub [x, y, pressure] +@msg bad reference nieprawidłowe odwołanie +@msg bad trailer size nieprawidłowy rozmiar w sekcji trailer +@msg bad {k} colour nieprawidłowy kolor {k} +@msg batches cannot nest przetwarzania wsadowego nie można zagnieżdżać +@msg bind something to a variable first najpierw powiąż coś ze zmienną +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed musi wynosić 0–{MAX_BLEED} pt: liczba, [top, bottom, left, right] lub {{top, bottom, left, right}} +@msg brush must be a name or null brush musi być nazwą lub null +@msg by must be a number by musi być liczbą +@msg can't add page thumbnails: {why} nie można dodać miniatur stron: {why} +@msg can't add the editing data for other apps: {why} nie można dodać danych edycyjnych dla innych aplikacji: {why} +@msg can't edit the written PDF: {why} nie można edytować zapisanego pliku PDF: {why} +@msg can't encrypt the PDF: {why} nie można zaszyfrować pliku PDF: {why} +@msg can't linearise the PDF: {why} nie można zlinearyzować pliku PDF: {why} +@msg can't open `{_1}`: {_2} nie można otworzyć `{_1}`: {_2} +@msg can't open `{_1}`: {e} nie można otworzyć `{_1}`: {e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) nie można otworzyć `{name}`: to nie jest format odczytywany przez VectorCraft (patrz document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) nie można umieścić `{name}`: to nie jest format odczytywany przez VectorCraft (patrz document.formats) +@msg can't read back the written PDF: {e} nie można ponownie odczytać zapisanego pliku PDF: {e} +@msg can't read this Word document: {why} nie można odczytać tego dokumentu Word: {why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp nie można zapisać obrazów .{e}: użyj .png, .jpg, .gif, .webp, .tif lub .bmp +@msg can't write PDF layers, overprint or invisible text: {why} nie można zapisać warstw PDF, nadruku ani niewidocznego tekstu: {why} +@msg can't write the file's copyright: {why} nie można zapisać informacji o prawach autorskich pliku: {why} +@msg can't write the file's modification date: {why} nie można zapisać daty modyfikacji pliku: {why} +@msg can't write the pages' user units: {why} nie można zapisać jednostek użytkownika stron: {why} +@msg cannot distort a zero-size or non-finite bounding box nie można zniekształcić obwiedni o zerowym lub nieskończonym rozmiarze +@msg cannot move an object into itself nie można przenieść obiektu do niego samego +@msg case must be upper|lower|title|sentence case musi mieć wartość upper|lower|title|sentence +@msg category `{c}`: one of {_1} kategoria `{c}`: jedna z wartości: {_1} +@msg center must be [x, y] center musi mieć postać [x, y] +@msg choose at least one artboard wybierz co najmniej jeden obszar kompozycji +@msg choose at least one status wybierz co najmniej jeden stan +@msg clip must be true or false clip musi mieć wartość true lub false +@msg clippingMask must be true or false clippingMask musi mieć wartość true lub false +@msg closed must be a boolean closed musi być wartością logiczną +@msg collapsed must be true or false collapsed musi mieć wartość true lub false +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile: żaden profil RGB ani CMYK nie nosi nazwy „{profile}” +@msg colorMode must be rgb or cmyk, not `{m}` colorMode musi mieć wartość rgb lub cmyk, a nie `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel musi mieć wartość {own} (tryb dokumentu), grayscale lub bitmap +@msg colour groups can't go into colour groups grup kolorów nie można umieszczać w grupach kolorów +@msg colour, mode and spot apply to solid-colour swatches only color, mode i spot dotyczą tylko próbek kolorów jednolitych +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read kolumna {column}: `{cell}` to kolumna `%` plików danych wykresu, które nie są odczytywane +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable kolumna {column}: `{cell}` wskazuje `{name}`, czyli zmienną typu {_1} +@msg column {column}: `{cell}` names no variable kolumna {column}: `{cell}` nie wskazuje żadnej zmiennej +@msg column {column}: the variable name is longer than {MAX_NAME} characters kolumna {column}: nazwa zmiennej ma więcej niż {MAX_NAME} znaków +@msg column: true floats the whole column and takes no onto, above or below column: true odłącza całą kolumnę i nie przyjmuje onto, above ani below +@msg command must be a shape.* command command musi być poleceniem shape.* +@msg commands[{i}]: missing `command` commands[{i}]: brak `command` +@msg composer must be "singleLine" or "everyLine" composer musi mieć wartość "singleLine" lub "everyLine" +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes kształty złożone przyjmują ścieżki, ścieżki złożone, grupy, kształty złożone, tekst, przejścia i koperty +@msg compound {_1} must contain exactly one open path to join obiekt złożony {_1} musi zawierać dokładnie jedną otwartą ścieżkę do połączenia +@msg connect needs a path other than the one extended connect wymaga ścieżki innej niż przedłużana +@msg connect needs id connect wymaga id +@msg connect path has no open end ścieżka w connect nie ma otwartego końca +@msg copy needs path: the file to copy into the document's folder copy wymaga path: pliku do skopiowania do folderu dokumentu +@msg copy takes images only: relink placed documents where their files are copy przyjmuje tylko obrazy: umieszczone dokumenty połącz ponownie w miejscu, w którym znajdują się ich pliki +@msg copying a file isn't available on the web kopiowanie pliku nie jest dostępne w wersji webowej +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus musi mieć wartość unknown, copyrighted lub publicDomain +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl musi być adresem URL (bez spacji, najwyżej 2048 znaków) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners musi zawierać 4 punkty [x, y] (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`: item lub file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`: tylko strony PDF mają pola art, trim, bleed i media; użyj "crop" lub "bounding" +@msg crop rectangle doesn't overlap the image prostokąt kadrowania nie pokrywa się z obrazem +@msg cross-reference and object streams aren't supported strumienie odsyłaczy i obiektów nie są obsługiwane +@msg damaged compressed data: {e} uszkodzone dane skompresowane: {e} +@msg dash lengths must be numbers, got {v} długości kresek muszą być liczbami, podano {v} +@msg dash must be a list of lengths or null, got {v} dash musi być listą długości lub null, podano {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) klucz danych `{k}`: litery, cyfry, -, _ i . (nie name ani vc-…) +@msg data must be an object of key: value, not {v} data musi być obiektem w postaci key: value, a nie {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare zestaw danych `{name}` zawiera wartość dla `{var}`, której biblioteka nie deklaruje +@msg data set `{name}` holds two values for `{var}` zestaw danych `{name}` zawiera dwie wartości dla `{var}` +@msg data set `{name}` is declared twice zestaw danych `{name}` zadeklarowano dwa razy +@msg data takes at most {MAX_DATA} keys at once data przyjmuje najwyżej {MAX_DATA} kluczy naraz +@msg dataset `{name}` already exists zestaw danych `{name}` już istnieje +@msg dataset `{to}` already exists zestaw danych `{to}` już istnieje +@msg date `{d}`: YYYY-MM-DD data `{d}`: YYYY-MM-DD +@msg decoded data is too large zdekodowane dane są zbyt duże +@msg define a variable first najpierw zdefiniuj zmienną +@msg detect must be true or false detect musi mieć wartość true lub false +@msg direct-select anchor points of a path zaznacz bezpośrednio punkty kontrolne ścieżki +@msg distance must be > 0 distance musi być > 0 +@msg dx and dy give the arrow's direction dx i dy określają kierunek strzałki +@msg dx and dy must be numbers dx i dy muszą być liczbami +@msg dxf fitTo must be [width, height], positive dxf fitTo musi mieć postać [width, height] i wartości dodatnie +@msg dxf must be an object of DXF import options dxf musi być obiektem z opcjami importu DXF +@msg dxf scale must be a positive number up to {_1}, not {s} dxf scale musi być liczbą dodatnią nie większą niż {_1}, a nie {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt dxf unit `{u}`: jednostka, na przykład mm, cm, in lub pt +@msg dxf: {e} dxf: {e} +@msg each format is an object {format, scale?, suffix?} każdy format to obiekt {format, scale?, suffix?} +@msg each item needs an id każdy element wymaga id +@msg each match needs `kind` każde dopasowanie wymaga `kind` +@msg each match needs a `name` każde dopasowanie wymaga `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them każdy element `fonts` ma postać {family, style?}; od 1 do 1000 elementów +@msg each of `items` is {kind, item} każdy element `items` ma postać {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} każdy element `items` ma postać {{id, corners?}} z identyfikatorem obiektu, a nie {it} +@msg each row needs `from` każdy wiersz wymaga `from` +@msg each stop needs a position każdy punkt koloru wymaga położenia +@msg each subpath needs an anchors array każda ścieżka podrzędna wymaga tablicy anchors +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType musi mieć wartość easeIn, easeOut, easeInOut lub easeOutIn +@msg editingData must be true or false editingData musi mieć wartość true lub false +@msg editingData must be true, false or "always" editingData musi mieć wartość true, false lub "always" +@msg edits overlap edycje nakładają się na siebie +@msg effects of objects whose appearances differ can't be moved together efektów obiektów o różnym wyglądzie nie można przenosić razem +@msg empty header in column {_1} pusty nagłówek w kolumnie {_1} +@msg encoding `{_1}`: utf8 or utf16 encoding `{_1}`: utf8 lub utf16 +@msg ends must be two of "first" and "last" ends musi zawierać dwie wartości spośród "first" i "last" +@msg every numbered name for the result is taken każda numerowana nazwa wyniku jest już zajęta +@msg expanded SVG styles exceed the import size limit rozwinięte style SVG przekraczają limit rozmiaru importu +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes plik ustawień eksportu jest większy niż {MAX_PRESET_FILE_BYTES} bajtów +@msg exportText must be editable or appearance exportText musi mieć wartość editable lub appearance +@msg exportType must be movieClip or graphic, not `{s}` exportType musi mieć wartość movieClip lub graphic, a nie `{s}` +@msg extend needs id extend wymaga id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges zawiera najwyżej {MAX_FEATURE_CHANGES} znaczników +@msg featureChanges must be a list of tags featureChanges musi być listą znaczników +@msg features must be a list of tags features musi być listą znaczników +@msg fidelity must be 0..100 fidelity musi mieścić się w zakresie 0..100 +@msg files must be {name, dataBase64} files musi mieć postać {name, dataBase64} +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit musi mieć wartość none|autoHeight|shrinkText|block, podano {id} +@msg flattener: {e} spłaszczanie: {e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`: high, medium, low lub zapisane ustawienie (patrz flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile musi mieć wartość along|across, podano {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`: none, rows, format, item lub itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels foldery zagnieżdżone głębiej niż na {MAX_BATCH_DEPTH} poziomów +@msg folders deeper than {MAX_DEPTH} levels foldery zagnieżdżone głębiej niż na {MAX_DEPTH} poziomów +@msg font family `{family}` is not available rodzina czcionek `{family}` nie jest dostępna +@msg font family `{tf}` is not available rodzina czcionek `{tf}` nie jest dostępna +@msg format `{f}`: pdf or postscript format `{f}`: pdf lub postscript +@msg format `{f}`: text or json format `{f}`: text lub json +@msg format is `{format}` format to `{format}` +@msg formats must be an array of rows, not {v} formats musi być tablicą wierszy, a nie {v} +@msg frequency must be between 1 and 300 lines per inch frequency musi mieścić się w zakresie od 1 do 300 linii na cal +@msg from and to must be finite from i to muszą być skończone +@msg from must be [x0, y0, x1, y1] from musi mieć postać [x0, y0, x1, y1] +@msg give `before` or `after`, not both podaj `before` albo `after`, nie oba naraz +@msg give `colors`, or `color` and `rule` podaj `colors` albo `color` i `rule` +@msg give `event`, such as onclick podaj `event`, na przykład onclick +@msg give `file`, the JavaScript file's path or URL podaj `file`, ścieżkę lub adres URL pliku JavaScript +@msg give `fill` and/or `stroke` podaj `fill` i/lub `stroke` +@msg give `fill`, `stroke` or both podaj `fill`, `stroke` lub oba +@msg give `folder` podaj `folder` +@msg give `fonts`, or open a document podaj `fonts` albo otwórz dokument +@msg give `from` or `indices`, not both podaj `from` albo `indices`, nie oba naraz +@msg give `id` (no text is being edited) podaj `id` (żaden tekst nie jest edytowany) +@msg give `id` and `anchors`, or `items`, not both podaj `id` i `anchors` albo `items`, nie oba naraz +@msg give `id` or `guide`, not both podaj `id` albo `guide`, nie oba naraz +@msg give `length` or `to` podaj `length` lub `to` +@msg give `matches`: [{kind, name, target}] podaj `matches`: [{kind, name, target}] +@msg give `name` (or edit a pattern) podaj `name` (albo edytuj wzorek) +@msg give `name` or `names` podaj `name` lub `names` +@msg give `path` (a .wasm file) or `dataBase64` (the module) podaj `path` (plik .wasm) lub `dataBase64` (moduł) +@msg give `path`, `data` or `dataBase64` podaj `path`, `data` lub `dataBase64` +@msg give `path`, `data`, `dataBase64` or `csv` podaj `path`, `data`, `dataBase64` lub `csv` +@msg give `path`, `xml` or `dataBase64` podaj `path`, `xml` lub `dataBase64` +@msg give `script`, the JavaScript the event runs ("" removes the event) podaj `script`, kod JavaScript uruchamiany przez zdarzenie ("" usuwa zdarzenie) +@msg give `text` (the reading) or `remove` podaj `text` (czytanie) lub `remove` +@msg give `x` and `y` (and a `tolerance` of 0 or more) podaj `x` i `y` (oraz `tolerance` równe 0 lub więcej) +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments podaj mode albo korekty r/g/b, c/m/y/k, gray lub tint +@msg give a name, or paint the active proxy with a saved preset podaj nazwę albo nadaj aktywnemu wskaźnikowi wypełnienia lub obrysu zapisane ustawienie +@msg give a name, or select a stroke with a saved profile podaj nazwę albo zaznacz obrys z zapisanym profilem +@msg give a pattern name or select an object painted with a pattern podaj nazwę wzorka albo zaznacz obiekt pomalowany wzorkiem +@msg give at least one of clip, invert, disabled, linked podaj co najmniej jedno z: clip, invert, disabled, linked +@msg give color, none, swatch or gradient podaj color, none, swatch lub gradient +@msg give dataBase64 podaj dataBase64 +@msg give either at (a click) or area (a path drawn around the overlaps) podaj at (kliknięcie) albo area (ścieżkę narysowaną wokół nakładających się obszarów) +@msg give formats or a preset, not both podaj formats albo ustawienie, nie oba naraz +@msg give newName or delete podaj newName lub delete +@msg give offset or by podaj offset lub by +@msg give one of `to`, `rect` or `at` podaj jedno z: `to`, `rect` lub `at` +@msg give one of assets, layers, selection or fullDocument podaj jedno z: assets, layers, selection lub fullDocument +@msg give only one of onto, above, below, icon and a column place podaj tylko jedno z: onto, above, below, icon i miejsce w kolumnie +@msg give origin: [x, y], or reset: true podaj origin: [x, y] albo reset: true +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note podaj overprintFill, overprintStroke, showCenter, imageMap, url lub note +@msg give path (a file) or folder podaj path (plik) lub folder +@msg give path, or name and dataBase64 podaj path albo name i dataBase64 +@msg give paths or files podaj paths lub files +@msg give rows, rowHeight, columns or columnWidth podaj rows, rowHeight, columns lub columnWidth +@msg give svg, or dataBase64 of an SVG or SVGZ file podaj svg albo dataBase64 pliku SVG lub SVGZ +@msg give text podaj text +@msg give the asset a name nadaj zasobowi nazwę +@msg give the preset a `name` nadaj ustawieniu `name` +@msg give the preset a name nadaj ustawieniu nazwę +@msg give the preset a name (1–64 characters) nadaj ustawieniu nazwę (1–64 znaki) +@msg give the settings to change, e.g. {"formats"}]} podaj ustawienia do zmiany, np. {"formats"}]} +@msg give the style a name nadaj stylowi nazwę +@msg give two or more style `names` podaj co najmniej dwie nazwy stylów w `names` +@msg give two or more swatches podaj co najmniej dwie próbki +@msg give x, y, or a handle's length or angle podaj x, y albo długość lub kąt uchwytu +@msg give {key, value} or {values} podaj {key, value} lub {values} +@msg global must be true or false global musi mieć wartość true lub false +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) glif {_1} nie jest rysowany dla {ch} przy stylu tego znaku (takim jak Wersaliki, Kapitaliki lub system liczbowy) +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} glif {g} nie jest wariantem {ch} w {_1} {_2} +@msg gradient must be "objects" or "mesh" gradient musi mieć wartość "objects" lub "mesh" +@msg grays must be "profile" or "black", not `{g}` grays musi mieć wartość "profile" lub "black", a nie `{g}` +@msg gridColors must be a preset name or two colours gridColors musi być nazwą ustawienia lub dwoma kolorami +@msg gridSize must be small, medium or large gridSize musi mieć wartość small, medium lub large +@msg guide {g} is not selected linia pomocnicza {g} nie jest zaznaczona +@msg handle must be 0..3 handle musi mieścić się w zakresie 0..3 +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}: "layer" (domyślnie), "omit" lub "visible" +@msg highlight a layer to merge into wyróżnij warstwę, z którą ma nastąpić scalenie +@msg highlight two or more rows in one layer or group wyróżnij co najmniej dwa wiersze w jednej warstwie lub grupie +@msg highlight two or more rows to merge wyróżnij co najmniej dwa wiersze do scalenia +@msg icon docks a panel in the icon column: use window.panel.dock icon dokuje panel w kolumnie ikon: użyj window.panel.dock +@msg id must be a dimension id id musi być identyfikatorem wymiaru +@msg image has no bounds obraz nie ma granic +@msg image {_1} is linked already obraz {_1} jest już połączony +@msg imageMap `{m}`: none, client or server imageMap `{m}`: none, client lub server +@msg index out of range indeks poza zakresem +@msg instances must be 1..1000 instances musi mieścić się w zakresie 1..1000 +@msg invalid `color` nieprawidłowe `color` +@msg invalid anchor index {v}: expected a non-negative integer nieprawidłowy indeks punktu kontrolnego {v}: oczekiwano nieujemnej liczby całkowitej +@msg invalid anchor reference {v}: expected [subpath, anchor] nieprawidłowe odwołanie do punktu kontrolnego {v}: oczekiwano [subpath, anchor] +@msg invalid angle nieprawidłowy kąt +@msg invalid brush: {e} nieprawidłowy pędzel: {e} +@msg invalid color nieprawidłowy kolor +@msg invalid colour {c} nieprawidłowy kolor {c} +@msg invalid delta nieprawidłowa delta +@msg invalid point nieprawidłowy punkt +@msg invalid size nieprawidłowy rozmiar +@msg invalid t nieprawidłowe t +@msg invalid tolerance nieprawidłowa tolerancja +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) wszystkie jego obszary kompozycji leżą poza płótnem (ponad {_1} pt od punktu początkowego) +@msg its clipping masks are nested too deeply jego maski przycinające są zbyt głęboko zagnieżdżone +@msg its metadata has no description jego metadane nie mają opisu +@msg its metadata has no direct length jego metadane nie mają bezpośredniej długości +@msg join two different paths połącz dwie różne ścieżki +@msg justify must be auto|left|center|right|justifyAll justify musi mieć wartość auto|left|center|right|justifyAll +@msg kbps must be a positive number, not {kbps} kbps musi być liczbą dodatnią, a nie {kbps} +@msg keywords must be a list of words or a comma-separated string keywords musi być listą słów lub ciągiem znaków rozdzielanym przecinkami +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters keywords może mieć najwyżej {MAX_KEYWORD} znaków, bez znaków sterujących +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind musi wynosić 1, 2 lub 3, albo podaj `name` ustawienia (patrz perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind musi mieć wartość graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle lub paraStyle +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout musi mieć wartość "on", "off", "neutral" lub wartość logiczną, a nie {v} +@msg layer `{name}` has no art to export warstwa `{name}` nie zawiera grafiki do wyeksportowania +@msg layer must name a layer or sublayer layer musi wskazywać warstwę lub podwarstwę +@msg layers can't be intertwined: select the objects in them warstw nie można przeplatać: zaznacz znajdujące się w nich obiekty +@msg layers go only at the top level or in layers, objects in layers and groups warstwy mogą się znajdować tylko na najwyższym poziomie lub w warstwach, a obiekty w warstwach i grupach +@msg layers must be true or false layers musi mieć wartość true lub false +@msg layout must be gridByRow, gridByColumn, row or column layout musi mieć wartość gridByRow, gridByColumn, row lub column +@msg leading must be a number or "auto" leading musi być liczbą lub wartością "auto" +@msg left/right must be non-negative widths left/right muszą być nieujemnymi szerokościami +@msg level `{t}`: 2 or 3 level `{t}`: 2 lub 3 +@msg line {line}: more than {MAX_CSV_COLS} columns wiersz {line}: więcej niż {MAX_CSV_COLS} kolumn +@msg line {line}: unclosed quote wiersz {line}: niezamknięty cudzysłów +@msg line {line}: value for `{var}` must match its variable kind wiersz {line}: wartość dla `{var}` musi odpowiadać rodzajowi zmiennej +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`: lf lub crlf +@msg loading profiles from disk isn't available in the browser wczytywanie profili z dysku nie jest dostępne w przeglądarce +@msg margin: a {m} pt margin leaves no imageable area on the paper margin: margines {m} pt nie pozostawia na papierze obszaru zadruku +@msg matrix must be finite and invertible, depth finite matrix musi być skończona i odwracalna, a depth skończona +@msg matte: {e} matte: {e} +@msg member is for order front, forward, backward or back member dotyczy order o wartości front, forward, backward lub back +@msg missing `artboards` brak `artboards` +@msg missing `at` [x, y] brak `at` [x, y] +@msg missing `attrs` brak `attrs` +@msg missing `color` or `opacity` brak `color` lub `opacity` +@msg missing `colors` brak `colors` +@msg missing `commands` brak `commands` +@msg missing `destinationFolder` brak `destinationFolder` +@msg missing `effect` id brak identyfikatora `effect` +@msg missing `family` brak `family` +@msg missing `find` brak `find` +@msg missing `gid` (a glyph id, or null for the default glyph) brak `gid` (identyfikator glifu albo null dla glifu domyślnego) +@msg missing `group` brak `group` +@msg missing `id` brak `id` +@msg missing `id`: give one, or select one type object brak `id`: podaj je albo zaznacz jeden obiekt tekstowy +@msg missing `index` (a point index, or null to clear) brak `index` (indeks punktu albo null, aby wyczyścić) +@msg missing `index` (a stop index, or null to clear) brak `index` (indeks punktu koloru albo null, aby wyczyścić) +@msg missing `index` (an item index or null) brak `index` (indeks elementu lub null) +@msg missing `item` brak `item` +@msg missing `library` brak `library` +@msg missing `library` (see graphicStyle.libraries) brak `library` (patrz graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) brak `library` (patrz swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) brak `map` (wiersze [{from, to}] lub {current}) +@msg missing `matrix` [a,b,c,d,e,f] brak `matrix` [a,b,c,d,e,f] +@msg missing `name` (see gradient.presets.list) brak `name` (patrz gradient.presets.list) +@msg missing `offset` (a distance in points) brak `offset` (odległość w punktach) +@msg missing `path` brak `path` +@msg missing `path` (a path object id) brak `path` (identyfikator obiektu ścieżki) +@msg missing `rule` brak `rule` +@msg missing `source` id brak identyfikatora `source` +@msg missing `start` (the byte offset of a character) brak `start` (przesunięcie znaku w bajtach) +@msg missing `status`: linked, embedded, missing or modified brak `status`: linked, embedded, missing lub modified +@msg missing `target` id brak identyfikatora `target` +@msg missing `text` brak `text` +@msg missing `to` brak `to` +@msg missing `to` (an index) brak `to` (indeks) +@msg missing `to`: a non-negative integer brak `to`: nieujemna liczba całkowita +@msg missing `type` brak `type` +@msg missing `value` brak `value` +@msg missing `{key}` brak `{key}` +@msg missing `{k}` brak `{k}` +@msg missing anchor brak punktu kontrolnego +@msg missing anchors brak punktów kontrolnych +@msg missing anchors [[subpath, anchor]…] brak punktów kontrolnych [[subpath, anchor]…] +@msg missing array `{key}` brak tablicy `{key}` +@msg missing case brak case +@msg missing color brak koloru +@msg missing commands brak poleceń +@msg missing destination folder brak folderu docelowego +@msg missing from brak from +@msg missing group brak grupy +@msg missing id or ids brak id lub ids +@msg missing ids brak ids +@msg missing indexes brak indexes +@msg missing matrix [a,b,c,d,e,f] brak matrix [a,b,c,d,e,f] +@msg missing name brak nazwy +@msg missing op brak op +@msg missing or invalid `color` brak `color` lub jest ono nieprawidłowe +@msg missing or unknown `tool` brak `tool` lub jest ono nieznane +@msg missing point brak punktu +@msg missing point [x,y] brak punktu [x,y] +@msg missing point `{key}` ([x, y]) brak punktu `{key}` ([x, y]) +@msg missing points brak punktów +@msg missing points [[x,y]…] brak punktów [[x,y]…] +@msg missing segment brak segmentu +@msg missing stops brak punktów koloru +@msg missing subpaths brak ścieżek podrzędnych +@msg missing target brak celu +@msg missing to brak to +@msg missing x, y brak x, y +@msg mode is set, add or toggle, not `{other}` mode ma wartość set, add lub toggle, a nie `{other}` +@msg mode must be "cmyk" or "rgb" mode musi mieć wartość "cmyk" lub "rgb" +@msg mode must be area|onPath, got {o} mode musi mieć wartość area|onPath, podano {o} +@msg more than 10000 linked files: package dependencies in smaller parts ponad 10000 połączonych plików: pakuj zależności w mniejszych częściach +@msg more than 256 commands więcej niż 256 poleceń +@msg more than {MAX_BATCH_FILES} data sets więcej niż {MAX_BATCH_FILES} zestawów danych +@msg more than {MAX_BATCH_FILES} files więcej niż {MAX_BATCH_FILES} plików +@msg more than {MAX_CSV_ROWS} data rows więcej niż {MAX_CSV_ROWS} wierszy danych +@msg more than {MAX_FILES} images więcej niż {MAX_FILES} obrazów +@msg more than {MAX_IMPORT} presets więcej niż {MAX_IMPORT} ustawień +@msg moved must be [[x,y]…] moved musi mieć postać [[x,y]…] +@msg name (a toolbar's name) is required name (nazwa paska narzędzi) jest wymagane +@msg name (a toolbar's name) must be a string name (nazwa paska narzędzi) musi być ciągiem znaków +@msg name must be a toolbar's name name musi być nazwą paska narzędzi +@msg names is empty names jest puste +@msg names must be an array of symbol names names musi być tablicą nazw symboli +@msg need `anchor` or `segment` + `t` wymagane `anchor` albo `segment` + `t` +@msg need at least one anchor wymagany co najmniej jeden punkt kontrolny +@msg need at least one point wymagany co najmniej jeden punkt +@msg need at least two points wymagane co najmniej dwa punkty +@msg needs points wymaga punktów +@msg nineSlice must be true or false nineSlice musi mieć wartość true lub false +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides musi mieć postać [left, top, right, bottom], każda wartość z zakresu -16..16, przy czym left + right < 1 i top + bottom < 1 +@msg no PDF preset named `{n}` brak ustawienia PDF o nazwie `{n}` +@msg no action set is current żaden zestaw operacji nie jest bieżący +@msg no active data set brak aktywnego zestawu danych +@msg no appearance item {index} brak elementu wyglądu {index} +@msg no artboard {_1} brak obszaru kompozycji {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) brak obszaru kompozycji {artboard} (liczone od 0; dokument ma {_1}) +@msg no asset {id} brak zasobu {id} +@msg no asset {id} (see assets.list) brak zasobu {id} (patrz assets.list) +@msg no assets to export brak zasobów do wyeksportowania +@msg no assets yet: collect art for export first (assets.add) nie ma jeszcze zasobów: najpierw zbierz grafikę do eksportu (assets.add) +@msg no brush named `{n}` brak pędzla o nazwie `{n}` +@msg no character at byte {start} brak znaku w bajcie {start} +@msg no colour brak koloru +@msg no colour group `{group}` brak grupy kolorów `{group}` +@msg no colour group `{name}` brak grupy kolorów `{name}` +@msg no data sets brak zestawów danych +@msg no dataset named `{from}` brak zestawu danych o nazwie `{from}` +@msg no dataset named `{name}` brak zestawu danych o nazwie `{name}` +@msg no datasets brak zestawów danych +@msg no design named `{name}` brak wzoru o nazwie `{name}` +@msg no design named `{n}` brak wzoru o nazwie `{n}` +@msg no design named `{old}` brak wzoru o nazwie `{old}` +@msg no encoder for {_1} yet nie ma jeszcze kodera dla {_1} +@msg no export preset `{name}` (see document.exportPresets.list) brak ustawienia eksportu `{name}` (patrz document.exportPresets.list) +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder brak folderu: przekaż `path` albo ustaw Preferencje › Wtyczki i dyski robocze › Dodatkowy folder wtyczek +@msg no fonts to look for: they are all available brak czcionek do wyszukania: wszystkie są dostępne +@msg no formats to export brak formatów do wyeksportowania +@msg no gradient preset `{name}` (see gradient.presets.list) brak ustawienia gradientu `{name}` (patrz gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` brak stylu graficznego `{n}` w `{_1}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) brak biblioteki stylów graficznych `{key}` (patrz graphicStyle.libraries) +@msg no guide selected nie zaznaczono linii pomocniczej +@msg no guide selected (or give an index) nie zaznaczono linii pomocniczej (albo podaj indeks) +@msg no guide {i} brak linii pomocniczej {i} +@msg no images in the folder brak obrazów w folderze +@msg no item at index {_1} brak elementu o indeksie {_1} +@msg no item at index {from} brak elementu o indeksie {from} +@msg no item at one of the indices {idx} brak elementu o jednym z indeksów {idx} +@msg no layer brak warstwy +@msg no layer {id} (see layer.list) brak warstwy {id} (patrz layer.list) +@msg no layers to export brak warstw do wyeksportowania +@msg no library `{key}` brak biblioteki `{key}` +@msg no metafile format brak formatu metapliku +@msg no object {_1} brak obiektu {_1} +@msg no objects brak obiektów +@msg no parent layer brak warstwy nadrzędnej +@msg no perspective grid preset named `{b}` (see perspective.presets.list) brak ustawienia siatki perspektywy o nazwie `{b}` (patrz perspective.presets.list) +@msg no perspective grid preset named `{n}` brak ustawienia siatki perspektywy o nazwie `{n}` +@msg no point selected (give `index`) nie zaznaczono punktu (podaj `index`) +@msg no preset called `{from}` (see file.newPresets) brak ustawienia o nazwie `{from}` (patrz file.newPresets) +@msg no preset called `{name}` (see file.newPresets) brak ustawienia o nazwie `{name}` (patrz file.newPresets) +@msg no preset named `{n}` brak ustawienia o nazwie `{n}` +@msg no print preset named `{b}` (see print.presets.list) brak ustawienia drukowania o nazwie `{b}` (patrz print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) brak ustawienia drukowania o nazwie `{name}` (ustawienia: {_1}) +@msg no print preset named `{n}` brak ustawienia drukowania o nazwie `{n}` +@msg no profile named `{name}` brak profilu o nazwie `{name}` +@msg no readable files in the folder brak czytelnych plików w folderze +@msg no recovery copy `{f}` (see file.recovery.list) brak kopii odzyskiwania `{f}` (patrz file.recovery.list) +@msg no rows brak wierszy +@msg no saved PDF preset named `{name}` brak zapisanego ustawienia PDF o nazwie `{name}` +@msg no saved export preset `{name}` brak zapisanego ustawienia eksportu `{name}` +@msg no saved gradient preset called {name} brak zapisanego ustawienia gradientu o nazwie {name} +@msg no saved gradient presets brak zapisanych ustawień gradientu +@msg no saved preset called `{name}` brak zapisanego ustawienia o nazwie `{name}` +@msg no saved presets to export (name [Default] in `names` to export it) brak zapisanych ustawień do wyeksportowania (wymień [Default] w `names`, aby je wyeksportować) +@msg no saved presets to export (name built-in ones in `names`) brak zapisanych ustawień do wyeksportowania (wbudowane wymień w `names`) +@msg no saved print preset named `{name}` brak zapisanego ustawienia drukowania o nazwie `{name}` +@msg no saved profile called {name} brak zapisanego profilu o nazwie {name} +@msg no saved selection `{name}` brak zapisanego zaznaczenia `{name}` +@msg no saved theme called {name} (see colorTheme.list) brak zapisanego motywu o nazwie {name} (patrz colorTheme.list) +@msg no saved views brak zapisanych widoków +@msg no series {i} brak serii {i} +@msg no slice {n} in the image (see slice.list) brak plasterka {n} w obrazie (patrz slice.list) +@msg no source art brak grafiki źródłowej +@msg no stop {i} (the gradient has {n}) brak punktu koloru {i} (gradient ma {n}) +@msg no style `{name}` brak stylu `{name}` +@msg no such assets (see assets.list) brak takich zasobów (patrz assets.list) +@msg no such frame anchor, or the frame would reach past the canvas brak takiego punktu kontrolnego ramki albo ramka wykroczyłaby poza płótno +@msg no such objects brak takich obiektów +@msg no such path anchor, or the path would reach past the canvas brak takiego punktu kontrolnego ścieżki albo ścieżka wykroczyłaby poza płótno +@msg no such rows brak takich wierszy +@msg no swatch `{keep}` brak próbki `{keep}` +@msg no swatch `{n}` brak próbki `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) brak próbki `{n}` (próbki i grupy kolorów przenosi się osobno) +@msg no swatch library `{key}` (see swatch.library.list) brak biblioteki próbek `{key}` (patrz swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) brak biblioteki próbek `{key}` (patrz swatch.library.list; "document": próbki dokumentu) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) brak biblioteki próbek `{key}` (patrz swatch.library.list; "{DOCUMENT_SWATCHES}": próbki dokumentu) +@msg no swatch named `{n}` brak próbki o nazwie `{n}` +@msg no swatch or colour group `{n}` brak próbki lub grupy kolorów `{n}` +@msg no swatch or colour group `{n}` in `{_1}` brak próbki lub grupy kolorów `{n}` w `{_1}` +@msg no symbol named `{name}` brak symbolu o nazwie `{name}` +@msg no text objects selected nie zaznaczono obiektów tekstowych +@msg no toolbar named `{_1}` brak paska narzędzi o nazwie `{_1}` +@msg no toolbar named `{name}` brak paska narzędzi o nazwie `{name}` +@msg no tracing preset `{preset}` (see imageTrace.presets) brak ustawienia obrysu `{preset}` (patrz imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) nie ma tu folderu bibliotek użytkownika (zapisz, podając ścieżkę, albo bez niej, aby otrzymać dane) +@msg no variable named `{from}` brak zmiennej o nazwie `{from}` +@msg no variable named `{name}` brak zmiennej o nazwie `{name}` +@msg no variable named `{var}` brak zmiennej o nazwie `{var}` +@msg no variable named `{v}` brak zmiennej o nazwie `{v}` +@msg no view named `{name}` brak widoku o nazwie `{name}` +@msg node {_1} is not a graph węzeł {_1} nie jest wykresem +@msg node {_1} is not an image węzeł {_1} nie jest obrazem +@msg node {_1} is not type węzeł {_1} nie jest tekstem +@msg not a PDF presets file (format `{_1}`) to nie jest plik ustawień PDF (format `{_1}`) +@msg not a PDF presets file: {e} to nie jest plik ustawień PDF: {e} +@msg not a Word document: its WordDocument stream doesn't start with a file information block to nie jest dokument Word: jego strumień WordDocument nie zaczyna się od bloku informacji o pliku +@msg not a Word document: its compound file has no WordDocument stream to nie jest dokument Word: jego plik złożony nie ma strumienia WordDocument +@msg not a Word document: its main part is not a WordprocessingML document to nie jest dokument Word: jego część główna nie jest dokumentem WordprocessingML +@msg not a Word document: its package has no main document part to nie jest dokument Word: jego pakiet nie ma głównej części dokumentu +@msg not a closed path to nie jest ścieżka zamknięta +@msg not a flattener presets file (format `{_1}`) to nie jest plik ustawień spłaszczania (format `{_1}`) +@msg not a flattener presets file: {e} to nie jest plik ustawień spłaszczania: {e} +@msg not a layer or group to nie jest warstwa ani grupa +@msg not a library file: {e} to nie jest plik biblioteki: {e} +@msg not a mesh envelope point to nie jest punkt koperty siatkowej +@msg not a perspective grid presets file (format `{_1}`) to nie jest plik ustawień siatki perspektywy (format `{_1}`) +@msg not a perspective grid presets file: {e} to nie jest plik ustawień siatki perspektywy: {e} +@msg not a print presets file (format `{_1}`) to nie jest plik ustawień drukowania (format `{_1}`) +@msg not a print presets file: {e} to nie jest plik ustawień drukowania: {e} +@msg not a stored or deflated central record to nie jest rekord centralny zapisany bez kompresji ani skompresowany metodą deflate +@msg not a valid SVGZ file: {e} to nie jest prawidłowy plik SVGZ: {e} +@msg not an RTF file: it doesn't start with {\\rtf to nie jest plik RTF: nie zaczyna się od {\\rtf +@msg not an image VectorCraft reads (see document.formats) to nie jest obraz odczytywany przez VectorCraft (patrz document.formats) +@msg not area type (text in a frame) outside perspective to nie jest tekst akapitowy (tekst w ramce) poza perspektywą +@msg not editing an opacity mask maska krycia nie jest edytowana +@msg not type on a path outside perspective to nie jest tekst na ścieżce poza perspektywą +@msg nothing prints nic nie jest drukowane +@msg nothing to bind nie ma czego powiązać +@msg nothing to change nie ma czego zmienić +@msg nothing to change: give dx, dy, hScale, vScale or rotation nie ma czego zmienić: podaj dx, dy, hScale, vScale lub rotation +@msg nothing to crop nie ma czego kadrować +@msg nothing to export nie ma czego eksportować +@msg nothing to export: the document has no visible art nie ma czego eksportować: dokument nie zawiera widocznej grafiki +@msg nothing to mark nie ma czego oznaczać +@msg nothing to print: every artboard picked is blank nie ma czego drukować: każdy wybrany obszar kompozycji jest pusty +@msg nothing to print: the document has no art that prints nie ma czego drukować: dokument nie zawiera grafiki przeznaczonej do druku +@msg object is not a path obiekt nie jest ścieżką +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object przezroczystości obiektu nie można próbkować na wyróżnione znaki; wyłącz Przezroczystość w Opcjach kroplomierza albo zaznacz cały obiekt +@msg object {_1} can't be set as point type obiektu {_1} nie można ustawić jako tekstu punktowego +@msg object {_1} has lost its file's contents: relink it obiekt {_1} utracił zawartość swojego pliku: połącz go ponownie +@msg object {_1} has no path obiekt {_1} nie ma ścieżki +@msg object {_1} has no size obiekt {_1} nie ma rozmiaru +@msg object {_1} is hidden or locked, or on a locked artboard obiekt {_1} jest ukryty lub zablokowany albo znajduje się na zablokowanym obszarze kompozycji +@msg object {_1} is not a placed document obiekt {_1} nie jest umieszczonym dokumentem +@msg object {_1} is not an Intertwine group obiekt {_1} nie jest grupą przeplatania +@msg object {_1} is not an image obiekt {_1} nie jest obrazem +@msg object {_1} is not an image or a linked file obiekt {_1} nie jest obrazem ani połączonym plikiem +@msg object {_1} is not an image or a placed document obiekt {_1} nie jest obrazem ani umieszczonym dokumentem +@msg object {_1} is not in this Intertwine group obiekt {_1} nie należy do tej grupy przeplatania +@msg object {_1} needs a 3D effect at index {index} obiekt {_1} wymaga efektu 3D o indeksie {index} +@msg object {id} has anchors 0–{_1} for `corners`, not {past} obiekt {id} ma dla `corners` punkty kontrolne 0–{_1}, a nie {past} +@msg object {id} has {n} anchors, so no anchor {past} obiekt {id} ma {n} punktów kontrolnych, więc nie ma punktu {past} +@msg objects must be none, move or copy objects musi mieć wartość none, move lub copy +@msg objects were left out pominięto obiekty +@msg offset out of range przesunięcie poza zakresem +@msg on must be true or false on musi mieć wartość true lub false +@msg only groups, layers, type and images have a Contents (Characters) row tylko grupy, warstwy, tekst i obrazy mają wiersz Zawartość (Znaki) +@msg only interior mesh lines can be deleted usuwać można tylko wewnętrzne linie siatki +@msg only solid colours can be added (not gradients or patterns) dodawać można tylko kolory jednolite (nie gradienty ani wzorki) +@msg only solid-colour swatches can be spot colours kolorami dodatkowymi mogą być tylko próbki kolorów jednolitych +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first udało się odczytać tylko osadzony podgląd tego pliku Affinity: użyj polecenia Plik › Otwórz, aby zobaczyć go wraz z ostrzeżeniem, albo najpierw wyeksportuj SVG lub PDF z programu Affinity +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning udało się odczytać tylko osadzony podgląd tego pliku Affinity; użyj polecenia Plik › Otwórz, aby zobaczyć go wraz z ostrzeżeniem +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto, above, below, icon i miejsce w kolumnie należą do window.panel.float i window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto, above, below, icon i miejsce w kolumnie nie przyjmują anchor, side, zone ani before +@msg onto, above, below, icon and a column place take no x, y, width or height onto, above, below, icon i miejsce w kolumnie nie przyjmują x, y, width ani height +@msg open a document to look for its linked files otwórz dokument, aby wyszukać jego połączone pliki +@msg options must be an object (see file.formatOptions) options musi być obiektem (patrz file.formatOptions) +@msg order must be leftToRight or rightToLeft order musi mieć wartość leftToRight lub rightToLeft +@msg orientation must be portrait or landscape, not `{o}` orientation musi mieć wartość portrait lub landscape, a nie `{o}` +@msg outlineText {v}: true or false outlineText {v}: true lub false +@msg output intent profile: {e} profil docelowych warunków wyjściowych: {e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination: żaden profil RGB ani CMYK nie nosi nazwy „{name}” +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination: pliki {_1} są w CMYK, a „{name}” to profil RGB +@msg output.destination: {e} output.destination: {e} +@msg output.inks: no ink is set to print output.inks: żadna farba nie jest ustawiona do druku +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent: żaden profil nie nosi nazwy „{name}”, a docelowe warunki wyjściowe {std} osadzają swój profil +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent: żaden profil nie nosi nazwy „{name}”: docelowe warunki wyjściowe {std} osadzają profil albo wskazują zarejestrowane warunki drukowania (outputConditionId i registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent: pliki {std} są drukowane w CMYK, a „{name}” to profil RGB +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent: „{name}” to profil szarości, który nie może stanowić docelowych warunków wyjściowych {std} +@msg overlap: {o} pt is half a tile or more overlap: {o} pt to co najmniej połowa kafelka +@msg overlapping Shaper paths must be in the same layer or group nakładające się ścieżki narzędzia Kształtowanie muszą znajdować się w tej samej warstwie lub grupie +@msg overlapping edits nakładające się edycje +@msg page art conflicts with image data for {key} grafika strony jest sprzeczna z danymi obrazu dla {key} +@msg page art conflicts with pattern {name} grafika strony jest sprzeczna z wzorkiem {name} +@msg page art conflicts with swatch {name} grafika strony jest sprzeczna z próbką {name} +@msg page art is missing image data for {key} w grafice strony brakuje danych obrazu dla {key} +@msg page art is missing pattern {name} w grafice strony brakuje wzorka {name} +@msg page art is missing swatch {name} w grafice strony brakuje próbki {name} +@msg page must be a whole number from 1 page musi być liczbą całkowitą od 1 +@msg page thumbnail: {e} miniatura strony: {e} +@msg page {page} of the PDF has no objects strona {page} pliku PDF nie zawiera obiektów +@msg page {page}: `{file}` has {n} {artboards} strona {page}: `{file}` ma {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} strona {page}: `{name}` ma {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages musi być zakresem, na przykład "2-3", albo numerem strony, a nie {v} +@msg pages: {e} pages: {e} +@msg panel position must be finite położenie panelu musi być skończone +@msg parameters must be an object parametry muszą być obiektem +@msg params must be an object params musi być obiektem +@msg passwords can have up to {MAX_PASSWORD} bytes hasła mogą mieć najwyżej {MAX_PASSWORD} bajtów +@msg path has no open end ścieżka nie ma otwartego końca +@msg paths must be strings paths musi zawierać ciągi znaków +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false wymaga wszystkich obszarów kompozycji, aby zachować edytowalną grafikę +@msg pdfs `{m}`: single or multiple pdfs `{m}`: single lub multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom ma wartość "artwork" lub "swatches", a nie {v} +@msg pins and moved must be non-empty lists of the same length pins i moved muszą być niepustymi listami tej samej długości +@msg pins must be [[x,y]…] pins musi mieć postać [[x,y]…] +@msg pins must be finite pins musi zawierać wartości skończone +@msg place is above, below or inside, not `{other}` place ma wartość above, below lub inside, a nie `{other}` +@msg plane must be left|right|ground plane musi mieć wartość left|right|ground +@msg plane must be left|right|ground|none plane musi mieć wartość left|right|ground|none +@msg point is outside the mesh punkt leży poza siatką +@msg points must be [t, left, right] with non-negative widths points musi mieć postać [t, left, right] z nieujemnymi szerokościami +@msg points must be a non-empty [[x,y,pressure?]…] list points musi być niepustą listą [[x,y,pressure?]…] +@msg points must be a non-empty list of finite [x,y] points musi być niepustą listą skończonych wartości [x,y] +@msg points must be an array or null points musi być tablicą lub null +@msg points must be finite points musi zawierać wartości skończone +@msg points must be finite [x,y] pairs points musi zawierać skończone pary [x,y] +@msg points must contain 1–4096 coordinates points musi zawierać od 1 do 4096 współrzędnych +@msg position must be normal, superscript or subscript position musi mieć wartość normal, superscript lub subscript +@msg position must be topLeft, topRight, bottomLeft or bottomRight position musi mieć wartość topLeft, topRight, bottomLeft lub bottomRight +@msg positions must be between 0 and 100000 pt positions musi mieścić się w zakresie od 0 do 100000 pt +@msg ppi must be a positive number, not {ppi} ppi musi być liczbą dodatnią, a nie {ppi} +@msg ppi must be between 1 and 2400 ppi musi mieścić się w zakresie od 1 do 2400 +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` nie jest jedną z wartości transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape musi mieć wartość clippingMask|transparency +@msg preset `{id}`: mobile or density preset `{id}`: mobile lub density +@msg preset must be standard, diffuse, topLeft or right preset musi mieć wartość standard, diffuse, topLeft lub right +@msg preset must be the name of a print preset preset musi być nazwą ustawienia drukowania +@msg previewMode must be default, pixel or overprint previewMode musi mieć wartość default, pixel lub overprint +@msg print settings: {e} ustawienia drukowania: {e} +@msg procedures nest too deeply procedury są zbyt głęboko zagnieżdżone +@msg procedures nested too deeply zbyt głęboko zagnieżdżone procedury +@msg quotes must be {double?, single?} quotes musi mieć postać {double?, single?} +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} musi zawierać cudzysłów otwierający i zamykający, np. "“”" +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi musi wynosić 1–2400 (72 screen, 150 medium, 300 high) +@msg rating must be a whole number from 0 to {MAX_RATING} rating musi być liczbą całkowitą od 0 do {MAX_RATING} +@msg rect and {k} contradict each other: give the area or the artboards rect i {k} wykluczają się wzajemnie: podaj obszar albo obszary kompozycji +@msg rect applies to raster formats, not {_1} rect dotyczy formatów rastrowych, a nie {_1} +@msg rect is [x, y, width, height] in points rect ma postać [x, y, width, height] w punktach +@msg rect must be [x, y, width, height] rect musi mieć postać [x, y, width, height] +@msg rect must be [x, y, width, height] with a positive width and height rect musi mieć postać [x, y, width, height] z dodatnią szerokością i wysokością +@msg rect needs finite numbers and a positive width and height rect wymaga liczb skończonych oraz dodatniej szerokości i wysokości +@msg redraw needs id redraw wymaga id +@msg redraw within must be a finite distance of 0 or more within w redraw musi być skończoną odległością równą 0 lub większą +@msg registration must be 0..8 registration musi mieścić się w zakresie 0..8 +@msg release a layer or a group uwolnij warstwę lub grupę +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace zachowuje położenie i przekształcenie zastępowanego obiektu: pomiń template, at i rect +@msg replace: select the one object to replace replace: zaznacz jeden obiekt do zastąpienia +@msg resolution must be a number or screen|medium|high resolution musi być liczbą lub mieć wartość screen|medium|high +@msg resolution must be between 1 and 2400 ppi resolution musi mieścić się w zakresie od 1 do 2400 ppi +@msg rotation must be a number of degrees rotation musi być liczbą stopni +@msg rotation must be finite rotation musi być wartością skończoną +@msg rows and cols must be 1..50 rows i cols muszą mieścić się w zakresie 1..50 +@msg rows and columns must be 1..500 rows i columns muszą mieścić się w zakresie 1..500 +@msg rows of objects whose appearances differ can't be reordered together wierszy obiektów o różnym wyglądzie nie można razem zmieniać kolejności +@msg save the document first: Package collects a saved document najpierw zapisz dokument: Pakiet zbiera zapisany dokument +@msg save the document first: copy puts the file in its folder najpierw zapisz dokument: copy umieszcza plik w jego folderze +@msg savedPreset: {e} savedPreset: {e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`: współczynnik, na przykład 2x, szerokość (100w), wysokość (100h) lub rozdzielczość (72ppi) +@msg scale must be a number above 0, at most 64 scale musi być liczbą większą od 0 i nie większą niż 64 +@msg scale must be non-zero scale musi być różne od zera +@msg scope must be selection or all (got {other}) scope musi mieć wartość selection lub all (podano {other}) +@msg scribble is too long gryzmoł jest zbyt długi +@msg segment does not exist segment nie istnieje +@msg select a Shaper group zaznacz grupę narzędzia Kształtowanie +@msg select a blend to set easing or color shift zaznacz przejście, aby ustawić wygładzanie tempa lub przesunięcie koloru +@msg select a compound shape zaznacz kształt złożony +@msg select a graph zaznacz wykres +@msg select a group or layer with visible, unlocked members zaznacz grupę lub warstwę z widocznymi, odblokowanymi elementami +@msg select a layer zaznacz warstwę +@msg select a text object zaznacz obiekt tekstowy +@msg select an image (or an Image Trace group) zaznacz obraz (lub grupę obrysu obrazu) +@msg select an image, or pass id zaznacz obraz albo przekaż id +@msg select an object (or pass `id`) zaznacz obiekt (albo przekaż `id`) +@msg select an object with a freeform gradient zaznacz obiekt z gradientem dowolnym +@msg select an object with an opacity mask zaznacz obiekt z maską krycia +@msg select area type zaznacz tekst akapitowy +@msg select area type (text in a frame) zaznacz tekst akapitowy (tekst w ramce) +@msg select area type and closed paths (at least one area type object) zaznacz tekst akapitowy i ścieżki zamknięte (co najmniej jeden obiekt tekstu akapitowego) +@msg select area type whose first line can fit its frame zaznacz tekst akapitowy, którego pierwszy wiersz mieści się w ramce +@msg select artwork to add its colours zaznacz grafikę, aby dodać jej kolory +@msg select artwork to make a group of its colours zaznacz grafikę, aby utworzyć grupę z jej kolorów +@msg select artwork to recolour, or give a colour `group` zaznacz grafikę do zmiany kolorów albo podaj `group` kolorów +@msg select exactly two path ends to join zaznacz dokładnie dwa końce ścieżek do połączenia +@msg select members of a compound shape zaznacz elementy kształtu złożonego +@msg select objects or give ids zaznacz obiekty albo podaj ids +@msg select objects to align to the guide zaznacz obiekty do wyrównania do linii pomocniczej +@msg select one Intertwine group zaznacz jedną grupę przeplatania +@msg select one image or pass id zaznacz jeden obraz albo przekaż id +@msg select paths zaznacz ścieżki +@msg select paths or compound paths zaznacz ścieżki lub ścieżki złożone +@msg select rows or objects to collect zaznacz wiersze lub obiekty do zebrania +@msg select something to export zaznacz coś do wyeksportowania +@msg select something to export (or turn off selectedOnly) zaznacz coś do wyeksportowania (albo wyłącz selectedOnly) +@msg select text to redefine the style from zaznacz tekst, na podstawie którego styl ma zostać zdefiniowany ponownie +@msg select the art and, on top of it, the mask object zaznacz grafikę oraz leżący nad nią obiekt maski +@msg select the art for the design zaznacz grafikę dla wzoru +@msg select the art for the pattern tile zaznacz grafikę dla kafelka wzorka +@msg select the art to add first najpierw zaznacz grafikę do dodania +@msg select the art to collect (or give its ids) zaznacz grafikę do zebrania (albo podaj jej identyfikatory) +@msg select the content and a path on top zaznacz zawartość oraz leżącą nad nią ścieżkę +@msg select the images to relink, or pass ids zaznacz obrazy do ponownego połączenia albo przekaż ids +@msg select the images, or pass ids zaznacz obrazy albo przekaż ids +@msg select the linked images or placed documents, or pass ids zaznacz połączone obrazy lub umieszczone dokumenty albo przekaż ids +@msg select the object to copy from zaznacz obiekt, z którego ma nastąpić kopiowanie +@msg select the objects (or give their ids) zaznacz obiekty (albo podaj ich identyfikatory) +@msg select the objects to attach to the path zaznacz obiekty do dołączenia do ścieżki +@msg select the objects to bind zaznacz obiekty do powiązania +@msg select two or more objects to intertwine zaznacz co najmniej dwa obiekty do przeplecenia +@msg select two or more slices to combine zaznacz co najmniej dwa plasterki do połączenia +@msg select type on a path zaznacz tekst na ścieżce +@msg selection `{m}`: one or each selection `{m}`: one lub each +@msg settings is an object of document.exportForScreens params settings to obiekt z parametrami document.exportForScreens +@msg settings must be an object settings musi być obiektem +@msg show `{show}`: all, missing, modified or embedded show `{show}`: all, missing, modified lub embedded +@msg show must be true or false show musi mieć wartość true lub false +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size musi mieć wartość "small", "medium", "large" lub być liczbą punktów większą od 0 i nie większą niż {_1} +@msg size must be positive size musi być wartością dodatnią +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode ma wartość "fixed" lub "pressure", a nie `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode ma wartość "fixed" lub "pressure", a nie {other} +@msg sizeVariation must be 0 or more sizeVariation musi wynosić 0 lub więcej +@msg smallCaps must be true or false smallCaps musi mieć wartość true lub false +@msg sort `{x}`: name, kind or status sort `{x}`: name, kind lub status +@msg source paths are too large for Shaper ścieżki źródłowe są zbyt duże dla narzędzia Kształtowanie +@msg spot, mode and group apply to solid colours only spot, mode i group dotyczą tylko kolorów jednolitych +@msg status `{s}`: linked, embedded, missing or modified status `{s}`: linked, embedded, missing lub modified +@msg steps must be 1..1000 steps musi mieścić się w zakresie 1..1000 +@msg steps must be 1..{MAX_STEPS} steps musi mieścić się w zakresie 1..{MAX_STEPS} +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) punkt koloru {i}: `plates` musi zawierać cztery liczby od 0 do 1 (C, M, Y, K) +@msg stopped before the archive was finished zatrzymano przed ukończeniem archiwum +@msg strokeMode must be within|along|across, got {m} strokeMode musi mieć wartość within|along|across, podano {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions musi być obiektem z opcjami stroke.set +@msg strokeWidth must be a positive number of pixels strokeWidth musi być dodatnią liczbą pikseli +@msg subpath {si} is not an open subpath ścieżka podrzędna {si} nie jest otwartą ścieżką podrzędną +@msg subpaths required when joining the same path subpaths jest wymagane przy łączeniu tej samej ścieżki +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict musi mieć wartość "merge" lub "add", a nie {v} +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text musi mieć postać {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} +@msg text transform is collapsed or unrepresentable przekształcenie tekstu jest zdegenerowane lub nie da się go przedstawić +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces musi być liczbą całkowitą od 2 do 100 +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`: użyj "{_1}" lub "{_2}" +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage: podaj {path} lub {name, dataBase64} pliku obrazu albo teksturę w postaci zwracanej przez effect.list +@msg textureImage: {e} textureImage: {e} +@msg that makes more than {MAX_PIECES} slices to daje więcej niż {MAX_PIECES} plasterków +@msg that object is not in the overlap ten obiekt nie znajduje się w obszarze nakładania +@msg the Affinity document has no page with a size dokument Affinity nie ma strony o określonym rozmiarze +@msg the CMYK image couldn't be written nie udało się zapisać obrazu CMYK +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected ustawienia zabezpieczeń PDF zostały zapisane bez haseł: podaj ponownie openPassword lub permissionsPassword albo security bez passwordRequired, aby wyeksportować bez zabezpieczeń +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) plik RTF ma zbyt wiele akapitów lub zmian formatowania, aby umieścić go jako tekst (najwyżej {MAX_PARAGRAPHS} akapitów i {MAX_RUNS} ciągów formatowania) +@msg the RTF file is too large to place as type (at most {_1} MB) plik RTF jest zbyt duży, aby umieścić go jako tekst (najwyżej {_1} MB) +@msg the SVG expands to more than {0} nested SVG images SVG rozwija się do ponad {0} zagnieżdżonych obrazów SVG +@msg the SVG expands to more than {0} objects SVG rozwija się do ponad {0} obiektów +@msg the SVG has no drawable objects SVG nie zawiera obiektów, które można narysować +@msg the SVGZ file unpacks to more than {_1} MB plik SVGZ po rozpakowaniu ma ponad {_1} MB +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place dokument Word ma ponad {MAX_PAGES} stron właściwości, zbyt wiele, aby go umieścić +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place dokument Word ma ponad {MAX_RUNS} ciągów właściwości znaków lub akapitów, zbyt wiele, aby go umieścić +@msg the Word document has more than {_1} MB of formatting properties, too many to place dokument Word ma ponad {_1} MB właściwości formatowania, zbyt wiele, aby go umieścić +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) dokument Word ma zbyt dużo tekstu lub formatowania, aby umieścić go jako tekst (najwyżej {MAX_PARAGRAPHS} akapitów, {MAX_RUNS} ciągów formatowania i {_1} MB tekstu) +@msg the Word document has too much text to place as type (at most {_1} MB) dokument Word ma zbyt dużo tekstu, aby umieścić go jako tekst (najwyżej {_1} MB) +@msg the Word document is encrypted or protected with a password; save it without one to place it dokument Word jest zaszyfrowany lub chroniony hasłem; zapisz go bez hasła, aby go umieścić +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) dokument Word jest zbyt duży, aby go umieścić (jego strumień {name} zawiera ponad {_1} MB) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off dokument Word został ostatnio zapisany z użyciem szybkiego zapisywania, którego VectorCraft nie odczytuje; zapisz go ponownie z wyłączonym szybkim zapisywaniem +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document dokument Word został zapisany w programie Word 6.0 lub Word 95, których VectorCraft nie odczytuje; zapisz go jako dokument Word 97-2003 +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place tekst dokumentu Word składa się z ponad {MAX_PIECES} części, zbyt wielu, aby go umieścić +@msg the action set ids are not a list identyfikatory zestawów operacji nie są listą +@msg the active paint is not a gradient aktywne malowanie nie jest gradientem +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size obszary kompozycji nie mieszczą się na płótnie: użyj mniejszej liczby obszarów, mniejszych odstępów lub mniejszego rozmiaru +@msg the character's size can't be measured nie można zmierzyć rozmiaru znaku +@msg the compound shape is too complex to expand kształt złożony jest zbyt skomplikowany, aby go rozszerzyć +@msg the container of the listed objects is gone kontener wymienionych obiektów już nie istnieje +@msg the copy `{user_id}` can't be read as a library kopii `{user_id}` nie można odczytać jako biblioteki +@msg the copy would leave the canvas kopia wykroczyłaby poza płótno +@msg the cut cannot be made on this art: {e} na tej grafice nie można wykonać cięcia: {e} +@msg the data isn't an EMF or WMF picture dane nie są ilustracją EMF ani WMF +@msg the data isn't an EPS or PostScript picture dane nie są ilustracją EPS ani PostScript +@msg the deferred paste is no longer available odroczone wklejanie nie jest już dostępne +@msg the design is too big to draw at every data point of this graph wzór jest zbyt duży, aby narysować go w każdym punkcie danych tego wykresu +@msg the design is too big to draw in every column of this graph wzór jest zbyt duży, aby narysować go w każdej kolumnie tego wykresu +@msg the dialog it was opened over is no longer open okno dialogowe, nad którym zostało otwarte, nie jest już otwarte +@msg the dictionary stack overflowed stos słowników został przepełniony +@msg the editing data's hash has no place na skrót danych edycyjnych nie ma miejsca +@msg the entities the document declares expand to too much text encje deklarowane przez dokument rozwijają się do zbyt dużej ilości tekstu +@msg the envelope's content has no bounds zawartość koperty nie ma granic +@msg the envelope's mesh is malformed siatka koperty jest nieprawidłowo zbudowana +@msg the file holds no `variableSets` element plik nie zawiera elementu `variableSets` +@msg the file is larger than {_1} MB plik jest większy niż {_1} MB +@msg the file is too large plik jest zbyt duży +@msg the file was updated or encrypted already plik został już zaktualizowany lub zaszyfrowany +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) pierwszy znak wiersza nie ma przed sobą znaku, względem którego można ustawić kerning: przesuwa się tylko w górę i w dół (dy) +@msg the font {_1} is not available czcionka {_1} nie jest dostępna +@msg the format has no file extension format nie ma rozszerzenia pliku +@msg the graphic can't be read: {e} nie można odczytać grafiki: {e} +@msg the graphic is empty grafika jest pusta +@msg the graphic's data is damaged dane grafiki są uszkodzone +@msg the graphic's document has no artboard dokument grafiki nie ma obszaru kompozycji +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them nagłówek `{cell}` dla `{name}` zostałby odczytany jako zmienna `{other}`: zmień nazwę jednego z nich +@msg the image `{path}` isn't followed: {r} obraz `{path}` nie jest pobierany: {r} +@msg the image has no embedded pixels obraz nie ma osadzonych pikseli +@msg the image is empty obraz jest pusty +@msg the image is fully transparent obraz jest całkowicie przezroczysty +@msg the image is not on an artboard; pass rect obraz nie znajduje się na obszarze kompozycji; przekaż rect +@msg the image's pixels are missing brakuje pikseli obrazu +@msg the image's pixels aren't in the document piksele obrazu nie znajdują się w dokumencie +@msg the image's pixels can't be read: {e} nie można odczytać pikseli obrazu: {e} +@msg the image's transparency doesn't fit it przezroczystość obrazu do niego nie pasuje +@msg the job would print more than {MAX_PAGES} pages zadanie wydrukowałoby ponad {MAX_PAGES} stron +@msg the job would print more than {_1} pages zadanie wydrukowałoby ponad {_1} stron +@msg the layers hold no art to mask warstwy nie zawierają grafiki do zamaskowania +@msg the layout moved układ uległ przesunięciu +@msg the library has a group `{name}` already biblioteka ma już grupę `{name}` +@msg the library has no group `{name}` biblioteka nie ma grupy `{name}` +@msg the library has no item `{item}` biblioteka nie ma elementu `{item}` +@msg the library has no item `{other}` biblioteka nie ma elementu `{other}` +@msg the library holds {MAX_GROUPS} groups already biblioteka zawiera już {MAX_GROUPS} grup +@msg the library holds {MAX_ITEMS} items of this kind already biblioteka zawiera już {MAX_ITEMS} elementów tego rodzaju +@msg the name is empty nazwa jest pusta +@msg the object has no fill, stroke or effect obiekt nie ma wypełnienia, obrysu ani efektu +@msg the object has no graphic style (pass `name`) obiekt nie ma stylu graficznego (przekaż `name`) +@msg the objects don't match the plan obiekty nie odpowiadają planowi +@msg the open and permissions passwords must differ hasło otwarcia i hasło uprawnień muszą się różnić +@msg the operand stack overflowed stos operandów został przepełniony +@msg the page has no area strona nie ma powierzchni +@msg the page's type in an unread object's box isn't in its page art tekstu strony w polu nieodczytanego obiektu nie ma w grafice tej strony +@msg the paint behind the proxy is not a gradient malowanie wskazywane przez wskaźnik wypełnienia lub obrysu nie jest gradientem +@msg the parent must be a layer or sublayer elementem nadrzędnym musi być warstwa lub podwarstwa +@msg the path can't be extended ścieżki nie można przedłużyć +@msg the path can't be inside one of the objects to attach ścieżka nie może znajdować się wewnątrz jednego z dołączanych obiektów +@msg the path has no segments to measure ścieżka nie ma segmentów do zmierzenia +@msg the path is empty ścieżka jest pusta +@msg the path is too short to shorten that much ścieżka jest zbyt krótka, aby skrócić ją o tyle +@msg the picture has no objects ilustracja nie zawiera obiektów +@msg the program runs too long program działa zbyt długo +@msg the program uses too much memory program zużywa zbyt dużo pamięci +@msg the rectangle must have a width and a height prostokąt musi mieć szerokość i wysokość +@msg the rectangle needs a size and must lie on the canvas prostokąt wymaga rozmiaru i musi leżeć na płótnie +@msg the selected art has no width zaznaczona grafika nie ma szerokości +@msg the selection draws nothing zaznaczenie niczego nie rysuje +@msg the selection has no area zaznaczenie nie ma powierzchni +@msg the selection has no art to export zaznaczenie nie zawiera grafiki do wyeksportowania +@msg the selection has no bounds zaznaczenie nie ma granic +@msg the selection has no graphic style zaznaczenie nie ma stylu graficznego +@msg the slice would leave the canvas plasterek wykroczyłby poza płótno +@msg the spot colour couldn't be added nie udało się dodać koloru dodatkowego +@msg the subpath has no ends to extend (it is closed or a single point) ścieżka podrzędna nie ma końców do przedłużenia (jest zamknięta lub jest pojedynczym punktem) +@msg the symbols are nested too deeply to expand their instances symbole są zbyt głęboko zagnieżdżone, aby rozszerzyć ich wystąpienia +@msg the text file is too large to place as type (at most {_1} MB) plik tekstowy jest zbyt duży, aby umieścić go jako tekst (najwyżej {_1} MB) +@msg the text is empty tekst jest pusty +@msg the text is over {_1} MB tekst ma ponad {_1} MB +@msg the top object must be a path górny obiekt musi być ścieżką +@msg the trace was cancelled obrys został anulowany +@msg the trailer has no size sekcja trailer nie ma rozmiaru +@msg the type object is flattened to nothing po spłaszczeniu z obiektu tekstowego nic nie pozostaje +@msg the value of axis `{tag}` must be a number or null wartość osi `{tag}` musi być liczbą lub null +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets biblioteka zmiennych deklaruje więcej niż {MAX_LIBRARY_DATASETS} zestawów danych +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities biblioteka zmiennych deklaruje więcej niż {MAX_LIBRARY_ENTITIES} encji +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables biblioteka zmiennych deklaruje więcej niż {MAX_LIBRARY_VARIABLES} zmiennych +@msg the variable library declares no variables biblioteka zmiennych nie deklaruje żadnych zmiennych +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes biblioteka zmiennych jest większa niż {MAX_LIBRARY_BYTES} bajtów +@msg the variable library is not UTF-8 biblioteka zmiennych nie jest w kodowaniu UTF-8 +@msg the variable library is not well-formed XML: {e} biblioteka zmiennych nie jest poprawnie sformułowanym kodem XML: {e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep biblioteka zmiennych zagnieżdża elementy głębiej niż na {MAX_LIBRARY_NESTING} poziomów +@msg the variable library's document type declaration holds something other than entities of plain text deklaracja typu dokumentu biblioteki zmiennych zawiera coś innego niż encje zwykłego tekstu +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes encje biblioteki zmiennych rozwijają się do ponad {MAX_LIBRARY_BYTES} bajtów +@msg the window has no dock okno nie ma doku +@msg the written PDF has no catalog zapisany plik PDF nie ma katalogu +@msg the written PDF has no cross-reference table zapisany plik PDF nie ma tabeli odsyłaczy +@msg the written PDF's metadata can't be edited nie można edytować metadanych zapisanego pliku PDF +@msg there are no colour swatches to draw nie ma próbek kolorów do narysowania +@msg there is already a design named `{name}` istnieje już wzór o nazwie `{name}` +@msg there is already a design named `{to}` istnieje już wzór o nazwie `{to}` +@msg there is already a toolbar named `{name}` istnieje już pasek narzędzi o nazwie `{name}` +@msg there is already a toolbar named `{to}` istnieje już pasek narzędzi o nazwie `{to}` +@msg there is no library: create one first nie ma biblioteki: najpierw ją utwórz +@msg these paths are too degenerate for Shaper te ścieżki są zbyt zdegenerowane dla narzędzia Kształtowanie +@msg thumbnail: no page {v} (the {what} has {count}) thumbnail: brak strony {v} ({what} ma {count}) +@msg tileRange of {what}: {e} tileRange dla {what}: {e} +@msg title can't be empty title nie może być puste +@msg to (the new name) is required to (nowa nazwa) jest wymagane +@msg to must be fill or stroke to musi mieć wartość fill lub stroke +@msg to must be smooth or corner to musi mieć wartość smooth lub corner +@msg too large for a zip archive: package to a folder zbyt duże dla archiwum zip: spakuj do folderu +@msg too many Shaper edits zbyt wiele edycji narzędzia Kształtowanie +@msg too many `points` (at most 100000) zbyt wiele `points` (najwyżej 100000) +@msg too many characters: at most {MAX_CHARS} zbyt wiele znaków: najwyżej {MAX_CHARS} +@msg too many dots for this size: lower the frequency zbyt wiele kropek dla tego rozmiaru: zmniejsz częstotliwość +@msg too many filters over one file zbyt wiele filtrów na jednym pliku +@msg too many overlapping paths for Shaper zbyt wiele nakładających się ścieżek dla narzędzia Kształtowanie +@msg too many saved clips zbyt wiele zapisanych obszarów przycinania +@msg too many saved graphics states zbyt wiele zapisanych stanów grafiki +@msg toolbar (a toolbar's name) must be a string toolbar (nazwa paska narzędzi) musi być ciągiem znaków +@msg toolbar name can't be empty nazwa paska narzędzi nie może być pusta +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}: "flatten" (domyślnie) lub "omit" +@msg two saved selections would be named `{_1}` dwa zapisane zaznaczenia miałyby nazwę `{_1}` +@msg type dimension cannot be represented with this text transform wymiaru tekstu nie można przedstawić przy tym przekształceniu tekstu +@msg type dimension must be finite wymiar tekstu musi być skończony +@msg unexpected resources nieoczekiwane zasoby +@msg unknown CMYK profile `{name}` nieznany profil CMYK `{name}` +@msg unknown File Info field `{k}` nieznane pole informacji o pliku `{k}` +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) nieznana funkcja OpenType `{t}` w featureChanges (znaczniki przyjmowane przez `features`) +@msg unknown PDF preset `{name}` (presets: {_1}) nieznane ustawienie PDF `{name}` (ustawienia: {_1}) +@msg unknown Warp style nieznany styl wypaczenia +@msg unknown alignToPath `{a}` nieznane alignToPath `{a}` +@msg unknown arrowhead {v} nieznany grot strzałki {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) nieznany klucz artboardLayout `{k}` (layout, columns, spacing, rightToLeft) +@msg unknown attribute `{attribute}` nieznany atrybut `{attribute}` +@msg unknown attribute `{k}` nieznany atrybut `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) nieznana oś `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` nieznany tryb mieszania `{b}` +@msg unknown blend mode {b} nieznany tryb mieszania {b} +@msg unknown brush type `{o}` nieznany typ pędzla `{o}` +@msg unknown category `{c}` nieznana kategoria `{c}` +@msg unknown category `{c}` ({_1}) nieznana kategoria `{c}` ({_1}) +@msg unknown character `{c}` nieznany znak `{c}` +@msg unknown character style number field nieznane pole liczbowe stylu znakowego +@msg unknown effect `{e}` nieznany efekt `{e}` +@msg unknown end {e} nieznany koniec {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) nieznane flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution lub zapisane ustawienie: patrz flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) nieznany format `{f}` (vcswatches, gpl, ase lub css) +@msg unknown from {f} nieznane from {f} +@msg unknown graph type `{t}` nieznany typ wykresu `{t}` +@msg unknown grid colours `{name}` (one of {_1}) nieznane kolory siatki `{name}` (jedna z wartości: {_1}) +@msg unknown gridType `{t}` nieznane gridType `{t}` +@msg unknown highlight `{v}` nieznane wyróżnienie `{v}` +@msg unknown intent `{i}` nieznana metoda konwersji kolorów `{i}` +@msg unknown issue kind {id} nieznany rodzaj problemu {id} +@msg unknown issue kind {v} nieznany rodzaj problemu {v} +@msg unknown knockout state {k} nieznany stan wybijania {k} +@msg unknown language `{language}` (one of {_1}) nieznany język `{language}` (jedna z wartości: {_1}) +@msg unknown language `{l}` (one of {_1}) nieznany język `{l}` (jedna z wartości: {_1}) +@msg unknown method `{m}` ({_1}) nieznana metoda `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) nieznany tryb `{other}` (gray, rgb, hsb, lab, cmyk lub web) +@msg unknown mode `{other}` (mono|cmyk) nieznany tryb `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) nieznany tryb `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) nieznany tryb `{other}` (set, add, subtract) +@msg unknown mode `{o}` nieznany tryb `{o}` +@msg unknown mode {m} nieznany tryb {m} +@msg unknown model `{other}` nieznany model `{other}` +@msg unknown model `{other}` (keep, new or document) nieznany model `{other}` (keep, new lub document) +@msg unknown op {o} nieznane op {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back nieznane order `{o}`: toggle, front, forward, backward lub back +@msg unknown overprints `{v}` (preserve, simulate or discard) nieznane overprints `{v}` (preserve, simulate lub discard) +@msg unknown panel nieznany panel +@msg unknown pieLegend `{v}` (none, standard or wedges) nieznane pieLegend `{v}` (none, standard lub wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) nieznane piePosition `{v}` (even, ratio lub stacked) +@msg unknown pieSort `{v}` (none, all or first) nieznane pieSort `{v}` (none, all lub first) +@msg unknown plate `{n}` nieznana płyta `{n}` +@msg unknown preference `{k}` nieznana preferencja `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) nieznany profil {id} (patrz stroke.widthProfile.list) +@msg unknown proof target `{t}` nieznany cel próby kolorów `{t}` +@msg unknown quotes key `{k}` (double, single) nieznany klucz quotes `{k}` (double, single) +@msg unknown resolution `{v}` nieznana rozdzielczość `{v}` +@msg unknown rule `{rule}` ({_1}) nieznana reguła `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) nieznana reguła `{r}` (patrz color.harmony) +@msg unknown setting `{k}` nieznane ustawienie `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) nieznany kształt `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) nieznane spacing `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) nieznane tileType `{t}` ({_1}) +@msg unknown units nieznane jednostki +@msg unknown units `{u}` nieznane jednostki `{u}` +@msg unknown value axis `{v}` (left, right or both) nieznana oś wartości `{v}` (left, right lub both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) nieznana odmiana `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` nieznany styl wypaczenia `{s}` +@msg unknown {key} `{v}` (none, short or full) nieznane {key} `{v}` (none, short lub full) +@msg unknown {key} profile `{n}` nieznany profil {key} `{n}` +@msg unknown {k} key `{key}` (size, position) nieznany klucz {k} `{key}` (size, position) +@msg unreadable object nieczytelny obiekt +@msg unsafe destination file name niebezpieczna nazwa pliku docelowego +@msg use paths or compound paths użyj ścieżek lub ścieżek złożonych +@msg useArtboards must be true or false, not {v} useArtboards musi mieć wartość true lub false, a nie {v} +@msg value for `{var}` must match its variable kind wartość dla `{var}` musi odpowiadać rodzajowi zmiennej +@msg variable `{_1}` is already defined zmienna `{_1}` jest już zdefiniowana +@msg variable `{name}` has no `trait` zmienna `{name}` nie ma `trait` +@msg variable `{name}` is already defined zmienna `{name}` jest już zdefiniowana +@msg variable `{name}` is declared twice zmienną `{name}` zadeklarowano dwa razy +@msg variable `{to}` already exists zmienna `{to}` już istnieje +@msg view is required view jest wymagane +@msg view must be one of {_1} view musi mieć jedną z wartości: {_1} +@msg warpStyle can only edit an applied Warp effect warpStyle może edytować tylko zastosowany efekt Wypaczenie +@msg width and height must be more than 0 and at most {limit} width i height muszą być większe od 0 i nie większe niż {limit} +@msg width factors must be finite współczynniki szerokości muszą być skończone +@msg width must be positive width musi być wartością dodatnią +@msg width out of range szerokość poza zakresem +@msg width/height must be > 0 width/height muszą być > 0 +@msg x and y must be finite numbers x i y muszą być liczbami skończonymi +@msg x and y must be given together x i y muszą być podane razem +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone wymaga punktu odniesienia: zadokuj obok panelu za pomocą anchor albo w nowej kolumnie doku za pomocą side (Start lub End) +@msg zoom must be positive zoom musi być wartością dodatnią +@msg {C}: no effect at index {list} {C}: brak efektu o indeksie {list} +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} jest chronione i pozostaje +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} jest chronione: zapisz ustawienia pod inną nazwą +@msg {LABEL} produced an empty result {LABEL}: wynik jest pusty +@msg {_1} MB of document, at most {_2} MB {_1} MB dokumentu, dozwolone najwyżej {_2} MB +@msg {_1} MB of images, at most {_2} MB {_1} MB obrazów, dozwolone najwyżej {_2} MB +@msg {_1} bytes of RGBA data for a {width} × {height} image {_1} bajtów danych RGBA dla obrazu {width} × {height} +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} nie można odczytać albo zmieniło się od czasu odczytu: najpierw połącz ponownie lub zaktualizuj +@msg {_1} can't go into {into} {_1} nie można umieścić w {into} +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} nie może ograniczyć drukowania do niskiej rozdzielczości: wybierz PDF 1.4 lub nowszy albo zezwól na drukowanie w wysokiej rozdzielczości +@msg {_1} files can't be opened yet plików {_1} nie można jeszcze otwierać +@msg {_1} files can't be password-protected: remove the passwords or choose no standard plików {_1} nie można chronić hasłem: usuń hasła albo nie wybieraj standardu +@msg {_1} files can't be {_2} pliki {_1} nie mogą być {_2} +@msg {_1} files can't carry editing data: turn off preserveEditing pliki {_1} nie mogą zawierać danych edycyjnych: wyłącz preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers pliki {_1} nie mogą mieć warstw PDF: wyłącz createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one pliki {_1} wymagają daty utworzenia, a ta platforma nie ma zegara, który by ją podał +@msg {_1} isn't a path {_1} nie jest ścieżką +@msg {_1} options: {e} opcje {_1}: {e} +@msg {_1} paints nothing to rasterize {_1} nie maluje niczego, co można zrasteryzować +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) hasła {_1} mogą mieć najwyżej {MAX_LEGACY_PASSWORD} znaków ASCII (PDF 1.7 i nowsze przyjmują dowolne) +@msg {_1} with linked images or an image map writes several files: use document.export {_1} z połączonymi obrazami lub mapą obrazu zapisuje kilka plików: użyj document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} zapisuje tu jeden plik na obszar kompozycji: użyj document.export albo wskaż jeden obszar kompozycji +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} pikseli to zbyt dużo dla {_3} (najwyżej {max} pikseli na bok): zmniejsz skalę +@msg {_1}: belongs to another account {_1}: należy do innego konta +@msg {_1}: not a folder of its own {_1}: to nie jest osobny folder +@msg {_1}: not a plain file {_1}: to nie jest zwykły plik +@msg {dest}: {e} {dest}: {e} +@msg {doc} has no folder {doc} nie ma folderu +@msg {file} isn't linked {file} nie jest połączony +@msg {folder} is not a package folder {folder} nie jest folderem pakietu +@msg {folder}: more than {max} files and folders {folder}: więcej niż {max} plików i folderów +@msg {folder}: {e} {folder}: {e} +@msg {g} is not a Live Paint group {g} nie jest grupą aktywnego malowania +@msg {id} is not a slice {id} nie jest plasterkiem +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} musi wynosić 0–{MAX_ADD_AROUND} pt +@msg {key} must be [x,y] {key} musi mieć postać [x,y] +@msg {key} must be a percentage {min}..100 {key} musi być wartością procentową z zakresu {min}..100 +@msg {key} must be a positive number, not {n} {key} musi być liczbą dodatnią, a nie {n} +@msg {key} must be a positive number, not {v} {key} musi być liczbą dodatnią, a nie {v} +@msg {key} must be one of {_1} {key} musi mieć jedną z wartości: {_1} +@msg {key} must be {_1}, got {s} {key} musi mieć wartość {_1}, podano {s} +@msg {key} must name a floating panel {key} musi wskazywać panel pływający +@msg {key} must name a panel in another group {key} musi wskazywać panel w innej grupie +@msg {key} must name a panel that shows {key} musi wskazywać wyświetlany panel +@msg {key} must name a registered panel {key} musi wskazywać zarejestrowany panel +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) pliki {kind} nie obsługują przezroczystości, a plik zawierałby {what}: najpierw spłaszcz przezroczystość (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) obrazów {kind} nie można otwierać (patrz document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` nie jest jedną z dostępnych opcji +@msg {k} has control characters{_1} {k} zawiera znaki sterujące{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} jest zbyt długie (najwyżej {MAX_TEXT} znaków) +@msg {k} must be > 0 {k} musi być > 0 +@msg {k} must be a number (pt) or a length such as "210 mm" {k} musi być liczbą (pt) lub długością, na przykład "210 mm" +@msg {k} must be a number from {min} to {max} {k} musi być liczbą od {min} do {max} +@msg {k} must be a number or "auto" {k} musi być liczbą lub wartością "auto" +@msg {k} must be a string {k} musi być ciągiem znaków +@msg {k} must be text {k} musi być tekstem +@msg {k} must be true or false {k} musi mieć wartość true lub false +@msg {k} must be {{size?, position?}} {k} musi mieć postać {{size?, position?}} +@msg {k}: can't read `{s}` as a length {k}: nie można odczytać `{s}` jako długości +@msg {label} must be a number at most {max} {label} musi być liczbą nie większą niż {max} +@msg {label} must be a number from -1296 to 1296 {label} musi być liczbą od -1296 do 1296 +@msg {label} must be a number from 1 to 10000 {label} musi być liczbą od 1 do 10000 +@msg {name} `{s}`: {ids} {name} `{s}`: {ids} +@msg {name} is built in and can't be deleted {name} jest wbudowane i nie można go usunąć +@msg {name} must be {lo}–{hi}{unit} (got {v}) {name} musi wynosić {lo}–{hi}{unit} (podano {v}) +@msg {name}: CRC or size mismatch {name}: niezgodność CRC lub rozmiaru +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}: udało się odczytać tylko osadzony podgląd tego pliku Affinity; użyj polecenia Plik › Otwórz, aby zobaczyć go wraz z ostrzeżeniem, albo przed umieszczeniem wyeksportuj SVG lub PDF z programu Affinity +@msg {name}: {e} {name}: {e} +@msg {native}; its embedded preview can't be read either ({e}) {native}; jego osadzonego podglądu również nie można odczytać ({e}) +@msg {std} files are CMYK, and the file would have colours in {_1} pliki {std} są w CMYK, a plik zawierałby kolory w {_1} +@msg {std} files embed their fonts, and the file would have one that isn't pliki {std} osadzają swoje czcionki, a plik zawierałby czcionkę nieosadzoną +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} to plik, z którego odczytano ten dokument, a przy odczycie pominięto część zawartości ({what}): nadpisanie go spowodowałoby jej trwałą utratę. Zapisz zamiast tego inny plik albo przekaż acknowledgeLoss: true, aby mimo to go zastąpić +@msg {ty} brushes need selected art (or params.{k}) pędzle {ty} wymagają zaznaczonej grafiki (lub params.{k}) +@msg {v} is not an asset id {v} nie jest identyfikatorem zasobu +@msg {what} can't be intertwined {what} nie można przeplatać +@msg {what} doesn't fit the paper with its bleed and marks {what} nie mieści się na papierze wraz ze spadem i znacznikami +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what}: wydruk zająłby ponad {MAX_TILES} kafelków: zmniejsz skalę diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..6958183f92 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -56,7 +56,7 @@ plus review. Native-speaker review is human work and isn't in the hours. | Russian | `ru` | 4,009 / 4,020 | full | | Ukrainian | `uk` | 4,009 / 4,020 (one/few/many plurals) | full | | Hungarian | `hu` | 5,253 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one plural form | full | -| Polish | `pl` | 5,130 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/few/many plurals | full; native review pending | +| Polish | `pl` | 9,044 rows, 3,439 of them `@msg` and 48 `@msgplural` (counted 2026-11-13): the untranslated report lists 46 missing messages for it, and 47 menu strings, 26 `tl!` literals, 11 undo labels and 45 swatch names; one/few/many plurals | full; native review pending | | Finnish | `fi` | 5,318 rows (counted 2026-10-21): menus, `tl!` literals and messages complete when counted; one/other plurals, 797 `@msg` rows | full; AI-assisted, native proofreading pending | | Czech | `cs` | 5,240 catalog entries, including 795 `@msg` rows (counted 2026-10-21); menus, literals and messages complete when counted | catalog complete; render-site gaps reduced; native review pending | | Bosnian | `bs` | 5,841 rows (counted 2026-10-30): menus, `tl!` literals and messages complete when counted; one/few/other plurals | full; AI-assisted, native review pending | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index f3707717e6..717d27e244 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -882,6 +882,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Window › Help Bar shows or hides the tool-help strip at the bottom of the window apart from the status bar (`window.helpBar`). Each setting is kept with the UI preferences and in saved workspaces, and Presentation Mode hides both bars. Layouts saved before the setting existed show the Help Bar where they show the status bar (`help_bar_toggle_is_independent_and_saved_in_workspaces`, `help_bar_and_status_bar_render_independently`, `legacy_help_bar_visibility_follows_each_layouts_status_bar`, `a_help_bar_setting_the_file_lacks_is_saved`). - Czech render-site labels: desktop/web file filters, diagnostics, suggested preset names and contributor tooltips/plurals now use catalog lookups; gradients have Czech adjective agreement and printer marks have a separate context (PDF/Print and Summary). Search, Home, toolbar/panel tooltips and Preferences already look up their labels. Every complete catalog has rows for these labels and for the Roman and Japanese printer mark styles. Evidence: `i18n::tests::czech_render_site_follow_up`, `i18n::tests::complete_catalogs_translate_render_site_labels`, `dialogs::tests_modal::printer_mark_labels_are_distinct_from_language_names`; see [localization-parity.md](localization-parity.md). - The History panel, Edit › Undo and Edit › Redo translate the names of the steps that apply or redefine a character or paragraph style and of the step that redefines a graphic style: each such step keeps its template (`Apply {name}`, `Redefine {name}`, `Redefine Graphic Style “{name}”`) and the style's name, every complete catalog has a row for each template, and the panel and the menu show the style's name as the document holds it, except the Normal styles' names, which they show in the interface language (`steps_named_after_a_style_list_the_template_around_the_name`, `undo_and_redo_name_a_style_step_around_the_style_name`). + - The Polish catalog (`crates/ui-egui/src/i18n/pl.tsv`) has an `@msg` row for the status and error messages the untranslated report (`untranslated_report`) finds in the sources, 3,439 `@msg` rows in all: the errors of commands, scripts, file import and export and plug-ins. The report lists 46 Polish messages: 44 that open #7211, #7214, #7116, #7330 and #1931 translate, and 2 that no language has a row for. A message with two placeholders in a row (`Template::parse` in `crates/ui-egui/src/i18n/catalog.rs`) is still shown in English. - The status bar translates the limits a plug-in run can exceed (its instruction, time and memory budgets and its output size) and the error of a plug-in removal that leaves the plug-in's grant in the plug-in store, in every language that translates every message. The untranslated-strings report also lists messages that an error or a status takes from a `&str` constant (`plugin_limit_errors_are_translated`, `message_scan_reads_messages_held_in_constants`). - The status bar translates the plug-in host's other errors (manifest, module and run checks, the output an importer, exporter or effect returns, and the approval a plug-in's permissions need) in every language that translates every message. A JSON object or array inside a message stays as written, and a message that wraps another one, such as `plug-in manifest: {0}`, is matched by its own row before a row that starts with a placeholder. The reasons inside these errors (what is wrong with a parameter's spec or dialog keys, the value a parameter needs, what is wrong with an object, stroke, paint or effect in a plug-in's output) are translated with them, and the untranslated-strings report lists the reasons the plug-in host's error helpers take (`plugin_messages_are_translated_in_every_complete_catalog`, `plugin_errors_translate_with_their_values`, `plugin_error_reasons_are_translated_with_the_error`, `message_scan_reads_plugin_helpers`, `templates_that_start_with_text_match_first`). - The status bar translates the errors a script's `File` and `Folder` objects, `vc.run`, `$.evalFile`, the `$` object's properties and functions, `alert`, `confirm` and `prompt`, `vc.document` and `vc.log` raise, the limits a run can pass, and the errors of Drop Script Engine, and the line for a run that fails (a syntax error, a thrown error, a limit, a stop or an internal error, with the script's name, the line and column, the included file it is in and the files left written), in every language that translates every message. The script's name, paths, numbers, command ids and the text a script throws itself are shown as they are. A failure with no line and column, such as an error in code `eval` compiled, is shown with one colon after the script's name (`an_error_without_a_place_names_the_script_and_kind`). A message template that names a value twice matches only when both places hold the same text, so the line for a failed run is not taken for the message inside it (`a_repeated_placeholder_matches_the_same_text`). The untranslated-strings report also lists the messages the script host's `fail`, `value` and `over` helpers take and the errors the script interpreter throws, and a constant's text comes from the file or crate that declares it, so the Scripts menu's headers are not listed as messages (`script_errors_are_translated`, `script_host_errors_are_translated`, `message_scan_reads_script_interpreter_errors`, `message_scan_reads_constants_of_the_file_and_its_crate`, `message_scan_skips_test_modules_with_any_test_cfg`). From f74fe812928289908eeeb7431a93357ddb7bade2 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 17:19:33 -0800 Subject: [PATCH 30/33] Live Paint: the swatch preview follows the release point; nudges read the key press's modifiers - After a fill drag, the Live Paint Bucket stores the release point, so the cursor swatch preview shows there instead of where the pointer last moved before the press. - Arrow nudges and Alt+arrow type spacing read Shift and Alt from the key press, as the tool keys do, so a modifier released within the same frame still makes a 10 pt nudge, a copy, or a spacing step. - tool.setOption accepts cursorSwatchPreview for the Live Paint Bucket and refuses a value that is not true or false. --- crates/engine/src/tests/build.rs | 7 +++-- crates/tools/src/builder/live_paint.rs | 4 ++- crates/tools/src/builder/tests.rs | 21 ++++++++++++++ crates/ui-egui/src/shortcuts.rs | 39 +++++++++++++++----------- crates/ui-egui/src/shortcuts/tests.rs | 37 ++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 21 deletions(-) diff --git a/crates/engine/src/tests/build.rs b/crates/engine/src/tests/build.rs index d958ae7cfa..f3681c5991 100644 --- a/crates/engine/src/tests/build.rs +++ b/crates/engine/src/tests/build.rs @@ -3294,9 +3294,10 @@ fn live_paint_tool_options_refuse_invalid_values_and_change_nothing() { let colors = [json!(3), json!("nope"), json!("#12345"), json!("#00ff0080"), json!("00ff0080"), json!("#0f08"), json!(null)]; let widths = [json!("wide"), json!(null), json!(true)]; let switches = [json!("true"), json!(null), json!(1)]; - for (tool, keys) in - [("livePaintBucket", &["paintFills", "paintStrokes", "highlight"][..]), ("livePaintSelection", &["selectFills", "selectStrokes"][..])] - { + for (tool, keys) in [ + ("livePaintBucket", &["paintFills", "paintStrokes", "cursorSwatchPreview", "highlight"][..]), + ("livePaintSelection", &["selectFills", "selectStrokes"][..]), + ] { let mut bad: Vec<(&str, Value)> = keys.iter().flat_map(|k| switches.iter().map(move |v| (*k, v.clone()))).collect(); bad.extend(colors.iter().map(|v| ("color", v.clone()))); bad.extend(widths.iter().map(|v| ("width", v.clone()))); diff --git a/crates/tools/src/builder/live_paint.rs b/crates/tools/src/builder/live_paint.rs index f629174e6a..a907a319bb 100644 --- a/crates/tools/src/builder/live_paint.rs +++ b/crates/tools/src/builder/live_paint.rs @@ -403,6 +403,8 @@ impl Tool for LivePaintBucketTool { } PointerKind::Drag => self.drag_fill(p), PointerKind::Up => { + // The swatch preview shows again at the release point. + self.at = Some((p, ev.mods.shift, ev.mods.alt)); let mut acts = self.drag_fill(p); if self.fill_drag.take().is_some() { acts.push(Action::Commit); @@ -673,7 +675,7 @@ pub(super) fn lp_rgb_color(v: &Value) -> bool { /// value it cannot read. pub fn check_live_paint_options(tool: &str, values: &serde_json::Map) -> Result<(), String> { let switches: &[&str] = match tool { - "livePaintBucket" => &["paintFills", "paintStrokes", "highlight"], + "livePaintBucket" => &["paintFills", "paintStrokes", "cursorSwatchPreview", "highlight"], "livePaintSelection" => &["selectFills", "selectStrokes"], _ => return Ok(()), }; diff --git a/crates/tools/src/builder/tests.rs b/crates/tools/src/builder/tests.rs index 14976f03c9..07a703be2d 100644 --- a/crates/tools/src/builder/tests.rs +++ b/crates/tools/src/builder/tests.rs @@ -1087,6 +1087,27 @@ fn bucket_click_fills_face_and_shift_click_strokes_edge() { assert_eq!(id, "livePaint.strokeEdge"); } +/// After a fill drag, the cursor swatch preview shows at the release point, not where the pointer +/// last moved before the press. +#[test] +fn bucket_swatch_preview_follows_the_release_point() { + let (d, ..) = live_group_doc(); + let s = Selection::default(); + let p = paint(); + let cx = cx(&d, &s, &p); + let mut t = LivePaintBucketTool::default(); + let chip_x = |t: &LivePaintBucketTool| { + t.overlays(&cx).iter().find_map(|o| if let Overlay::Swatch { p, selected: true, .. } = o { Some(p.x) } else { None }) + }; + t.pointer(&cx, &ev(PointerKind::Move, 25.0, 25.0)); + assert_eq!(chip_x(&t), Some(25.0)); + t.pointer(&cx, &ev(PointerKind::Down, 25.0, 25.0)); + t.pointer(&cx, &ev(PointerKind::Drag, 50.0, 25.0)); + assert_eq!(chip_x(&t), None, "no chips during the drag"); + t.pointer(&cx, &ev(PointerKind::Up, 75.0, 25.0)); + assert_eq!(chip_x(&t), Some(75.0)); +} + #[test] fn bucket_options_choose_what_a_click_paints() { let (d, gid, ..) = live_group_doc(); diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index 1dd2ef1d96..ea32f6c078 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -625,8 +625,10 @@ fn dialog_or_palette_open(app: &VectorcraftApp) -> bool { /// Route Alt+arrows to spacing controls when text objects are selected. fn alt_type_arrows(app: &mut VectorcraftApp, ctx: &egui::Context) -> bool { - let m = ctx.input(|i| i.modifiers); - if !m.alt { + let keys = [(Key::ArrowLeft, ToolKey::Left), (Key::ArrowRight, ToolKey::Right), (Key::ArrowUp, ToolKey::Up), (Key::ArrowDown, ToolKey::Down)]; + // The modifiers held when each arrow went down, as `nudge_and_clear` reads them. + let pressed = keys.map(|(key, tool_key)| (key, tool_key, ctx.input(|i| press_modifiers(i, key)))); + if !pressed.iter().any(|(.., m)| m.alt) { return false; } let orientation = app.session.active().and_then(|st| { @@ -644,9 +646,8 @@ fn alt_type_arrows(app: &mut VectorcraftApp, ctx: &egui::Context) -> bool { let Some(vertical) = orientation else { return false; }; - let keys = [(Key::ArrowLeft, ToolKey::Left), (Key::ArrowRight, ToolKey::Right), (Key::ArrowUp, ToolKey::Up), (Key::ArrowDown, ToolKey::Down)]; - for (key, tool_key) in keys { - if ctx.input_mut(|i| i.consume_key(m, key)) { + for (key, tool_key, m) in pressed { + if m.alt && ctx.input_mut(|i| i.consume_key(m, key)) { if let Some((attribute, by)) = vectorcraft_tools::text::step_key(tool_key, m.shift, false, vertical) { let by = if m.command { by * 5.0 } else { by }; let _ = app.run("type.step", json!({"attribute": attribute, "by": by})); @@ -685,13 +686,27 @@ fn type_tool_input(app: &mut VectorcraftApp, ctx: &egui::Context, composing: boo settings_on_a_mac(app, ctx, mac(ctx)); } +/// The modifiers held when `key` last went down in this frame, which can differ from the frame's +/// at its end; the frame's when the key did not go down. +fn press_modifiers(i: &egui::InputState, key: Key) -> Modifiers { + i.events + .iter() + .rev() + .find_map(|e| match e { + egui::Event::Key { key: k, pressed: true, modifiers, .. } if *k == key => Some(*modifiers), + _ => None, + }) + .unwrap_or(i.modifiers) +} + /// Apply arrow-key movement and clear selected art when no text editor owns input, unless a Layers /// or Appearance row has Delete/Backspace (`rows_keys`). fn nudge_and_clear(app: &mut VectorcraftApp, ctx: &egui::Context, rows_keys: bool) { // Arrow nudges (Shift = ×10, Alt = copy). let arrows = [(Key::ArrowLeft, -1.0, 0.0), (Key::ArrowRight, 1.0, 0.0), (Key::ArrowUp, 0.0, -1.0), (Key::ArrowDown, 0.0, 1.0)]; for (k, dx, dy) in arrows { - let m = ctx.input(|i| i.modifiers); + // The modifiers held when the key went down, as `claimed_tool_keys` reads them. + let m = ctx.input(|i| press_modifiers(i, k)); if ctx.input_mut(|i| i.consume_key(m, k)) && app.session.active().is_some_and(|d| d.selection.has_objects_or_guides()) { let _ = app.run("object.nudge", json!({"dx": dx, "dy": dy, "big": m.shift, "copy": m.alt})); } @@ -878,17 +893,7 @@ fn claimed_tool_keys(app: &mut VectorcraftApp, ctx: &egui::Context, view: ViewIn // The Smart Pen Tool's Tab while it draws a path (the key otherwise hides the panels). (Key::Tab, ToolKey::Tab), ] { - // The modifiers held when the key went down, which can differ from the frame's at its end. - let pm = ctx.input(|i| { - i.events - .iter() - .rev() - .find_map(|e| match e { - egui::Event::Key { key, pressed: true, modifiers, .. } if *key == k => Some(*modifiers), - _ => None, - }) - .unwrap_or(m) - }); + let pm = ctx.input(|i| press_modifiers(i, k)); if !rows_take(k) && ctx.input(|i| i.key_pressed(k)) && app.session.tool_claims_key_with(tk, crate::canvas::mods(pm, false), view) diff --git a/crates/ui-egui/src/shortcuts/tests.rs b/crates/ui-egui/src/shortcuts/tests.rs index ae1f966696..92a1ab9768 100644 --- a/crates/ui-egui/src/shortcuts/tests.rs +++ b/crates/ui-egui/src/shortcuts/tests.rs @@ -2220,6 +2220,15 @@ fn alt_arrows_step_selected_type_object() { assert_eq!(r["runs"][0]["style"]["tracking"], json!(20.0)); assert_eq!(app.session.active().unwrap().selection.objects, [vectorcraft_doc::NodeId(id)]); + // Alt released within the frame of the press: the press's Alt still steps the spacing. + frame( + &mut app, + vec![egui::Event::ModifiersChanged(Modifiers::ALT), press(Key::ArrowRight, Modifiers::ALT), egui::Event::ModifiersChanged(Modifiers::NONE)], + ); + let r = app.session.execute("text.getRange", &json!({"id": id})).unwrap(); + assert_eq!(r["runs"][0]["style"]["tracking"], json!(40.0)); + assert_eq!(app.session.active().unwrap().selection.objects, [vectorcraft_doc::NodeId(id)], "the text is not copied"); + frame(&mut app, vec![egui::Event::ModifiersChanged(Modifiers::ALT), press(Key::ArrowDown, Modifiers::ALT)]); let r = app.session.execute("text.getRange", &json!({"id": id})).unwrap(); assert_eq!(r["runs"][0]["style"]["leading"], json!(26.0)); @@ -2276,6 +2285,34 @@ fn live_paint_bucket_takes_only_plain_arrows_for_swatches() { assert_ne!(app.session.paint.fill, fill, "a plain Right picks the next swatch"); } +/// An arrow nudge takes Shift and Alt from the key press, also when they are released within the +/// same frame: Shift+Right moves 10 points, and Alt+Right copies. +#[test] +fn arrow_nudge_takes_the_modifiers_of_the_key_press() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap(); + let rect = app.session.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()["id"].as_u64().unwrap(); + let rect = vectorcraft_doc::NodeId(rect); + app.select_tool("selection"); + app.session.execute("select.set", &json!({"ids": [rect.0]})).unwrap(); + let released = |modifiers| { + vec![ + egui::Event::ModifiersChanged(modifiers), + egui::Event::Key { key: Key::ArrowRight, physical_key: Some(Key::ArrowRight), pressed: true, repeat: false, modifiers }, + egui::Event::ModifiersChanged(Modifiers::NONE), + ] + }; + let left = |app: &VectorcraftApp, id| app.session.active().unwrap().doc.bounds_of(&[id], false).unwrap().x0; + + frame(&mut app, released(Modifiers::SHIFT)); + assert_eq!(left(&app, rect), 10.0, "Shift+Right moves 10 points"); + + frame(&mut app, released(Modifiers::ALT)); + let selected = app.session.active().unwrap().selection.objects.clone(); + assert_ne!(selected.as_slice(), [rect], "Alt+Right copies"); + assert_eq!(left(&app, rect), 10.0, "the original stays"); +} + /// #793: with a layout without Latin letters the one-key shortcuts go by the key's position: /// the P key typing Persian `ح` chooses the Pen, the V key typing Russian `м` the Selection /// tool. A Latin layout keeps the letter typed: AZERTY's A (the Q key's place) is A. From 270e58a4613ab71a0dbc98f39e9c22084283058d Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 17:24:40 -0800 Subject: [PATCH 31/33] Live Paint: the Bucket's swatch preview and highlight go when the pointer leaves the canvas --- crates/tools/src/builder/live_paint.rs | 6 ++++++ crates/tools/src/builder/tests.rs | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/crates/tools/src/builder/live_paint.rs b/crates/tools/src/builder/live_paint.rs index a907a319bb..2ae2f082ee 100644 --- a/crates/tools/src/builder/live_paint.rs +++ b/crates/tools/src/builder/live_paint.rs @@ -456,6 +456,12 @@ impl Tool for LivePaintBucketTool { if self.fill_drag.take().is_some() { vec![Action::Commit] } else { vec![] } } + /// The pointer hovers nothing for the tool: its highlight and its swatch preview go. + fn hover_ended(&mut self, _cx: &ToolContext) { + self.hover = LpHover::None; + self.at = None; + } + fn overlays(&self, cx: &ToolContext) -> Vec { let mut out = lp_overlays(&self.hover, &self.cache, self.color, self.width as f32, (self.width * LP_BUCKET_EDGE_FRACTION) as f32); // No chips during a fill drag, or while Alt makes the tool sample with the eyedropper. diff --git a/crates/tools/src/builder/tests.rs b/crates/tools/src/builder/tests.rs index 07a703be2d..01b64ee8c3 100644 --- a/crates/tools/src/builder/tests.rs +++ b/crates/tools/src/builder/tests.rs @@ -1108,6 +1108,21 @@ fn bucket_swatch_preview_follows_the_release_point() { assert_eq!(chip_x(&t), Some(75.0)); } +/// When the pointer leaves the canvas, the Live Paint Bucket's swatch preview and highlight go. +#[test] +fn bucket_hover_ended_drops_the_swatch_preview_and_highlight() { + let (d, ..) = live_group_doc(); + let s = Selection::default(); + let p = paint(); + let cx = cx(&d, &s, &p); + let mut t = LivePaintBucketTool::default(); + t.pointer(&cx, &ev(PointerKind::Move, 75.0, 25.0)); + let ov = t.overlays(&cx); + assert!(ov.iter().any(|o| matches!(o, Overlay::Swatch { .. })) && ov.iter().any(|o| matches!(o, Overlay::Path { .. })), "{ov:?}"); + t.hover_ended(&cx); + assert!(t.overlays(&cx).is_empty()); +} + #[test] fn bucket_options_choose_what_a_click_paints() { let (d, gid, ..) = live_group_doc(); From edd2b83d1da8190b8769545b83ac13f27294b545 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 13 Nov 2026 16:49:56 -0800 Subject: [PATCH 32/33] i18n: Simplified Chinese has a row for 1,813 of the messages the untranslated report lists Adds 1,813 @msg rows to zh-hans.tsv, one for each status and error message the untranslated report listed for Simplified Chinese, except the 38 that open pull requests translate in their own rows: the composite font, justification order and Blob Brush connect end errors. The catalog test rejects a second row for the same key, so leaving them out keeps the catalog consistent when those pull requests merge. The report now lists 40 zh-hans messages: those 38, and two that no catalog has a row for yet. Its menu, tl!, undo label and swatch name sections are unchanged. Placeholders, backticked names, quoted values and JavaScript identifiers stay as in the source. Docs: docs/localization-parity.md (zh-hans row), docs/target-app-parity.md. --- crates/ui-egui/src/i18n/zh-hans.tsv | 1814 +++++++++++++++++++++++++++ docs/localization-parity.md | 2 +- docs/target-app-parity.md | 1 + 3 files changed, 1816 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 3658ca1241..7ded059f2f 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -7330,3 +7330,1817 @@ breakText Glyphs 字形 Grommets 扣眼 Grommet Inset 扣眼边距 Diameter 直径 +# Status and error messages of commands, scripts, file import and export, and plug-ins. +@msg 'caller', 'callee' and 'arguments' cannot be used on strict mode functions 严格模式的函数不能使用 'caller'、'callee' 和 'arguments' +@msg Alignment must be one of the choices 对齐方式必须是给出的选项之一 +@msg Boolean.prototype.{name} was called on a value that is not a Boolean Boolean.prototype.{name} 的调用对象不是 Boolean 值 +@msg CMYK images whose samples couldn't be read in CMYK were imported in RGB 采样无法按 CMYK 读取的 CMYK 图像已按 RGB 导入 +@msg Cannot write to the location of “{name}”; choose where to save it 无法写入“{name}”所在的位置;请选择保存位置 +@msg DCS files can only be placed linked: use File › Place with Link on DCS 文件只能以链接方式置入:请使用 文件 › 置入 并勾选“链接” +@msg Date.prototype method called on a value that is not a Date object Date.prototype 方法的调用对象不是 Date 对象 +@msg Error.prototype.toString was called on a value that is not an object Error.prototype.toString 的调用对象不是对象 +@msg Function.prototype.toString was called on a value that is not a function Function.prototype.toString 的调用对象不是函数 +@msg Import SVG Filter reads SVG files, not SVGZ 导入 SVG 滤镜读取的是 SVG 文件,不是 SVGZ +@msg JSON.parse: {why} (at offset {at}) JSON.parse:{why} (位于偏移 {at}) +@msg JSON.stringify cannot write a structure that contains itself JSON.stringify 无法写出包含自身的结构 +@msg Line Gap must be a number 行间距必须是数字 +@msg Namespace.prototype.toString needs a Namespace Namespace.prototype.toString 需要一个 Namespace +@msg New Document profiles need the preferences folder, which this run doesn't have 新建文档配置文件需要首选项文件夹,而本次运行没有该文件夹 +@msg Number.prototype.{name} was called on a value that is not a number Number.prototype.{name} 的调用对象不是数字 +@msg Object.create's prototype must be an object or null Object.create 的原型必须是对象或 null +@msg Object.{function} was given a value that is not an object 传给 Object.{function} 的值不是对象 +@msg QName.prototype.toString needs a QName QName.prototype.toString 需要一个 QName +@msg RegExp.prototype.{name} was called on a value that is not a RegExp object RegExp.prototype.{name} 的调用对象不是 RegExp 对象 +@msg Stencil Bridges: the selected art is too large or too detailed to bridge 镂空版连接桥:所选图稿过大或过于复杂,无法添加连接桥 +@msg String.prototype.toString was called on a value that is not a string String.prototype.toString 的调用对象不是字符串 +@msg The {name} profile could not be read, so the new document has the default settings: {reason} 无法读取配置文件 {name},因此新文档使用默认设置:{reason} +@msg Trap: more than {_1} pairs of objects lie close enough to trap, or too many objects share a horizontal range; trap fewer objects at a time 陷印:距离近到需要陷印的对象超过 {_1} 对,或处于同一水平范围的对象过多;请减少每次陷印的对象 +@msg Weed Box and Lines: no weeding line fits outside the art, and the weed box is off 除废框和除废线:图稿外侧放不下任何除废线,且除废框已关闭 +@msg \ at end of pattern 模式以 \ 结尾 +@msg `alignOn` cannot be empty `alignOn` 不能为空 +@msg `alignOn` must be a string `alignOn` 必须是字符串 +@msg `align` must be a string `align` 必须是字符串 +@msg `answer` for this {kind} dialog must be one of: {_1} 此 {kind} 对话框的 `answer` 必须是以下之一:{_1} +@msg `answers` has more than {MAX_ANSWERS} answers `answers` 中的回答超过 {MAX_ANSWERS} 个 +@msg `answers` is larger than 1 MiB `answers` 大于 1 MiB +@msg `answers` must be a list of answers `answers` 必须是回答的列表 +@msg `artboard` must be the index of an artboard of the document `artboard` 必须是文档中某个画板的索引 +@msg `artboardsAt` must be [x, y] `artboardsAt` 必须是 [x, y] +@msg `artboards` names an artboard the document does not have `artboards` 指定了文档中没有的画板 +@msg `artboards` names an artboard twice `artboards` 重复指定了同一个画板 +@msg `at` must be [x, y] (got {at}) `at` 必须是 [x, y] (实际为 {at}) +@msg `at` must be a point [x, y] `at` 必须是点 [x, y] +@msg `beside` needs `side` `beside` 需要 `side` +@msg `code` must be a whole number `code` 必须是整数 +@msg `colorMode` must be one of rgb, cmyk `colorMode` 必须是 rgb、cmyk 之一 +@msg `colors` does not apply to the {name} reduction `colors` 不适用于 {name} 减色方式 +@msg `colors` must be a whole number from 2 to 256 `colors` 必须是 2 到 256 之间的整数 +@msg `colors` must be at least 9 for the uniform reduction 使用 uniform 减色方式时,`colors` 至少为 9 +@msg `colors` must be between 1 and {MAX_THEME_COLORS} (got {n}) `colors` 必须介于 1 到 {MAX_THEME_COLORS} 之间 (实际为 {n}) +@msg `count` must be a whole number from 0 to {MAX_COUNT} `count` 必须是 0 到 {MAX_COUNT} 之间的整数 +@msg `ditherAmount` must be a number from 0 to 100 `ditherAmount` 必须是 0 到 100 之间的数字 +@msg `fidelity` must be from {lo} to {hi} pt `fidelity` 必须介于 {lo} 到 {hi} pt 之间 +@msg `focal` and `radius` cannot be used together `focal` 和 `radius` 不能同时使用 +@msg `focal` must be an array of [subpath, anchor] pairs `focal` 必须是 [subpath, anchor] 对的数组 +@msg `focal` names [{si}, {ai}], which is not an anchor of the path `focal` 指定了 [{si}, {ai}],它不是该路径的锚点 +@msg `glyphAlternate` takes only null, which takes a glyph choice away (text.setGlyphAlternate makes one) `glyphAlternate` 只接受 null,用于取消字形选择 (text.setGlyphAlternate 用于设置选择) +@msg `group` must be one of {_1} `group` 必须是 {_1} 之一 +@msg `group` must be the Objects on Path group of `member` and `target` `group` 必须是 `member` 和 `target` 所在的“路径上的对象”组 +@msg `id` is an object id, not {v} `id` 是对象 ID,不是 {v} +@msg `item` needs `index` `item` 需要 `index` +@msg `kerningBefore` belongs to a line's first character, not to a character style (text.setRangeStyle sets it) `kerningBefore` 属于行首字符,不属于字符样式 (由 text.setRangeStyle 设置) +@msg `kinsokuType` must be "pushInFirst", "pushOutFirst" or "pushOutOnly" `kinsokuType` 必须是 "pushInFirst"、"pushOutFirst" 或 "pushOutOnly" +@msg `leader` must be a string `leader` 必须是字符串 +@msg `max` must be 1 to {MAX_SUGGESTIONS} `max` 必须为 1 到 {MAX_SUGGESTIONS} +@msg `name` must be a name that is not empty `name` 必须是非空的名称 +@msg `name` must not be empty `name` 不能为空 +@msg `origin` must be [x, y] in points `origin` 必须是以点为单位的 [x, y] +@msg `picks` must be an array (got {v}) `picks` 必须是数组 (实际为 {v}) +@msg `picks` must hold 1 to {MAX_PICKS} picks (got {_1}) `picks` 必须包含 1 到 {MAX_PICKS} 个拾取项 (实际为 {_1}) +@msg `points` must hold from 2 to 100000 points `points` 必须包含 2 到 100000 个点 +@msg `radius` must be 0 or more `radius` 必须大于或等于 0 +@msg `rect` must be [x, y, width, height] `rect` 必须是 [x, y, width, height] +@msg `rect` must be [x0, y0, x1, y1] (got {r}) `rect` 必须是 [x0, y0, x1, y1] (实际为 {r}) +@msg `replace` must be a string `replace` 必须是字符串 +@msg `rows` must be lists of numbers or nulls, not {r} `rows` 必须是由数字或 null 组成的列表,不能是 {r} +@msg `scale` must be a number from 0.001 to 1000 `scale` 必须是 0.001 到 1000 之间的数字 +@msg `scope.{key}` must be a non-empty array `scope.{key}` 必须是非空数组 +@msg `scope` must be "document", {artboards} or {layers} `scope` 必须是 "document"、{artboards} 或 {layers} +@msg `side` must be "left", "right", "top" or "bottom" `side` 必须是 "left"、"right"、"top" 或 "bottom" +@msg `side` places the artboard: leave out x, y, width and height `side` 决定画板的位置:请不要给出 x、y、width 和 height +@msg `source` is the only path in series group `target` `source` 是系列组 `target` 中唯一的路径 +@msg `spacing` makes more than {MAX_COUNT} lines across the box `spacing` 会在框内生成超过 {MAX_COUNT} 条线 +@msg `spacing` must be more than 0 and at most {MAX_DISTANCE} pt `spacing` 必须大于 0 且不超过 {MAX_DISTANCE} pt +@msg `target` must be an object of the same Objects on Path group as `member` `target` 必须是与 `member` 同属一个“路径上的对象”组的对象 +@msg `tolerance` must be from 0 to 100 pt `tolerance` 必须为 0 到 100 pt +@msg `version` must be 0 or {_1} `version` 必须是 0 或 {_1} +@msg `version` must be 0 or {newest} `version` 必须是 0 或 {newest} +@msg `warichuAlign` must be one of {_1} `warichuAlign` 必须是 {_1} 之一 +@msg `warichuLines` must be a whole number from {_1} to {_2} `warichuLines` 必须是 {_1} 到 {_2} 之间的整数 +@msg `weight` must be from 0.01 to 1000 pt `weight` 必须为 0.01 到 1000 pt +@msg `weights` must be a list of {_1} numbers, one per row (got {v}) `weights` 必须是 {_1} 个数字的列表,每行一个 (实际为 {v}) +@msg `xml` must be a string `xml` 必须是字符串 +@msg `{KEY}` entry {n} gives the paragraph at byte {start} of {_1} other indents than an entry before it `{KEY}` 的第 {n} 项为 {_1} 中字节 {start} 处的段落指定的缩进与前面某一项不同 +@msg `{KEY}` entry {n} must be an object `{KEY}` 的第 {n} 项必须是对象 +@msg `{KEY}` entry {n} sets neither `leftIndent` nor `firstLineIndent` `{KEY}` 的第 {n} 项既未设置 `leftIndent`,也未设置 `firstLineIndent` +@msg `{KEY}` entry {n}: `id` must be an object id `{KEY}` 的第 {n} 项:`id` 必须是对象 ID +@msg `{KEY}` entry {n}: `start` must be a byte offset `{KEY}` 的第 {n} 项:`start` 必须是字节偏移 +@msg `{KEY}` entry {n}: `{k}` must be a number from -{MAX_PARA} to {MAX_PARA} pt `{KEY}` 的第 {n} 项:`{k}` 必须是 -{MAX_PARA} 到 {MAX_PARA} pt 之间的数字 +@msg `{KEY}` entry {n}: no paragraph of {_1} starts at byte {start} `{KEY}` 的第 {n} 项:{_1} 中没有从字节 {start} 开始的段落 +@msg `{KEY}` entry {n}: unknown key `{k}` `{KEY}` 的第 {n} 项:未知的键 `{k}` +@msg `{KEY}` entry {n}: {_1} is not a type object `{KEY}` 的第 {n} 项:{_1} 不是文字对象 +@msg `{KEY}` is empty `{KEY}` 为空 +@msg `{KEY}` must be a list `{KEY}` 必须是列表 +@msg `{KEY}` takes at most {MAX_PARAGRAPH_INDENTS} entries (got {_1}) `{KEY}` 最多包含 {MAX_PARAGRAPH_INDENTS} 项 (实际为 {_1}) +@msg `{KEY}` takes no `{other}` beside it `{KEY}` 不能与 `{other}` 同时给出 +@msg `{base}` is larger than {_1} MB `{base}` 大于 {_1} MB +@msg `{base}` is not in the DCS file's folder `{base}` 不在 DCS 文件所在的文件夹中 +@msg `{key}` holds more than {MAX_EDIT_WORDS} words `{key}` 中的单词超过 {MAX_EDIT_WORDS} 个 +@msg `{key}` must be 0 or more `{key}` 必须大于或等于 0 +@msg `{key}` must be [x, y] numbers `{key}` 必须是数字 [x, y] +@msg `{key}` must be a list of words `{key}` 必须是单词的列表 +@msg `{key}` must be a list, not {v} `{key}` 必须是列表,不能是 {v} +@msg `{key}` must be a number or null, not {v} `{key}` 必须是数字或 null,不能是 {v} +@msg `{key}` must be a whole number of 0 or more `{key}` 必须是大于或等于 0 的整数 +@msg `{key}` must be from 0 to {MAX_DISTANCE} pt `{key}` 必须为 0 到 {MAX_DISTANCE} pt +@msg `{key}` must be from 0.01 to 1000 pt `{key}` 必须为 0.01 到 1000 pt +@msg `{key}` must be from {lo} to {hi}, not {v} `{key}` 必须介于 {lo} 到 {hi} 之间,不能是 {v} +@msg `{key}` must be one of auto, always, never `{key}` 必须是 auto、always、never 之一 +@msg `{key}` must be one of miter, round, bevel `{key}` 必须是 miter、round、bevel 之一 +@msg `{key}` must be one word of at most {_1} characters `{key}` 必须是一个不超过 {_1} 个字符的单词 +@msg `{key}` must hold single words of at most {_1} characters `{key}` 中的每一项必须是不超过 {_1} 个字符的单个单词 +@msg `{key}` must name an object attached to the path of an Objects on Path group `{key}` 必须指定附着在“路径上的对象”组的路径上的对象 +@msg `{key}` names an object that is hidden or locked `{key}` 指定的对象已隐藏或锁定 +@msg `{name}` is a file of its own, which can only be found next to the DCS file's path `{name}` 是一个单独的文件,只能在 DCS 文件的路径旁找到 +@msg `{name}`: {_1}: {e} `{name}`:{_1}:{e} +@msg `{u}` is no Unicode character `{u}` 不是 Unicode 字符 +@msg `{word}` isn't in the custom dictionary `{word}` 不在自定义词典中 +@msg a CMap has too many mappings 某个 CMap 的映射过多 +@msg a \u escape needs four hexadecimal digits \u 转义需要四位十六进制数字 +@msg a comma in an XPath expression must separate function arguments XPath 表达式中的逗号只能用来分隔函数参数 +@msg a document holds at most {_1} SVG filters 一个文档最多包含 {_1} 个 SVG 滤镜 +@msg a document with unsaved changes has no recovery copy 有未保存更改的文档没有恢复副本 +@msg a font's data is too large 某个字体的数据过大 +@msg a font's subroutines are too large 某个字体的子程序过大 +@msg a function declaration without a name 函数声明没有名称 +@msg a host value has too many entries 宿主值的条目过多 +@msg a host value is nested too deep 宿主值嵌套过深 +@msg a namespace declaration uses a reserved prefix or URI 命名空间声明使用了保留的前缀或 URI +@msg a namespace qualifier is undefined 命名空间限定符未定义 +@msg a namespace with an empty URI cannot have a prefix URI 为空的命名空间不能有前缀 +@msg a number has no digits 数字中没有数位 +@msg a number's exponent has no digits 数字的指数部分没有数位 +@msg a number's fraction has no digits 数字的小数部分没有数位 +@msg a property descriptor cannot have both a value or writable and a get or set 属性描述符不能同时包含 value 或 writable 以及 get 或 set +@msg a property descriptor must be an object 属性描述符必须是对象 +@msg a property's {name} must be a function or undefined 属性的 {name} 必须是函数或 undefined +@msg a stream in it would decode to more than 256 MB, or its decode parameters can't be applied 其中某个数据流解码后会超过 256 MB,或无法应用其解码参数 +@msg a string holds a control character 字符串中含有控制字符 +@msg a string holds an escape sequence JSON does not have 字符串中含有 JSON 不支持的转义序列 +@msg a string is not closed 字符串没有结束 +@msg an SVG filter is named by its id: give `xml` with `name` SVG 滤镜以其 id 命名:请同时给出 `xml` 和 `name` +@msg an XML element has two attributes with the same name XML 元素有两个同名的属性 +@msg an XML value cannot be inserted into itself or its descendants XML 值不能插入到自身或其后代中 +@msg an XML value's children cannot be assigned by position 不能按位置为 XML 值的子项赋值 +@msg an XML value's children cannot be deleted by position 不能按位置删除 XML 值的子项 +@msg an XMLList converts to XML only when it has one item XMLList 只有在仅含一项时才能转换为 XML +@msg an XPath node test is missing 缺少 XPath 节点测试 +@msg an XPath operand is missing 缺少 XPath 操作数 +@msg an XPath operator is missing 缺少 XPath 运算符 +@msg an XPath predicate or step cannot follow here 此处不能接 XPath 谓词或步 +@msg an XPath predicate or step follows a value that is not a node-set XPath 谓词或步接在了不是节点集的值之后 +@msg an XPath step is missing 缺少 XPath 步 +@msg an image couldn't be decoded and is missing from the picture 有一个图像无法解码,因此图片中缺少该图像 +@msg an image of {w} × {h} pixels is too large to convert (at most {_1} pixels) {w} × {h} 像素的图像过大,无法转换 (最多 {_1} 像素) +@msg apply was called on a value that is not a function apply 的调用对象不是函数 +@msg area type in a thread cannot use Dynamic Text: {frames} 串接中的区域文字不能使用动态文本:{frames} +@msg array literal too long 数组字面量过长 +@msg assignment to a constant binding 对常量绑定赋值 +@msg at least one row that isn't excluded needs a weight above 0 至少要有一个未被排除的行的权重大于 0 +@msg bad point {v}: expected [x, y] 点 {v} 无效:应为 [x, y] +@msg blend must be a mode name blend 必须是模式名称 +@msg both sides of an XPath | must select nodes XPath 的 | 两侧都必须选择节点 +@msg canceled 已取消 +@msg cannot convert the object to a primitive value 无法将对象转换为原始值 +@msg cannot convert undefined or null to an object 无法将 undefined 或 null 转换为对象 +@msg character dimensions cannot be represented with the source text transform 无法用源文本的变换表示字符尺寸 +@msg character dimensions cannot be represented with the target text transform 无法用目标文本的变换表示字符尺寸 +@msg continuation must be an array of path chunks continuation 必须是路径分段的数组 +@msg continuation requires points continuation 需要 points +@msg each path chunk must contain 1 to 4096 points 每个路径分段必须包含 1 到 4096 个点 +@msg each pick is {{at: [x, y]}} or {{rect: [x0, y0, x1, y1]}} (got {v}) 每个拾取项是 {{at: [x, y]}} 或 {{rect: [x0, y0, x1, y1]}} (实际为 {v}) +@msg each weight must be a number of 0 or more (got {v}) 每个权重必须是大于或等于 0 的数字 (实际为 {v}) +@msg finally table {t} out of range finally 表 {t} 超出范围 +@msg flags cannot be given with a RegExp object 不能在给出 RegExp 对象的同时给出 flags +@msg function declaration index out of range 函数声明索引超出范围 +@msg give `char`, `text`, `code` or `unicode` 请给出 `char`、`text`、`code` 或 `unicode` +@msg give `count` or `spacing`, not both 请给出 `count` 或 `spacing`,不能两者都给 +@msg give `name`, an SVG filter's name 请给出 `name`,即 SVG 滤镜的名称 +@msg give `path`, an SVG file, or `svg`, SVG text 请给出 `path` (SVG 文件) 或 `svg` (SVG 文本) +@msg give `xml`, one SVG element 请给出 `xml`,即一个 SVG 元素 +@msg give exactly one of `at`, `rect` and `points` `at`、`rect` 和 `points` 中必须且只能给出一个 +@msg give exactly one of `path`, `data` and `dataBase64` `path`、`data` 和 `dataBase64` 中必须且只能给出一个 +@msg give index or indexes, not both 请给出 index 或 indexes,不能两者都给 +@msg give one element 请给出一个 元素 +@msg give opacity or blend 请给出 opacity 或 blend +@msg handler at {_1} expects {_2} values but its range starts with {d} 位于 {_1} 的处理程序需要 {_2} 个值,但其范围开始时有 {d} 个 +@msg handler depth too large 处理程序深度过大 +@msg handler range {_1}..{_2} out of range 处理程序范围 {_1}..{_2} 超出范围 +@msg hiddenLayers must be true or false hiddenLayers 必须为 true 或 false +@msg hvcC hvcC +@msg images in a spot ink whose samples couldn't be read were imported in RGB 采样无法读取的专色油墨图像已按 RGB 导入 +@msg images past the most pixels one file may hold were left out 超出单个文件像素上限的图像已被略去 +@msg images too large to read were left out 过大而无法读取的图像已被略去 +@msg indexes may hold at most {MAX_GUIDE_INDEXES} entries indexes 最多包含 {MAX_GUIDE_INDEXES} 项 +@msg indexes must be an array of guide indexes indexes 必须是参考线索引的数组 +@msg instruction out of range 指令超出范围 +@msg instruction {pc} ({op}) pops below the stack 指令 {pc} ({op}) 的出栈超出了栈底 +@msg instruction {pc} reached with stack depths {seen} and {d} 到达指令 {pc} 时的栈深度分别为 {seen} 和 {d} +@msg invalid array length 数组长度无效 +@msg invalid group 分组无效 +@msg invalid object id {v}: expected a non-negative integer 对象 ID {v} 无效:应为非负整数 +@msg invalid regular expression flag '{_1}' 正则表达式标志 '{_1}' 无效 +@msg invalid regular expression: {m} 正则表达式无效:{m} +@msg invalid time value 时间值无效 +@msg is a confirm's answer, whose `value` must be true or false 是 confirm 的回答,其 `value` 必须为 true 或 false +@msg is a prompt's answer, whose `value` must be a string or null 是 prompt 的回答,其 `value` 必须是字符串或 null +@msg is an alert's answer, whose `value` must be "ok" 是 alert 的回答,其 `value` 必须是 "ok" +@msg is longer than 16 KiB 长度超过 16 KiB +@msg it draws its image through a clipping path 它通过剪切路径绘制图像 +@msg it draws no image 它没有绘制图像 +@msg it is larger than {MAX_DICTIONARY_FILE_BYTES} bytes 它大于 {MAX_DICTIONARY_FILE_BYTES} 字节 +@msg it isn't a file 它不是文件 +@msg it names {_1} plates, and at most {MAX_PLATES} are read 它列出了 {_1} 个印版,而最多读取 {MAX_PLATES} 个 +@msg its art is too long or too detailed for a head 其图稿过长或过于复杂,不能用作箭头 +@msg its art paints nothing 其图稿没有绘制任何内容 +@msg its image is too large to read 其图像过大,无法读取 +@msg its plates are too large: more than {MAX_SAMPLES} pixels together 其印版过大:合计超过 {MAX_SAMPLES} 像素 +@msg its plates are too large: {_1} plates of {w} × {h} pixels 其印版过大:{_1} 个 {w} × {h} 像素的印版 +@msg its plates can't be kept together 其印版无法保存在一起 +@msg its shapes could not be combined into one 其形状无法合并为一个 +@msg jump to {pc}, past the end 跳转到 {pc},超出了末尾 +@msg legacyNameEncoding must be one of {_1} legacyNameEncoding 必须是 {_1} 之一 +@msg localize: the first argument must be a localization object or a string localize:第一个参数必须是本地化对象或字符串 +@msg missing `picks` 缺少 `picks` +@msg missing `word` 缺少 `word` +@msg must be an object with `kind` and `value` 必须是包含 `kind` 和 `value` 的对象 +@msg must have `kind` alert, confirm or prompt `kind` 必须是 alert、confirm 或 prompt +@msg must have exactly `kind` and `value` 必须只包含 `kind` 和 `value` +@msg needs a closing quote after the name 名称后需要右引号 +@msg needs a name 需要名称 +@msg needs a name of at most 64 bytes 需要不超过 64 字节的名称 +@msg needs a name of letters, digits and _, or a quoted name 需要由字母、数字和 _ 组成的名称,或带引号的名称 +@msg needs a name without control characters 需要不含控制字符的名称 +@msg nesting too deep 嵌套过深 +@msg no HP-GL format 没有 HP-GL 格式 +@msg no New Document profile called `{shown}` ({_1}) 没有名为 `{shown}` 的新建文档配置文件 ({_1}) +@msg no SVG filter at index {k} 索引 {k} 处没有 SVG 滤镜 +@msg no arrowheads file here 这里没有箭头文件 +@msg no character has code {code} 没有代码为 {code} 的字符 +@msg no custom arrowhead {name} (see stroke.arrowheads.list) 没有自定义箭头 {name} (参见 stroke.arrowheads.list) +@msg no file to open 没有要打开的文件 +@msg no instructions 没有指令 +@msg no layer to rename 没有要重命名的图层 +@msg no random numbers ({e}) 无法获取随机数 ({e}) +@msg no selected or given object has a visible 3D effect 所选或指定的对象都没有可见的 3D 效果 +@msg no such artboard {v} 没有画板 {v} +@msg no symbol named `{_1}` 没有名为 `{_1}` 的符号 +@msg node {_1} already has the text variable `{_2}` 节点 {_1} 已有文本变量 `{_2}` +@msg not a DCS file 不是 DCS 文件 +@msg not a RegExp object 不是 RegExp 对象 +@msg not a regular expression 不是正则表达式 +@msg not an action set: the text doesn't start with /version and a number 不是动作组:文本不是以 /version 和一个数字开头 +@msg nothing to repeat 没有可重复的内容 +@msg numbers out of order in {_1} quantifier 量词 {_1} 中的数字顺序颠倒 +@msg object {_1} needs a 3D effect 对象 {_1} 需要有 3D 效果 +@msg object {_1}: a hollow Extrude & Bevel has no caps: turn on Cap to map a graphic 对象 {_1}:空心的突出与斜角没有端面:请打开“端点”后再贴图 +@msg object {_1}: with a depth of 0 the object has one face: map its graphics onto the front 对象 {_1}:深度为 0 时对象只有一个面:请将图形贴到正面 +@msg object {_1}: {_2} 对象 {_1}:{_2} +@msg object {_1}: {e} 对象 {_1}:{e} +@msg object {_1}: {name} at index {index} has no lighting presets; set its lights with effect.setParams 对象 {_1}:索引 {index} 处的 {name} 没有照明预设;请用 effect.setParams 设置其光源 +@msg opacity must be a number opacity 必须是数字 +@msg operand too large 操作数过大 +@msg order is a whole number from 0 to {others}, not `{v}` order 是 0 到 {others} 之间的整数,不能是 `{v}` +@msg path chunks must share their boundary point 相邻的路径分段必须共用边界点 +@msg pdfCompatible: false needs the artboards to keep the editable artwork when useArtboards is false pdfCompatible: false 在 useArtboards 为 false 时需要用画板来保留可编辑的图稿 +@msg point x must be a number 点的 x 必须是数字 +@msg point y must be a number 点的 y 必须是数字 +@msg points must be [x,y] pairs points 必须是 [x,y] 对 +@msg points must be finite canvas points points 必须是画布上的有限坐标点 +@msg range out of order in character class 字符类中的范围顺序颠倒 +@msg reduce of an empty array with no initial value 对空数组调用 reduce 且没有初始值 +@msg regular expression flag '{_1}' given twice 正则表达式标志 '{_1}' 给出了两次 +@msg sampled text would exceed the canvas (coordinates out of range) 取样的文本会超出画布 (坐标超出范围) +@msg select an SVG filter 请选择一个 SVG 滤镜 +@msg select an object with a 3D effect 请选择带有 3D 效果的对象 +@msg select an object with a 3D effect, or give its id in ids 请选择带有 3D 效果的对象,或在 ids 中给出其 ID +@msg select one layer, group or object to rename 请选择一个要重命名的图层、编组或对象 +@msg select the art to weed 请选择要除废的图稿 +@msg select the banner, or mark every artboard 请选择横幅,或为每个画板添加标记 +@msg select type on a path or area type 请选择路径文字或区域文字 +@msg side is left or right, not `{v}` side 是 left 或 right,不能是 `{v}` +@msg source text transform is collapsed or unrepresentable 源文本的变换已塌缩或无法表示 +@msg table must be true or false table 必须为 true 或 false +@msg takes one name 只接受一个名称 +@msg target text transform is collapsed or unrepresentable 目标文本的变换已塌缩或无法表示 +@msg that makes too many pieces (at most 100000) or dash steps: simplify the dash pattern or make the dashes longer 这样生成的片段 (最多 100000 个) 或虚线步数过多:请简化虚线图案或加长虚线 +@msg the Layers panel is not shown “图层”面板未显示 +@msg the Layers panel's search, filter or Show Layers Only hides its row “图层”面板的搜索、筛选或“仅显示图层”隐藏了它所在的行 +@msg the Objects on Path group is hidden or locked “路径上的对象”组已隐藏或锁定 +@msg the SVG file is larger than {MAX_IMPORT_BYTES} bytes SVG 文件大于 {MAX_IMPORT_BYTES} 字节 +@msg the XML operation needs an XML or XMLList value 此 XML 操作需要 XML 或 XMLList 值 +@msg the XPath expression does not evaluate to a node-set XPath 表达式的求值结果不是节点集 +@msg the XPath expression ends early XPath 表达式提前结束 +@msg the XPath expression has an unmatched ) XPath 表达式中有不匹配的 ) +@msg the XPath expression has an unmatched ] XPath 表达式中有不匹配的 ] +@msg the XPath expression has an unmatched closing bracket XPath 表达式中有不匹配的右括号 +@msg the XPath expression selects a namespace node, which is not an XML value XPath 表达式选中了命名空间节点,它不是 XML 值 +@msg the XPath expression selects the root node, which is not an XML value XPath 表达式选中了根节点,它不是 XML 值 +@msg the XPath function {name} needs a node-set XPath 函数 {name} 需要节点集 +@msg the XPath function {name} takes the wrong number of arguments XPath 函数 {name} 的参数个数不正确 +@msg the XPath name functions need a node-set XPath 的名称函数需要节点集 +@msg the XPath prefix {shown} is not bound to a namespace XPath 前缀 {shown} 未绑定到命名空间 +@msg the XPath variable ${shown} is not bound XPath 变量 ${shown} 未绑定 +@msg the argument list is not an object 参数列表不是对象 +@msg the artwork is too large for EPS: what follows 256 MB of PostScript was left out 图稿过大,无法完整写入 EPS:256 MB 之后的 PostScript 内容已被略去 +@msg the banner has no width or height 横幅没有宽度或高度 +@msg the bitmaps hold more pixels than one file may 位图的像素数超过了单个文件的上限 +@msg the code runs past its last instruction 代码运行到了最后一条指令之后 +@msg the curve tolerance must be from 0.01 to 10 points 曲线容差必须为 0.01 到 10 点 +@msg the custom dictionary couldn't be saved to {_1}: {e} 无法将自定义词典保存到 {_1}:{e} +@msg the custom dictionary {_1} can't be read ({why}), so it is left as it is 无法读取自定义词典 {_1} ({why}),因此保持原样 +@msg the document already has an SVG filter named {name} 文档中已有名为 {name} 的 SVG 滤镜 +@msg the document has another SVG filter named {name}: use svgFilter.apply, or svgFilter.define with `replace` to edit it 文档中已有另一个名为 {name} 的 SVG 滤镜:请使用 svgFilter.apply,或使用带 `replace` 的 svgFilter.define 来编辑它 +@msg the document has no SVG filter named {name} 文档中没有名为 {name} 的 SVG 滤镜 +@msg the document has no SVG filter named {r} 文档中没有名为 {r} 的 SVG 滤镜 +@msg the document has no color swatches 文档中没有颜色色板 +@msg the document's swatches hold {_1} colors; the document reduction takes at most 255 文档的色板含有 {_1} 种颜色;document 减色方式最多使用 255 种 +@msg the file isn't valid SVG: {e} 文件不是有效的 SVG:{e} +@msg the file names no plate 文件没有列出任何印版 +@msg the function's prototype is not an object 函数的原型不是对象 +@msg the grommet inset is too large for the banner 扣眼边距对横幅而言过大 +@msg the guide is hidden or locked 参考线已隐藏或锁定 +@msg the hem is too deep for the banner 折边对横幅而言过深 +@msg the library's file is outside the read roots 库文件位于可读取的根目录之外 +@msg the line is too long, or the paths it passes too many, to check 线条过长,或它经过的路径过多,无法检查 +@msg the method needs an XML value 此方法需要 XML 值 +@msg the paint is not a freeform gradient (apply one with paint.editGradient {kind}) 该上色不是自由渐变 (请用 paint.editGradient {kind} 应用一个) +@msg the parameters or body do not form one function 参数或函数体不能构成一个函数 +@msg the pasted SVG filter {name} draws differently from the document's filter of that name, and no free name fits it 粘贴的 SVG 滤镜 {name} 与文档中同名滤镜的绘制结果不同,且没有可用的空闲名称 +@msg the paths are too many or too tangled to find where they cross 路径过多或过于交错,无法找出它们的交叉位置 +@msg the pen table holds more than 255 pens 笔表中的笔超过 255 支 +@msg the plates can't be kept 无法保留这些印版 +@msg the position table does not match the instructions 位置表与指令不匹配 +@msg the prompt's text is longer than {MAX_PROMPT_BYTES} bytes 提示文本的长度超过 {MAX_PROMPT_BYTES} 字节 +@msg the radix must be between 2 and 36 基数必须介于 2 到 36 之间 +@msg the rectangle or line reaches too many paths to check 矩形或线条涉及的路径过多,无法检查 +@msg the right side of 'in' is not an object 'in' 的右侧不是对象 +@msg the right side of 'instanceof' is not a function 'instanceof' 的右侧不是函数 +@msg the selected art is nested more than {MAX_DEPTH} levels deep 所选图稿的嵌套超过 {MAX_DEPTH} 层 +@msg the selected art is too large to measure 所选图稿过大,无法测量 +@msg the selection has no outlines to weed around 所选对象没有可供除废的轮廓 +@msg the spacing makes more than {MAX_GROMMETS} grommets 此间距会生成超过 {MAX_GROMMETS} 个扣眼 +@msg the value bind was called on is not a function 调用 bind 的值不是函数 +@msg the value is not a constructor 该值不是构造函数 +@msg the value is not a function 该值不是函数 +@msg the value is not a valid XML name 该值不是有效的 XML 名称 +@msg the value is not an XML attribute name 该值不是 XML 属性名称 +@msg the value is not an XML name 该值不是 XML 名称 +@msg the weeding lines cross the art's outlines too many times to measure; use fewer lines 除废线与图稿轮廓的交叉次数过多,无法测量;请减少除废线 +@msg the weeding lines pass too close to too many outline segments to measure; use fewer lines or a smaller gap 除废线离过多的轮廓线段太近,无法测量;请减少除废线或缩小间隙 +@msg the weeding lines would be more than {max_pieces} pieces 除废线会超过 {max_pieces} 段 +@msg the {_1} plate lies outside the file {_1} 印版位于文件之外 +@msg there is no banner to mark 没有可添加标记的横幅 +@msg this is a binary DXF file, which can't be read: save it from the CAD app as an ASCII DXF and open that 这是二进制 DXF 文件,无法读取:请在 CAD 应用中将其另存为 ASCII DXF 后再打开 +@msg this warp's points can't be edited: use Reset with Mesh first 此变形的点无法编辑:请先使用“以网格重置” +@msg toExponential() digits must be between 0 and 20 toExponential() 的位数必须介于 0 到 20 之间 +@msg toFixed() digits must be between 0 and 20 toFixed() 的位数必须介于 0 到 20 之间 +@msg toISOString is not a function toISOString 不是函数 +@msg toPrecision() argument must be between 1 and 21 toPrecision() 的参数必须介于 1 到 21 之间 +@msg toSource: a getter cannot be written as source toSource:getter 无法写成源代码 +@msg toSource: an array with properties besides its elements, a property that is not enumerable or another prototype cannot be written as source toSource:除元素外还有其他属性、含不可枚举属性或使用其他原型的数组无法写成源代码 +@msg toSource: an object with a property that is not enumerable, or whose prototype is not Object.prototype, cannot be written as source toSource:含不可枚举属性或原型不是 Object.prototype 的对象无法写成源代码 +@msg toSource: the source is too long toSource:源代码过长 +@msg toSource: the value contains itself toSource:该值包含自身 +@msg toSource: the value has too many entries toSource:该值的条目过多 +@msg toSource: the value is nested too deep toSource:该值嵌套过深 +@msg toString is not a function toString 不是函数 +@msg too many arguments 参数过多 +@msg too many quantifiers 量词过多 +@msg turn on at least one side 请至少打开一条边 +@msg unknown OpenType feature `{t}` (liga, calt, dlig, smcp, frac, onum, tnum, ordn, swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom, aalt; prefix - to turn off; swsh, ss01–ss20, salt, titl, pnum, lnum, sups, subs, numr, dnom and aalt take an alternate's number as `tag=N` with N from 0 to {_1}, as `salt=2`) 未知的 OpenType 特性 `{t}` (liga、calt、dlig、smcp、frac、onum、tnum、ordn、swsh、ss01–ss20、salt、titl、pnum、lnum、sups、subs、numr、dnom、aalt;加前缀 - 表示关闭;swsh、ss01–ss20、salt、titl、pnum、lnum、sups、subs、numr、dnom 和 aalt 可用 `tag=N` 指定替代字形的编号,N 为 0 到 {_1},例如 `salt=2`) +@msg unknown dither `{d}` (diffusion, pattern, noise or none) 未知的仿色 `{d}` (diffusion、pattern、noise 或 none) +@msg unknown panel `{panel}` 未知的面板 `{panel}` +@msg unknown parameter `{key}` 未知的参数 `{key}` +@msg unknown parameter `{within}settings.{format}`; {why} 未知的参数 `{within}settings.{format}`;{why} +@msg unknown parameter `{within}{key}`; {why} 未知的参数 `{within}{key}`;{why} +@msg unknown reduction `{reduction}` (selective, perceptual, adaptive, web, gray, blackWhite, uniform or document) 未知的减色方式 `{reduction}` (selective、perceptual、adaptive、web、gray、blackWhite、uniform 或 document) +@msg unmatched ')' 不匹配的 ')' +@msg unterminated character class 字符类没有结束 +@msg unterminated group 分组没有结束 +@msg with no hem and no grommets there is nothing to mark 没有折边也没有扣眼,因此没有可标记的内容 +@msg wordGap and lineGap need area type in the selection wordGap 和 lineGap 需要所选对象中有区域文字 +@msg xpath needs its variables as an object xpath 的变量必须以对象形式给出 +@msg xpath needs the expression as a string xpath 的表达式必须以字符串形式给出 +@msg {GRANT_NOT_REMOVED}, and its settings and cache can't be moved back from {_1} to {_2}: {e} {GRANT_NOT_REMOVED},而且其设置和缓存无法从 {_1} 移回 {_2}:{e} +@msg {_1} is not a folder: {e} {_1} 不是文件夹:{e} +@msg {_1}: {_2} and {_3} are in the way {_1}:被 {_2} 和 {_3} 挡住 +@msg {code}: {0} {code}:{0} +@msg {dir}: {e} {dir}:{e} +@msg {file} names more than one linked JavaScript file {file} 指定了多个链接的 JavaScript 文件 +@msg {k}: a custom arrowhead's path needs an area to fill and at most {most} anchors and {most} subpaths, each anchor within {far} head units of its tip {k}:自定义箭头的路径需要有可填充的面积,最多 {most} 个锚点和 {most} 条子路径,且每个锚点距箭头尖端不超过 {far} 个箭头单位 +@msg {k}: bad custom arrowhead path: {e} {k}:自定义箭头路径无效:{e} +@msg {k}: bad custom arrowhead: {e} {k}:自定义箭头无效:{e} +@msg {k}: custom must be a name {k}:custom 必须是名称 +@msg {name} must be a number {name} 必须是数字 +@msg {path}: copy takes a DCS file only when its plates are inside it; relink to it where its plate files are {path}:只有印版包含在文件内时才能复制 DCS 文件;请在其印版文件所在的位置重新链接它 +@msg {path}: larger than {_1} MB {path}:大于 {_1} MB +@msg {saving} failed: {e} {saving} 失败:{e} +@msg {shown} is not an XPath 1.0 function {shown} 不是 XPath 1.0 函数 +@msg {v} names no layer or sublayer {v} 不对应任何图层或子图层 +@msg {what} is not a function {what} 不是函数 +@msg {what} would print more than {MAX_COPIES_PER_SHEET} copies on a sheet: scale it up {what} 在一张纸上会打印超过 {MAX_COPIES_PER_SHEET} 份:请将其放大 +@msg {what} {i} out of range {what} {i} 超出范围 +@msg A profile supports one Revolve effect. Edit the existing effect's options instead. 一个轮廓只支持一个回转效果。请改为编辑现有效果的选项。 +@msg Add Points at Crossings: a path went missing 在交叉处添加锚点:有一条路径丢失 +@msg CSS options: {e} CSS 选项:{e} +@msg CSV has a header but no data rows CSV 有标题行,但没有数据行 +@msg CSV holds NUL bytes: save it as UTF-8, or as UTF-16 with a byte-order mark CSV 中含有 NUL 字节:请将其保存为 UTF-8,或带字节顺序标记的 UTF-16 +@msg CSV is empty CSV 为空 +@msg CSV is larger than {MAX_CSV_BYTES} bytes CSV 大于 {MAX_CSV_BYTES} 字节 +@msg CSV is not UTF-16 CSV 不是 UTF-16 +@msg CSV is not UTF-8 CSV 不是 UTF-8 +@msg CSV would be larger than {MAX_CSV_BYTES} bytes CSV 会大于 {MAX_CSV_BYTES} 字节 +@msg Choose original artwork, not a dimension's generated paths 请选择原始图稿,不要选择尺寸标注生成的路径 +@msg Contour: select paths or shapes 轮廓:请选择路径或形状 +@msg Contour: the Offset Path effect is not available 轮廓:偏移路径效果不可用 +@msg Create needs fills, strokes or both 创建时需要填色、描边或两者 +@msg DXF options: {e} DXF 选项:{e} +@msg DXF scale must be a positive number up to {_1}, not {scale} DXF 缩放比例必须是不超过 {_1} 的正数,不能是 {scale} +@msg DXF {field} `{s}`: {allowed} DXF {field} `{s}`:{allowed} +@msg Dimension is locked or hidden 尺寸标注已锁定或隐藏 +@msg Dimension mode does not match its source 尺寸标注的模式与其来源不匹配 +@msg Draw Inside needs exactly one selected path, compound path or text 内部绘图需要恰好选中一条路径、一条复合路径或一个文本 +@msg EPS Save writes all artwork: use document.export for selected-only EPS 保存 EPS 会写入全部图稿:要只导出所选对象的 EPS,请使用 document.export +@msg EPS flattener: {e} EPS 拼合器:{e} +@msg EPS options: {e} EPS 选项:{e} +@msg EPS {field} `{s}`: {allowed} EPS {field} `{s}`:{allowed} +@msg Export for Screens writes png, png8, jpg, webp, gif, tiff, bmp, tga, psd, svg, svgz, pdf, eps, ai or vectorcraft 导出为屏幕所用格式可写出 png、png8、jpg、webp、gif、tiff、bmp、tga、psd、svg、svgz、pdf、eps、ai 或 vectorcraft +@msg Flare click coordinates must be finite numbers 光晕的单击坐标必须是有限的数字 +@msg HTML Text applies to object slices of type only HTML 文本只适用于文字对象的切片 +@msg Identify Objects By is XML ID: the name must be a valid XML ID (start with a letter, underscore or colon; no spaces) 对象识别方式为 XML ID:名称必须是有效的 XML ID (以字母、下划线或冒号开头;不含空格) +@msg Image Trace failed: internal error: its thread stopped (please report this bug) 图像描摹失败:内部错误:其线程已停止 (请报告此错误) +@msg Image Trace failed: {e} 图像描摹失败:{e} +@msg Image Trace finished, but its document was closed 图像描摹已完成,但其文档已关闭 +@msg Image Trace finished, but the image or its settings changed meanwhile: trace it again 图像描摹已完成,但图像或其设置在此期间发生了更改:请重新描摹 +@msg Invalid dimension offset 尺寸标注的偏移无效 +@msg Invalid point 点无效 +@msg Join: a subpath went missing 连接:有一条子路径丢失 +@msg Lock Workspace is not available in VectorCraft VectorCraft 不提供锁定工作区 +@msg Move Panel to New Window is not available: floating panels stay in the main window 无法将面板移到新窗口:浮动面板只能留在主窗口中 +@msg Offset Path: the path is too complex to offset 偏移路径:路径过于复杂,无法偏移 +@msg Offset Path: {e} 偏移路径:{e} +@msg PDF content is incomplete or exceeds the reader's limits; no document was imported PDF 内容不完整或超出读取器的限制;未导入任何文档 +@msg PDF flattener: {e} PDF 拼合器:{e} +@msg PDF options: {e} PDF 选项:{e} +@msg Recolor Artwork is no longer open 重新着色图稿已不再打开 +@msg SVG is not UTF-8, UTF-16 or ISO 8859-1 SVG 不是 UTF-8、UTF-16 或 ISO 8859-1 +@msg Save writes one artboard: name one, or export several with document.export 保存只写入一个画板:请指定一个画板,或用 document.export 导出多个 +@msg Select a dimension first 请先选择一个尺寸标注 +@msg Select dimensions to update 请选择要更新的尺寸标注 +@msg Select only dimensions 请只选择尺寸标注 +@msg Select two or more objects, or a group 请选择两个或更多对象,或一个编组 +@msg Shaper needs 1–64 source paths Shaper 需要 1–64 条源路径 +@msg Switch x/y is for scatter graphs 切换 x/y 用于散点图 +@msg Text options: {e} 文本选项:{e} +@msg `align` is start, center or end `align` 为 start、center 或 end +@msg `anchors` must be an array of [subpath, anchor] pairs `anchors` 必须是 [subpath, anchor] 对的数组 +@msg `anchors` must list anchor indices (0, 1, 2…), not {k} `anchors` 必须列出锚点索引 (0, 1, 2…),不能是 {k} +@msg `anchors` must list anchor indices (0, 1, 2…), not {v} `anchors` 必须列出锚点索引 (0, 1, 2…),不能是 {v} +@msg `artboards` must be a list of artboard indices `artboards` 必须是画板索引的列表 +@msg `asset` is the asset id (see assets.list) `asset` 是资源 ID (参见 assets.list) +@msg `at` must be a byte offset `at` 必须是字节偏移 +@msg `autoLeading` must be a number `autoLeading` 必须是数字 +@msg `background` must be "", "matte" or #rrggbb `background` 必须是 ""、"matte" 或 #rrggbb +@msg `burasagari` must be "none", "standard" or "forced" `burasagari` 必须是 "none"、"standard" 或 "forced" +@msg `center` must be [x, y] `center` 必须是 [x, y] +@msg `charAlign` must be "romanBaseline", "emBoxTop", "emBoxCenter", "emBoxBottom", "icfTop" or "icfBottom" `charAlign` 必须是 "romanBaseline"、"emBoxTop"、"emBoxCenter"、"emBoxBottom"、"icfTop" 或 "icfBottom" +@msg `charDirection` must be "default", "leftToRight" or "rightToLeft" `charDirection` 必须是 "default"、"leftToRight" 或 "rightToLeft" +@msg `clearStroke` requires `stroke: false`, `ids: []` and no `stop` `clearStroke` 要求 `stroke: false`、`ids: []`,且不给出 `stop` +@msg `clear` must be a list of type object ids, not {v} `clear` 必须是文字对象 ID 的列表,不能是 {v} +@msg `clear` must list type objects; {_1} is not one `clear` 必须列出文字对象;{_1} 不是文字对象 +@msg `clear` must not list `id` `clear` 不能列出 `id` +@msg `color` needs a `rule` to make the theme from `color` 需要用于生成主题的 `rule` +@msg `colors` is empty `colors` 为空 +@msg `columnType` is vertical, uniform, repeating or sliding `columnType` 为 vertical、uniform、repeating 或 sliding +@msg `columns` must be a whole number 1..50 `columns` 必须是 1..50 的整数 +@msg `commands` must not be empty `commands` 不能为空 +@msg `contents` goes with `to` `contents` 要与 `to` 一起给出 +@msg `corners` must be sharp, round or square, not `{other}` `corners` 必须是 sharp、round 或 square,不能是 `{other}` +@msg `corners` must list anchor indices (0, 1, 2…), not {v} `corners` 必须列出锚点索引 (0, 1, 2…),不能是 {v} +@msg `count` must be a whole number from 1 to 100 `count` 必须是 1 到 100 之间的整数 +@msg `dataBase64` is larger than the module limit `dataBase64` 超过了模块大小上限 +@msg `dataBase64` is not valid base64 `dataBase64` 不是有效的 base64 +@msg `defaultsOnly` cannot be combined with `ids`, `reverse` or `append` `defaultsOnly` 不能与 `ids`、`reverse` 或 `append` 同时使用 +@msg `depth` and `childLimit` slice the summary: pass `summary: true` `depth` 和 `childLimit` 用于截取摘要:请传入 `summary: true` +@msg `design` is a design name, or null for plain columns `design` 是设计名称,或用 null 表示普通柱形 +@msg `design` is a design name, or null for the default marker `design` 是设计名称,或用 null 表示默认标记 +@msg `destinationFolder` is required when destination is `folder` destination 为 `folder` 时必须给出 `destinationFolder` +@msg `destination` must be `none`, `saveAndClose` or `folder` `destination` 必须是 `none`、`saveAndClose` 或 `folder` +@msg `diameter` must be from 0.1 to 2500 pt `diameter` 必须为 0.1 到 2500 pt +@msg `digits` must be one of {_1} `digits` 必须是 {_1} 之一 +@msg `dimImages` is a percentage or false `dimImages` 是百分比或 false +@msg `direction` must be "auto", "leftToRight" or "rightToLeft" `direction` 必须是 "auto"、"leftToRight" 或 "rightToLeft" +@msg `each` is the value one design stands for, more than 0 `each` 是一个设计所代表的数值,须大于 0 +@msg `end` must be "start" or "end" `end` 必须是 "start" 或 "end" +@msg `ends` applies to joining exactly two paths `ends` 只适用于恰好连接两条路径的情况 +@msg `event` must be one of {_1} `event` 必须是 {_1} 之一 +@msg `every` must be "all", "odd" or "even", not {v} `every` 必须是 "all"、"odd" 或 "even",不能是 {v} +@msg `expand` must be true or false `expand` 必须为 true 或 false +@msg `files` must be 1 to 1000 paths of font files the last text.findFontFiles search found `files` 必须是上次 text.findFontFiles 搜索找到的 1 到 1000 个字体文件路径 +@msg `folder` must be an absolute folder path `folder` 必须是文件夹的绝对路径 +@msg `fontAxes` must be an object of axis tags and values, such as {"wght"} `fontAxes` 必须是由轴标签和值组成的对象,例如 {"wght"} +@msg `format` must be a string `format` 必须是字符串 +@msg `format` must be one of {_1} `format` 必须是 {_1} 之一 +@msg `fractions` is chop or scale `fractions` 为 chop 或 scale +@msg `from` and `to` must differ `from` 和 `to` 必须不同 +@msg `from` has an empty frame `from` 的文本框为空 +@msg `from` must be a non-negative integer `from` 必须是非负整数 +@msg `from` must be area type or type on a path `from` 必须是区域文字或路径文字 +@msg `gid` must be a glyph id, not {v} `gid` 必须是字形 ID,不能是 {v} +@msg `glyphAlternate` belongs to one character, not to a character style (text.setGlyphAlternate sets it) `glyphAlternate` 属于单个字符,不属于字符样式 (由 text.setGlyphAlternate 设置) +@msg `guide` must be a guide index (guide.list) `guide` 必须是参考线索引 (guide.list) +@msg `hAlign` must be default, left, center or right `hAlign` 必须是 default、left、center 或 right +@msg `id` must be a node id `id` 必须是节点 ID +@msg `id` must be an object id, not {v} `id` 必须是对象 ID,不能是 {v} +@msg `ids` must name Objects on Path groups or objects in them `ids` 必须指定“路径上的对象”组或其中的对象 +@msg `ids` takes at most {MAX_INSPECT_IDS} ids `ids` 最多接受 {MAX_INSPECT_IDS} 个 ID +@msg `imageMap` must be none, rectangle or polygon `imageMap` 必须是 none、rectangle 或 polygon +@msg `includeArtboardFill` must be true or false `includeArtboardFill` 必须为 true 或 false +@msg `includeSubdirs` must be true or false `includeSubdirs` 必须为 true 或 false +@msg `index` must be a point index `index` 必须是点索引 +@msg `index` must be a whole number or null `index` 必须是整数或 null +@msg `indices` must be a non-empty array of item indices `indices` 必须是项目索引的非空数组 +@msg `items` is a list of {kind, item} `items` 是 {kind, item} 的列表 +@msg `items` is empty `items` 为空 +@msg `items` must be a list of {id, points} `items` 必须是 {id, points} 的列表 +@msg `items` must be a list of {{id, anchors?}}, not {v} `items` 必须是 {{id, anchors?}} 的列表,不能是 {v} +@msg `items` must be an array of {id, anchors} `items` 必须是 {id, anchors} 的数组 +@msg `items` must be an array of {{id, corners?}}, not {v} `items` 必须是 {{id, corners?}} 的数组,不能是 {v} +@msg `kashida` must be "none", "short", "medium", "long" or "stylistic" `kashida` 必须是 "none"、"short"、"medium"、"long" 或 "stylistic" +@msg `kind` is one of {_1} `kind` 为 {_1} 之一 +@msg `kind` must be "round", "invertedRound" or "chamfer", not {v} `kind` 必须是 "round"、"invertedRound" 或 "chamfer",不能是 {v} +@msg `kind` must be `text`, `visibility`, `image` or `graph` `kind` 必须是 `text`、`visibility`、`image` 或 `graph` +@msg `kind` must be a string `kind` 必须是字符串 +@msg `kind` must be image, noImage or htmlText `kind` 必须是 image、noImage 或 htmlText +@msg `kinsoku` must be "none", "hard" or "soft" `kinsoku` 必须是 "none"、"hard" 或 "soft" +@msg `label` must be a non-empty name of at most 200 characters `label` 必须是不超过 200 个字符的非空名称 +@msg `language` must be a language name or tag, such as "Turkish" or "tr" `language` 必须是语言名称或标签,例如 "Turkish" 或 "tr" +@msg `leadingModel` must be "romanBaseline" or "emBoxTop" `leadingModel` 必须是 "romanBaseline" 或 "emBoxTop" +@msg `leading` must be from 0.1 to 5000, or null for Auto `leading` 必须为 0.1 到 5000,或用 null 表示自动 +@msg `length` must be a number of at most 1000000 pt either way `length` 必须是数字,正负方向均不超过 1000000 pt +@msg `line` must be true or false `line` 必须为 true 或 false +@msg `minAngle` ({min_angle}°) can't be more than `maxAngle` ({max_angle}°) `minAngle` ({min_angle}°) 不能大于 `maxAngle` ({max_angle}°) +@msg `mode` must be "handles", "straight" or "smooth" `mode` 必须是 "handles"、"straight" 或 "smooth" +@msg `mode` must be "straight", "bezier" or "arc" `mode` 必须是 "straight"、"bezier" 或 "arc" +@msg `mode` must be one of {_1} `mode` 必须是 {_1} 之一 +@msg `mojikumi` must be "none", "yakumonoHankaku", "gyomatsuYakumonoHankaku", "gyomatsuYakumonoZenkaku", "yakumonoZenkaku" or "lineEndHalf" `mojikumi` 必须是 "none"、"yakumonoHankaku"、"gyomatsuYakumonoHankaku"、"gyomatsuYakumonoZenkaku"、"yakumonoZenkaku" 或 "lineEndHalf" +@msg `names` is a list of preset names `names` 是预设名称的列表 +@msg `names` lists preset names `names` 用于列出预设名称 +@msg `names` must be a list of names `names` 必须是名称的列表 +@msg `names` must be a list of swatch names `names` 必须是色板名称的列表 +@msg `names` must be an array of style names `names` 必须是样式名称的数组 +@msg `new` is a design name, or true `new` 是设计名称,或 true +@msg `path` must be a folder `path` 必须是文件夹 +@msg `path` must be a path object `path` 必须是路径对象 +@msg `pivot` must be a whole number from 0 to 8 `pivot` 必须是 0 到 8 之间的整数 +@msg `points` must be a list of [x, y] points `points` 必须是 [x, y] 点的列表 +@msg `points` must be a list of point indexes `points` 必须是点索引的列表 +@msg `points` must be an array of point indices `points` 必须是点索引的数组 +@msg `points` must be point indexes `points` 必须是点索引 +@msg `port` must be "in" or "out" `port` 必须是 "in" 或 "out" +@msg `position` is above or below `position` 为 above 或 below +@msg `preset` must be a string `preset` 必须是字符串 +@msg `ratio` must be from 0.1 to 0.5 `ratio` 必须为 0.1 到 0.5 +@msg `rect` must be [x, y, width, height] with a positive size `rect` 必须是尺寸为正的 [x, y, width, height] +@msg `relation` must be overlap, inside or outside `relation` 必须是 overlap、inside 或 outside +@msg `reversed` must be true or false `reversed` 必须为 true 或 false +@msg `rotate` must be a finite number `rotate` 必须是有限的数字 +@msg `rule` must be nonZero or evenOdd `rule` 必须是 nonZero 或 evenOdd +@msg `scale` must be a number in (0, 100] `scale` 必须是 (0, 100] 范围内的数字 +@msg `scope` must be "selection" or "document", not {other} `scope` 必须是 "selection" 或 "document",不能是 {other} +@msg `script` must be a string `script` 必须是字符串 +@msg `seriesIndexes` is a non-empty list of series indexes `seriesIndexes` 是系列索引的非空列表 +@msg `seriesIndexes` needs a `type` or a `valueAxis` `seriesIndexes` 需要 `type` 或 `valueAxis` +@msg `shift` must be a number of points `shift` 必须是以点为单位的数字 +@msg `showCenter` must be true or false `showCenter` 必须为 true 或 false +@msg `singleWordJustification` must be "fullJustify", "alignLeft", "alignCenter" or "alignRight" `singleWordJustification` 必须是 "fullJustify"、"alignLeft"、"alignCenter" 或 "alignRight" +@msg `size` is a percentage of the base text's size, 1 to 100 `size` 是相对于基础文本大小的百分比,取值 1 到 100 +@msg `source` and `target` are the same object `source` 和 `target` 是同一个对象 +@msg `source` must be `folder` or `datasets` `source` 必须是 `folder` 或 `datasets` +@msg `spacing` must be a number `spacing` 必须是数字 +@msg `spacing` must be a number of points, 0 or more, or "auto" `spacing` 必须是以点为单位且大于或等于 0 的数字,或 "auto" +@msg `start` must be a finite number `start` 必须是有限的数字 +@msg `starts` must be an array of anchor indices (or nulls) `starts` 必须是锚点索引 (或 null) 的数组 +@msg `status` must be linked, embedded, missing or modified, or a list of them `status` 必须是 linked、embedded、missing 或 modified,或由它们组成的列表 +@msg `stop` must be a stop index `stop` 必须是色标索引 +@msg `story` styles the whole story: give no `start` or `end` `story` 为整篇文章设置样式:请不要给出 `start` 或 `end` +@msg `story` takes the whole story: give no `start` or `end` `story` 针对整篇文章:请不要给出 `start` 或 `end` +@msg `strokeMode` applies to strokes (stroke: true) `strokeMode` 适用于描边 (stroke: true) +@msg `style` must be roman or japanese `style` 必须是 roman 或 japanese +@msg `subpaths` must be two integer indices `subpaths` 必须是两个整数索引 +@msg `symbol` must be a symbol name `symbol` 必须是符号名称 +@msg `target` must be "object" or "contents", not {v} `target` 必须是 "object" 或 "contents",不能是 {v} +@msg `tint` applies to global and spot colours `tint` 适用于全局色和专色 +@msg `tint` goes with a global or spot `swatch` `tint` 要与全局色或专色的 `swatch` 一起给出 +@msg `tint` must be a number (%) `tint` 必须是数字 (%) +@msg `tint` needs a global or spot colour `tint` 需要全局色或专色 +@msg `to` is already threaded `to` 已被串接 +@msg `to` is the design's new name `to` 是设计的新名称 +@msg `to` must be [x, y] `to` 必须是 [x, y] +@msg `to` must be a closed path, area type or type on a path other than `from` `to` 必须是 `from` 以外的闭合路径、区域文字或路径文字 +@msg `to` must be a whole number `to` 必须是整数 +@msg `to` takes the rows of one object's stack, not aligned rows `to` 接受的是单个对象堆栈中的行,不是对齐的行 +@msg `useLab` must be true or false `useLab` 必须为 true 或 false +@msg `vAlign` must be default, top, middle, baseline or bottom `vAlign` 必须是 default、top、middle、baseline 或 bottom +@msg `values` must be a list `values` 必须是列表 +@msg `values` must be an object `values` 必须是对象 +@msg `values` must name hex, rgb, cmyk, lab or gray `values` 必须指定 hex、rgb、cmyk、lab 或 gray +@msg `values` names a kind twice `values` 重复指定了同一种类型 +@msg `{_1}` can't be edited or removed `{_1}` 不能编辑或移除 +@msg `{_1}` is built in and always listed `{_1}` 是内置项,始终会列出 +@msg `{_1}` is not a font file the last text.findFontFiles search found `{_1}` 不是上次 text.findFontFiles 搜索找到的字体文件 +@msg `{_1}` would be overwritten by the package: choose a different folder or name 打包会覆盖 `{_1}`:请选择其他文件夹或名称 +@msg `{file}` has no art to place `{file}` 中没有可置入的图稿 +@msg `{file}` is over {_1} MB `{file}` 超过 {_1} MB +@msg `{f}` belongs to a VectorCraft that is running `{f}` 属于正在运行的 VectorCraft +@msg `{f}` is the copy of a document open now `{f}` 是当前已打开文档的副本 +@msg `{id}` is no writable format (see document.formats) `{id}` 不是可写入的格式 (参见 document.formats) +@msg `{id}` isn't a loaded library `{id}` 不是已载入的库 +@msg `{key}` has no colours `{key}` 中没有颜色 +@msg `{key}` is a design name `{key}` 是设计名称 +@msg `{key}` is a design name or a list of them `{key}` 是一个设计名称或设计名称的列表 +@msg `{key}` is longer than {MAX_NAME} characters `{key}` 超过 {MAX_NAME} 个字符 +@msg `{key}` is out of range `{key}` 超出范围 +@msg `{key}` must be [min, desired, max] or {{min?, desired?, max?}}, in percent `{key}` 必须是以百分比表示的 [min, desired, max] 或 {{min?, desired?, max?}} +@msg `{key}` must be a non-negative integer `{key}` 必须是非负整数 +@msg `{key}` must be a number {_1}..{_2} `{key}` 必须是 {_1}..{_2} 的数字 +@msg `{key}` must be a whole number of at least 0, not {n} `{key}` 必须是不小于 0 的整数,不能是 {n} +@msg `{key}` must be an appearance item index (paint order) or null `{key}` 必须是外观项索引 (绘制顺序) 或 null +@msg `{key}` must be an array of asset ids (see assets.list) `{key}` 必须是资源 ID 的数组 (参见 assets.list) +@msg `{key}` must be from 0 to 180 degrees, not {a} `{key}` 必须为 0 到 180 度,不能是 {a} +@msg `{key}` must be from {_1} to {shift_max} `{key}` 必须介于 {_1} 到 {shift_max} 之间 +@msg `{key}`: not a colour: {v} `{key}`:不是颜色:{v} +@msg `{k}.family` is required 必须给出 `{k}.family` +@msg `{k}` is a number 0..1 `{k}` 是 0..1 的数字 +@msg `{k}` is true or false `{k}` 为 true 或 false +@msg `{k}` isn't an export setting: {_1} `{k}` 不是导出设置:{_1} +@msg `{k}` must be a byte offset `{k}` 必须是字节偏移 +@msg `{k}` must be a number `{k}` 必须是数字 +@msg `{k}` must be a number of points `{k}` 必须是以点为单位的数字 +@msg `{k}` must be a string `{k}` 必须是字符串 +@msg `{k}` must be a whole number `{k}` 必须是整数 +@msg `{k}` must be from {lo} to {hi} for {key} 对于 {key},`{k}` 必须介于 {lo} 到 {hi} 之间 +@msg `{k}` must be {what}, not {v} `{k}` 必须是 {what},不能是 {v} +@msg `{l}` isn't a global or spot colour `{l}` 不是全局色或专色 +@msg `{m}` isn't a bitmap: use clipboard.importSvg, importPdf or importText `{m}` 不是位图:请使用 clipboard.importSvg、importPdf 或 importText +@msg `{name}` can't be deleted `{name}` 不能删除 +@msg `{name}` can't be duplicated `{name}` 不能复制 +@msg `{name}` can't be edited `{name}` 不能编辑 +@msg `{name}` can't be edited this way `{name}` 不能以这种方式编辑 +@msg `{name}` can't be renamed `{name}` 不能重命名 +@msg `{name}` can't name a folder `{name}` 不能用作文件夹名称 +@msg `{name}` has no art to place `{name}` 中没有可置入的图稿 +@msg `{name}` has no text to place `{name}` 中没有可置入的文本 +@msg `{name}` in the Word document is not UTF-8 or UTF-16 text Word 文档中的 `{name}` 不是 UTF-8 或 UTF-16 文本 +@msg `{name}` in the Word document is not well-formed XML: {e} Word 文档中的 `{name}` 不是格式正确的 XML:{e} +@msg `{name}` in the Word document nests elements more than {MAX_NESTING} deep Word 文档中的 `{name}` 的元素嵌套超过 {MAX_NESTING} 层 +@msg `{name}` is a built-in preset and is read-only: save it under another name `{name}` 是内置预设,为只读:请用其他名称保存 +@msg `{name}` is a built-in preset: save under another name `{name}` 是内置预设:请用其他名称保存 +@msg `{name}` is a built-in profile and stays `{name}` 是内置配置文件,会保留 +@msg `{name}` is a tint of `{base}`: edit `{base}` (a tint swatch only takes a new name) `{name}` 是 `{base}` 的色调:请编辑 `{base}` (色调色板只能改名) +@msg `{name}` is a working space: choose another with edit.colorSettings first `{name}` 是工作空间:请先用 edit.colorSettings 选择另一个 +@msg `{name}` is built in and stays `{name}` 是内置项,会保留 +@msg `{name}` takes a paint of its own kind (colour, gradient or pattern) `{name}` 只接受同类的上色 (颜色、渐变或图案) +@msg `{n}` can't be deleted `{n}` 不能删除 +@msg `{n}` can't be merged `{n}` 不能合并 +@msg `{n}` can't be moved `{n}` 不能移动 +@msg `{n}` is not a colour swatch `{n}` 不是颜色色板 +@msg `{n}` isn't a solid colour `{n}` 不是纯色 +@msg `{n}`: colour groups hold solid colours only `{n}`:颜色组只能包含纯色 +@msg `{other}` doesn't trace an image `{other}` 不能描摹图像 +@msg `{s}`: the step's references copy in more than {_1} MB of values `{s}`:该步骤的引用会复制超过 {_1} MB 的值 +@msg `{tag}` is not an axis tag: a tag is four characters, such as "wght" `{tag}` 不是轴标签:标签为四个字符,例如 "wght" +@msg `{to}` is empty or already used `{to}` 为空或已被使用 +@msg `{what}` is longer than {MAX_TEXT} bytes `{what}` 的长度超过 {MAX_TEXT} 字节 +@msg `{what}` must be text `{what}` 必须是文本 +@msg a DXF drawing has one origin: pass useArtboards: true for one drawing per artboard 一个 DXF 图形只有一个原点:要让每个画板各成一个图形,请传入 useArtboards: true +@msg a PDF presets file with over {_1} presets PDF 预设文件中的预设超过 {_1} 个 +@msg a batch preview cannot switch documents 批处理预览不能切换文档 +@msg a brush named `{nn}` exists 已存在名为 `{nn}` 的画笔 +@msg a container of the listed objects is gone 所列对象的某个容器已不存在 +@msg a content stream can't be read 有一个内容流无法读取 +@msg a content stream isn't a stream 有一个内容流不是数据流 +@msg a design holds at most {_1} objects 一个设计最多包含 {_1} 个对象 +@msg a design needs a name 设计需要名称 +@msg a dictionary grew too large 有一个字典变得过大 +@msg a document keeps at most {_1} graph designs 一个文档最多保留 {_1} 个图表设计 +@msg a document keeps at most {_1} saved selections 一个文档最多保留 {_1} 个已保存的选区 +@msg a document keeps at most {_1} views 一个文档最多保留 {_1} 个视图 +@msg a document links at most {MAX_SVG_SCRIPTS} JavaScript files 一个文档最多链接 {MAX_SVG_SCRIPTS} 个 JavaScript 文件 +@msg a feConvolveMatrix filter's order is {x}×{y}, which overflows the kernel-size multiply usvg makes 某个 feConvolveMatrix 滤镜的 order 为 {x}×{y},会使 usvg 计算卷积核大小的乘法溢出 +@msg a gradient preset is already called {n} 已有名为 {n} 的渐变预设 +@msg a graph has at most {_1} series, so it transposes with at most that many categories 一个图表最多有 {_1} 个系列,因此转置时类别数不能超过这个数量 +@msg a layer can't be merged into its own sublayer 图层不能合并到自己的子图层中 +@msg a layer's form box isn't an array 某个图层的表单外框不是数组 +@msg a layer's form can't be read 某个图层的表单无法读取 +@msg a layer's form has no box 某个图层的表单没有外框 +@msg a layer's form has no dictionary 某个图层的表单没有字典 +@msg a legend draws at most {MAX_CHIPS} swatches 一个图例最多绘制 {MAX_CHIPS} 个色板 +@msg a name names one asset: give multiple: false, or one object 一个名称只对应一个资源:请给出 multiple: false,或只给出一个对象 +@msg a page can't be read 有一个页面无法读取 +@msg a page object is used by something else 有一个页面对象已被其他内容使用 +@msg a page thumbnail must be RGB pixels, 1 to {THUMBNAIL_SIZE} a side (got {w} × {h}) 页面缩略图必须是 RGB 像素,每边 1 到 {THUMBNAIL_SIZE} 像素 (实际为 {w} × {h}) +@msg a pattern layer doesn't end what it starts 某个图案图层没有结束它开始的内容 +@msg a pattern layer's colors hold something else 某个图案图层的颜色中含有其他内容 +@msg a pattern named `{n}` exists 已存在名为 `{n}` 的图案 +@msg a plug-in with id {_1} is already installed 已安装 ID 为 {_1} 的增效工具 +@msg a preset named `{n}` exists 已存在名为 `{n}` 的预设 +@msg a print presets file with over {_1} presets 打印预设文件中的预设超过 {_1} 个 +@msg a profile is already called {n} 已有名为 {n} 的配置文件 +@msg a row can't go into itself 行不能放入其自身 +@msg a series goes on the left or the right value axis 系列只能放在左侧或右侧的数值轴上 +@msg a start must be an anchor index ≥ 0 or null 起点必须是 ≥ 0 的锚点索引或 null +@msg a style named `{name}` already exists 已存在名为 `{name}` 的样式 +@msg a style named `{to}` already exists 已存在名为 `{to}` 的样式 +@msg a swatch needs a colour, gradient or pattern 色板需要颜色、渐变或图案 +@msg a symbol named `{n}` already exists 已存在名为 `{n}` 的符号 +@msg a theme has 1 to {THEME_SIZE} colours 一个主题有 1 到 {THEME_SIZE} 种颜色 +@msg a theme is already called {name} (replace: "{name}" overwrites it) 已有名为 {name} 的主题 (replace: "{name}" 会覆盖它) +@msg a toolbar name has at most {MAX_NAME} characters 工具栏名称最多 {MAX_NAME} 个字符 +@msg a view named `{_1}` exists 已存在名为 `{_1}` 的视图 +@msg a view named `{name}` exists 已存在名为 `{name}` 的视图 +@msg a workspace keeps at most {MAX} toolbars 一个工作区最多保留 {MAX} 个工具栏 +@msg a {_1} series can't go in a {_2} graph {_1} 系列不能放入 {_2} 图表 +@msg aiVersion {v}: "pdf" (PDF-compatible, the default) or "7" (legacy .ai, version 7) aiVersion {v}:"pdf" (兼容 PDF,默认值) 或 "7" (旧版 .ai,版本 7) +@msg already editing an opacity mask 已在编辑不透明度蒙版 +@msg already editing another object's opacity mask 已在编辑另一个对象的不透明度蒙版 +@msg already in pattern editing mode 已处于图案编辑模式 +@msg an artboard is listed twice 有一个画板被列出了两次 +@msg an empty artboard has no width or height to scale to 空画板没有可供缩放的宽度或高度 +@msg an image is too large for DXF and was left out 有一个图像对 DXF 而言过大,已被略去 +@msg an image of {_1} MB, at most {_2} MB 图像为 {_1} MB,最多允许 {_2} MB +@msg an image of {declared_w} × {declared_h} pixels is too large to draw {declared_w} × {declared_h} 像素的图像过大,无法绘制 +@msg an image of {width} × {height} pixels is too large to draw {width} × {height} 像素的图像过大,无法绘制 +@msg an index must be a whole number 索引必须是整数 +@msg an inline graphic has no glyphs 有一个随文图形没有字形 +@msg an invisible text form can't be read 有一个不可见文本表单无法读取 +@msg an object is missing 缺少一个对象 +@msg an object's header is missing 缺少某个对象的头部 +@msg an overprinting form can't be read 有一个叠印表单无法读取 +@msg an unread object has no readable bounds 有一个未读取的对象没有可读取的边界 +@msg an unread object holds a frame of threaded type 有一个未读取的对象包含串接文字的文本框 +@msg an unread object is missing from its layer 有一个未读取的对象不在其图层中 +@msg anchor `smooth` must be true or false 锚点的 `smooth` 必须为 true 或 false +@msg anchor `{key}` must be [x, y] 锚点的 `{key}` 必须是 [x, y] +@msg anchor `{key}` must be a number 锚点的 `{key}` 必须是数字 +@msg angle and roundness must be finite angle 和 roundness 必须是有限值 +@msg angles must be a list as long as pins (degrees or null) angles 必须是与 pins 等长的列表 (度数或 null) +@msg appearance must be flat|center|edge appearance 必须是 flat|center|edge +@msg apply is "fill" or "stroke", not `{other}` apply 为 "fill" 或 "stroke",不能是 `{other}` +@msg area must be a closed polygon of 3 to 100000 [x, y] points area 必须是由 3 到 100000 个 [x, y] 点组成的闭合多边形 +@msg area type needs a path that encloses an area 区域文字需要一条围出区域的路径 +@msg area type {_1} is named twice 区域文字 {_1} 被指定了两次 +@msg arrowAlign must be extend|tip, got {o} arrowAlign 必须是 extend|tip,实际为 {o} +@msg arrowScale must be [start, end] arrowScale 必须是 [start, end] +@msg artboard is a 0-based index artboard 是从 0 开始的索引 +@msg artboard must be a 0-based index artboard 必须是从 0 开始的索引 +@msg artboard {i} doesn't print 画板 {i} 不打印 +@msg artboardLayout must be {layout?, columns?, spacing?, rightToLeft?} artboardLayout 必须是 {layout?, columns?, spacing?, rightToLeft?} +@msg artboardLayout.columns must be 1 or more artboardLayout.columns 必须大于或等于 1 +@msg artboardLayout.layout must be gridByRow, gridByColumn, row or column artboardLayout.layout 必须是 gridByRow、gridByColumn、row 或 column +@msg artboardLayout.rightToLeft must be true or false artboardLayout.rightToLeft 必须为 true 或 false +@msg artboardLayout.spacing must be 0–{MAX_SPACING} pt artboardLayout.spacing 必须为 0–{MAX_SPACING} pt +@msg artboards must be a non-empty list artboards 必须是非空列表 +@msg artboards must be a number artboards 必须是数字 +@msg asset `{_1}` has no art to export 资源 `{_1}` 中没有可导出的图稿 +@msg at most {MAX_KEYWORDS} keywords 最多 {MAX_KEYWORDS} 个关键字 +@msg at most {MAX_MATCHES} matches are kept 最多保留 {MAX_MATCHES} 个匹配项 +@msg at most {MAX_POINTS} points 最多 {MAX_POINTS} 个点 +@msg at most {MAX_QUEUE} files 最多 {MAX_QUEUE} 个文件 +@msg at must be [x, y] at 必须是 [x, y] +@msg at/rect lie outside the canvas at/rect 位于画布之外 +@msg attribute must be size|leading|tracking|kerning|baselineShift attribute 必须是 size|leading|tracking|kerning|baselineShift +@msg attrs must be an object attrs 必须是对象 +@msg automation path rejected: the folder's name isn't valid text 自动化路径被拒绝:文件夹名称不是有效的文本 +@msg automation path rejected: {_1} isn't valid text 自动化路径被拒绝:{_1} 不是有效的文本 +@msg automation path rejected: {why}: {path} 自动化路径被拒绝:{why}:{path} +@msg axis must be "vertical", "horizontal" or an angle in degrees, not `{given}` axis 必须是 "vertical"、"horizontal" 或以度为单位的角度,不能是 `{given}` +@msg backgroundContents must be transparent or white backgroundContents 必须是 transparent 或 white +@msg bad AES key AES 密钥无效 +@msg bad `runs`: {e} `runs` 无效:{e} +@msg bad arrowScale arrowScale 无效 +@msg bad base64 base64 无效 +@msg bad color {c} 颜色 {c} 无效 +@msg bad colour 颜色无效 +@msg bad colour in `colors` `colors` 中有无效的颜色 +@msg bad colour {v} 颜色 {v} 无效 +@msg bad colour {v} in groupColors groupColors 中的颜色 {v} 无效 +@msg bad cross-reference entry 交叉引用条目无效 +@msg bad cross-reference table 交叉引用表无效 +@msg bad current colour {v} 当前颜色 {v} 无效 +@msg bad dataBase64 dataBase64 无效 +@msg bad edit 编辑无效 +@msg bad fill {v} 填色 {v} 无效 +@msg bad fit: {e} 适合方式无效:{e} +@msg bad model {other} (keep, new or document) 模型 {other} 无效 (keep、new 或 document) +@msg bad new colour {v} 新颜色 {v} 无效 +@msg bad object 对象无效 +@msg bad object number 对象编号无效 +@msg bad offset 偏移无效 +@msg bad path data: {e} 路径数据无效:{e} +@msg bad point {v}: expected [x, y] or [x, y, pressure] 点 {v} 无效:应为 [x, y] 或 [x, y, pressure] +@msg bad reference 引用无效 +@msg bad trailer size 尾部的大小无效 +@msg bad {k} colour {k} 颜色无效 +@msg batches cannot nest 批处理不能嵌套 +@msg bind something to a variable first 请先将某个对象绑定到变量 +@msg bleed must be 0–{MAX_BLEED} pt: a number, [top, bottom, left, right] or {{top, bottom, left, right}} bleed 必须为 0–{MAX_BLEED} pt:一个数字、[top, bottom, left, right] 或 {{top, bottom, left, right}} +@msg brush must be a name or null brush 必须是名称或 null +@msg by must be a number by 必须是数字 +@msg can't add page thumbnails: {why} 无法添加页面缩略图:{why} +@msg can't add the editing data for other apps: {why} 无法添加供其他应用使用的编辑数据:{why} +@msg can't edit the written PDF: {why} 无法编辑已写出的 PDF:{why} +@msg can't encrypt the PDF: {why} 无法加密 PDF:{why} +@msg can't linearise the PDF: {why} 无法线性化 PDF:{why} +@msg can't open `{_1}`: {_2} 无法打开 `{_1}`:{_2} +@msg can't open `{_1}`: {e} 无法打开 `{_1}`:{e} +@msg can't open `{name}`: not a format VectorCraft reads (see document.formats) 无法打开 `{name}`:不是 VectorCraft 可读取的格式 (参见 document.formats) +@msg can't place `{name}`: not a format VectorCraft reads (see document.formats) 无法置入 `{name}`:不是 VectorCraft 可读取的格式 (参见 document.formats) +@msg can't read back the written PDF: {e} 无法读回已写出的 PDF:{e} +@msg can't read this Word document: {why} 无法读取此 Word 文档:{why} +@msg can't write .{e} images: use .png, .jpg, .gif, .webp, .tif or .bmp 无法写出 .{e} 图像:请使用 .png、.jpg、.gif、.webp、.tif 或 .bmp +@msg can't write PDF layers, overprint or invisible text: {why} 无法写出 PDF 图层、叠印或不可见文本:{why} +@msg can't write the file's copyright: {why} 无法写入文件的版权信息:{why} +@msg can't write the file's modification date: {why} 无法写入文件的修改日期:{why} +@msg can't write the pages' user units: {why} 无法写入页面的用户单位:{why} +@msg cannot distort a zero-size or non-finite bounding box 无法扭曲大小为零或非有限的定界框 +@msg cannot move an object into itself 不能将对象移入其自身 +@msg case must be upper|lower|title|sentence case 必须是 upper|lower|title|sentence +@msg category `{c}`: one of {_1} 类别 `{c}`:应为 {_1} 之一 +@msg center must be [x, y] center 必须是 [x, y] +@msg choose at least one artboard 请至少选择一个画板 +@msg choose at least one status 请至少选择一种状态 +@msg clip must be true or false clip 必须为 true 或 false +@msg clippingMask must be true or false clippingMask 必须为 true 或 false +@msg closed must be a boolean closed 必须是布尔值 +@msg collapsed must be true or false collapsed 必须为 true 或 false +@msg color.profile: no RGB or CMYK profile is called “{profile}” color.profile:没有名为“{profile}”的 RGB 或 CMYK 配置文件 +@msg colorMode must be rgb or cmyk, not `{m}` colorMode 必须是 rgb 或 cmyk,不能是 `{m}` +@msg colorModel must be {own} (the document's mode), grayscale or bitmap colorModel 必须是 {own} (文档的模式)、grayscale 或 bitmap +@msg colour groups can't go into colour groups 颜色组不能放入颜色组 +@msg colour, mode and spot apply to solid-colour swatches only colour、mode 和 spot 只适用于纯色色板 +@msg column {column}: `{cell}` is a `%` column of graph data files, which are not read 第 {column} 列:`{cell}` 是图表数据文件的 `%` 列,不会读取 +@msg column {column}: `{cell}` names `{name}`, which is a {_1} variable 第 {column} 列:`{cell}` 指定了 `{name}`,而它是 {_1} 变量 +@msg column {column}: `{cell}` names no variable 第 {column} 列:`{cell}` 没有对应的变量 +@msg column {column}: the variable name is longer than {MAX_NAME} characters 第 {column} 列:变量名称超过 {MAX_NAME} 个字符 +@msg column: true floats the whole column and takes no onto, above or below column: true 会使整列浮动,不能再给出 onto、above 或 below +@msg command must be a shape.* command command 必须是 shape.* 命令 +@msg commands[{i}]: missing `command` commands[{i}]:缺少 `command` +@msg composer must be "singleLine" or "everyLine" composer 必须是 "singleLine" 或 "everyLine" +@msg compound shapes take paths, compound paths, groups, compound shapes, type, blends and envelopes 复合形状接受路径、复合路径、编组、复合形状、文字、混合和封套 +@msg compound {_1} must contain exactly one open path to join 复合对象 {_1} 必须恰好包含一条要连接的开放路径 +@msg connect needs a path other than the one extended connect 需要一条不同于被延伸路径的路径 +@msg connect needs id connect 需要 id +@msg connect path has no open end connect 的路径没有开放的端点 +@msg copy needs path: the file to copy into the document's folder copy 需要 path:即要复制到文档所在文件夹的文件 +@msg copy takes images only: relink placed documents where their files are copy 只接受图像:置入的文档请在其文件所在位置重新链接 +@msg copying a file isn't available on the web 网页版无法复制文件 +@msg copyrightStatus must be unknown, copyrighted or publicDomain copyrightStatus 必须是 unknown、copyrighted 或 publicDomain +@msg copyrightUrl must be a URL (no spaces, at most 2048 characters) copyrightUrl 必须是 URL (不含空格,最多 2048 个字符) +@msg corners must be 4 [x, y] points (TL, TR, BR, BL) corners 必须是 4 个 [x, y] 点 (TL, TR, BR, BL) +@msg counterPer `{c}`: item or file counterPer `{c}`:应为 item 或 file +@msg crop `{_1}`: only PDF pages have art, trim, bleed and media boxes; use "crop" or "bounding" crop `{_1}`:只有 PDF 页面才有 art、trim、bleed 和 media 框;请使用 "crop" 或 "bounding" +@msg crop rectangle doesn't overlap the image 裁剪矩形与图像不重叠 +@msg cross-reference and object streams aren't supported 不支持交叉引用流和对象流 +@msg damaged compressed data: {e} 压缩数据已损坏:{e} +@msg dash lengths must be numbers, got {v} 虚线长度必须是数字,实际为 {v} +@msg dash must be a list of lengths or null, got {v} dash 必须是长度的列表或 null,实际为 {v} +@msg data key `{k}`: letters, digits, -, _ and . (not name or vc-…) 数据键 `{k}`:只能使用字母、数字、-、_ 和 . (不能是 name 或 vc-…) +@msg data must be an object of key: value, not {v} data 必须是 key: value 形式的对象,不能是 {v} +@msg data set `{name}` holds a value for `{var}`, which the library doesn't declare 数据集 `{name}` 含有 `{var}` 的值,而库中没有声明该变量 +@msg data set `{name}` holds two values for `{var}` 数据集 `{name}` 含有 `{var}` 的两个值 +@msg data set `{name}` is declared twice 数据集 `{name}` 被声明了两次 +@msg data takes at most {MAX_DATA} keys at once data 一次最多接受 {MAX_DATA} 个键 +@msg dataset `{name}` already exists 数据集 `{name}` 已存在 +@msg dataset `{to}` already exists 数据集 `{to}` 已存在 +@msg date `{d}`: YYYY-MM-DD 日期 `{d}`:应为 YYYY-MM-DD +@msg decoded data is too large 解码后的数据过大 +@msg define a variable first 请先定义一个变量 +@msg detect must be true or false detect 必须为 true 或 false +@msg direct-select anchor points of a path 请直接选择路径的锚点 +@msg distance must be > 0 distance 必须 > 0 +@msg dx and dy give the arrow's direction dx 和 dy 给出箭头的方向 +@msg dx and dy must be numbers dx 和 dy 必须是数字 +@msg dxf fitTo must be [width, height], positive dxf 的 fitTo 必须是正数 [width, height] +@msg dxf must be an object of DXF import options dxf 必须是 DXF 导入选项的对象 +@msg dxf scale must be a positive number up to {_1}, not {s} dxf 的 scale 必须是不超过 {_1} 的正数,不能是 {s} +@msg dxf unit `{u}`: a unit such as mm, cm, in or pt dxf 的 unit `{u}`:应为 mm、cm、in 或 pt 之类的单位 +@msg dxf: {e} dxf:{e} +@msg each format is an object {format, scale?, suffix?} 每个格式都是对象 {format, scale?, suffix?} +@msg each item needs an id 每一项都需要 id +@msg each match needs `kind` 每个匹配项都需要 `kind` +@msg each match needs a `name` 每个匹配项都需要 `name` +@msg each of `fonts` is {family, style?}; 1 to 1000 of them `fonts` 的每一项为 {family, style?};共 1 到 1000 项 +@msg each of `items` is {kind, item} `items` 的每一项为 {kind, item} +@msg each of `items` is {{id, corners?}} with an object id, not {it} `items` 的每一项为带对象 ID 的 {{id, corners?}},不能是 {it} +@msg each row needs `from` 每一行都需要 `from` +@msg each stop needs a position 每个色标都需要位置 +@msg each subpath needs an anchors array 每条子路径都需要 anchors 数组 +@msg easingType must be easeIn, easeOut, easeInOut or easeOutIn easingType 必须是 easeIn、easeOut、easeInOut 或 easeOutIn +@msg editingData must be true or false editingData 必须为 true 或 false +@msg editingData must be true, false or "always" editingData 必须为 true、false 或 "always" +@msg edits overlap 编辑范围相互重叠 +@msg effects of objects whose appearances differ can't be moved together 外观不同的对象的效果不能一起移动 +@msg empty header in column {_1} 第 {_1} 列的标题为空 +@msg encoding `{_1}`: utf8 or utf16 编码 `{_1}`:应为 utf8 或 utf16 +@msg ends must be two of "first" and "last" ends 必须是两个取自 "first" 和 "last" 的值 +@msg every numbered name for the result is taken 可用于结果的带编号名称都已被占用 +@msg expanded SVG styles exceed the import size limit 展开后的 SVG 样式超出了导入大小上限 +@msg export presets file is larger than {MAX_PRESET_FILE_BYTES} bytes 导出预设文件大于 {MAX_PRESET_FILE_BYTES} 字节 +@msg exportText must be editable or appearance exportText 必须是 editable 或 appearance +@msg exportType must be movieClip or graphic, not `{s}` exportType 必须是 movieClip 或 graphic,不能是 `{s}` +@msg extend needs id extend 需要 id +@msg featureChanges lists at most {MAX_FEATURE_CHANGES} tags featureChanges 最多列出 {MAX_FEATURE_CHANGES} 个标签 +@msg featureChanges must be a list of tags featureChanges 必须是标签的列表 +@msg features must be a list of tags features 必须是标签的列表 +@msg fidelity must be 0..100 fidelity 必须为 0..100 +@msg files must be {name, dataBase64} files 必须是 {name, dataBase64} +@msg fit must be none|autoHeight|shrinkText|block, got {id} fit 必须是 none|autoHeight|shrinkText|block,实际为 {id} +@msg flattener: {e} 拼合器:{e} +@msg flattenerPreset `{name}`: high, medium, low or a saved one (see flattener.presets.list) flattenerPreset `{name}`:应为 high、medium、low 或已保存的预设 (参见 flattener.presets.list) +@msg flipProfile must be along|across, got {o} flipProfile 必须是 along|across,实际为 {o} +@msg folders `{f}`: none, rows, format, item or itemFormat folders `{f}`:应为 none、rows、format、item 或 itemFormat +@msg folders deeper than {MAX_BATCH_DEPTH} levels 文件夹层级超过 {MAX_BATCH_DEPTH} 层 +@msg folders deeper than {MAX_DEPTH} levels 文件夹层级超过 {MAX_DEPTH} 层 +@msg font family `{family}` is not available 字体系列 `{family}` 不可用 +@msg font family `{tf}` is not available 字体系列 `{tf}` 不可用 +@msg format `{f}`: pdf or postscript 格式 `{f}`:应为 pdf 或 postscript +@msg format `{f}`: text or json 格式 `{f}`:应为 text 或 json +@msg format is `{format}` 格式为 `{format}` +@msg formats must be an array of rows, not {v} formats 必须是行的数组,不能是 {v} +@msg frequency must be between 1 and 300 lines per inch frequency 必须介于每英寸 1 到 300 线之间 +@msg from and to must be finite from 和 to 必须是有限值 +@msg from must be [x0, y0, x1, y1] from 必须是 [x0, y0, x1, y1] +@msg give `before` or `after`, not both 请给出 `before` 或 `after`,不能两者都给 +@msg give `colors`, or `color` and `rule` 请给出 `colors`,或 `color` 和 `rule` +@msg give `event`, such as onclick 请给出 `event`,例如 onclick +@msg give `file`, the JavaScript file's path or URL 请给出 `file`,即 JavaScript 文件的路径或 URL +@msg give `fill` and/or `stroke` 请给出 `fill` 和/或 `stroke` +@msg give `fill`, `stroke` or both 请给出 `fill`、`stroke` 或两者 +@msg give `folder` 请给出 `folder` +@msg give `fonts`, or open a document 请给出 `fonts`,或打开一个文档 +@msg give `from` or `indices`, not both 请给出 `from` 或 `indices`,不能两者都给 +@msg give `id` (no text is being edited) 请给出 `id` (当前没有正在编辑的文本) +@msg give `id` and `anchors`, or `items`, not both 请给出 `id` 和 `anchors`,或给出 `items`,不能两者都给 +@msg give `id` or `guide`, not both 请给出 `id` 或 `guide`,不能两者都给 +@msg give `length` or `to` 请给出 `length` 或 `to` +@msg give `matches`: [{kind, name, target}] 请给出 `matches`: [{kind, name, target}] +@msg give `name` (or edit a pattern) 请给出 `name` (或编辑一个图案) +@msg give `name` or `names` 请给出 `name` 或 `names` +@msg give `path` (a .wasm file) or `dataBase64` (the module) 请给出 `path` (.wasm 文件) 或 `dataBase64` (模块) +@msg give `path`, `data` or `dataBase64` 请给出 `path`、`data` 或 `dataBase64` +@msg give `path`, `data`, `dataBase64` or `csv` 请给出 `path`、`data`、`dataBase64` 或 `csv` +@msg give `path`, `xml` or `dataBase64` 请给出 `path`、`xml` 或 `dataBase64` +@msg give `script`, the JavaScript the event runs ("" removes the event) 请给出 `script`,即事件要运行的 JavaScript ("" 会移除该事件) +@msg give `text` (the reading) or `remove` 请给出 `text` (读音) 或 `remove` +@msg give `x` and `y` (and a `tolerance` of 0 or more) 请给出 `x` 和 `y` (以及大于或等于 0 的 `tolerance`) +@msg give a mode or r/g/b, c/m/y/k, gray or tint adjustments 请给出模式,或 r/g/b、c/m/y/k、gray 或 tint 的调整值 +@msg give a name, or paint the active proxy with a saved preset 请给出名称,或用已保存的预设为当前代理上色 +@msg give a name, or select a stroke with a saved profile 请给出名称,或选择使用已保存配置文件的描边 +@msg give a pattern name or select an object painted with a pattern 请给出图案名称,或选择用图案上色的对象 +@msg give at least one of clip, invert, disabled, linked 请至少给出 clip、invert、disabled、linked 之一 +@msg give color, none, swatch or gradient 请给出 color、none、swatch 或 gradient +@msg give dataBase64 请给出 dataBase64 +@msg give either at (a click) or area (a path drawn around the overlaps) 请给出 at (一次单击) 或 area (围绕重叠处绘制的路径) +@msg give formats or a preset, not both 请给出 formats 或一个预设,不能两者都给 +@msg give newName or delete 请给出 newName 或 delete +@msg give offset or by 请给出 offset 或 by +@msg give one of `to`, `rect` or `at` 请给出 `to`、`rect` 或 `at` 之一 +@msg give one of assets, layers, selection or fullDocument 请给出 assets、layers、selection 或 fullDocument 之一 +@msg give only one of onto, above, below, icon and a column place onto、above、below、icon 和列位置中只能给出一个 +@msg give origin: [x, y], or reset: true 请给出 origin: [x, y],或 reset: true +@msg give overprintFill, overprintStroke, showCenter, imageMap, url or note 请给出 overprintFill、overprintStroke、showCenter、imageMap、url 或 note +@msg give path (a file) or folder 请给出 path (文件) 或 folder +@msg give path, or name and dataBase64 请给出 path,或 name 和 dataBase64 +@msg give paths or files 请给出 paths 或 files +@msg give rows, rowHeight, columns or columnWidth 请给出 rows、rowHeight、columns 或 columnWidth +@msg give svg, or dataBase64 of an SVG or SVGZ file 请给出 svg,或 SVG/SVGZ 文件的 dataBase64 +@msg give text 请给出 text +@msg give the asset a name 请为资源命名 +@msg give the preset a `name` 请为预设给出 `name` +@msg give the preset a name 请为预设命名 +@msg give the preset a name (1–64 characters) 请为预设命名 (1–64 个字符) +@msg give the settings to change, e.g. {"formats"}]} 请给出要更改的设置,例如 {"formats"}]} +@msg give the style a name 请为样式命名 +@msg give two or more style `names` 请在 `names` 中给出两个或更多样式 +@msg give two or more swatches 请给出两个或更多色板 +@msg give x, y, or a handle's length or angle 请给出 x、y,或手柄的长度或角度 +@msg give {key, value} or {values} 请给出 {key, value} 或 {values} +@msg global must be true or false global 必须为 true 或 false +@msg glyph {_1} is not drawn for {ch} with the character's style (such as All Caps, Small Caps or a numeral system) 按该字符的样式 (例如全部大写、小型大写或某种数字系统),{ch} 不会绘制为字形 {_1} +@msg glyph {g} is not an alternate of {ch} in {_1} {_2} 在 {_1} {_2} 中,字形 {g} 不是 {ch} 的替代字形 +@msg gradient must be "objects" or "mesh" gradient 必须是 "objects" 或 "mesh" +@msg grays must be "profile" or "black", not `{g}` grays 必须是 "profile" 或 "black",不能是 `{g}` +@msg gridColors must be a preset name or two colours gridColors 必须是预设名称或两种颜色 +@msg gridSize must be small, medium or large gridSize 必须是 small、medium 或 large +@msg guide {g} is not selected 参考线 {g} 未被选中 +@msg handle must be 0..3 handle 必须为 0..3 +@msg hiddenObjects {v}: "layer" (the default), "omit" or "visible" hiddenObjects {v}:应为 "layer" (默认值)、"omit" 或 "visible" +@msg highlight a layer to merge into 请高亮显示要合并到的图层 +@msg highlight two or more rows in one layer or group 请在同一图层或编组中高亮显示两行或更多行 +@msg highlight two or more rows to merge 请高亮显示两行或更多行以合并 +@msg icon docks a panel in the icon column: use window.panel.dock icon 会将面板停放在图标列中:请使用 window.panel.dock +@msg id must be a dimension id id 必须是尺寸标注 ID +@msg image has no bounds 图像没有边界 +@msg image {_1} is linked already 图像 {_1} 已是链接图像 +@msg imageMap `{m}`: none, client or server imageMap `{m}`:应为 none、client 或 server +@msg index out of range 索引超出范围 +@msg instances must be 1..1000 instances 必须为 1..1000 +@msg invalid `color` `color` 无效 +@msg invalid anchor index {v}: expected a non-negative integer 锚点索引 {v} 无效:应为非负整数 +@msg invalid anchor reference {v}: expected [subpath, anchor] 锚点引用 {v} 无效:应为 [subpath, anchor] +@msg invalid angle 角度无效 +@msg invalid brush: {e} 画笔无效:{e} +@msg invalid color 颜色无效 +@msg invalid colour {c} 颜色 {c} 无效 +@msg invalid delta 增量无效 +@msg invalid point 点无效 +@msg invalid size 大小无效 +@msg invalid t t 无效 +@msg invalid tolerance 容差无效 +@msg its artboards all lie beyond the canvas (more than {_1} pt from the origin) 其画板全部位于画布之外 (距原点超过 {_1} pt) +@msg its clipping masks are nested too deeply 其剪切蒙版嵌套过深 +@msg its metadata has no description 其元数据没有说明 +@msg its metadata has no direct length 其元数据没有直接给出的长度 +@msg join two different paths 请连接两条不同的路径 +@msg justify must be auto|left|center|right|justifyAll justify 必须是 auto|left|center|right|justifyAll +@msg kbps must be a positive number, not {kbps} kbps 必须是正数,不能是 {kbps} +@msg keywords must be a list of words or a comma-separated string keywords 必须是单词的列表或以逗号分隔的字符串 +@msg keywords must be at most {MAX_KEYWORD} characters, without control characters keywords 的每一项最多 {MAX_KEYWORD} 个字符,且不含控制字符 +@msg kind must be 1, 2 or 3, or give a preset `name` (see perspective.presets.list) kind 必须是 1、2 或 3,或者给出预设的 `name` (参见 perspective.presets.list) +@msg kind must be graphic, fillColor, strokeColor, fillGradient, strokeGradient, fillPattern, strokePattern, charStyle or paraStyle kind 必须是 graphic、fillColor、strokeColor、fillGradient、strokeGradient、fillPattern、strokePattern、charStyle 或 paraStyle +@msg knockout must be "on", "off", "neutral" or a bool, not {v} knockout 必须是 "on"、"off"、"neutral" 或布尔值,不能是 {v} +@msg layer `{name}` has no art to export 图层 `{name}` 中没有可导出的图稿 +@msg layer must name a layer or sublayer layer 必须指定一个图层或子图层 +@msg layers can't be intertwined: select the objects in them 图层不能交织:请选择图层中的对象 +@msg layers go only at the top level or in layers, objects in layers and groups 图层只能位于顶层或图层内,对象只能位于图层和编组内 +@msg layers must be true or false layers 必须为 true 或 false +@msg layout must be gridByRow, gridByColumn, row or column layout 必须是 gridByRow、gridByColumn、row 或 column +@msg leading must be a number or "auto" leading 必须是数字或 "auto" +@msg left/right must be non-negative widths left/right 必须是非负的宽度 +@msg level `{t}`: 2 or 3 level `{t}`:应为 2 或 3 +@msg line {line}: more than {MAX_CSV_COLS} columns 第 {line} 行:超过 {MAX_CSV_COLS} 列 +@msg line {line}: unclosed quote 第 {line} 行:引号未闭合 +@msg line {line}: value for `{var}` must match its variable kind 第 {line} 行:`{var}` 的值必须与其变量类型相符 +@msg lineEndings `{other}`: lf or crlf lineEndings `{other}`:应为 lf 或 crlf +@msg loading profiles from disk isn't available in the browser 浏览器中无法从磁盘载入配置文件 +@msg margin: a {m} pt margin leaves no imageable area on the paper margin:{m} pt 的边距使纸张上没有可成像区域 +@msg matrix must be finite and invertible, depth finite matrix 必须是有限且可逆的,depth 必须是有限值 +@msg matte: {e} matte:{e} +@msg member is for order front, forward, backward or back member 用于 order 为 front、forward、backward 或 back 的情况 +@msg missing `artboards` 缺少 `artboards` +@msg missing `at` [x, y] 缺少 `at` [x, y] +@msg missing `attrs` 缺少 `attrs` +@msg missing `color` or `opacity` 缺少 `color` 或 `opacity` +@msg missing `colors` 缺少 `colors` +@msg missing `commands` 缺少 `commands` +@msg missing `destinationFolder` 缺少 `destinationFolder` +@msg missing `effect` id 缺少 `effect` ID +@msg missing `family` 缺少 `family` +@msg missing `find` 缺少 `find` +@msg missing `gid` (a glyph id, or null for the default glyph) 缺少 `gid` (字形 ID,或用 null 表示默认字形) +@msg missing `group` 缺少 `group` +@msg missing `id` 缺少 `id` +@msg missing `id`: give one, or select one type object 缺少 `id`:请给出一个,或选择一个文字对象 +@msg missing `index` (a point index, or null to clear) 缺少 `index` (点索引,或用 null 清除) +@msg missing `index` (a stop index, or null to clear) 缺少 `index` (色标索引,或用 null 清除) +@msg missing `index` (an item index or null) 缺少 `index` (项目索引或 null) +@msg missing `item` 缺少 `item` +@msg missing `library` 缺少 `library` +@msg missing `library` (see graphicStyle.libraries) 缺少 `library` (参见 graphicStyle.libraries) +@msg missing `library` (see swatch.library.list) 缺少 `library` (参见 swatch.library.list) +@msg missing `map` (rows [{from, to}] or {current}) 缺少 `map` (行 [{from, to}] 或 {current}) +@msg missing `matrix` [a,b,c,d,e,f] 缺少 `matrix` [a,b,c,d,e,f] +@msg missing `name` (see gradient.presets.list) 缺少 `name` (参见 gradient.presets.list) +@msg missing `offset` (a distance in points) 缺少 `offset` (以点为单位的距离) +@msg missing `path` 缺少 `path` +@msg missing `path` (a path object id) 缺少 `path` (路径对象 ID) +@msg missing `rule` 缺少 `rule` +@msg missing `source` id 缺少 `source` ID +@msg missing `start` (the byte offset of a character) 缺少 `start` (字符的字节偏移) +@msg missing `status`: linked, embedded, missing or modified 缺少 `status`:应为 linked、embedded、missing 或 modified +@msg missing `target` id 缺少 `target` ID +@msg missing `text` 缺少 `text` +@msg missing `to` 缺少 `to` +@msg missing `to` (an index) 缺少 `to` (索引) +@msg missing `to`: a non-negative integer 缺少 `to`:应为非负整数 +@msg missing `type` 缺少 `type` +@msg missing `value` 缺少 `value` +@msg missing `{key}` 缺少 `{key}` +@msg missing `{k}` 缺少 `{k}` +@msg missing anchor 缺少锚点 +@msg missing anchors 缺少锚点 +@msg missing anchors [[subpath, anchor]…] 缺少锚点 [[subpath, anchor]…] +@msg missing array `{key}` 缺少数组 `{key}` +@msg missing case 缺少 case +@msg missing color 缺少颜色 +@msg missing commands 缺少命令 +@msg missing destination folder 缺少目标文件夹 +@msg missing from 缺少 from +@msg missing group 缺少 group +@msg missing id or ids 缺少 id 或 ids +@msg missing ids 缺少 ids +@msg missing indexes 缺少 indexes +@msg missing matrix [a,b,c,d,e,f] 缺少 matrix [a,b,c,d,e,f] +@msg missing name 缺少名称 +@msg missing op 缺少 op +@msg missing or invalid `color` `color` 缺失或无效 +@msg missing or unknown `tool` `tool` 缺失或未知 +@msg missing point 缺少点 +@msg missing point [x,y] 缺少点 [x,y] +@msg missing point `{key}` ([x, y]) 缺少点 `{key}` ([x, y]) +@msg missing points 缺少点 +@msg missing points [[x,y]…] 缺少点 [[x,y]…] +@msg missing segment 缺少线段 +@msg missing stops 缺少色标 +@msg missing subpaths 缺少子路径 +@msg missing target 缺少目标 +@msg missing to 缺少 to +@msg missing x, y 缺少 x、y +@msg mode is set, add or toggle, not `{other}` mode 为 set、add 或 toggle,不能是 `{other}` +@msg mode must be "cmyk" or "rgb" mode 必须是 "cmyk" 或 "rgb" +@msg mode must be area|onPath, got {o} mode 必须是 area|onPath,实际为 {o} +@msg more than 10000 linked files: package dependencies in smaller parts 链接文件超过 10000 个:请分成较小的部分打包依赖项 +@msg more than 256 commands 命令超过 256 条 +@msg more than {MAX_BATCH_FILES} data sets 数据集超过 {MAX_BATCH_FILES} 个 +@msg more than {MAX_BATCH_FILES} files 文件超过 {MAX_BATCH_FILES} 个 +@msg more than {MAX_CSV_ROWS} data rows 数据行超过 {MAX_CSV_ROWS} 行 +@msg more than {MAX_FILES} images 图像超过 {MAX_FILES} 个 +@msg more than {MAX_IMPORT} presets 预设超过 {MAX_IMPORT} 个 +@msg moved must be [[x,y]…] moved 必须是 [[x,y]…] +@msg name (a toolbar's name) is required 必须给出 name (工具栏名称) +@msg name (a toolbar's name) must be a string name (工具栏名称) 必须是字符串 +@msg name must be a toolbar's name name 必须是工具栏的名称 +@msg names is empty names 为空 +@msg names must be an array of symbol names names 必须是符号名称的数组 +@msg need `anchor` or `segment` + `t` 需要 `anchor`,或 `segment` + `t` +@msg need at least one anchor 至少需要一个锚点 +@msg need at least one point 至少需要一个点 +@msg need at least two points 至少需要两个点 +@msg needs points 需要点 +@msg nineSlice must be true or false nineSlice 必须为 true 或 false +@msg nineSliceGuides must be [left, top, right, bottom], each -16..16, with left + right < 1 and top + bottom < 1 nineSliceGuides 必须是 [left, top, right, bottom],每个值为 -16..16,且 left + right < 1、top + bottom < 1 +@msg no PDF preset named `{n}` 没有名为 `{n}` 的 PDF 预设 +@msg no action set is current 当前没有动作组 +@msg no active data set 没有活动的数据集 +@msg no appearance item {index} 没有外观项 {index} +@msg no artboard {_1} 没有画板 {_1} +@msg no artboard {artboard} (0-based; the document has {_1}) 没有画板 {artboard} (从 0 开始计数;文档有 {_1} 个) +@msg no asset {id} 没有资源 {id} +@msg no asset {id} (see assets.list) 没有资源 {id} (参见 assets.list) +@msg no assets to export 没有可导出的资源 +@msg no assets yet: collect art for export first (assets.add) 还没有资源:请先收集要导出的图稿 (assets.add) +@msg no brush named `{n}` 没有名为 `{n}` 的画笔 +@msg no character at byte {start} 字节 {start} 处没有字符 +@msg no colour 没有颜色 +@msg no colour group `{group}` 没有颜色组 `{group}` +@msg no colour group `{name}` 没有颜色组 `{name}` +@msg no data sets 没有数据集 +@msg no dataset named `{from}` 没有名为 `{from}` 的数据集 +@msg no dataset named `{name}` 没有名为 `{name}` 的数据集 +@msg no datasets 没有数据集 +@msg no design named `{name}` 没有名为 `{name}` 的设计 +@msg no design named `{n}` 没有名为 `{n}` 的设计 +@msg no design named `{old}` 没有名为 `{old}` 的设计 +@msg no encoder for {_1} yet 还没有 {_1} 的编码器 +@msg no export preset `{name}` (see document.exportPresets.list) 没有导出预设 `{name}` (参见 document.exportPresets.list) +@msg no folder: pass `path` or set Preferences › Plug-ins & Scratch Disks › Additional Plug-ins Folder 没有文件夹:请传入 `path`,或设置 首选项 › 增效工具 & 暂存盘 › 附加增效工具文件夹 +@msg no fonts to look for: they are all available 没有需要查找的字体:字体全部可用 +@msg no formats to export 没有要导出的格式 +@msg no gradient preset `{name}` (see gradient.presets.list) 没有渐变预设 `{name}` (参见 gradient.presets.list) +@msg no graphic style `{n}` in `{_1}` `{_1}` 中没有图形样式 `{n}` +@msg no graphic style library `{key}` (see graphicStyle.libraries) 没有图形样式库 `{key}` (参见 graphicStyle.libraries) +@msg no guide selected 没有选中参考线 +@msg no guide selected (or give an index) 没有选中参考线 (或给出索引) +@msg no guide {i} 没有参考线 {i} +@msg no images in the folder 文件夹中没有图像 +@msg no item at index {_1} 索引 {_1} 处没有项目 +@msg no item at index {from} 索引 {from} 处没有项目 +@msg no item at one of the indices {idx} 索引 {idx} 中有一处没有项目 +@msg no layer 没有图层 +@msg no layer {id} (see layer.list) 没有图层 {id} (参见 layer.list) +@msg no layers to export 没有可导出的图层 +@msg no library `{key}` 没有库 `{key}` +@msg no metafile format 没有图元文件格式 +@msg no object {_1} 没有对象 {_1} +@msg no objects 没有对象 +@msg no parent layer 没有父图层 +@msg no perspective grid preset named `{b}` (see perspective.presets.list) 没有名为 `{b}` 的透视网格预设 (参见 perspective.presets.list) +@msg no perspective grid preset named `{n}` 没有名为 `{n}` 的透视网格预设 +@msg no point selected (give `index`) 没有选中点 (请给出 `index`) +@msg no preset called `{from}` (see file.newPresets) 没有名为 `{from}` 的预设 (参见 file.newPresets) +@msg no preset called `{name}` (see file.newPresets) 没有名为 `{name}` 的预设 (参见 file.newPresets) +@msg no preset named `{n}` 没有名为 `{n}` 的预设 +@msg no print preset named `{b}` (see print.presets.list) 没有名为 `{b}` 的打印预设 (参见 print.presets.list) +@msg no print preset named `{name}` (presets: {_1}) 没有名为 `{name}` 的打印预设 (现有预设:{_1}) +@msg no print preset named `{n}` 没有名为 `{n}` 的打印预设 +@msg no profile named `{name}` 没有名为 `{name}` 的配置文件 +@msg no readable files in the folder 文件夹中没有可读取的文件 +@msg no recovery copy `{f}` (see file.recovery.list) 没有恢复副本 `{f}` (参见 file.recovery.list) +@msg no rows 没有行 +@msg no saved PDF preset named `{name}` 没有名为 `{name}` 的已保存 PDF 预设 +@msg no saved export preset `{name}` 没有已保存的导出预设 `{name}` +@msg no saved gradient preset called {name} 没有名为 {name} 的已保存渐变预设 +@msg no saved gradient presets 没有已保存的渐变预设 +@msg no saved preset called `{name}` 没有名为 `{name}` 的已保存预设 +@msg no saved presets to export (name [Default] in `names` to export it) 没有可导出的已保存预设 (要导出 [Default],请在 `names` 中指定它) +@msg no saved presets to export (name built-in ones in `names`) 没有可导出的已保存预设 (内置预设请在 `names` 中指定) +@msg no saved print preset named `{name}` 没有名为 `{name}` 的已保存打印预设 +@msg no saved profile called {name} 没有名为 {name} 的已保存配置文件 +@msg no saved selection `{name}` 没有已保存的选区 `{name}` +@msg no saved theme called {name} (see colorTheme.list) 没有名为 {name} 的已保存主题 (参见 colorTheme.list) +@msg no saved views 没有已保存的视图 +@msg no series {i} 没有系列 {i} +@msg no slice {n} in the image (see slice.list) 图像中没有切片 {n} (参见 slice.list) +@msg no source art 没有源图稿 +@msg no stop {i} (the gradient has {n}) 没有色标 {i} (渐变有 {n} 个) +@msg no style `{name}` 没有样式 `{name}` +@msg no such assets (see assets.list) 没有这些资源 (参见 assets.list) +@msg no such frame anchor, or the frame would reach past the canvas 没有此文本框锚点,或文本框会超出画布 +@msg no such objects 没有这些对象 +@msg no such path anchor, or the path would reach past the canvas 没有此路径锚点,或路径会超出画布 +@msg no such rows 没有这些行 +@msg no swatch `{keep}` 没有色板 `{keep}` +@msg no swatch `{n}` 没有色板 `{n}` +@msg no swatch `{n}` (swatches and colour groups move separately) 没有色板 `{n}` (色板和颜色组要分别移动) +@msg no swatch library `{key}` (see swatch.library.list) 没有色板库 `{key}` (参见 swatch.library.list) +@msg no swatch library `{key}` (see swatch.library.list; "document": the document's swatches) 没有色板库 `{key}` (参见 swatch.library.list;"document" 表示文档的色板) +@msg no swatch library `{key}` (see swatch.library.list; "{DOCUMENT_SWATCHES}": the document's swatches) 没有色板库 `{key}` (参见 swatch.library.list;"{DOCUMENT_SWATCHES}" 表示文档的色板) +@msg no swatch named `{n}` 没有名为 `{n}` 的色板 +@msg no swatch or colour group `{n}` 没有色板或颜色组 `{n}` +@msg no swatch or colour group `{n}` in `{_1}` `{_1}` 中没有色板或颜色组 `{n}` +@msg no symbol named `{name}` 没有名为 `{name}` 的符号 +@msg no text objects selected 没有选中文本对象 +@msg no toolbar named `{_1}` 没有名为 `{_1}` 的工具栏 +@msg no toolbar named `{name}` 没有名为 `{name}` 的工具栏 +@msg no tracing preset `{preset}` (see imageTrace.presets) 没有描摹预设 `{preset}` (参见 imageTrace.presets) +@msg no user library folder here (save with a path, or without one to get the data) 这里没有用户库文件夹 (请指定路径保存,或不指定路径以取得数据) +@msg no variable named `{from}` 没有名为 `{from}` 的变量 +@msg no variable named `{name}` 没有名为 `{name}` 的变量 +@msg no variable named `{var}` 没有名为 `{var}` 的变量 +@msg no variable named `{v}` 没有名为 `{v}` 的变量 +@msg no view named `{name}` 没有名为 `{name}` 的视图 +@msg node {_1} is not a graph 节点 {_1} 不是图表 +@msg node {_1} is not an image 节点 {_1} 不是图像 +@msg node {_1} is not type 节点 {_1} 不是文字 +@msg not a PDF presets file (format `{_1}`) 不是 PDF 预设文件 (格式为 `{_1}`) +@msg not a PDF presets file: {e} 不是 PDF 预设文件:{e} +@msg not a Word document: its WordDocument stream doesn't start with a file information block 不是 Word 文档:其 WordDocument 流不是以文件信息块开头 +@msg not a Word document: its compound file has no WordDocument stream 不是 Word 文档:其复合文件中没有 WordDocument 流 +@msg not a Word document: its main part is not a WordprocessingML document 不是 Word 文档:其主部件不是 WordprocessingML 文档 +@msg not a Word document: its package has no main document part 不是 Word 文档:其包中没有主文档部件 +@msg not a closed path 不是闭合路径 +@msg not a flattener presets file (format `{_1}`) 不是拼合器预设文件 (格式为 `{_1}`) +@msg not a flattener presets file: {e} 不是拼合器预设文件:{e} +@msg not a layer or group 不是图层或编组 +@msg not a library file: {e} 不是库文件:{e} +@msg not a mesh envelope point 不是网格封套的点 +@msg not a perspective grid presets file (format `{_1}`) 不是透视网格预设文件 (格式为 `{_1}`) +@msg not a perspective grid presets file: {e} 不是透视网格预设文件:{e} +@msg not a print presets file (format `{_1}`) 不是打印预设文件 (格式为 `{_1}`) +@msg not a print presets file: {e} 不是打印预设文件:{e} +@msg not a stored or deflated central record 不是以存储或 deflate 方式保存的中央记录 +@msg not a valid SVGZ file: {e} 不是有效的 SVGZ 文件:{e} +@msg not an RTF file: it doesn't start with {\rtf 不是 RTF 文件:它不是以 {\rtf 开头 +@msg not an image VectorCraft reads (see document.formats) 不是 VectorCraft 可读取的图像 (参见 document.formats) +@msg not area type (text in a frame) outside perspective 不是透视之外的区域文字 (文本框中的文本) +@msg not editing an opacity mask 当前没有在编辑不透明度蒙版 +@msg not type on a path outside perspective 不是透视之外的路径文字 +@msg nothing prints 没有可打印的内容 +@msg nothing to bind 没有可绑定的内容 +@msg nothing to change 没有可更改的内容 +@msg nothing to change: give dx, dy, hScale, vScale or rotation 没有可更改的内容:请给出 dx、dy、hScale、vScale 或 rotation +@msg nothing to crop 没有可裁剪的内容 +@msg nothing to export 没有可导出的内容 +@msg nothing to export: the document has no visible art 没有可导出的内容:文档中没有可见的图稿 +@msg nothing to mark 没有可标记的内容 +@msg nothing to print: every artboard picked is blank 没有可打印的内容:所选的画板都是空白的 +@msg nothing to print: the document has no art that prints 没有可打印的内容:文档中没有会打印的图稿 +@msg object is not a path 对象不是路径 +@msg object transparency cannot be sampled onto highlighted characters; disable Transparency in Eyedropper Options or select the whole object 对象的透明度不能取样到高亮显示的字符上;请在吸管选项中停用“透明度”,或选择整个对象 +@msg object {_1} can't be set as point type 对象 {_1} 不能设为点文字 +@msg object {_1} has lost its file's contents: relink it 对象 {_1} 已丢失其文件的内容:请重新链接 +@msg object {_1} has no path 对象 {_1} 没有路径 +@msg object {_1} has no size 对象 {_1} 没有大小 +@msg object {_1} is hidden or locked, or on a locked artboard 对象 {_1} 已隐藏或锁定,或位于已锁定的画板上 +@msg object {_1} is not a placed document 对象 {_1} 不是置入的文档 +@msg object {_1} is not an Intertwine group 对象 {_1} 不是交织组 +@msg object {_1} is not an image 对象 {_1} 不是图像 +@msg object {_1} is not an image or a linked file 对象 {_1} 不是图像或链接文件 +@msg object {_1} is not an image or a placed document 对象 {_1} 不是图像或置入的文档 +@msg object {_1} is not in this Intertwine group 对象 {_1} 不在此交织组中 +@msg object {_1} needs a 3D effect at index {index} 对象 {_1} 在索引 {index} 处需要有 3D 效果 +@msg object {id} has anchors 0–{_1} for `corners`, not {past} 对象 {id} 可用于 `corners` 的锚点为 0–{_1},没有 {past} +@msg object {id} has {n} anchors, so no anchor {past} 对象 {id} 有 {n} 个锚点,因此没有锚点 {past} +@msg objects must be none, move or copy objects 必须是 none、move 或 copy +@msg objects were left out 有对象被略去 +@msg offset out of range 偏移超出范围 +@msg on must be true or false on 必须为 true 或 false +@msg only groups, layers, type and images have a Contents (Characters) row 只有编组、图层、文字和图像才有“内容 (字符)”行 +@msg only interior mesh lines can be deleted 只能删除内部的网格线 +@msg only solid colours can be added (not gradients or patterns) 只能添加纯色 (不能添加渐变或图案) +@msg only solid-colour swatches can be spot colours 只有纯色色板才能设为专色 +@msg only this Affinity file's embedded preview could be read: use File › Open to see it with its warning, or export SVG or PDF from Affinity first 只能读取此 Affinity 文件的嵌入预览:请使用 文件 › 打开 查看预览及其警告,或先从 Affinity 导出 SVG 或 PDF +@msg only this Affinity file's embedded preview could be read; use File › Open to see it with its warning 只能读取此 Affinity 文件的嵌入预览;请使用 文件 › 打开 查看预览及其警告 +@msg onto, above, below, icon and a column place belong to window.panel.float and window.panel.dock onto、above、below、icon 和列位置属于 window.panel.float 和 window.panel.dock +@msg onto, above, below, icon and a column place take no anchor, side, zone or before onto、above、below、icon 和列位置不能与 anchor、side、zone 或 before 同时给出 +@msg onto, above, below, icon and a column place take no x, y, width or height onto、above、below、icon 和列位置不能与 x、y、width 或 height 同时给出 +@msg open a document to look for its linked files 请打开一个文档以查找其链接文件 +@msg options must be an object (see file.formatOptions) options 必须是对象 (参见 file.formatOptions) +@msg order must be leftToRight or rightToLeft order 必须是 leftToRight 或 rightToLeft +@msg orientation must be portrait or landscape, not `{o}` orientation 必须是 portrait 或 landscape,不能是 `{o}` +@msg outlineText {v}: true or false outlineText {v}:应为 true 或 false +@msg output intent profile: {e} 输出意图配置文件:{e} +@msg output.destination: no RGB or CMYK profile is called “{name}” output.destination:没有名为“{name}”的 RGB 或 CMYK 配置文件 +@msg output.destination: {_1} files are CMYK, and “{name}” is an RGB profile output.destination:{_1} 文件为 CMYK,而“{name}”是 RGB 配置文件 +@msg output.destination: {e} output.destination:{e} +@msg output.inks: no ink is set to print output.inks:没有设为打印的油墨 +@msg output.outputIntent: no profile is called “{name}”, and a {std} output intent embeds its profile output.outputIntent:没有名为“{name}”的配置文件,而 {std} 输出意图需要嵌入其配置文件 +@msg output.outputIntent: no profile is called “{name}”: a {std} output intent embeds a profile, or names a registered printing condition (outputConditionId and registry) output.outputIntent:没有名为“{name}”的配置文件:{std} 输出意图需要嵌入配置文件,或指定已注册的印刷条件 (outputConditionId 和 registry) +@msg output.outputIntent: {std} files print in CMYK, and “{name}” is an RGB profile output.outputIntent:{std} 文件以 CMYK 印刷,而“{name}”是 RGB 配置文件 +@msg output.outputIntent: “{name}” is a grey profile, which can't be a {std} output intent output.outputIntent:“{name}”是灰度配置文件,不能用作 {std} 输出意图 +@msg overlap: {o} pt is half a tile or more overlap:{o} pt 已达到或超过拼贴的一半 +@msg overlapping Shaper paths must be in the same layer or group 重叠的 Shaper 路径必须位于同一图层或编组中 +@msg overlapping edits 编辑范围相互重叠 +@msg page art conflicts with image data for {key} 页面图稿与 {key} 的图像数据冲突 +@msg page art conflicts with pattern {name} 页面图稿与图案 {name} 冲突 +@msg page art conflicts with swatch {name} 页面图稿与色板 {name} 冲突 +@msg page art is missing image data for {key} 页面图稿缺少 {key} 的图像数据 +@msg page art is missing pattern {name} 页面图稿缺少图案 {name} +@msg page art is missing swatch {name} 页面图稿缺少色板 {name} +@msg page must be a whole number from 1 page 必须是从 1 开始的整数 +@msg page thumbnail: {e} 页面缩略图:{e} +@msg page {page} of the PDF has no objects PDF 的第 {page} 页没有对象 +@msg page {page}: `{file}` has {n} {artboards} 第 {page} 页:`{file}` 有 {n} {artboards} +@msg page {page}: `{name}` has {n} {pages} 第 {page} 页:`{name}` 有 {n} {pages} +@msg pages must be a range such as "2-3" or a page number, not {v} pages 必须是 "2-3" 这样的范围或一个页码,不能是 {v} +@msg pages: {e} pages:{e} +@msg panel position must be finite 面板位置必须是有限值 +@msg parameters must be an object 参数必须是对象 +@msg params must be an object params 必须是对象 +@msg passwords can have up to {MAX_PASSWORD} bytes 密码最多 {MAX_PASSWORD} 字节 +@msg path has no open end 路径没有开放的端点 +@msg paths must be strings paths 必须是字符串 +@msg pdfCompatible: false needs every artboard to keep the editable artwork pdfCompatible: false 需要用全部画板来保留可编辑的图稿 +@msg pdfs `{m}`: single or multiple pdfs `{m}`:应为 single 或 multiple +@msg pickColorFrom is "artwork" or "swatches", not {v} pickColorFrom 为 "artwork" 或 "swatches",不能是 {v} +@msg pins and moved must be non-empty lists of the same length pins 和 moved 必须是长度相同的非空列表 +@msg pins must be [[x,y]…] pins 必须是 [[x,y]…] +@msg pins must be finite pins 必须是有限值 +@msg place is above, below or inside, not `{other}` place 为 above、below 或 inside,不能是 `{other}` +@msg plane must be left|right|ground plane 必须是 left|right|ground +@msg plane must be left|right|ground|none plane 必须是 left|right|ground|none +@msg point is outside the mesh 点位于网格之外 +@msg points must be [t, left, right] with non-negative widths points 必须是宽度非负的 [t, left, right] +@msg points must be a non-empty [[x,y,pressure?]…] list points 必须是非空的 [[x,y,pressure?]…] 列表 +@msg points must be a non-empty list of finite [x,y] points 必须是由有限 [x,y] 组成的非空列表 +@msg points must be an array or null points 必须是数组或 null +@msg points must be finite points 必须是有限值 +@msg points must be finite [x,y] pairs points 必须是有限的 [x,y] 对 +@msg points must contain 1–4096 coordinates points 必须包含 1–4096 个坐标 +@msg position must be normal, superscript or subscript position 必须是 normal、superscript 或 subscript +@msg position must be topLeft, topRight, bottomLeft or bottomRight position 必须是 topLeft、topRight、bottomLeft 或 bottomRight +@msg positions must be between 0 and 100000 pt positions 必须介于 0 到 100000 pt 之间 +@msg ppi must be a positive number, not {ppi} ppi 必须是正数,不能是 {ppi} +@msg ppi must be between 1 and 2400 ppi 必须介于 1 到 2400 之间 +@msg preserve `{v}` is not transforms|bounds|fileDimensions|fit|fill preserve `{v}` 不是 transforms|bounds|fileDimensions|fit|fill +@msg preserveShape must be clippingMask|transparency preserveShape 必须是 clippingMask|transparency +@msg preset `{id}`: mobile or density preset `{id}`:应为 mobile 或 density +@msg preset must be standard, diffuse, topLeft or right preset 必须是 standard、diffuse、topLeft 或 right +@msg preset must be the name of a print preset preset 必须是打印预设的名称 +@msg previewMode must be default, pixel or overprint previewMode 必须是 default、pixel 或 overprint +@msg print settings: {e} 打印设置:{e} +@msg procedures nest too deeply 过程嵌套过深 +@msg procedures nested too deeply 过程嵌套得过深 +@msg quotes must be {double?, single?} quotes 必须是 {double?, single?} +@msg quotes.{k} must be an opening and a closing quote, e.g. "“”" quotes.{k} 必须是一个左引号和一个右引号,例如 "“”" +@msg rasterEffectsPpi must be 1–2400 (72 screen, 150 medium, 300 high) rasterEffectsPpi 必须为 1–2400 (72 为屏幕,150 为中,300 为高) +@msg rating must be a whole number from 0 to {MAX_RATING} rating 必须是 0 到 {MAX_RATING} 之间的整数 +@msg rect and {k} contradict each other: give the area or the artboards rect 与 {k} 相互矛盾:请只给出区域或画板 +@msg rect applies to raster formats, not {_1} rect 适用于栅格格式,不适用于 {_1} +@msg rect is [x, y, width, height] in points rect 是以点为单位的 [x, y, width, height] +@msg rect must be [x, y, width, height] rect 必须是 [x, y, width, height] +@msg rect must be [x, y, width, height] with a positive width and height rect 必须是宽度和高度为正的 [x, y, width, height] +@msg rect needs finite numbers and a positive width and height rect 需要有限的数字以及为正的宽度和高度 +@msg redraw needs id redraw 需要 id +@msg redraw within must be a finite distance of 0 or more redraw 的 within 必须是大于或等于 0 的有限距离 +@msg registration must be 0..8 registration 必须为 0..8 +@msg release a layer or a group 请释放一个图层或编组 +@msg replace keeps the replaced object's place and transform: drop template, at and rect replace 会保留被替换对象的位置和变换:请去掉 template、at 和 rect +@msg replace: select the one object to replace replace:请选择要替换的那一个对象 +@msg resolution must be a number or screen|medium|high resolution 必须是数字或 screen|medium|high +@msg resolution must be between 1 and 2400 ppi resolution 必须介于 1 到 2400 ppi 之间 +@msg rotation must be a number of degrees rotation 必须是以度为单位的数字 +@msg rotation must be finite rotation 必须是有限值 +@msg rows and cols must be 1..50 rows 和 cols 必须为 1..50 +@msg rows and columns must be 1..500 rows 和 columns 必须为 1..500 +@msg rows of objects whose appearances differ can't be reordered together 外观不同的对象的行不能一起重新排序 +@msg save the document first: Package collects a saved document 请先保存文档:打包只收集已保存的文档 +@msg save the document first: copy puts the file in its folder 请先保存文档:copy 会把文件放到文档所在的文件夹中 +@msg savedPreset: {e} savedPreset:{e} +@msg scale `{s}`: a factor such as 2x, a width (100w), a height (100h) or a resolution (72ppi) scale `{s}`:应为 2x 这样的倍数、宽度 (100w)、高度 (100h) 或分辨率 (72ppi) +@msg scale must be a number above 0, at most 64 scale 必须是大于 0 且不超过 64 的数字 +@msg scale must be non-zero scale 不能为零 +@msg scope must be selection or all (got {other}) scope 必须是 selection 或 all (实际为 {other}) +@msg scribble is too long 涂抹线过长 +@msg segment does not exist 线段不存在 +@msg select a Shaper group 请选择一个 Shaper 组 +@msg select a blend to set easing or color shift 请选择一个混合对象以设置缓动或颜色偏移 +@msg select a compound shape 请选择一个复合形状 +@msg select a graph 请选择一个图表 +@msg select a group or layer with visible, unlocked members 请选择含有可见且未锁定成员的编组或图层 +@msg select a layer 请选择一个图层 +@msg select a text object 请选择一个文本对象 +@msg select an image (or an Image Trace group) 请选择一个图像 (或图像描摹组) +@msg select an image, or pass id 请选择一个图像,或传入 id +@msg select an object (or pass `id`) 请选择一个对象 (或传入 `id`) +@msg select an object with a freeform gradient 请选择带有自由渐变的对象 +@msg select an object with an opacity mask 请选择带有不透明度蒙版的对象 +@msg select area type 请选择区域文字 +@msg select area type (text in a frame) 请选择区域文字 (文本框中的文本) +@msg select area type and closed paths (at least one area type object) 请选择区域文字和闭合路径 (至少一个区域文字对象) +@msg select area type whose first line can fit its frame 请选择首行能放入其文本框的区域文字 +@msg select artwork to add its colours 请选择图稿以添加其颜色 +@msg select artwork to make a group of its colours 请选择图稿以用其颜色创建颜色组 +@msg select artwork to recolour, or give a colour `group` 请选择要重新着色的图稿,或给出颜色 `group` +@msg select exactly two path ends to join 请恰好选择两个要连接的路径端点 +@msg select members of a compound shape 请选择复合形状的成员 +@msg select objects or give ids 请选择对象或给出 ids +@msg select objects to align to the guide 请选择要与参考线对齐的对象 +@msg select one Intertwine group 请选择一个交织组 +@msg select one image or pass id 请选择一个图像或传入 id +@msg select paths 请选择路径 +@msg select paths or compound paths 请选择路径或复合路径 +@msg select rows or objects to collect 请选择要收集的行或对象 +@msg select something to export 请选择要导出的内容 +@msg select something to export (or turn off selectedOnly) 请选择要导出的内容 (或关闭 selectedOnly) +@msg select text to redefine the style from 请选择用来重新定义样式的文本 +@msg select the art and, on top of it, the mask object 请选择图稿以及位于其上方的蒙版对象 +@msg select the art for the design 请选择用于设计的图稿 +@msg select the art for the pattern tile 请选择用于图案拼贴的图稿 +@msg select the art to add first 请先选择要添加的图稿 +@msg select the art to collect (or give its ids) 请选择要收集的图稿 (或给出其 ids) +@msg select the content and a path on top 请选择内容以及位于其上方的一条路径 +@msg select the images to relink, or pass ids 请选择要重新链接的图像,或传入 ids +@msg select the images, or pass ids 请选择图像,或传入 ids +@msg select the linked images or placed documents, or pass ids 请选择链接的图像或置入的文档,或传入 ids +@msg select the object to copy from 请选择要从中复制的对象 +@msg select the objects (or give their ids) 请选择对象 (或给出其 ids) +@msg select the objects to attach to the path 请选择要附着到路径上的对象 +@msg select the objects to bind 请选择要绑定的对象 +@msg select two or more objects to intertwine 请选择两个或更多要交织的对象 +@msg select two or more slices to combine 请选择两个或更多要组合的切片 +@msg select type on a path 请选择路径文字 +@msg selection `{m}`: one or each selection `{m}`:应为 one 或 each +@msg settings is an object of document.exportForScreens params settings 是由 document.exportForScreens 的参数组成的对象 +@msg settings must be an object settings 必须是对象 +@msg show `{show}`: all, missing, modified or embedded show `{show}`:应为 all、missing、modified 或 embedded +@msg show must be true or false show 必须为 true 或 false +@msg size must be "small", "medium", "large" or a number of points over 0 up to {_1} size 必须是 "small"、"medium"、"large",或大于 0 且不超过 {_1} 的点数 +@msg size must be positive size 必须为正数 +@msg sizeMode is "fixed" or "pressure", not `{other}` sizeMode 为 "fixed" 或 "pressure",不能是 `{other}` +@msg sizeMode is "fixed" or "pressure", not {other} sizeMode 为 "fixed" 或 "pressure",不能是 {other} +@msg sizeVariation must be 0 or more sizeVariation 必须大于或等于 0 +@msg smallCaps must be true or false smallCaps 必须为 true 或 false +@msg sort `{x}`: name, kind or status sort `{x}`:应为 name、kind 或 status +@msg source paths are too large for Shaper 源路径对 Shaper 而言过大 +@msg spot, mode and group apply to solid colours only spot、mode 和 group 只适用于纯色 +@msg status `{s}`: linked, embedded, missing or modified status `{s}`:应为 linked、embedded、missing 或 modified +@msg steps must be 1..1000 steps 必须为 1..1000 +@msg steps must be 1..{MAX_STEPS} steps 必须为 1..{MAX_STEPS} +@msg stop {i}: `plates` must be four numbers from 0 to 1 (C, M, Y, K) 色标 {i}:`plates` 必须是四个 0 到 1 之间的数字 (C, M, Y, K) +@msg stopped before the archive was finished 在归档完成之前已停止 +@msg strokeMode must be within|along|across, got {m} strokeMode 必须是 within|along|across,实际为 {m} +@msg strokeOptions must be an object of stroke.set options strokeOptions 必须是由 stroke.set 的选项组成的对象 +@msg strokeWidth must be a positive number of pixels strokeWidth 必须是以像素为单位的正数 +@msg subpath {si} is not an open subpath 子路径 {si} 不是开放的子路径 +@msg subpaths required when joining the same path 连接同一条路径时必须给出 subpaths +@msg swatchConflict must be "merge" or "add", not {v} swatchConflict 必须是 "merge" 或 "add",不能是 {v} +@msg text must be {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} text 必须是 {characterSet?, platform?, removeLineReturns?, removeParagraphReturns?, replaceSpaces?} +@msg text transform is collapsed or unrepresentable 文本的变换已塌缩或无法表示 +@msg text.replaceSpaces must be a whole number from 2 to 100 text.replaceSpaces 必须是 2 到 100 之间的整数 +@msg text.{key} `{v}`: use "{_1}" or "{_2}" text.{key} `{v}`:请使用 "{_1}" 或 "{_2}" +@msg textureImage: give {path} or {name, dataBase64} of an image file, or a texture as effect.list gives it textureImage:请给出图像文件的 {path} 或 {name, dataBase64},或 effect.list 所给出的纹理 +@msg textureImage: {e} textureImage:{e} +@msg that makes more than {MAX_PIECES} slices 这样会生成超过 {MAX_PIECES} 个切片 +@msg that object is not in the overlap 该对象不在重叠处 +@msg the Affinity document has no page with a size Affinity 文档中没有带尺寸的页面 +@msg the CMYK image couldn't be written 无法写出 CMYK 图像 +@msg the PDF security settings were saved without their passwords: give openPassword or permissionsPassword again, or security without passwordRequired to export unprotected PDF 安全性设置保存时未包含密码:请重新给出 openPassword 或 permissionsPassword,或给出不含 passwordRequired 的 security 以便不加保护地导出 +@msg the RTF file has too many paragraphs or formatting changes to place as type (at most {MAX_PARAGRAPHS} paragraphs and {MAX_RUNS} runs) RTF 文件的段落或格式变化过多,无法作为文字置入 (最多 {MAX_PARAGRAPHS} 个段落和 {MAX_RUNS} 个文本段) +@msg the RTF file is too large to place as type (at most {_1} MB) RTF 文件过大,无法作为文字置入 (最多 {_1} MB) +@msg the SVG expands to more than {0} nested SVG images SVG 展开后的嵌套 SVG 图像超过 {0} 个 +@msg the SVG expands to more than {0} objects SVG 展开后的对象超过 {0} 个 +@msg the SVG has no drawable objects SVG 中没有可绘制的对象 +@msg the SVGZ file unpacks to more than {_1} MB SVGZ 文件解压后超过 {_1} MB +@msg the Word document has more than {MAX_PAGES} pages of properties, too many to place Word 文档的属性页超过 {MAX_PAGES} 页,过多而无法置入 +@msg the Word document has more than {MAX_RUNS} runs of character or paragraph properties, too many to place Word 文档的字符或段落属性段超过 {MAX_RUNS} 个,过多而无法置入 +@msg the Word document has more than {_1} MB of formatting properties, too many to place Word 文档的格式属性超过 {_1} MB,过多而无法置入 +@msg the Word document has too much text or formatting to place as type (at most {MAX_PARAGRAPHS} paragraphs, {MAX_RUNS} runs and {_1} MB of text) Word 文档的文本或格式过多,无法作为文字置入 (最多 {MAX_PARAGRAPHS} 个段落、{MAX_RUNS} 个文本段和 {_1} MB 文本) +@msg the Word document has too much text to place as type (at most {_1} MB) Word 文档的文本过多,无法作为文字置入 (最多 {_1} MB) +@msg the Word document is encrypted or protected with a password; save it without one to place it Word 文档已加密或受密码保护;请去掉密码保存后再置入 +@msg the Word document is too large to place (its {name} stream holds more than {_1} MB) Word 文档过大,无法置入 (其 {name} 流超过 {_1} MB) +@msg the Word document was last saved with fast save, which VectorCraft doesn't read; save it again with fast save turned off Word 文档上次是以快速保存方式保存的,VectorCraft 无法读取;请关闭快速保存后重新保存 +@msg the Word document was saved by Word 6.0 or Word 95, which VectorCraft doesn't read; save it as a Word 97-2003 document Word 文档是由 Word 6.0 或 Word 95 保存的,VectorCraft 无法读取;请将其另存为 Word 97-2003 文档 +@msg the Word document's text is made of more than {MAX_PIECES} pieces, too many to place Word 文档的文本由超过 {MAX_PIECES} 个片段组成,过多而无法置入 +@msg the action set ids are not a list 动作组 ID 不是列表 +@msg the active paint is not a gradient 当前上色不是渐变 +@msg the artboards don't fit on the canvas: fewer artboards, less spacing or a smaller size 画板在画布上放不下:请减少画板、缩小间距或减小尺寸 +@msg the character's size can't be measured 无法测量该字符的大小 +@msg the compound shape is too complex to expand 复合形状过于复杂,无法扩展 +@msg the container of the listed objects is gone 所列对象的容器已不存在 +@msg the copy `{user_id}` can't be read as a library 副本 `{user_id}` 无法作为库读取 +@msg the copy would leave the canvas 副本会超出画布 +@msg the cut cannot be made on this art: {e} 无法在此图稿上进行切割:{e} +@msg the data isn't an EMF or WMF picture 该数据不是 EMF 或 WMF 图片 +@msg the data isn't an EPS or PostScript picture 该数据不是 EPS 或 PostScript 图片 +@msg the deferred paste is no longer available 延迟粘贴的内容已不可用 +@msg the design is too big to draw at every data point of this graph 设计过大,无法在此图表的每个数据点上绘制 +@msg the design is too big to draw in every column of this graph 设计过大,无法在此图表的每个柱形中绘制 +@msg the dialog it was opened over is no longer open 它所依附的对话框已不再打开 +@msg the dictionary stack overflowed 字典栈溢出 +@msg the editing data's hash has no place 编辑数据的哈希值没有存放位置 +@msg the entities the document declares expand to too much text 文档声明的实体展开后的文本过多 +@msg the envelope's content has no bounds 封套的内容没有边界 +@msg the envelope's mesh is malformed 封套的网格格式不正确 +@msg the file holds no `variableSets` element 文件中没有 `variableSets` 元素 +@msg the file is larger than {_1} MB 文件大于 {_1} MB +@msg the file is too large 文件过大 +@msg the file was updated or encrypted already 文件已被更新或加密 +@msg the first character of a line has no character before it to kern: it moves only up and down (dy) 行首字符前面没有可供调整字偶距的字符:它只能上下移动 (dy) +@msg the font {_1} is not available 字体 {_1} 不可用 +@msg the format has no file extension 该格式没有文件扩展名 +@msg the graphic can't be read: {e} 无法读取该图形:{e} +@msg the graphic is empty 该图形为空 +@msg the graphic's data is damaged 该图形的数据已损坏 +@msg the graphic's document has no artboard 该图形的文档没有画板 +@msg the header `{cell}` for `{name}` would read back as the variable `{other}`: rename one of them `{name}` 的标题 `{cell}` 读回时会被当作变量 `{other}`:请重命名其中一个 +@msg the image `{path}` isn't followed: {r} 未跟随图像 `{path}`:{r} +@msg the image has no embedded pixels 该图像没有嵌入的像素 +@msg the image is empty 图像为空 +@msg the image is fully transparent 图像完全透明 +@msg the image is not on an artboard; pass rect 图像不在画板上;请传入 rect +@msg the image's pixels are missing 图像的像素缺失 +@msg the image's pixels aren't in the document 图像的像素不在文档中 +@msg the image's pixels can't be read: {e} 无法读取图像的像素:{e} +@msg the image's transparency doesn't fit it 图像的透明度数据与图像不相符 +@msg the job would print more than {MAX_PAGES} pages 此作业会打印超过 {MAX_PAGES} 页 +@msg the job would print more than {_1} pages 此作业会打印超过 {_1} 页 +@msg the layers hold no art to mask 这些图层中没有可添加蒙版的图稿 +@msg the layout moved 布局已移动 +@msg the library has a group `{name}` already 库中已有组 `{name}` +@msg the library has no group `{name}` 库中没有组 `{name}` +@msg the library has no item `{item}` 库中没有项目 `{item}` +@msg the library has no item `{other}` 库中没有项目 `{other}` +@msg the library holds {MAX_GROUPS} groups already 库中已有 {MAX_GROUPS} 个组 +@msg the library holds {MAX_ITEMS} items of this kind already 库中已有 {MAX_ITEMS} 个此类项目 +@msg the name is empty 名称为空 +@msg the object has no fill, stroke or effect 该对象没有填色、描边或效果 +@msg the object has no graphic style (pass `name`) 该对象没有图形样式 (请传入 `name`) +@msg the objects don't match the plan 对象与计划不相符 +@msg the open and permissions passwords must differ 打开密码和权限密码必须不同 +@msg the operand stack overflowed 操作数栈溢出 +@msg the page has no area 该页面没有面积 +@msg the page's type in an unread object's box isn't in its page art 页面中位于某个未读取对象框内的文字不在其页面图稿中 +@msg the paint behind the proxy is not a gradient 代理对应的上色不是渐变 +@msg the parent must be a layer or sublayer 父项必须是图层或子图层 +@msg the path can't be extended 该路径无法延伸 +@msg the path can't be inside one of the objects to attach 路径不能位于要附着的某个对象内部 +@msg the path has no segments to measure 该路径没有可测量的线段 +@msg the path is empty 路径为空 +@msg the path is too short to shorten that much 路径太短,无法缩短这么多 +@msg the picture has no objects 图片中没有对象 +@msg the program runs too long 程序运行时间过长 +@msg the program uses too much memory 程序占用的内存过多 +@msg the rectangle must have a width and a height 矩形必须有宽度和高度 +@msg the rectangle needs a size and must lie on the canvas 矩形需要有大小,且必须位于画布上 +@msg the selected art has no width 所选图稿没有宽度 +@msg the selection draws nothing 所选对象没有绘制任何内容 +@msg the selection has no area 所选对象没有面积 +@msg the selection has no art to export 所选对象中没有可导出的图稿 +@msg the selection has no bounds 所选对象没有边界 +@msg the selection has no graphic style 所选对象没有图形样式 +@msg the slice would leave the canvas 切片会超出画布 +@msg the spot colour couldn't be added 无法添加该专色 +@msg the subpath has no ends to extend (it is closed or a single point) 该子路径没有可延伸的端点 (它是闭合的或只有一个点) +@msg the symbols are nested too deeply to expand their instances 符号嵌套过深,无法扩展其实例 +@msg the text file is too large to place as type (at most {_1} MB) 文本文件过大,无法作为文字置入 (最多 {_1} MB) +@msg the text is empty 文本为空 +@msg the text is over {_1} MB 文本超过 {_1} MB +@msg the top object must be a path 最上方的对象必须是路径 +@msg the trace was cancelled 描摹已取消 +@msg the trailer has no size 尾部没有大小 +@msg the type object is flattened to nothing 文字对象拼合后没有任何内容 +@msg the value of axis `{tag}` must be a number or null 轴 `{tag}` 的值必须是数字或 null +@msg the variable library declares more than {MAX_LIBRARY_DATASETS} data sets 变量库声明的数据集超过 {MAX_LIBRARY_DATASETS} 个 +@msg the variable library declares more than {MAX_LIBRARY_ENTITIES} entities 变量库声明的实体超过 {MAX_LIBRARY_ENTITIES} 个 +@msg the variable library declares more than {MAX_LIBRARY_VARIABLES} variables 变量库声明的变量超过 {MAX_LIBRARY_VARIABLES} 个 +@msg the variable library declares no variables 变量库没有声明任何变量 +@msg the variable library is larger than {MAX_LIBRARY_BYTES} bytes 变量库大于 {MAX_LIBRARY_BYTES} 字节 +@msg the variable library is not UTF-8 变量库不是 UTF-8 +@msg the variable library is not well-formed XML: {e} 变量库不是格式正确的 XML:{e} +@msg the variable library nests elements more than {MAX_LIBRARY_NESTING} deep 变量库的元素嵌套超过 {MAX_LIBRARY_NESTING} 层 +@msg the variable library's document type declaration holds something other than entities of plain text 变量库的文档类型声明中含有纯文本实体以外的内容 +@msg the variable library's entities expand to more than {MAX_LIBRARY_BYTES} bytes 变量库的实体展开后超过 {MAX_LIBRARY_BYTES} 字节 +@msg the window has no dock 该窗口没有停放区 +@msg the written PDF has no catalog 已写出的 PDF 没有目录 +@msg the written PDF has no cross-reference table 已写出的 PDF 没有交叉引用表 +@msg the written PDF's metadata can't be edited 无法编辑已写出的 PDF 的元数据 +@msg there are no colour swatches to draw 没有可绘制的颜色色板 +@msg there is already a design named `{name}` 已有名为 `{name}` 的设计 +@msg there is already a design named `{to}` 已有名为 `{to}` 的设计 +@msg there is already a toolbar named `{name}` 已有名为 `{name}` 的工具栏 +@msg there is already a toolbar named `{to}` 已有名为 `{to}` 的工具栏 +@msg there is no library: create one first 没有库:请先创建一个 +@msg these paths are too degenerate for Shaper 这些路径过于退化,Shaper 无法处理 +@msg thumbnail: no page {v} (the {what} has {count}) thumbnail:没有第 {v} 页 ({what} 有 {count} 页) +@msg tileRange of {what}: {e} {what} 的 tileRange:{e} +@msg title can't be empty title 不能为空 +@msg to (the new name) is required 必须给出 to (新名称) +@msg to must be fill or stroke to 必须是 fill 或 stroke +@msg to must be smooth or corner to 必须是 smooth 或 corner +@msg too large for a zip archive: package to a folder 对 zip 归档而言过大:请打包到文件夹 +@msg too many Shaper edits Shaper 编辑过多 +@msg too many `points` (at most 100000) `points` 过多 (最多 100000 个) +@msg too many characters: at most {MAX_CHARS} 字符过多:最多 {MAX_CHARS} 个 +@msg too many dots for this size: lower the frequency 此尺寸下的网点过多:请降低频率 +@msg too many filters over one file 单个文件上的滤镜过多 +@msg too many overlapping paths for Shaper 重叠的路径过多,Shaper 无法处理 +@msg too many saved clips 已保存的剪切过多 +@msg too many saved graphics states 已保存的图形状态过多 +@msg toolbar (a toolbar's name) must be a string toolbar (工具栏名称) 必须是字符串 +@msg toolbar name can't be empty 工具栏名称不能为空 +@msg transparentImages {v}: "flatten" (the default) or "omit" transparentImages {v}:应为 "flatten" (默认值) 或 "omit" +@msg two saved selections would be named `{_1}` 会有两个已保存的选区都名为 `{_1}` +@msg type dimension cannot be represented with this text transform 无法用此文本变换表示文字尺寸 +@msg type dimension must be finite 文字尺寸必须是有限值 +@msg unexpected resources 出现了意外的资源 +@msg unknown CMYK profile `{name}` 未知的 CMYK 配置文件 `{name}` +@msg unknown File Info field `{k}` 未知的文件信息字段 `{k}` +@msg unknown OpenType feature `{t}` in featureChanges (the tags `features` takes) featureChanges 中有未知的 OpenType 特性 `{t}` (应为 `features` 接受的标签) +@msg unknown PDF preset `{name}` (presets: {_1}) 未知的 PDF 预设 `{name}` (现有预设:{_1}) +@msg unknown Warp style 未知的变形样式 +@msg unknown alignToPath `{a}` 未知的 alignToPath `{a}` +@msg unknown arrowhead {v} 未知的箭头 {v} +@msg unknown artboardLayout key `{k}` (layout, columns, spacing, rightToLeft) 未知的 artboardLayout 键 `{k}` (layout、columns、spacing、rightToLeft) +@msg unknown attribute `{attribute}` 未知的属性 `{attribute}` +@msg unknown attribute `{k}` 未知的属性 `{k}` +@msg unknown axis `{other}` (vertical|horizontal|free) 未知的轴 `{other}` (vertical|horizontal|free) +@msg unknown blend mode `{b}` 未知的混合模式 `{b}` +@msg unknown blend mode {b} 未知的混合模式 {b} +@msg unknown brush type `{o}` 未知的画笔类型 `{o}` +@msg unknown category `{c}` 未知的类别 `{c}` +@msg unknown category `{c}` ({_1}) 未知的类别 `{c}` ({_1}) +@msg unknown character `{c}` 未知的字符 `{c}` +@msg unknown character style number field 未知的字符样式数值字段 +@msg unknown effect `{e}` 未知的效果 `{e}` +@msg unknown end {e} 未知的端点 {e} +@msg unknown flattenerPreset `{name}` (High Resolution, Medium Resolution, Low Resolution or a saved one: see flattener.presets.list) 未知的 flattenerPreset `{name}` (应为 High Resolution、Medium Resolution、Low Resolution 或已保存的预设:参见 flattener.presets.list) +@msg unknown format `{f}` (vcswatches, gpl, ase or css) 未知的格式 `{f}` (vcswatches、gpl、ase 或 css) +@msg unknown from {f} 未知的 from {f} +@msg unknown graph type `{t}` 未知的图表类型 `{t}` +@msg unknown grid colours `{name}` (one of {_1}) 未知的网格颜色 `{name}` (应为 {_1} 之一) +@msg unknown gridType `{t}` 未知的 gridType `{t}` +@msg unknown highlight `{v}` 未知的 highlight `{v}` +@msg unknown intent `{i}` 未知的意图 `{i}` +@msg unknown issue kind {id} 未知的问题类型 {id} +@msg unknown issue kind {v} 未知的问题类型 {v} +@msg unknown knockout state {k} 未知的挖空状态 {k} +@msg unknown language `{language}` (one of {_1}) 未知的语言 `{language}` (应为 {_1} 之一) +@msg unknown language `{l}` (one of {_1}) 未知的语言 `{l}` (应为 {_1} 之一) +@msg unknown method `{m}` ({_1}) 未知的方法 `{m}` ({_1}) +@msg unknown mode `{other}` (gray, rgb, hsb, lab, cmyk or web) 未知的模式 `{other}` (gray、rgb、hsb、lab、cmyk 或 web) +@msg unknown mode `{other}` (mono|cmyk) 未知的模式 `{other}` (mono|cmyk) +@msg unknown mode `{other}` (rgb|cmyk|gray|global) 未知的模式 `{other}` (rgb|cmyk|gray|global) +@msg unknown mode `{other}` (set, add, subtract) 未知的模式 `{other}` (set、add、subtract) +@msg unknown mode `{o}` 未知的模式 `{o}` +@msg unknown mode {m} 未知的模式 {m} +@msg unknown model `{other}` 未知的模型 `{other}` +@msg unknown model `{other}` (keep, new or document) 未知的模型 `{other}` (keep、new 或 document) +@msg unknown op {o} 未知的 op {o} +@msg unknown order `{o}`: toggle, front, forward, backward or back 未知的 order `{o}`:应为 toggle、front、forward、backward 或 back +@msg unknown overprints `{v}` (preserve, simulate or discard) 未知的 overprints `{v}` (preserve、simulate 或 discard) +@msg unknown panel 未知的面板 +@msg unknown pieLegend `{v}` (none, standard or wedges) 未知的 pieLegend `{v}` (none、standard 或 wedges) +@msg unknown piePosition `{v}` (even, ratio or stacked) 未知的 piePosition `{v}` (even、ratio 或 stacked) +@msg unknown pieSort `{v}` (none, all or first) 未知的 pieSort `{v}` (none、all 或 first) +@msg unknown plate `{n}` 未知的印版 `{n}` +@msg unknown preference `{k}` 未知的首选项 `{k}` +@msg unknown profile {id} (see stroke.widthProfile.list) 未知的配置文件 {id} (参见 stroke.widthProfile.list) +@msg unknown proof target `{t}` 未知的校样目标 `{t}` +@msg unknown quotes key `{k}` (double, single) 未知的 quotes 键 `{k}` (double、single) +@msg unknown resolution `{v}` 未知的分辨率 `{v}` +@msg unknown rule `{rule}` ({_1}) 未知的规则 `{rule}` ({_1}) +@msg unknown rule `{r}` (see color.harmony) 未知的规则 `{r}` (参见 color.harmony) +@msg unknown setting `{k}` 未知的设置 `{k}` +@msg unknown shape `{v}` (circle|ellipse|square|diamond|line) 未知的形状 `{v}` (circle|ellipse|square|diamond|line) +@msg unknown spacing `{o}` (smooth|steps|distance) 未知的间距 `{o}` (smooth|steps|distance) +@msg unknown tileType `{t}` ({_1}) 未知的 tileType `{t}` ({_1}) +@msg unknown units 未知的单位 +@msg unknown units `{u}` 未知的单位 `{u}` +@msg unknown value axis `{v}` (left, right or both) 未知的数值轴 `{v}` (left、right 或 both) +@msg unknown variation `{v}` (tintsShades|warmCool|vividMuted) 未知的变化 `{v}` (tintsShades|warmCool|vividMuted) +@msg unknown warp style `{s}` 未知的变形样式 `{s}` +@msg unknown {key} `{v}` (none, short or full) 未知的 {key} `{v}` (none、short 或 full) +@msg unknown {key} profile `{n}` 未知的 {key} 配置文件 `{n}` +@msg unknown {k} key `{key}` (size, position) 未知的 {k} 键 `{key}` (size、position) +@msg unreadable object 对象无法读取 +@msg unsafe destination file name 目标文件名不安全 +@msg use paths or compound paths 请使用路径或复合路径 +@msg useArtboards must be true or false, not {v} useArtboards 必须为 true 或 false,不能是 {v} +@msg value for `{var}` must match its variable kind `{var}` 的值必须与其变量类型相符 +@msg variable `{_1}` is already defined 变量 `{_1}` 已定义 +@msg variable `{name}` has no `trait` 变量 `{name}` 没有 `trait` +@msg variable `{name}` is already defined 变量 `{name}` 已定义 +@msg variable `{name}` is declared twice 变量 `{name}` 被声明了两次 +@msg variable `{to}` already exists 变量 `{to}` 已存在 +@msg view is required 必须给出 view +@msg view must be one of {_1} view 必须是 {_1} 之一 +@msg warpStyle can only edit an applied Warp effect warpStyle 只能编辑已应用的变形效果 +@msg width and height must be more than 0 and at most {limit} width 和 height 必须大于 0 且不超过 {limit} +@msg width factors must be finite 宽度系数必须是有限值 +@msg width must be positive width 必须为正数 +@msg width out of range 宽度超出范围 +@msg width/height must be > 0 width/height 必须 > 0 +@msg x and y must be finite numbers x 和 y 必须是有限的数字 +@msg x and y must be given together x 和 y 必须一起给出 +@msg zone needs an anchor: dock beside a panel with anchor, or in a new dock column with side (Start or End) zone 需要 anchor:请用 anchor 停放在某个面板旁,或用 side (Start 或 End) 停放到新的停放列中 +@msg zoom must be positive zoom 必须为正数 +@msg {C}: no effect at index {list} {C}:索引 {list} 处没有效果 +@msg {DEFAULT_PRESET} is protected and stays {DEFAULT_PRESET} 受保护,会保留 +@msg {DEFAULT_PRESET} is protected: save the settings under another name {DEFAULT_PRESET} 受保护:请用其他名称保存这些设置 +@msg {LABEL} produced an empty result {LABEL} 得到了空结果 +@msg {_1} MB of document, at most {_2} MB 文档为 {_1} MB,最多允许 {_2} MB +@msg {_1} MB of images, at most {_2} MB 图像共 {_1} MB,最多允许 {_2} MB +@msg {_1} bytes of RGBA data for a {width} × {height} image {width} × {height} 的图像却有 {_1} 字节的 RGBA 数据 +@msg {_1} can't be read, or changed since it was read: relink or update it first {_1} 无法读取,或在读取后已被更改:请先重新链接或更新它 +@msg {_1} can't go into {into} {_1} 不能放入 {into} +@msg {_1} can't limit printing to low resolution: choose PDF 1.4 or later, or allow high-resolution printing {_1} 不能将打印限制为低分辨率:请选择 PDF 1.4 或更高版本,或允许高分辨率打印 +@msg {_1} files can't be opened yet 目前还无法打开 {_1} 文件 +@msg {_1} files can't be password-protected: remove the passwords or choose no standard {_1} 文件不能用密码保护:请移除密码或不选择标准 +@msg {_1} files can't be {_2} {_1} 文件不能 {_2} +@msg {_1} files can't carry editing data: turn off preserveEditing {_1} 文件不能包含编辑数据:请关闭 preserveEditing +@msg {_1} files can't have PDF layers: turn off createLayers {_1} 文件不能包含 PDF 图层:请关闭 createLayers +@msg {_1} files need a creation date, and this platform has no clock to give one {_1} 文件需要创建日期,而此平台没有可提供日期的时钟 +@msg {_1} isn't a path {_1} 不是路径 +@msg {_1} options: {e} {_1} 选项:{e} +@msg {_1} paints nothing to rasterize {_1} 没有绘制任何可栅格化的内容 +@msg {_1} passwords can have up to {MAX_LEGACY_PASSWORD} ASCII characters (PDF 1.7 and later take any) {_1} 的密码最多 {MAX_LEGACY_PASSWORD} 个 ASCII 字符 (PDF 1.7 及更高版本可使用任意字符) +@msg {_1} with linked images or an image map writes several files: use document.export 带有链接图像或图像映射的 {_1} 会写出多个文件:请使用 document.export +@msg {_1} writes one file per artboard here: use document.export, or name one artboard {_1} 在这里会为每个画板写出一个文件:请使用 document.export,或指定一个画板 +@msg {_1} × {_2} pixels is too large for {_3} (at most {max} pixels a side): lower the scale {_1} × {_2} 像素对 {_3} 而言过大 (每边最多 {max} 像素):请降低缩放比例 +@msg {_1}: belongs to another account {_1}:属于其他帐户 +@msg {_1}: not a folder of its own {_1}:不是独立的文件夹 +@msg {_1}: not a plain file {_1}:不是普通文件 +@msg {dest}: {e} {dest}:{e} +@msg {doc} has no folder {doc} 没有所在的文件夹 +@msg {file} isn't linked {file} 不是链接文件 +@msg {folder} is not a package folder {folder} 不是打包文件夹 +@msg {folder}: more than {max} files and folders {folder}:文件和文件夹超过 {max} 个 +@msg {folder}: {e} {folder}:{e} +@msg {g} is not a Live Paint group {g} 不是实时上色组 +@msg {id} is not a slice {id} 不是切片 +@msg {key} must be 0–{MAX_ADD_AROUND} pt {key} 必须为 0–{MAX_ADD_AROUND} pt +@msg {key} must be [x,y] {key} 必须是 [x,y] +@msg {key} must be a percentage {min}..100 {key} 必须是 {min}..100 的百分比 +@msg {key} must be a positive number, not {n} {key} 必须是正数,不能是 {n} +@msg {key} must be a positive number, not {v} {key} 必须是正数,不能是 {v} +@msg {key} must be one of {_1} {key} 必须是 {_1} 之一 +@msg {key} must be {_1}, got {s} {key} 必须是 {_1},实际为 {s} +@msg {key} must name a floating panel {key} 必须指定一个浮动面板 +@msg {key} must name a panel in another group {key} 必须指定另一个组中的面板 +@msg {key} must name a panel that shows {key} 必须指定一个已显示的面板 +@msg {key} must name a registered panel {key} 必须指定一个已注册的面板 +@msg {kind} files have no transparency, and the file would have {what}: flatten it first (object.flattenTransparency) {kind} 文件不支持透明度,而该文件会包含 {what}:请先拼合 (object.flattenTransparency) +@msg {kind} images can't be opened (see document.formats) 无法打开 {kind} 图像 (参见 document.formats) +@msg {k} `{v}` is not one of the choices {k} `{v}` 不在可选项之中 +@msg {k} has control characters{_1} {k} 含有控制字符{_1} +@msg {k} is too long (at most {MAX_TEXT} characters) {k} 过长 (最多 {MAX_TEXT} 个字符) +@msg {k} must be > 0 {k} 必须 > 0 +@msg {k} must be a number (pt) or a length such as "210 mm" {k} 必须是数字 (pt) 或 "210 mm" 这样的长度 +@msg {k} must be a number from {min} to {max} {k} 必须是 {min} 到 {max} 之间的数字 +@msg {k} must be a number or "auto" {k} 必须是数字或 "auto" +@msg {k} must be a string {k} 必须是字符串 +@msg {k} must be text {k} 必须是文本 +@msg {k} must be true or false {k} 必须为 true 或 false +@msg {k} must be {{size?, position?}} {k} 必须是 {{size?, position?}} +@msg {k}: can't read `{s}` as a length {k}:无法将 `{s}` 读作长度 +@msg {label} must be a number at most {max} {label} 必须是不超过 {max} 的数字 +@msg {label} must be a number from -1296 to 1296 {label} 必须是 -1296 到 1296 之间的数字 +@msg {label} must be a number from 1 to 10000 {label} 必须是 1 到 10000 之间的数字 +@msg {name} `{s}`: {ids} {name} `{s}`:{ids} +@msg {name} is built in and can't be deleted {name} 是内置项,不能删除 +@msg {name} must be {lo}–{hi}{unit} (got {v}) {name} 必须为 {lo}–{hi}{unit} (实际为 {v}) +@msg {name}: CRC or size mismatch {name}:CRC 或大小不匹配 +@msg {name}: only this Affinity file's embedded preview could be read; use File › Open to see it with its warning, or export SVG or PDF from Affinity before placing {name}:只能读取此 Affinity 文件的嵌入预览;请使用 文件 › 打开 查看预览及其警告,或在置入前先从 Affinity 导出 SVG 或 PDF +@msg {name}: {e} {name}:{e} +@msg {native}; its embedded preview can't be read either ({e}) {native};其嵌入的预览也无法读取 ({e}) +@msg {std} files are CMYK, and the file would have colours in {_1} {std} 文件为 CMYK,而该文件会包含 {_1} 颜色 +@msg {std} files embed their fonts, and the file would have one that isn't {std} 文件需要嵌入字体,而该文件会有一个未嵌入的字体 +@msg {target} is the file this document was read from, and reading it left things out ({what}): writing over it would lose them for good. Write another file instead, or pass acknowledgeLoss: true to replace it anyway {target} 是读取此文档时所用的文件,读取时略去了一些内容 ({what}):覆盖该文件会永久丢失这些内容。请改为写入另一个文件,或传入 acknowledgeLoss: true 以仍然替换它 +@msg {ty} brushes need selected art (or params.{k}) {ty} 画笔需要选中的图稿 (或 params.{k}) +@msg {v} is not an asset id {v} 不是资源 ID +@msg {what} can't be intertwined {what} 不能交织 +@msg {what} doesn't fit the paper with its bleed and marks {what} 连同出血和标记在纸张上放不下 +@msg {what} would print on more than {MAX_TILES} tiles: scale it down {what} 会打印成超过 {MAX_TILES} 个拼贴:请将其缩小 diff --git a/docs/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..913a973f70 100644 --- a/docs/localization-parity.md +++ b/docs/localization-parity.md @@ -27,7 +27,7 @@ Chinese by `imachus`). Only Georgian is recorded as reviewed by a native speaker | Language | Code | UI strings translated | Dialogs / tooltips / messages | Script support | Native review | Status | To `full` (h) | |---|---|---|---|---|---|---|---| | English | `en` | source, 100% | all | Latin | — | full | — | -| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | all | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) | +| Simplified Chinese (Mandarin) | `zh-hans` | 5,102 rows, 791 of them `@msg` (counted 2026-10-21): menus, `tl!` literals and messages complete when counted | all (3,439 `@msg` and 48 `@msgplural` rows, counted 2026-11-13: the untranslated report lists 40 missing messages for it) | CJK UI font from craft-fonts (BIZ UDPGothic) when embedded, else a system font; IME with marked text at the caret; vertical type partial ([type-parity.md](type-parity.md)) | no | full | — (review) | | Spanish | `es` | 4,009 / 4,020 (99.7%) | all (604 messages) | Latin | no | full | — (review) | | Hindi | `hi` | 8,782 rows (2026-11-13): 3,014 `@msg`, 47 `@msgplural`, 41 `@plural`; no gaps in the untranslated report | menus, dialogs, tooltips and scanned messages; contributor-approved loanword terminology; agent wording pass | UI and document text shape with HarfRust; installed desktop Devanagari fallback, or a `Deva` craft-fonts face; web font still needed | contributor terminology review; 2026-11-13 wording pass on messages and longer labels, with a glossary-term test; wider proofread welcome | complete catalog, machine-assisted draft | native review, layout and web font | | Arabic | `ar` | 0 | none | document text: bidi layout and Paragraph Direction; interface: no right-to-left layout, no Arabic shaping in egui | no | none | 25–40 | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index f3707717e6..a047f0c5f6 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -889,6 +889,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur The counts that Package's status and question, the folder search of Missing Fonts and Find Linked Files, the modified and missing linked file prompts and the CSS Properties panel's notes show use the interface language's plural forms instead of a fixed "(s)" form. Evidence: `the_missing_links_note_uses_each_count_form` in `crates/ui-egui/src/dialogs/tests_package.rs`, `the_modified_links_question_uses_each_count_form` in `crates/ui-egui/src/dialogs/missing_links.rs`, `search_counts_use_each_count_form` in `crates/ui-egui/src/dialogs/missing_fonts/tests.rs` and `the_unnamed_objects_note_uses_each_count_form` in `crates/ui-egui/src/panels/tests_css_properties.rs`. The status bar's messages for relinked, replaced, updated and embedded images, broken links, linked files that can't be read or changed on disk, images that can't be decoded, added and unavailable fonts, copied CSS, downloads and exports with their notes name one item in the singular and more in the plural, in English and in each language's plural forms (`counted_status_messages_take_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`). The status bar's messages for documents opened or saved with notes and for discarded or restored recovered documents, and the Global Edit options' Results line, do the same (`open_save_and_recovery_messages_take_the_plural_form` in `crates/ui-egui/src/i18n/tests.rs`); the warnings for linked images and placed documents output as their previews and the errors for a page past a placed file's last page or artboard name their counts in the singular or the plural in English. + - The Simplified Chinese catalog (`crates/ui-egui/src/i18n/zh-hans.tsv`) has an `@msg` row for the status and error messages the untranslated report (`untranslated_report`) finds in the sources, 3,439 `@msg` rows in all: the errors of commands, scripts, file import and export and plug-ins. The report lists 40 Simplified Chinese messages: the composite font, justification order and Blob Brush connect end errors that open #7211, #7116 and #7330 translate, and two messages that no catalog has a row for yet. A message with two placeholders in a row (`Template::parse` in `crates/ui-egui/src/i18n/catalog.rs`) is still shown in English. - Edit › Undo and Edit › Redo, in the in-window and native menus, name the step in the interface language as the History panel lists it (`panels::history::step_name`), so when the catalog has no plain row for a step named after an effect, such as Feather, the menu shows the translation of the effect's menu row without its ellipsis (`undo_and_redo_name_the_step_as_the_history_panel_does`). - The History panel translates the undo step names the engine, the tools and the shell write as literals: the untranslated report (`untranslated_report`) lists any a complete catalog lacks, and a step whose catalog row exists only with an ellipsis (such as an effect's menu name) shows that row without the ellipsis. - The History panel menu has Step Backward and Step Forward, which run Edit › Undo and Edit › Redo and are disabled when there is no step to undo or redo, and Set History Limit…, which opens Preferences at Performance, where History States is set. From 30eb14d7dc74a1beb6bfe6ee51eebca448e35ca1 Mon Sep 17 00:00:00 2001 From: ruchirguitar <113586074+ruchirguitar@users.noreply.github.com> Date: Sat, 14 Nov 2026 07:56:20 +0530 Subject: [PATCH 33/33] PDF import: outlined Indic glyphs keep their own stroke and transparency (#5235) (#6957) * PDF import: outlined Indic glyphs keep their own stroke and transparency (#5235) Follow-up to #6906, for two review findings on how a line kept as outlines is joined into paths: - Glyphs were joined when their stroke widths matched; dash, cap, join and miter limit were ignored, so a dashed glyph took the stroke of the solid one before it. They join only when the whole stroke matches. - Translucent glyphs were joined into one fill, where overlapping marks paint once instead of compositing as the file draws them. Only opaque glyphs join. Also records the fix in the PDF row of docs/target-app-parity.md and in docs/roadmap.md, as AGENTS.md asks. Tests: outlined_glyphs_stroked_differently_stay_apart and outlined_translucent_glyphs_stay_apart (both fail without the change). * PDF import: outlined Indic glyphs under a soft mask stay apart; check kept geometry (#5235) Review follow-ups on #6957 and #6906: - Glyphs kept as outlines under a soft mask were joined into one path, so the mask was applied once to all of them where the file applies it to each glyph. They now join only when no soft mask is active. - The fallback tests counted outline nodes only. A new test compares the kept outlines (element count and bounding box) with the same file opened as outlines, so a dropped or moved glyph fails it. Tests: outlined_glyphs_under_a_soft_mask_stay_apart (fails without the change) and kept_glyphs_are_where_the_file_draws_them. * PDF import: mask outlined Indic glyphs one by one under a soft mask (#5235) Review follow-up on #6957. Keeping glyphs out of one shared path was not enough: each path still went into the soft mask's queue and flush_masked wrapped them all in one masked group, so a mask that is not constant was applied once to every glyph, where overlapping glyphs composite one by one in the file. With a soft mask active the queued art is now drained before the first glyph and after each one, so every glyph gets a mask of its own. Test: outlined_glyphs_under_a_varying_soft_mask_are_masked_one_by_one checks that each outlined glyph carries its own mask (0 of 3 without the change). * #6957: clarify soft-mask exception in glyph grouping docs --------- Co-authored-by: ruchirguitar --- crates/pdf/src/import/builder.rs | 32 ++++++--- crates/pdf/src/import/state.rs | 17 +++++ crates/pdf/src/tests/reshaped.rs | 109 +++++++++++++++++++++++++++++-- docs/roadmap.md | 3 + docs/target-app-parity.md | 4 ++ 5 files changed, 152 insertions(+), 13 deletions(-) diff --git a/crates/pdf/src/import/builder.rs b/crates/pdf/src/import/builder.rs index c1be8fba84..4a979e8742 100644 --- a/crates/pdf/src/import/builder.rs +++ b/crates/pdf/src/import/builder.rs @@ -4,7 +4,7 @@ use super::entry::{ UNNAMED_CHARACTERS_NOTE, max_objects, }; use super::reshaped; -use super::state::{Builder, Frame, FrameKind, GlyphRun, Saved, bounds, grouped, line_width, mean_scale, paints, round3, stroke_layer}; +use super::state::{Builder, Frame, FrameKind, GlyphRun, Saved, bounds, grouped, line_width, mean_scale, paints, round3, same_stroke, stroke_layer}; use super::{ Affine, Appearance, AppearanceItem, Arc, BezPath, BfString, BlendMode, CacheKey, Color, Colors, Families, FillLayer, FillRule, FunctionBudget, FunctionPaint, HashMap, HashSet, ImageBlob, ImageObject, Knockout, Look, MaskSpec, MaskType, Native, Node, NodeId, NodeKind, OpacityMask, Paint, @@ -445,15 +445,23 @@ impl<'p> Builder<'p> { if let Some((t, opacity, facts)) = line.finish() { if checked && reshaped::reshapes(&t.plain_text()) && !reshaped::draws_as(&t, &facts.ink) { outlined = true; + if self.mask.is_some() { + self.flush_glyphs(); + self.flush_masked(); + } for g in glyphs { - let same = self.glyphs.as_ref().is_some_and(|r| { - r.paint == g.paint - && r.opacity == g.opacity - && r.overprint == g.overprint - && r.stroke.is_some() == g.stroke.is_some() - && r.stroke.as_ref().map(|s| s.line_width) == g.stroke.as_ref().map(|s| s.line_width) - && (r.scale - g.scale).abs() < 1e-9 - }); + // Glyphs the file fills one by one composite where they overlap: only opaque + // ones, drawn alike down to the stroke, and not under a soft mask (which + // would be applied once to all of them), share a path. + let same = g.opacity >= 0.999 + && self.mask.is_none() + && self.glyphs.as_ref().is_some_and(|r| { + r.paint == g.paint + && r.opacity == g.opacity + && r.overprint == g.overprint + && same_stroke(r.stroke.as_ref(), g.stroke.as_ref()) + && (r.scale - g.scale).abs() < 1e-9 + }); match &mut self.glyphs { Some(r) if same => r.path.extend(g.path.iter()), _ => { @@ -461,6 +469,12 @@ impl<'p> Builder<'p> { self.glyphs = Some(g); } } + // Under a soft mask each glyph is masked on its own: the art queued for the + // mask is drained at each glyph, or one group of them all takes it once. + if self.mask.is_some() { + self.flush_glyphs(); + self.flush_masked(); + } } self.warn(RESHAPED_TEXT_NOTE); } else { diff --git a/crates/pdf/src/import/state.rs b/crates/pdf/src/import/state.rs index 0470d641e2..12eb4498dc 100644 --- a/crates/pdf/src/import/state.rs +++ b/crates/pdf/src/import/state.rs @@ -235,6 +235,23 @@ pub(super) fn line_width(p: &StrokeProps, scale: f64) -> f64 { if p.line_width == 0.0 { HAIRLINE } else { p.line_width as f64 * scale } } +/// Do strokes `a` and `b` draw alike: both unstroked, or the same width, caps, joins, miter limit +/// and dashes ([`stroke_layer`] takes all of them)? +pub(super) fn same_stroke(a: Option<&StrokeProps>, b: Option<&StrokeProps>) -> bool { + match (a, b) { + (None, None) => true, + (Some(a), Some(b)) => { + a.line_width == b.line_width + && a.line_cap == b.line_cap + && a.line_join == b.line_join + && a.miter_limit == b.miter_limit + && a.dash_array == b.dash_array + && a.dash_offset == b.dash_offset + } + _ => false, + } +} + pub(super) fn stroke_layer(paint: Paint, opacity: f32, p: &StrokeProps, scale: f64) -> StrokeLayer { let mut st = StrokeLayer::new(paint, line_width(p, scale)); st.opacity = opacity; diff --git a/crates/pdf/src/tests/reshaped.rs b/crates/pdf/src/tests/reshaped.rs index 060eca40b7..af173fa55b 100644 --- a/crates/pdf/src/tests/reshaped.rs +++ b/crates/pdf/src/tests/reshaped.rs @@ -5,6 +5,7 @@ //! ([`vectorcraft_text::test_fonts::matra_font`]): Ka draws Source Sans 3's `a` and the vowel sign //! I, set before its consonant, draws its `i`. +use kurbo::{Rect, Shape}; use vectorcraft_doc::{Document, NodeKind}; use vectorcraft_testkit::pdf::{PdfPage, first_extra, pdf_with}; use vectorcraft_text::FontDb; @@ -29,6 +30,14 @@ fn glyph(c: char) -> (u32, f64) { /// and a ToUnicode map naming each glyph's character. `sign_width`: the advance the file gives /// the vowel sign, in thousandths of an em (`None`: the font's). fn page(drawn: &[char], sign_width: Option) -> Vec { + page_with(|id| format!("[{}] TJ", drawn.iter().map(|&c| id(c)).collect::()), "", &[], sign_width) +} + +/// [`page`] showing glyphs with the text operators `ops` makes (after the position of the first +/// glyph) from a function giving a character's glyph id in hex, and `extra` more resources: a +/// graphics state, to set between glyphs or before them, with `objects` it refers to (the first +/// is object `first_extra(1) + 4`). +fn page_with(ops: impl Fn(&dyn Fn(char) -> String) -> String, extra: &str, objects: &[String], sign_width: Option) -> Vec { FontDb::global().add_font(matra_font().expect("the test font builds")); let program = matra_font().unwrap(); let hex: String = program.iter().map(|b| format!("{b:02X}")).collect::() + ">"; @@ -55,14 +64,13 @@ fn page(drawn: &[char], sign_width: Option) -> Vec { ), ), ]; - let refs: Vec<&str> = objs.iter().map(String::as_str).collect(); + let refs: Vec<&str> = objs.iter().chain(objects).map(String::as_str).collect(); let resources = format!( - "/Font << /F1 << /Type /Font /Subtype /Type0 /BaseFont /MatratestSans-Regular /Encoding /Identity-H /DescendantFonts [{} 0 R] /ToUnicode {} 0 R >> >>", + "/Font << /F1 << /Type /Font /Subtype /Type0 /BaseFont /MatratestSans-Regular /Encoding /Identity-H /DescendantFonts [{} 0 R] /ToUnicode {} 0 R >> >> {extra}", n + 2, n + 3 ); - let shown: String = drawn.iter().map(|&c| format!("{:04X}", glyph(c).0)).collect(); - let content = format!("BT /F1 40 Tf 20 100 Td <{shown}> Tj ET"); + let content = format!("BT /F1 40 Tf 20 100 Td {} ET", ops(&|c| format!("<{:04X}>", glyph(c).0))); pdf_with(&[PdfPage { resources, ..PdfPage::new(300.0, 200.0, &content) }], &refs, None) } @@ -117,3 +125,96 @@ fn a_mark_without_an_advance_stays_with_its_line() { assert_eq!(texts(&report.document), [""; 0], "the mark is no type of its own"); assert_eq!(outlines(&report.document), 1, "both glyphs, as one path"); } + +/// The file's glyphs are outlines of one path when drawn alike, and a path each when a stroke +/// property other than the width (here the dash) differs between them: the first glyph's would +/// otherwise be drawn on all. +#[test] +fn outlined_glyphs_stroked_differently_stay_apart() { + // Stroked (`1 Tr`) at the same width, one glyph solid and the next dashed. + let alike = page_with(|id| format!("1 Tr 2 w {} Tj {} Tj {} Tj", id(KA), id(SIGN_I), id(KA)), "", &[], None); + let apart = page_with(|id| format!("1 Tr 2 w {} Tj [3 2] 0 d {} Tj [] 0 d {} Tj", id(KA), id(SIGN_I), id(KA)), "", &[], None); + assert_eq!(outlines(&open(&alike).document), 1, "drawn alike: one path"); + assert_eq!(outlines(&open(&apart).document), 3, "solid, dashed, solid: a path each"); +} + +/// Translucent glyphs the file fills one by one composite where they overlap, which one fill of +/// their joined outlines does not do: each keeps a path of its own. +#[test] +fn outlined_translucent_glyphs_stay_apart() { + let bytes = page_with(|id| format!("/G gs {} Tj {} Tj {} Tj", id(KA), id(SIGN_I), id(KA)), "/ExtGState << /G << /ca 0.5 >> >>", &[], None); + let report = open(&bytes); + assert_eq!(texts(&report.document), [""; 0]); + assert_eq!(outlines(&report.document), 3, "a path each"); +} + +/// Glyphs the file draws under a soft mask are not joined into one path: the mask would be applied +/// once to the whole, where the file applies it to each glyph. +#[test] +fn outlined_glyphs_under_a_soft_mask_stay_apart() { + // A luminosity mask (object `first_extra(1) + 4`) of mid gray over the page. + let group = stream("/Type /XObject /Subtype /Form /BBox [0 0 300 200] /Group << /S /Transparency /CS /DeviceGray >>", "0.5 g 0 0 300 200 re f"); + let mask = format!("/ExtGState << /M << /SMask << /S /Luminosity /G {} 0 R >> >> >>", first_extra(1) + 4); + let bytes = page_with(|id| format!("/M gs {} Tj {} Tj {} Tj", id(KA), id(SIGN_I), id(KA)), &mask, &[group], None); + let report = open(&bytes); + assert_eq!(texts(&report.document), [""; 0]); + assert_eq!(outlines(&report.document), 3, "a path each"); +} + +/// What the glyph outlines of `d` draw: how many path elements, and the box round them all. +fn outline_geometry(d: &Document) -> (usize, Rect) { + let (mut elements, mut bounds) = (0, None::); + d.walk(|n| { + if is_glyph_outlines(n) + && let Some(p) = n.path_data() + { + let path = p.to_bezpath(); + elements += path.elements().len(); + let b = path.bounding_box(); + bounds = Some(bounds.map_or(b, |u| u.union(b))); + } + }); + (elements, bounds.unwrap_or(Rect::ZERO)) +} + +/// A line kept as outlines draws the glyphs where they are drawn when the file is opened as +/// outlines: none dropped or moved. +#[test] +fn kept_glyphs_are_where_the_file_draws_them() { + for (drawn, sign_width) in [(vec![KA, SIGN_I, KA], None), (vec![KA, SIGN_I], Some(0.0))] { + let bytes = page(&drawn, sign_width); + let kept = open(&bytes); + let all = import_with_report(&bytes, &ImportOptions { text_as: TextAs::Outlines, ..Default::default() }).unwrap(); + assert_eq!(texts(&kept.document), [""; 0], "the line is kept as outlines"); + let (kept, all) = (outline_geometry(&kept.document), outline_geometry(&all.document)); + assert!(all.0 > 0, "the glyphs have outlines"); + assert_eq!(kept.0, all.0, "every outline element is kept"); + for (a, b) in [(kept.1.x0, all.1.x0), (kept.1.y0, all.1.y0), (kept.1.x1, all.1.x1), (kept.1.y1, all.1.y1)] { + assert!((a - b).abs() < 1e-6, "{:?} vs {:?}", kept.1, all.1); + } + } +} + +/// How many glyph outlines carry a soft mask of their own. +fn masked_outlines(d: &Document) -> usize { + let mut n = 0; + d.walk(|node| n += usize::from(is_glyph_outlines(node) && node.extras().mask.is_some())); + n +} + +/// A mask that isn't constant (dark on the left, light on the right) is a mask of its own on each +/// glyph, not one on a group of them: glyphs that overlap composite one by one, as the file draws +/// them. +#[test] +fn outlined_glyphs_under_a_varying_soft_mask_are_masked_one_by_one() { + let group = stream( + "/Type /XObject /Subtype /Form /BBox [0 0 300 200] /Group << /S /Transparency /CS /DeviceGray >>", + "0.2 g 0 0 150 200 re f 0.9 g 150 0 150 200 re f", + ); + let mask = format!("/ExtGState << /M << /SMask << /S /Luminosity /G {} 0 R >> >> >>", first_extra(1) + 4); + let bytes = page_with(|id| format!("/M gs {} Tj {} Tj {} Tj", id(KA), id(SIGN_I), id(KA)), &mask, &[group], None); + let report = open(&bytes); + assert_eq!(texts(&report.document), [""; 0]); + assert_eq!(outlines(&report.document), 3, "a path each"); + assert_eq!(masked_outlines(&report.document), 3, "each path has its own mask"); +} diff --git a/docs/roadmap.md b/docs/roadmap.md index 6f631fe88b..74dac90ec8 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -10,6 +10,8 @@ > > Numeric spinner update (2026-11-10): held mouse arrows repeat past the click timeout; scores and estimates unchanged (#6307). +> **PDF import status (2026-11-14, #6957):** Outlined Indic glyphs retain their full stroke properties; translucent glyphs stay separate, and a soft mask attaches to each glyph separately. Scores and estimates unchanged. + ## Feature submission evidence M4: Explicitly requested editing data keeps a single artboard larger than its 14,400 pt capped PDF page (#6865). Scores and estimates unchanged. @@ -56,6 +58,7 @@ M7 type: Touch Type moves a right-to-left character along its line and up from i | 2026-11-13 | minor | PDF import: glyphs inside a marked-content span with an empty `ActualText` come in as outlines in their fill and stroke instead of being left out, adding no text (#6971; `empty_actual_text_keeps_the_glyphs_art` in `crates/pdf/src/tests/actual_text.rs`). Scores and estimates unchanged. | | 2026-11-13 | minor | Library panels (swatch, Color Book, brush, symbol and graphic style libraries): in a panel made taller the list fills the space above the footer instead of scrolling inside 220 or 280 pt (#2992; `the_list_fills_a_tall_panel_above_its_footer` in `crates/ui-egui/src/panels/library_panel/tests.rs`). Scores and estimates unchanged. | | 2026-11-13 | minor | Swatches: in a panel made taller the swatch list fills the space above the footer instead of scrolling inside 150 or 200 pt (#1638; `the_list_fills_a_tall_panel_above_its_footer` in `crates/ui-egui/src/panels/swatches/tests.rs`). Scores and estimates unchanged. | +| 2026-11-13 | minor | M4: PDF import (#5235, #6906): Indic type keeps its marks on their line, and a line whose type would not draw as the file does keeps the file's glyphs as outlines (opaque, alike-stroked ones share a path except under a soft mask, where each glyph keeps its own path and mask; translucent ones keep their own). Scores and estimates unchanged. Evidence: `crates/pdf/src/tests/reshaped.rs`. | | 2026-11-10 | minor | M13 / G6: MCP catalogue (#6460): headless lists UI commands, eight prompts, 33 tools, effect vs command ids; scores and estimates unchanged. | | 2026-11-08 | minor | Character Styles panel: a double-click on a user style's row opens its Character Style Options (`ui.charStyleOptions`); the dialog keeps a fixed width and scrolls its sections in a shorter window, so OK and Cancel stay on screen. Tests `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options` and `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`. Scores and estimates unchanged. | | 2026-11-08 | minor | #3380, deletion only: preserve gradient placement and exact surviving geometry; consume owned subpaths with a shared original-node snapshot; consolidate status/evidence and document command/helper contracts. Compound behavior and the existing M2 status entry are retained. Endpoint highlighting remains open; scores and estimates unchanged. | diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 19b9c8d319..ee72f2cbe8 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -12,6 +12,8 @@ > > **Flyout update:** 2026-11-06 — resizable icon-strip flyouts and bottom-aligned panel footers (#5713); scores and estimates unchanged. +> **PDF import status (2026-11-14, #6957):** Outlined Indic glyphs retain their full stroke properties; translucent glyphs stay separate, and a soft mask attaches to each glyph separately. Scores and estimates unchanged. + ## Feature submission evidence EPS embedded fonts, 2026-11-09 (#6023–#6067): the fonts an EPS carries draw their glyphs instead of being set as type in an installed font: composite (Type 0) fonts with the 8/8 mapping, plain Type 42 fonts (characters from their glyph names or character map, widths from the font), CID-keyed fonts over CMaps (Type 0 and Type 2 CIDFonts, CIDFonts in a FontSet's Compact Font Format data, base-font CMaps, named CIDFonts over predefined Unicode CMaps set as type) and embedded Type 1 fonts; the show operators' spacing along the baseline is kept as tracking, `kshow` included. Each step is charged to the interpreter's budgets. Hand-written test files only (no corpus file uses these fonts); scores and estimates unchanged. Evidence: `crates/eps/src/import/tests_composite.rs`, `tests_type42.rs`, `tests_cid.rs`, `tests_type1.rs`; details in [file-format-parity.md](file-format-parity.md). @@ -60,6 +62,7 @@ Artboard Options forwards the active Artboard tool's Scale Artwork with Artboard | 2026-11-13 | minor | Library panels (swatch, Color Book, brush, symbol and graphic style libraries): in a panel made taller the list fills the space above the footer instead of scrolling inside 220 or 280 pt (#2992; `the_list_fills_a_tall_panel_above_its_footer` in `crates/ui-egui/src/panels/library_panel/tests.rs`). Scores and estimates unchanged. | | 2026-11-13 | minor | Swatches: in a panel made taller the swatch list fills the space above the footer instead of scrolling inside 150 or 200 pt (#1638; `the_list_fills_a_tall_panel_above_its_footer` in `crates/ui-egui/src/panels/swatches/tests.rs`). Scores and estimates unchanged. | | 2026-11-13 | minor | Swatches panel: its menu's Show Recent Colors hides and shows the panel's Recent Colors row, checked by default and kept between sessions apart from the Color panel's (#6617); scores unchanged. | +| 2026-11-13 | minor | M4: PDF import (#5235, #6906): Indic type keeps its marks on their line, and a line whose type would not draw as the file does keeps the file's glyphs as outlines (opaque, alike-stroked ones share a path except under a soft mask, where each glyph keeps its own path and mask; translucent ones keep their own). Scores and estimates unchanged. Evidence: `crates/pdf/src/tests/reshaped.rs`. | | 2026-11-09 | minor | Stroke panel: Line Arrow, a 41st generated arrowhead: two arms of the head's weight (stroke weight × arrowhead scale, at least 0.25 pt: the line's weight at 100 % for lines of 0.25 pt or more) at right angles, a sharp tip and square-cut ends, twice as wide as the other heads; bounds and clicks follow each head's own reach (`stroke.set` `LineArrow`, #5421); scores and estimates unchanged | | 2026-11-09 | minor | Canvas: a 3D effect (Extrude & Bevel, Inflate, Rotate, Revolve) changed on type or a group draws again where its art was and is, measured by that art; the canvas measures through its renderer and draws the part with the art it made to measure it. Exports and Object › Rasterize take the solid of 3D type. Fit to Artwork Bounds and per-artboard SVG export measure every artboard through one renderer, building each solid once. A 3D effect on a fill or stroke that builds nothing (Revolve after Roughen) no longer overflows the stack on the canvas, in Object › Rasterize and in raster and EPS exports: the fill or stroke draws as without it. Tests `tests::changed::a_3d_effect_changed_on_type_or_a_group_changes_where_its_solid_was`, `fx::tests::a_renderer_measures_the_art_on_each_artboard_building_each_solid_once`, `canvas::tests::an_edit_to_a_3d_effect_redraws_with_the_art_measured`, `fx::tests::a_3d_effect_that_builds_nothing_draws_the_item_as_without_it`. Scores and estimates unchanged. | | 2026-11-08 | minor | Style panels: a double-click on a user character style's row opens its Character Style Options (`ui.charStyleOptions`), and on a paragraph style's row its Paragraph Style Options (rename); the Character Style Options dialog keeps a fixed width and scrolls its sections in a shorter window, so OK and Cancel stay on screen. Tests `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options` and `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`. Scores and estimates unchanged. | @@ -568,6 +571,7 @@ since 2026-10-01. - **Save refusal (#6146):** ordinary File > Save to its own location offers Save As after a terminal permission/read-only refusal, suggesting the original name and format, preserving options for the unchanged format and opening the chosen format’s Save options on a change. Cancel keeps the failed-save state. Busy pickers, recovery-copy errors, other failures, explicit destinations and Close/Quit keep the existing dialog. Engine, UI and MCP regression cases are in `cmd/fileio/tests_readonly.rs`, `io/tests_read_only_save.rs` and MCP `tests/fileio.rs`; focused UI regressions passed; engine regression execution and native panel acceptance pending. Scores and hours unchanged. - **PDF authored replacement text (#6165):** `crates/pdf/src/tests/actual_text.rs` covers replacement once with suffix placement, PDF text-string decoding, empty and nested owners, sibling spans, page reset, multiple text-show operations, style/line changes, fill-and-stroke, reader windows and bounded input. Authored logical-order stretches are protected while adjacent ordinary text still undergoes reconstruction. The replacement takes its first placed glyph's style and line; its characters are not redistributed over later lines/styles. Exact mixed-direction paragraph layout is not claimed. No export changes; scores and hours stay at 75% / 10–20 h. +- **PDF Indic type (#5235, #6906):** `crates/pdf/src/tests/reshaped.rs` covers a vowel sign the file draws before its consonant (the line keeps the file's glyphs as outlines, with a note), text in reading order (stays type), a mark with no advance (stays on its line, not vertical type of its own), outlined glyphs stroked with different dashes and translucent outlined glyphs (each keeps its own path). Synthetic font only; one real Gujarati file checked by eye on page 1. Indic type in an uninstalled font keeps the file's glyphs as outlines; reordering signs so more lines stay editable is not done. Scores and hours stay at 75% / 10-20 h. - **Selection — disconnected path deletion (#3380, deletion only):** Clear/Delete and Cut turn surviving disconnected pieces of an ordinary contour into independent selected sibling objects. The first keeps its ID; all keep appearance, baked transforms and stacking order. `deletion_split_preserves_gradient_placement` compares rendered survivors with the original paint space and checks linear/radial fill/stroke vectors, already placed gradients and atomic Undo/Redo; connected remainders keep automatic fitting. Existing multi-subpath paths and explicit compound members retain their semantics. Original synthetic regressions in `crates/engine/tests/it/delete_path_pieces.rs` cover styled/transformed rectangles, cubic handles, compound preservation and atomic Undo/Redo; `crates/ui-egui/src/tests/removeanchors.rs` covers real-frame Delete/Backspace and a second Delete removing all survivors. Segment endpoint highlighting remains open. Scores and hours unchanged.