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/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); +} diff --git a/crates/engine/src/cmd/addons/color.rs b/crates/engine/src/cmd/addons/color.rs index fa8182c4c2..2fe9c77a36 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/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/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/docinfo.rs b/crates/engine/src/cmd/docinfo.rs index 54da0562b1..4068c2506c 100644 --- a/crates/engine/src/cmd/docinfo.rs +++ b/crates/engine/src/cmd/docinfo.rs @@ -781,7 +781,10 @@ fn info(s: &mut Session, p: &Value) -> Result { // 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 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/graph/tests.rs b/crates/engine/src/cmd/graph/tests.rs index 18bf7aa2cb..c997e803d5 100644 --- a/crates/engine/src/cmd/graph/tests.rs +++ b/crates/engine/src/cmd/graph/tests.rs @@ -6182,3 +6182,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/src/cmd/paint.rs b/crates/engine/src/cmd/paint.rs index c7159d563a..3ec28e7ce7 100644 --- a/crates/engine/src/cmd/paint.rs +++ b/crates/engine/src/cmd/paint.rs @@ -96,7 +96,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) ), @@ -105,7 +105,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/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/prefscmds.rs b/crates/engine/src/cmd/prefscmds.rs index d4ad54932e..7a948bfc87 100644 --- a/crates/engine/src/cmd/prefscmds.rs +++ b/crates/engine/src/cmd/prefscmds.rs @@ -793,7 +793,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 ), @@ -802,7 +802,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 ), @@ -818,8 +818,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`]. @@ -868,7 +880,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()) @@ -893,14 +907,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. @@ -911,7 +919,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/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/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/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/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/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/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/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 716de48c71..879eb9d9f5 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"; @@ -622,6 +623,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 or in an opacity mask's art, outside art cut to a clipping object: with its // art style, when it has one. if !self.in_look 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 83d5698abf..50a356e524 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 @@ -428,33 +426,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 @@ -588,9 +559,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 e2a1943de7..daf99047f3 100644 --- a/crates/eps/src/import/ai/tests.rs +++ b/crates/eps/src/import/ai/tests.rs @@ -10547,6 +10547,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`. @@ -10688,6 +10689,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 0cba32ec72..7e38b5ba51 100644 --- a/crates/eps/src/legacy_ai.rs +++ b/crates/eps/src/legacy_ai.rs @@ -124,6 +124,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 e5879c1626..ad0ccbce43 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/crates/mcp/src/ui_catalog.rs b/crates/mcp/src/ui_catalog.rs index 2388e9d10d..4e1e56619a 100644 --- a/crates/mcp/src/ui_catalog.rs +++ b/crates/mcp/src/ui_catalog.rs @@ -1549,7 +1549,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", 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/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 a910f75978..3b4e84989a 100644 --- a/crates/trace/src/tests.rs +++ b/crates/trace/src/tests.rs @@ -2111,6 +2111,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/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/control.rs b/crates/ui-egui/src/control.rs index 2eea07b3ee..2779bc7f6d 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -947,12 +947,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 @@ -975,11 +969,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(); @@ -1048,24 +1042,63 @@ 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 /// paths the request may not read: the value of a path param /// ([`vectorcraft_engine::file_access::is_path_key`]: `path`, `file`, `diffPath`, `destinationFolder` /// and the like), each path of a list under such a param, and under any other param, as a key too, a -/// string written as a path ([`unreadable_path_like`]). +/// string written as a path ([`unreadable_path_like`]). An empty or blank value names no path and stays. fn leave_out_unreadable_params(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, } }); @@ -1073,7 +1106,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); } @@ -1108,8 +1141,8 @@ fn leave_out_unreadable_paths(app: &VectorcraftApp, ui: &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 a0007d0676..b60b1adfdc 100644 --- a/crates/ui-egui/src/control/tests.rs +++ b/crates/ui-egui/src/control/tests.rs @@ -293,6 +293,42 @@ 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": []})); +} + +/// 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] 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/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/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 899f177897..1f469c69c9 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -5889,6 +5889,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 3667c25b1c..a23133933a 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -5892,6 +5892,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 24db0fa430..373eb0b79d 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -5771,6 +5771,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 dc435f1d7e..7178be3143 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -6203,6 +6203,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 fa9365b849..433c6af3d6 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5771,6 +5771,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 5016bbd953..951f6f9c37 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -5789,6 +5789,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 @@ -7081,6 +7082,1811 @@ 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 `{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}`: {_1}: {e} `{name}`: {_1}: {e} +@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 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 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 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 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 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 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 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 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 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 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 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 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}: custom must be a name {k}: custom muss ein Name 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 `{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 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 Banner Hems and Grommets… Bannersäume und Ösen… Banner Hems and Grommets Bannersäume und Ösen Hem Saum diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index d316f7d704..62e3adc491 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -6155,6 +6155,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 4cfeea4854..d1c6aa53af 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -6146,6 +6146,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 a1ddf6101f..45e47fd355 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -6166,6 +6166,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 706c83909e..057dce6872 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -6198,6 +6198,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 dc28724be2..9d056e085a 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -7218,6 +7218,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 ab06863782..c2f1a52cd9 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -6186,6 +6186,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 4900aa3cbf..a36c629332 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5771,6 +5771,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 5ef768e629..0ac2816b07 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -5824,6 +5824,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 @@ -7115,6 +7116,1810 @@ 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 Banner Hems and Grommets… Orli e occhielli per striscioni… Banner Hems and Grommets Orli e occhielli per striscioni Hem Orlo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index f2a1ffc1e1..71de1ce91c 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -5868,6 +5868,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 0a0f488707..2cff51d94b 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5927,6 +5927,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 5344b0124b..4715a413f4 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -5785,6 +5785,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 7ce152334e..94957fb782 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -6347,6 +6347,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 ab70dfa4db..5184985726 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -6058,6 +6058,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 67bd16ba24..f6e54552d4 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -6118,6 +6118,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 @@ -7558,9 +7559,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/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 2302b75e01..8dc8336089 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -5809,6 +5809,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 ef2265d001..cfb9d9734b 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -6156,6 +6156,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/tests.rs b/crates/ui-egui/src/i18n/tests.rs index f6be04511e..705fc68db2 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] @@ -3911,8 +4037,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("]; @@ -3957,9 +4088,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}`). @@ -4077,8 +4209,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); @@ -4150,6 +4299,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/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index d22c3b961f..2b3e843ba3 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -5828,6 +5828,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 8df23cab71..d4a2493914 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -5804,6 +5804,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 0ac99661db..d8469bce53 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -5805,6 +5805,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 3658ca1241..8ab185e5f9 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -6034,6 +6034,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 9306fdaf21..a0d987fe69 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -5868,6 +5868,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 093a395841..e0e84b137f 100644 --- a/crates/ui-egui/src/menus/ui_commands.rs +++ b/crates/ui-egui/src/menus/ui_commands.rs @@ -1550,7 +1550,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/panels/brushes.rs b/crates/ui-egui/src/panels/brushes.rs index 82991005ab..0b540a4ba5 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..aeab927e23 100644 --- a/crates/ui-egui/src/panels/brushes/tests.rs +++ b/crates/ui-egui/src/panels/brushes/tests.rs @@ -821,3 +821,36 @@ 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, 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(); + 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 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"); +} diff --git a/crates/ui-egui/src/tests/automation.rs b/crates/ui-egui/src/tests/automation.rs index cf071b0c16..ee0d0a4c35 100644 --- a/crates/ui-egui/src/tests/automation.rs +++ b/crates/ui-egui/src/tests/automation.rs @@ -945,6 +945,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/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)]); 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/control-protocol.md b/docs/control-protocol.md index 3ae9ddbe9a..40ad89d4b4 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..82ffbd4a5a 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: @@ -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/file-format-parity.md b/docs/file-format-parity.md index 490b47464d..d6816744fb 100644 --- a/docs/file-format-parity.md +++ b/docs/file-format-parity.md @@ -237,7 +237,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/localization-parity.md b/docs/localization-parity.md index cd8dc33f30..42b90cf48c 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,10 +32,10 @@ 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) | +| 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) | @@ -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/mcp.md b/docs/mcp.md index 26f1300e0f..aa188c7eb3 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:` @@ -3232,7 +3234,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: @@ -3535,6 +3541,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 @@ -3944,6 +3956,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. @@ -4935,7 +4950,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 @@ -6271,12 +6288,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. @@ -8353,6 +8371,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 @@ -11116,7 +11142,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}}}} @@ -11283,7 +11309,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}` diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 1dfaf846f5..48d10d50c8 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. @@ -758,6 +759,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. @@ -853,6 +855,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). @@ -879,6 +882,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - 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. - Hindi English words (2026-11-13): 264 rows no longer keep English words in their sentences, repeat a parameter's name in Hindi or garble them, among them "unknown align", which now names the alignment; lists of accepted values such as `(left|right|top|bottom)` stay as their English writes them; two tests check every row; native review remains; 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`). @@ -891,6 +895,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 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. @@ -909,6 +914,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. @@ -1377,6 +1383,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`. @@ -1435,6 +1442,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. @@ -1647,6 +1655,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`). @@ -1844,6 +1853,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 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. - **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.