Merge pull request #7267 from dotCooCoo/fix/doc-info-values-translated

Document Info: values show in the interface language
This commit is contained in:
BFlat
2026-11-14 06:27:27 +07:00
committed by GitHub
34 changed files with 1165 additions and 61 deletions
+228 -51
View File
@@ -18,7 +18,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Document Info", "Document Info",
["Window", "Document Info"], ["Window", "Document Info"],
None, None,
"{selectionOnly?, category?: document|objects|graphicStyles|brushes|spotColors|patterns|gradients|symbols|fonts|fontDetails|linkedImages|embeddedImages|repeats (only that one in sections and the text), format?: \"text\" (→ {text}: the report Document Info › Save… and Package write)} → {document, objects: {paths, compoundPaths, groups, …}, pathLength: pt (the paths' total length, compound paths' included, guides left out), fonts (the type in opacity masks and in the art of symbols and patterns included; with selectionOnly: the selected type's), images, swatches, graphicStyleNames (with selectionOnly: the styles the selected objects are linked to), brushNames (the brushes the art's drawn strokes use, each once; with selectionOnly: those the selected objects' strokes use), brushes: [{name, type: calligraphic|scatter|art|pattern|bristle|null}] (those brushes with their types; null: not in the Brushes panel), panelBrushNames (the Brushes panel's brushes), repeatObjects: [{id, name, type: radial|grid|mirror, instances, rows?, cols?}] (the Repeat objects, those in groups and other repeats included; with selectionOnly: those in the selection), spotObjects: [{id, name, spotColors}] (the objects filled or stroked with a spot color swatch, as a solid color or in the stops of a gradient that color a part of it above opacity 0 (a stop at opacity 0 counts only where it mixes with a stop above opacity 0 over a stretch of some width), at a tint above 0, and grayscale images colorized with one, each once, by the rules patternObjects follows, with the spot colors they use; Registration and process colors are not spot colors; objects.spots counts them), spotColorNames and spotColors (the spot colors those objects use), patternObjects: [{id, name, patterns}] (the objects filled or stroked with a pattern, each once, those in groups and type whose drawn characters use one included, hidden fills and strokes, fills and strokes at an opacity of 0, strokes with no width, guides, compound path members' own fills and strokes, images' fills and strokes without a shape and characters that draw nothing left out, with the patterns they use, which objects.patterns counts; with selectionOnly: those in the selection), patternNames (the patterns those objects use), gradientObjects: [{id, name, gradients}] (the objects filled or stroked with a gradient, by the same rules, with the gradients they use: a linked gradient swatch's name, else Linear gradient, Radial gradient or Freeform gradient; objects.gradients counts them), gradientNames (the gradients those objects use), …, sections: [{id, title, rows: [[label, value]]}] (the panel's categories; fontDetails: each font's file and whether its license lets it be embedded)}. An image linked to a file outside the automation read roots has link null, and its Linked Images row names the file alone. brushNames, brushes, spotObjects, patternObjects and gradientObjects leave out hidden objects, objects at an opacity of 0 and the objects inside a hidden group or layer or one at an opacity of 0 (with selectionOnly too), their characters' paints and colorized images included; objects still counts them as paths, groups and the other kinds", "{selectionOnly?, category?: document|objects|graphicStyles|brushes|spotColors|patterns|gradients|symbols|fonts|fontDetails|linkedImages|embeddedImages|repeats (only that one in sections and the text), format?: \"text\" (→ {text}: the report Document Info › Save… and Package write)} → {document: {…, units (English), unitId: the units' Preferences › Units id (points, millimeters, feetInches, …), rasterEffectsPpi}, objects: {paths, compoundPaths, groups, …}, pathLength: pt (the paths' total length, compound paths' included, guides left out), fonts (the type in opacity masks and in the art of symbols and patterns included; with selectionOnly: the selected type's), fontDetails: [{font, match: exact|style|missingWithFallback|missing, family, style, file, embedding: allowed|editing|previewPrint|forbidden|null}] (each font of fonts, in order: how it was found, the face shown (null when none), that face's file name (null for a built-in face) and what its license allows), images, imageObjects: [{name, width, height, linked, file}] (the image objects in the order Linked Images and Embedded Images list them; file: what a linked image's row shows, null for an embedded one), swatches, graphicStyleNames (with selectionOnly: the styles the selected objects are linked to), brushNames (the brushes the art's drawn strokes use, each once; with selectionOnly: those the selected objects' strokes use), brushes: [{name, type: calligraphic|scatter|art|pattern|bristle|null}] (those brushes with their types; null: not in the Brushes panel), panelBrushNames (the Brushes panel's brushes), repeatObjects: [{id, name, type: radial|grid|mirror, instances, rows?, cols?}] (the Repeat objects, those in groups and other repeats included; with selectionOnly: those in the selection), spotObjects: [{id, name, spotColors}] (the objects filled or stroked with a spot color swatch, as a solid color or in the stops of a gradient that color a part of it above opacity 0 (a stop at opacity 0 counts only where it mixes with a stop above opacity 0 over a stretch of some width), at a tint above 0, and grayscale images colorized with one, each once, by the rules patternObjects follows, with the spot colors they use; Registration and process colors are not spot colors; objects.spots counts them), spotColorNames and spotColors (the spot colors those objects use), patternObjects: [{id, name, patterns}] (the objects filled or stroked with a pattern, each once, those in groups and type whose drawn characters use one included, hidden fills and strokes, fills and strokes at an opacity of 0, strokes with no width, guides, compound path members' own fills and strokes, images' fills and strokes without a shape and characters that draw nothing left out, with the patterns they use, which objects.patterns counts; with selectionOnly: those in the selection), patternNames (the patterns those objects use), gradientObjects: [{id, name, gradients, kinds}] (the objects filled or stroked with a gradient, by the same rules, with the gradients they use: a linked gradient swatch's name, else its kind's name, such as Linear gradient; kinds gives each one's kind, linear|radial|conic|diamond|freeform, when it is named by its kind, else null; objects.gradients counts them), gradientNames (the gradients those objects use), …, sections: [{id, title, rows: [[label, value]]}] (the panel's categories; fontDetails: each font's file and whether its license lets it be embedded)}. An image linked to a file outside the automation read roots has link null, and its Linked Images row names the file alone. brushNames, brushes, spotObjects, patternObjects and gradientObjects leave out hidden objects, objects at an opacity of 0 and the objects inside a hidden group or layer or one at an opacity of 0 (with selectionOnly too), their characters' paints and colorized images included; objects still counts them as paths, groups and the other kinds",
has_doc, has_doc,
info info
), ),
@@ -85,17 +85,65 @@ pub struct Section {
/// What a font's `fsType` lets a copy of it do, read as exports read it ([`Embedding`]): the /// What a font's `fsType` lets a copy of it do, read as exports read it ([`Embedding`]): the
/// most permissive usage bit wins, and bitmap-only fonts may not be embedded. /// most permissive usage bit wins, and bitmap-only fonts may not be embedded.
pub fn embedding_label(fs_type: u16) -> &'static str { pub fn embedding_label(fs_type: u16) -> &'static str {
if Embedding::from_fs_type(fs_type) == Embedding::Forbidden { match embedding_id(fs_type) {
"embedding not allowed" "forbidden" => "embedding not allowed",
} else if fs_type & 0x0008 != 0 { "editing" => "embedding for editing",
"embedding for editing" "previewPrint" => "embedding for preview and print",
} else if fs_type & 0x0004 != 0 { _ => "embedding allowed",
"embedding for preview and print"
} else {
"embedding allowed"
} }
} }
/// [`embedding_label`] as the `embedding` value of a `fontDetails` entry: `forbidden`, `editing`,
/// `previewPrint` or `allowed`.
pub fn embedding_id(fs_type: u16) -> &'static str {
if Embedding::from_fs_type(fs_type) == Embedding::Forbidden {
"forbidden"
} else if fs_type & 0x0008 != 0 {
"editing"
} else if fs_type & 0x0004 != 0 {
"previewPrint"
} else {
"allowed"
}
}
/// A font as Font Details lists it: its `fontDetails` entry and its row. The row gives the file
/// the font is drawn from (`built in` for a face with no file) and whether its license lets it be
/// embedded; for a style the family lacks, the face shown in its place; for a font that is not
/// installed, the face shown instead, or `missing`. The entry holds the same facts as values
/// (`match`: `exact`, `style`, `missingWithFallback` or `missing`), so the panel can show them in
/// the interface language.
fn font_detail(used: &super::fonts::UsedFont) -> (Value, (String, String)) {
let db = vectorcraft_text::FontDb::global();
let (family, style, version) = used;
let label = super::fonts::font_label(used);
// Found by any of its names (in the version the type names); a style the family lacks shows in
// another.
let resolved = db.resolve(family, style).map(|(f, m)| (db.face_version(family, style, version.as_deref()).unwrap_or(f), m));
let (entry, detail) = match resolved {
Some((f, m)) if m != vectorcraft_text::FontMatch::Missing => {
let file = f.path().and_then(|p| p.file_name()).map(|n| n.to_string_lossy().into_owned());
let found = format!("{}; {}", file.as_deref().unwrap_or("built in"), embedding_label(f.fs_type()));
let substituted = m == vectorcraft_text::FontMatch::Style;
let entry = json!({
"font": label,
"match": if substituted { "style" } else { "exact" },
"family": f.family,
"style": f.style,
"file": file,
"embedding": embedding_id(f.fs_type()),
});
(entry, if substituted { format!("substituted: shown in {} {} ({found})", f.family, f.style) } else { found })
}
Some((f, _)) => (
json!({ "font": label, "match": "missingWithFallback", "family": f.family, "style": f.style, "file": null, "embedding": null }),
format!("missing: shown in {} {}", f.family, f.style),
),
None => (json!({ "font": label, "match": "missing", "family": null, "style": null, "file": null, "embedding": null }), "missing".into()),
};
(entry, (label, detail))
}
/// An image object as Document Info lists it: (name, width, height, linked file). /// An image object as Document Info lists it: (name, width, height, linked file).
type ImageRow = (String, u32, u32, Option<String>); type ImageRow = (String, u32, u32, Option<String>);
@@ -131,14 +179,15 @@ fn repeat_entry(d: &Document, n: &Node, spec: &RepeatSpec) -> (Value, (String, S
} }
/// The Document Info categories of `d` (only `category` when given) from `info` (what /// The Document Info categories of `d` (only `category` when given) from `info` (what
/// `document.info` reports), its fonts, its image objects, the rows of the brushes its art uses, the /// `document.info` reports), its fonts and their Font Details rows, its image objects, the rows of
/// rows of its Repeat objects and the rows of its objects filled or stroked with a spot color, with a /// the brushes its art uses, the rows of its Repeat objects and the rows of its objects filled or
/// pattern and with a gradient. /// stroked with a spot color, with a pattern and with a gradient.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn sections( fn sections(
d: &Document, d: &Document,
info: &Value, info: &Value,
fonts: &BTreeSet<super::fonts::UsedFont>, fonts: &BTreeSet<super::fonts::UsedFont>,
font_details: &[(String, String)],
images: &[ImageRow], images: &[ImageRow],
brushes: &[(String, String)], brushes: &[(String, String)],
repeats: &[(String, String)], repeats: &[(String, String)],
@@ -192,34 +241,7 @@ fn sections(
"gradients" => gradient_objects.to_vec(), "gradients" => gradient_objects.to_vec(),
"symbols" => names("symbols"), "symbols" => names("symbols"),
"fonts" => fonts.iter().map(|f| (super::fonts::font_label(f), String::new())).collect(), "fonts" => fonts.iter().map(|f| (super::fonts::font_label(f), String::new())).collect(),
"fontDetails" => { "fontDetails" => font_details.to_vec(),
let db = vectorcraft_text::FontDb::global();
fonts
.iter()
.map(|used| {
let (family, style, version) = used;
// Found by any of its names (in the version the type names); a style the
// family lacks shows in another.
let resolved =
db.resolve(family, style).map(|(f, m)| (db.face_version(family, style, version.as_deref()).unwrap_or(f), m));
let detail = match resolved {
Some((f, m)) if m != vectorcraft_text::FontMatch::Missing => {
let file =
f.path().and_then(|p| p.file_name()).map_or_else(|| "built in".into(), |n| n.to_string_lossy().into_owned());
let found = format!("{file}; {}", embedding_label(f.fs_type()));
if m == vectorcraft_text::FontMatch::Style {
format!("substituted: shown in {} {} ({found})", f.family, f.style)
} else {
found
}
}
Some((f, _)) => format!("missing: shown in {} {}", f.family, f.style),
None => "missing".into(),
};
(super::fonts::font_label(used), detail)
})
.collect()
}
"linkedImages" => { "linkedImages" => {
images.iter().filter_map(|(name, w, h, link)| Some((name.clone(), format!("{w} × {h} px — {}", link.as_ref()?)))).collect() images.iter().filter_map(|(name, w, h, link)| Some((name.clone(), format!("{w} × {h} px — {}", link.as_ref()?)))).collect()
} }
@@ -277,6 +299,39 @@ fn gradient_label(p: &Paint) -> Option<String> {
} }
} }
/// Where [`gradient_label`]'s name for gradient paint `p` comes from: `swatch:<name>` for a
/// gradient linked to a swatch, else `kind:<kind>` (`linear`, `radial`, `conic`, `diamond` or
/// `freeform`). The two prefixes keep a swatch's name apart from a kind's name it reads like.
fn gradient_source(p: &Paint) -> Option<String> {
match p {
Paint::Gradient(g) => Some(match g.swatch.as_deref().filter(|s| !s.is_empty()) {
Some(swatch) => format!("swatch:{swatch}"),
None => format!("kind:{}", g.gradient.kind.label().to_ascii_lowercase()),
}),
_ => None,
}
}
/// For each of `names` (an object's [`gradient_label`]s), the kind it names when only a kind
/// gives that name: a name one of `sources` (the object's [`gradient_source`]s) gives as a kind's
/// and none gives as a swatch's, else null. A swatch named like a kind keeps its name even where
/// a gradient of that kind is listed under the same name.
fn gradient_kinds(names: &[String], sources: &[String]) -> Vec<Value> {
names
.iter()
.map(|name| {
if sources.iter().any(|s| s.strip_prefix("swatch:") == Some(name.as_str())) {
return Value::Null;
}
sources
.iter()
.filter_map(|s| s.strip_prefix("kind:"))
.find(|k| vectorcraft_color::GradientKind::parse(k).is_some_and(|kind| *name == format!("{} gradient", kind.label())))
.map_or(Value::Null, |k| json!(k))
})
.collect()
}
/// The names of `d`'s spot color swatches: the swatches whose prints go to a plate of their own /// The names of `d`'s spot color swatches: the swatches whose prints go to a plate of their own
/// ([`vectorcraft_doc::inks::inks`]). A name held by several swatches is the first one's, as /// ([`vectorcraft_doc::inks::inks`]). A name held by several swatches is the first one's, as
/// [`Document::swatch`] finds it, and the Registration swatch prints on every plate, so it is not /// [`Document::swatch`] finds it, and the Registration swatch prints on every plate, so it is not
@@ -486,9 +541,11 @@ fn node_and_drawn<'a>(d: &'a Document, path: &[usize]) -> Option<(&'a Node, bool
/// The spot colors, the patterns and the gradients (`spots`: [`spot_swatches`]) that object `n` /// The spot colors, the patterns and the gradients (`spots`: [`spot_swatches`]) that object `n`
/// paints, each once: those of its own fills and strokes ([`own_paints`], `members` included), of /// paints, each once: those of its own fills and strokes ([`own_paints`], `members` included), of
/// its drawn characters and, for a grayscale or 1-bit image, of the color it is colorized with. /// its drawn characters and, for a grayscale or 1-bit image, of the color it is colorized with.
fn object_paints(d: &Document, spots: &HashSet<&str>, members: &HashSet<*const Node>, n: &Node) -> [Vec<String>; 3] { /// The fourth list gives where each gradient comes from: a swatch or a kind ([`gradient_source`]).
fn object_paints(d: &Document, spots: &HashSet<&str>, members: &HashSet<*const Node>, n: &Node) -> [Vec<String>; 4] {
let (spot, pattern, gradient) = (|p: &Paint| spot_labels(spots, p), one_label(pattern_label), one_label(gradient_label)); let (spot, pattern, gradient) = (|p: &Paint| spot_labels(spots, p), one_label(pattern_label), one_label(gradient_label));
let labels: [&PaintNames<'_>; 3] = [&spot, &pattern, &gradient]; let gradient_from = one_label(gradient_source);
let labels: [&PaintNames<'_>; 4] = [&spot, &pattern, &gradient, &gradient_from];
let painted_runs = painted_runs(d, spots, n); let painted_runs = painted_runs(d, spots, n);
let mut used = labels.map(|label| own_paints(d, n, members, painted_runs.as_ref(), label)); let mut used = labels.map(|label| own_paints(d, n, members, painted_runs.as_ref(), label));
// The characters' own paints: an inline graphic draws no outline and does not take its run's // The characters' own paints: an inline graphic draws no outline and does not take its run's
@@ -654,8 +711,10 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
if let Some(k) = k { if let Some(k) = k {
*counts.entry(k).or_default() += 1; *counts.entry(k).or_default() += 1;
} }
// The spot colors, the patterns and the gradients this object paints, each once; none when it does not draw. // The spot colors, the patterns and the gradients this object paints, each once, and where each gradient comes from;
let [used_spots, used_patterns, used_gradients] = if drawn { object_paints(d, &spot_swatches, &members, n) } else { Default::default() }; // none when it does not draw.
let [used_spots, used_patterns, used_gradients, used_sources] =
if drawn { object_paints(d, &spot_swatches, &members, n) } else { Default::default() };
// Objects counts the objects that Spot Color Objects, Pattern Objects and Gradient Objects list. // Objects counts the objects that Spot Color Objects, Pattern Objects and Gradient Objects list.
if !used_spots.is_empty() { if !used_spots.is_empty() {
let name = n.display_name(); let name = n.display_name();
@@ -675,7 +734,8 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
let name = n.display_name(); let name = n.display_name();
gradients.extend(used_gradients.iter().cloned()); gradients.extend(used_gradients.iter().cloned());
gradient_rows.push((name.clone(), used_gradients.join(", "))); gradient_rows.push((name.clone(), used_gradients.join(", ")));
gradient_objects.push(json!({ "id": n.id.0, "name": name, "gradients": used_gradients })); let kinds = gradient_kinds(&used_gradients, &used_sources);
gradient_objects.push(json!({ "id": n.id.0, "name": name, "gradients": used_gradients, "kinds": kinds }));
*counts.entry("gradients").or_default() += 1; *counts.entry("gradients").or_default() += 1;
} }
if n.extras().mask.is_some() { if n.extras().mask.is_some() {
@@ -710,6 +770,14 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
fonts.extend(super::fonts::art_texts_of(d).into_iter().flat_map(|(_, t, _)| t.runs.iter().map(|r| super::fonts::font_of(&r.style)))); fonts.extend(super::fonts::art_texts_of(d).into_iter().flat_map(|(_, t, _)| t.runs.iter().map(|r| super::fonts::font_of(&r.style))));
} }
let font_names: Vec<String> = fonts.iter().map(super::fonts::font_label).collect(); let font_names: Vec<String> = fonts.iter().map(super::fonts::font_label).collect();
let (font_entries, font_rows): (Vec<Value>, Vec<(String, String)>) = fonts.iter().map(font_detail).unzip();
// The image objects in walk order, as Linked Images and Embedded Images list them: `file` is a
// linked image's row value (its path, or its file name alone when the request may not read it),
// and null for an embedded one.
let image_objects: Vec<Value> = image_rows
.iter()
.map(|(name, w, h, file)| json!({ "name": name, "width": w, "height": h, "linked": file.is_some(), "file": file }))
.collect();
// Each brush the art uses with its type, found by name in the Brushes panel as the renderer finds it (the first of two // Each brush the art uses with its type, found by name in the Brushes panel as the renderer finds it (the first of two
// brushes with one name); a brush the panel does not hold draws as a plain stroke and has no type. A row names the type // 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. // as the New Brush dialog does (`Art Brush`), and the panel translates these values when it shows them.
@@ -727,7 +795,9 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
"objects": counts, "objects": counts,
"pathLength": path_length, "pathLength": path_length,
"fonts": font_names, "fonts": font_names,
"fontDetails": font_entries,
"images": images, "images": images,
"imageObjects": image_objects,
"symbols": symbols, "symbols": symbols,
"spotColors": spot, "spotColors": spot,
"spotColorNames": spot, "spotColorNames": spot,
@@ -749,7 +819,7 @@ fn info(s: &mut Session, p: &Value) -> Result<Value> {
}); });
// The document's name: its file's, else its title. // The document's name: its file's, else its title.
out["document"]["name"] = json!(st.title()); out["document"]["name"] = json!(st.title());
let sections = sections(d, &out, &fonts, &image_rows, &brush_rows, &repeat_rows, &spot_rows, &pattern_rows, &gradient_rows, category); let sections = sections(d, &out, &fonts, &font_rows, &image_rows, &brush_rows, &repeat_rows, &spot_rows, &pattern_rows, &gradient_rows, category);
if text { if text {
return Ok(json!({ "text": report_text(&st.title(), selection_only, &sections) })); return Ok(json!({ "text": report_text(&st.title(), selection_only, &sections) }));
} }
@@ -763,6 +833,7 @@ fn document_summary(d: &Document) -> Value {
"title": d.title, "title": d.title,
"colorMode": match d.color_mode { ColorMode::Rgb => "RGB", ColorMode::Cmyk => "CMYK" }, "colorMode": match d.color_mode { ColorMode::Rgb => "RGB", ColorMode::Cmyk => "CMYK" },
"units": d.units.label(), "units": d.units.label(),
"unitId": d.units.key(),
"artboards": d.artboards.iter().map(|a| json!({ "name": a.name, "width": a.rect.width() / u, "height": a.rect.height() / u })).collect::<Vec<_>>(), "artboards": d.artboards.iter().map(|a| json!({ "name": a.name, "width": a.rect.width() / u, "height": a.rect.height() / u })).collect::<Vec<_>>(),
"rasterEffectsPpi": d.raster_effects_ppi, "rasterEffectsPpi": d.raster_effects_ppi,
}) })
@@ -1289,11 +1360,11 @@ mod tests {
assert_eq!( assert_eq!(
i["gradientObjects"], i["gradientObjects"],
json!([ json!([
{"id": a, "name": "Both", "gradients": ["Ramp", "Linear gradient"]}, {"id": a, "name": "Both", "gradients": ["Ramp", "Linear gradient"], "kinds": [null, "linear"]},
{"id": b, "name": "<Rectangle>", "gradients": ["Radial gradient"]}, {"id": b, "name": "<Rectangle>", "gradients": ["Radial gradient"], "kinds": ["radial"]},
{"id": f, "name": "<Rectangle>", "gradients": ["Freeform gradient"]}, {"id": f, "name": "<Rectangle>", "gradients": ["Freeform gradient"], "kinds": ["freeform"]},
{"id": t, "name": "Type", "gradients": ["Ramp"]}, {"id": t, "name": "Type", "gradients": ["Ramp"], "kinds": [null]},
{"id": bar, "name": "Bar", "gradients": ["Ramp"]}, {"id": bar, "name": "Bar", "gradients": ["Ramp"], "kinds": [null]},
]) ])
); );
assert_eq!(i["gradientNames"], json!(["Freeform gradient", "Linear gradient", "Radial gradient", "Ramp"])); assert_eq!(i["gradientNames"], json!(["Freeform gradient", "Linear gradient", "Radial gradient", "Ramp"]));
@@ -1800,6 +1871,112 @@ mod tests {
assert_eq!(embedding_label(0x0003), "embedding not allowed"); assert_eq!(embedding_label(0x0003), "embedding not allowed");
assert_eq!(embedding_label(0x0006), "embedding for preview and print"); assert_eq!(embedding_label(0x0006), "embedding for preview and print");
assert_eq!(embedding_label(0x000c), "embedding for editing"); assert_eq!(embedding_label(0x000c), "embedding for editing");
// `fontDetails` names each permission by id.
assert_eq!(
[0, 0x0002, 0x0104, 0x0008, 0x0200, 0x000c].map(embedding_id),
["allowed", "forbidden", "previewPrint", "editing", "forbidden", "editing"]
);
}
/// `fontDetails` gives each Font Details row's facts as values, in the rows' order: how the
/// font was found, the face shown, its file (null for a built-in face) and its embedding
/// permission. The rows and the report stay in English.
#[test]
fn info_exposes_structured_font_details_without_changing_english_rows() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap();
s.execute("text.create", &json!({"x": 10, "y": 20, "text": "Hi"})).unwrap();
s.execute("text.create", &json!({"x": 10, "y": 50, "text": "Heavy", "font": "Source Sans 3", "style": "Black Wide"})).unwrap();
s.execute("text.create", &json!({"x": 10, "y": 80, "text": "Gone", "font": "Qx Absent Family"})).unwrap();
let i = s.execute("document.info", &json!({"category": "fontDetails"})).unwrap();
let (entries, rows) = (i["fontDetails"].as_array().unwrap(), i["sections"][0]["rows"].as_array().unwrap());
assert_eq!(entries.iter().map(|e| e["font"].clone()).collect::<Vec<_>>(), rows.iter().map(|r| r[0].clone()).collect::<Vec<_>>());
let at = |prefix: &str| entries.iter().position(|e| e["font"].as_str().unwrap().starts_with(prefix)).unwrap();
let regular = at("Source Sans 3 Regular");
assert_eq!(
entries[regular],
json!({"font": "Source Sans 3 Regular", "match": "exact", "family": "Source Sans 3", "style": "Regular", "file": null, "embedding": "allowed"})
);
assert_eq!(rows[regular][1], "built in; embedding allowed");
let (wide, e) = (at("Source Sans 3 Black Wide"), &entries[at("Source Sans 3 Black Wide")]);
assert_eq!((e["match"].as_str(), e["family"].as_str()), (Some("style"), Some("Source Sans 3")), "{e}");
let file = e["file"].as_str().unwrap_or("built in");
let fs_type = [("allowed", 0), ("forbidden", 0x0002), ("previewPrint", 0x0004), ("editing", 0x0008)]
.into_iter()
.find_map(|(id, bits)| (e["embedding"] == id).then_some(bits))
.unwrap();
let found = format!("{file}; {}", embedding_label(fs_type));
assert_eq!(rows[wide][1], format!("substituted: shown in Source Sans 3 {} ({found})", e["style"].as_str().unwrap()));
let (gone, e) = (at("Qx Absent Family"), &entries[at("Qx Absent Family")]);
assert!(e["file"].is_null() && e["embedding"].is_null(), "{e}");
match e["match"].as_str() {
Some("missingWithFallback") => {
assert_eq!(rows[gone][1], format!("missing: shown in {} {}", e["family"].as_str().unwrap(), e["style"].as_str().unwrap()))
}
Some("missing") => assert_eq!((rows[gone][1].as_str(), e["family"].is_null()), (Some("missing"), true)),
m => panic!("{m:?}"),
}
let text = s.execute("document.info", &json!({"category": "fontDetails", "format": "text"})).unwrap()["text"].as_str().unwrap().to_string();
assert!(text.contains("Source Sans 3 Regular: built in; embedding allowed\n"), "{text}");
}
/// `document.unitId` names the document's units as Preferences › Units does, beside the English
/// `units`, and `imageObjects` lists the image objects in the order Embedded Images and Linked
/// Images list them, with the file a linked one shows (null for an embedded one).
#[test]
fn info_exposes_ordered_images_and_unit_id() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap();
s.edit("Units", |d, _| {
d.units = vectorcraft_doc::Unit::Millimeters;
Ok(())
})
.unwrap();
for x in [0.0, 50.0] {
let r = s.execute("shape.rectangle", &json!({"x": x, "y": 0, "width": 20, "height": 10})).unwrap()["id"].clone();
s.execute("select.set", &json!({"ids": [r]})).unwrap();
s.execute("object.rasterize", &json!({"ppi": 72})).unwrap();
}
let i = s.execute("document.info", &json!({})).unwrap();
assert_eq!((i["document"]["units"].as_str(), i["document"]["unitId"].as_str()), (Some("Millimeters"), Some("millimeters")));
let section = |id: &str| i["sections"].as_array().unwrap().iter().find(|s| s["id"] == id).unwrap()["rows"].as_array().unwrap().clone();
assert!(section("document").iter().any(|r| *r == json!(["Units", "Millimeters"])));
let embedded = section("embeddedImages");
let objects = i["imageObjects"].as_array().unwrap();
assert_eq!(objects.len(), 2, "{objects:?}");
for (o, r) in objects.iter().zip(&embedded) {
assert_eq!((o["linked"].clone(), o["file"].clone(), o["name"].clone()), (json!(false), Value::Null, r[0].clone()), "{o}");
assert_eq!(r[1], format!("{} × {} px", o["width"], o["height"]));
}
assert!(section("linkedImages").is_empty());
}
/// A `gradientObjects` entry's `kinds` gives the kind of each gradient listed by its kind
/// rather than by a swatch, and null for a swatch's name, even one that reads like a kind and
/// one an object's gradient of that kind shares; a type object's characters count as its own.
#[test]
fn gradient_kinds_tell_kinds_from_swatch_names() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 200, "height": 100})).unwrap();
let ramp = |kind: &str| json!({"kind": kind, "stops": [{"offset": 0, "color": "#000"}, {"offset": 1, "color": "#fff"}]});
s.execute("swatch.new", &json!({"name": "Radial gradient", "gradient": ramp("linear")})).unwrap();
let r = s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()["id"].clone();
s.execute("paint.setFill", &json!({"ids": [r], "swatch": "Radial gradient"})).unwrap();
s.execute("paint.setStroke", &json!({"ids": [r], "gradient": ramp("conic")})).unwrap();
let t = s.execute("text.create", &json!({"x": 10, "y": 40, "text": "Hi"})).unwrap()["id"].clone();
s.execute("paint.setFill", &json!({"ids": [t], "gradient": ramp("diamond")})).unwrap();
// The swatch's name and a radial gradient's kind name are listed once, as the name.
let both = s.execute("shape.rectangle", &json!({"x": 20, "y": 0, "width": 10, "height": 10})).unwrap()["id"].clone();
s.execute("paint.setFill", &json!({"ids": [both], "swatch": "Radial gradient"})).unwrap();
s.execute("paint.setStroke", &json!({"ids": [both], "gradient": ramp("radial")})).unwrap();
let i = s.execute("document.info", &json!({})).unwrap();
let entry = |id: &Value| i["gradientObjects"].as_array().unwrap().iter().find(|e| e["id"] == *id).unwrap().clone();
assert_eq!(
(entry(&r)["gradients"].clone(), entry(&r)["kinds"].clone()),
(json!(["Radial gradient", "Conic gradient"]), json!([null, "conic"]))
);
assert_eq!((entry(&t)["gradients"].clone(), entry(&t)["kinds"].clone()), (json!(["Diamond gradient"]), json!(["diamond"])));
assert_eq!((entry(&both)["gradients"].clone(), entry(&both)["kinds"].clone()), (json!(["Radial gradient"]), json!([null])));
} }
#[test] #[test]
+10
View File
@@ -534,6 +534,16 @@ fn the_document_info_report_has_every_category() {
let i = run(&mut s, "document.info", json!({"category": "linkedImages"})); let i = run(&mut s, "document.info", json!({"category": "linkedImages"}));
assert_eq!((i["sections"].as_array().unwrap().len(), i["sections"][0]["id"].as_str()), (1, Some("linkedImages"))); assert_eq!((i["sections"].as_array().unwrap().len(), i["sections"][0]["id"].as_str()), (1, Some("linkedImages")));
assert_eq!(i["sections"][0]["rows"].as_array().unwrap().len(), 2); assert_eq!(i["sections"][0]["rows"].as_array().unwrap().len(), 2);
// `imageObjects` lists the linked images in the rows' order with the files they show, and the
// embedded one with no file.
let linked: Vec<Value> = i["imageObjects"].as_array().unwrap().iter().filter(|o| o["linked"] == true).cloned().collect();
for (o, r) in linked.iter().zip(i["sections"][0]["rows"].as_array().unwrap()) {
let shown = format!("{} × {} px — {}", o["width"], o["height"], o["file"].as_str().unwrap());
assert_eq!((&o["name"], shown.as_str()), (&r[0], r[1].as_str().unwrap()));
}
assert_eq!(linked.len(), 2);
let embedded: Vec<&Value> = i["imageObjects"].as_array().unwrap().iter().filter(|o| o["linked"] == false).collect();
assert_eq!((embedded.len(), &embedded[0]["file"], &embedded[0]["width"]), (1, &Value::Null, &json!(600)));
let all = run(&mut s, "document.info", json!({})); let all = run(&mut s, "document.info", json!({}));
assert_eq!(all["sections"].as_array().unwrap().len(), 13); assert_eq!(all["sections"].as_array().unwrap().len(), 13);
let i = run(&mut s, "document.info", json!({"category": "brushes"})); let i = run(&mut s, "document.info", json!({"category": "brushes"}));
+1 -1
View File
@@ -688,7 +688,7 @@ fn message_at(lang: Lang, s: &str, depth: usize) -> String {
/// Fill `{name}` placeholders in one pass: a value that holds `{name}` text stays as it is. /// Fill `{name}` placeholders in one pass: a value that holds `{name}` text stays as it is.
/// Unknown placeholders are left as written. `{{` and `}}` are a literal `{` and `}`. /// Unknown placeholders are left as written. `{{` and `}}` are a literal `{` and `}`.
fn fill_once(template: &str, args: &[(&str, &str)]) -> String { pub(crate) fn fill_once(template: &str, args: &[(&str, &str)]) -> String {
let mut rest = template; let mut rest = template;
let mut out = String::with_capacity(rest.len()); let mut out = String::with_capacity(rest.len());
while let Some(open) = rest.find(['{', '}']) { while let Some(open) = rest.find(['{', '}']) {
+23
View File
@@ -2120,6 +2120,29 @@ font-technology Variable Променливи
Bristle Brush Четка с косъм Bristle Brush Четка с косъм
Pattern Brush Четка с мостра Pattern Brush Четка с мостра
missing from the Brushes panel липсва в панела „Четки“ missing from the Brushes panel липсва в панела „Четки“
built in вграден
embedding not allowed вграждането не е разрешено
embedding for editing вграждане за редактиране
embedding for preview and print вграждане за преглед и печат
embedding allowed вграждането е разрешено
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) заместен: показан с {family} {style} ({detail})
missing: shown in {family} {style} липсва: показан с {family} {style}
missing липсва
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Линеен градиент
Radial gradient Радиален градиент
Conic gradient Конусен градиент
Diamond gradient Ромбовиден градиент
Freeform gradient Градиент със свободна форма
@plural {n} instance|{n} instances {n} екземпляр|{n} екземпляра
@plural {n} row|{n} rows {n} ред|{n} реда
@plural {n} column|{n} columns {n} колона|{n} колони
Select art to make a scatter, art or pattern brush. Изберете графика, за да създадете разпръсната четка, артистична четка или четка с мостра. Select art to make a scatter, art or pattern brush. Изберете графика, за да създадете разпръсната четка, артистична четка или четка с мостра.
Brush Art Графика на четка Brush Art Графика на четка
New Calligraphic Brush Нова калиграфска четка New Calligraphic Brush Нова калиграфска четка
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3356,6 +3356,29 @@ theme Appearance Izgled
Bristle Brush Četkica s čekinjama Bristle Brush Četkica s čekinjama
Pattern Brush Četkica uzorka Pattern Brush Četkica uzorka
missing from the Brushes panel nedostaje u panelu Četkice missing from the Brushes panel nedostaje u panelu Četkice
built in ugrađen
embedding not allowed ugrađivanje nije dozvoljeno
embedding for editing ugrađivanje za uređivanje
embedding for preview and print ugrađivanje za pregled i štampu
embedding allowed ugrađivanje dozvoljeno
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) zamijenjen: prikazan fontom {family} {style} ({detail})
missing: shown in {family} {style} nedostaje: prikazan fontom {family} {style}
missing nedostaje
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Linearni gradijent
Radial gradient Radijalni gradijent
Conic gradient Konusni gradijent
Diamond gradient Rombni gradijent
Freeform gradient Gradijent slobodnog oblika
@plural {n} instance|{n} instances {n} instanca|{n} instance|{n} instanci
@plural {n} row|{n} rows {n} red|{n} reda|{n} redova
@plural {n} column|{n} columns {n} kolona|{n} kolone|{n} kolona
Select art to make a scatter, art or pattern brush. Odaberite crtež da napravite raspršenu, umjetničku četkicu ili četkicu uzorka. Select art to make a scatter, art or pattern brush. Odaberite crtež da napravite raspršenu, umjetničku četkicu ili četkicu uzorka.
Brush Art Crtež četkice Brush Art Crtež četkice
New Symbol Novi simbol New Symbol Novi simbol
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3115,6 +3115,29 @@ digits Malayalam Malaiàlam
Pattern Borders Vores de motiu Pattern Borders Vores de motiu
Pattern Brush Pinzell de motiu Pattern Brush Pinzell de motiu
missing from the Brushes panel absent del tauler Pinzells missing from the Brushes panel absent del tauler Pinzells
built in integrat
embedding not allowed incrustació no permesa
embedding for editing incrustació per a l’edició
embedding for preview and print incrustació per a la visualització i la impressió
embedding allowed incrustació permesa
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) substituït: es mostra amb {family} {style} ({detail})
missing: shown in {family} {style} absent: es mostra amb {family} {style}
missing absent
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppp
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Degradat lineal
Radial gradient Degradat radial
Conic gradient Degradat cònic
Diamond gradient Degradat de rombe
Freeform gradient Degradat de forma lliure
@plural {n} instance|{n} instances {n} instància|{n} instàncies
@plural {n} row|{n} rows {n} fila|{n} files
@plural {n} column|{n} columns {n} columna|{n} columnes
Pattern Brush Options Opcions del pinzell de motiu Pattern Brush Options Opcions del pinzell de motiu
Pattern Editing Edició de motius Pattern Editing Edició de motius
Pattern Options Opcions del motiu Pattern Options Opcions del motiu
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3406,6 +3406,29 @@ theme Appearance Motiv
Bristle Brush Štětinový štětec Bristle Brush Štětinový štětec
Pattern Brush Vzorkový štětec Pattern Brush Vzorkový štětec
missing from the Brushes panel chybí v panelu Štětce missing from the Brushes panel chybí v panelu Štětce
built in vestavěné
embedding not allowed vkládání není povoleno
embedding for editing vkládání pro úpravy
embedding for preview and print vkládání pro náhled a tisk
embedding allowed vkládání povoleno
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) nahrazeno: zobrazeno písmem {family} {style} ({detail})
missing: shown in {family} {style} chybí: zobrazeno písmem {family} {style}
missing chybí
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Lineární přechod
Radial gradient Kruhový přechod
Conic gradient Kuželový přechod
Diamond gradient Kosočtverečný přechod
Freeform gradient Volný přechod
@plural {n} instance|{n} instances {n} instance|{n} instance|{n} instancí
@plural {n} row|{n} rows {n} řádek|{n} řádky|{n} řádků
@plural {n} column|{n} columns {n} sloupec|{n} sloupce|{n} sloupců
Select art to make a scatter, art or pattern brush. Vyberte kresbu, abyste mohli vytvořit rozptylový, obrazový nebo vzorkový štětec. Select art to make a scatter, art or pattern brush. Vyberte kresbu, abyste mohli vytvořit rozptylový, obrazový nebo vzorkový štětec.
Brush Art Kresba štětce Brush Art Kresba štětce
New Symbol Nový symbol New Symbol Nový symbol
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3139,6 +3139,29 @@ theme Appearance Udseende
Bristle Brush Børstepensel Bristle Brush Børstepensel
Pattern Brush Mønsterpensel Pattern Brush Mønsterpensel
missing from the Brushes panel mangler i panelet Pensler missing from the Brushes panel mangler i panelet Pensler
built in indbygget
embedding not allowed indlejring ikke tilladt
embedding for editing indlejring til redigering
embedding for preview and print indlejring til visning og udskrivning
embedding allowed indlejring tilladt
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) erstattet: vist med {family} {style} ({detail})
missing: shown in {family} {style} mangler: vist med {family} {style}
missing mangler
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Lineær gradient
Radial gradient Radial gradient
Conic gradient Konisk gradient
Diamond gradient Diamantgradient
Freeform gradient Freeform-gradient
@plural {n} instance|{n} instances {n} forekomst|{n} forekomster
@plural {n} row|{n} rows {n} række|{n} rækker
@plural {n} column|{n} columns {n} kolonne|{n} kolonner
Select art to make a scatter, art or pattern brush. Markér grafik for at oprette en spredningspensel, grafikpensel eller mønsterpensel. Select art to make a scatter, art or pattern brush. Markér grafik for at oprette en spredningspensel, grafikpensel eller mønsterpensel.
Brush Art Penselgrafik Brush Art Penselgrafik
New Symbol Nyt symbol New Symbol Nyt symbol
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -2154,6 +2154,29 @@ file-filter Templates Vorlagen
Bristle Brush Borstenpinsel Bristle Brush Borstenpinsel
Pattern Brush Musterpinsel Pattern Brush Musterpinsel
missing from the Brushes panel fehlt im Bedienfeld „Pinsel“ missing from the Brushes panel fehlt im Bedienfeld „Pinsel“
built in integriert
embedding not allowed Einbetten nicht erlaubt
embedding for editing Einbetten zum Bearbeiten
embedding for preview and print Einbetten für Vorschau und Druck
embedding allowed Einbetten erlaubt
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) ersetzt: angezeigt in {family} {style} ({detail})
missing: shown in {family} {style} fehlt: angezeigt in {family} {style}
missing fehlt
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Linearer Verlauf
Radial gradient Radialer Verlauf
Conic gradient Konischer Verlauf
Diamond gradient Rautenverlauf
Freeform gradient Freiformverlauf
@plural {n} instance|{n} instances {n} Instanz|{n} Instanzen
@plural {n} row|{n} rows {n} Zeile|{n} Zeilen
@plural {n} column|{n} columns {n} Spalte|{n} Spalten
Select art to make a scatter, art or pattern brush. Wählen Sie eine Grafik aus, um einen Spezial-, Bild- oder Musterpinsel zu erstellen. Select art to make a scatter, art or pattern brush. Wählen Sie eine Grafik aus, um einen Spezial-, Bild- oder Musterpinsel zu erstellen.
Brush Art Pinselgrafik Brush Art Pinselgrafik
New Calligraphic Brush Neuer Kalligrafiepinsel New Calligraphic Brush Neuer Kalligrafiepinsel
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -1827,6 +1827,29 @@ font-technology Variable Μεταβλητές
Bristle Brush Πινέλο με τρίχες Bristle Brush Πινέλο με τρίχες
Pattern Brush Πινέλο μοτίβου Pattern Brush Πινέλο μοτίβου
missing from the Brushes panel λείπει από τον πίνακα Πινέλα missing from the Brushes panel λείπει από τον πίνακα Πινέλα
built in ενσωματωμένη
embedding not allowed η ενσωμάτωση δεν επιτρέπεται
embedding for editing ενσωμάτωση για επεξεργασία
embedding for preview and print ενσωμάτωση για προεπισκόπηση και εκτύπωση
embedding allowed η ενσωμάτωση επιτρέπεται
{file}; {embedding} {file}· {embedding}
substituted: shown in {family} {style} ({detail}) αντικαταστάθηκε: εμφανίζεται με {family} {style} ({detail})
missing: shown in {family} {style} λείπει: εμφανίζεται με {family} {style}
missing λείπει
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Γραμμικό ντεγκραντέ
Radial gradient Ακτινικό ντεγκραντέ
Conic gradient Κωνικό ντεγκραντέ
Diamond gradient Ρομβοειδές ντεγκραντέ
Freeform gradient Ντεγκραντέ ελεύθερης μορφής
@plural {n} instance|{n} instances {n} παρουσία|{n} παρουσίες
@plural {n} row|{n} rows {n} γραμμή|{n} γραμμές
@plural {n} column|{n} columns {n} στήλη|{n} στήλες
Select art to make a scatter, art or pattern brush. Επιλέξτε σχέδιο για να δημιουργήσετε πινέλο διασποράς, σχεδίου ή μοτίβου. Select art to make a scatter, art or pattern brush. Επιλέξτε σχέδιο για να δημιουργήσετε πινέλο διασποράς, σχεδίου ή μοτίβου.
Brush Art Σχέδιο πινέλου Brush Art Σχέδιο πινέλου
New Layer… Νέο επίπεδο… New Layer… Νέο επίπεδο…
Can't render this file because it is too large.
+23
View File
@@ -2192,6 +2192,29 @@ font-technology Variable Variables
Bristle Brush Pincel de cerdas Bristle Brush Pincel de cerdas
Pattern Brush Pincel de motivo Pattern Brush Pincel de motivo
missing from the Brushes panel falta en el panel Pinceles missing from the Brushes panel falta en el panel Pinceles
built in integrada
embedding not allowed incrustación no permitida
embedding for editing incrustación para edición
embedding for preview and print incrustación para vista previa e impresión
embedding allowed incrustación permitida
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) sustituida: se muestra con {family} {style} ({detail})
missing: shown in {family} {style} falta: se muestra con {family} {style}
missing falta
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppp
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Degradado lineal
Radial gradient Degradado radial
Conic gradient Degradado cónico
Diamond gradient Degradado de rombo
Freeform gradient Degradado libre
@plural {n} instance|{n} instances {n} instancia|{n} instancias
@plural {n} row|{n} rows {n} fila|{n} filas
@plural {n} column|{n} columns {n} columna|{n} columnas
Select art to make a scatter, art or pattern brush. Selecciona una ilustración para crear un pincel de dispersión, de arte o de motivo. Select art to make a scatter, art or pattern brush. Selecciona una ilustración para crear un pincel de dispersión, de arte o de motivo.
Brush Art Ilustración del pincel Brush Art Ilustración del pincel
New Calligraphic Brush Nuevo pincel caligráfico New Calligraphic Brush Nuevo pincel caligráfico
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3345,6 +3345,29 @@ theme Appearance Ulkoasu
Bristle Brush Harjassivellin Bristle Brush Harjassivellin
Pattern Brush Kuviosivellin Pattern Brush Kuviosivellin
missing from the Brushes panel puuttuu Siveltimet-paneelista missing from the Brushes panel puuttuu Siveltimet-paneelista
built in sisäänrakennettu
embedding not allowed upottaminen ei sallittu
embedding for editing upottaminen muokkausta varten
embedding for preview and print upottaminen esikatselua ja tulostusta varten
embedding allowed upottaminen sallittu
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) korvattu: näytetään fontilla {family} {style} ({detail})
missing: shown in {family} {style} puuttuu: näytetään fontilla {family} {style}
missing puuttuu
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Lineaarinen liukuväri
Radial gradient Säteittäinen liukuväri
Conic gradient Kartiomainen liukuväri
Diamond gradient Vinoneliöliukuväri
Freeform gradient Vapaamuotoinen liukuväri
@plural {n} instance|{n} instances {n} esiintymä|{n} esiintymää
@plural {n} row|{n} rows {n} rivi|{n} riviä
@plural {n} column|{n} columns {n} sarake|{n} saraketta
Select art to make a scatter, art or pattern brush. Valitse kuvitus, jotta voit luoda sirote-, taide- tai kuviosiveltimen. Select art to make a scatter, art or pattern brush. Valitse kuvitus, jotta voit luoda sirote-, taide- tai kuviosiveltimen.
Brush Art Siveltimen kuvitus Brush Art Siveltimen kuvitus
New Symbol Uusi symboli New Symbol Uusi symboli
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -2184,6 +2184,29 @@ font-technology Variable Variables
Bristle Brush Forme Soies Bristle Brush Forme Soies
Pattern Brush Forme de motif Pattern Brush Forme de motif
missing from the Brushes panel absente du panneau Formes missing from the Brushes panel absente du panneau Formes
built in intégrée
embedding not allowed incorporation interdite
embedding for editing incorporation pour la modification
embedding for preview and print incorporation pour l’aperçu et l’impression
embedding allowed incorporation autorisée
{file}; {embedding} {file}, {embedding}
substituted: shown in {family} {style} ({detail}) remplacée, affichée en {family} {style} ({detail})
missing: shown in {family} {style} absente, affichée en {family} {style}
missing absente
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppp
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Dégradé linéaire
Radial gradient Dégradé radial
Conic gradient Dégradé conique
Diamond gradient Dégradé en losange
Freeform gradient Dégradé de forme libre
@plural {n} instance|{n} instances {n} occurrence|{n} occurrences
@plural {n} row|{n} rows {n} ligne|{n} lignes
@plural {n} column|{n} columns {n} colonne|{n} colonnes
Select art to make a scatter, art or pattern brush. Sélectionnez une illustration pour créer une forme diffuse, artistique ou de motif. Select art to make a scatter, art or pattern brush. Sélectionnez une illustration pour créer une forme diffuse, artistique ou de motif.
Brush Art Illustration de la forme Brush Art Illustration de la forme
New Calligraphic Brush Nouvelle forme calligraphique New Calligraphic Brush Nouvelle forme calligraphique
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3397,6 +3397,29 @@ antiAlias Sharp शार्प
Pattern Borders पैटर्न सीमा Pattern Borders पैटर्न सीमा
Pattern Brush पैटर्न ब्रश Pattern Brush पैटर्न ब्रश
missing from the Brushes panel ब्रश पैनल में अनुपलब्ध missing from the Brushes panel ब्रश पैनल में अनुपलब्ध
built in अंतर्निहित
embedding not allowed एम्बेडिंग की अनुमति नहीं
embedding for editing संपादन के लिए एम्बेडिंग
embedding for preview and print पूर्वावलोकन और प्रिंट के लिए एम्बेडिंग
embedding allowed एम्बेडिंग की अनुमति है
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) प्रतिस्थापित: {family} {style} में दिखाया गया ({detail})
missing: shown in {family} {style} अनुपलब्ध: {family} {style} में दिखाया गया
missing अनुपलब्ध
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient लीनियर ग्रेडिएंट
Radial gradient रेडियल ग्रेडिएंट
Conic gradient कोनिक ग्रेडिएंट
Diamond gradient डायमंड ग्रेडिएंट
Freeform gradient फ्रीफॉर्म ग्रेडिएंट
@plural {n} instance|{n} instances {n} इंस्टेंस|{n} इंस्टेंस
@plural {n} row|{n} rows {n} पंक्ति|{n} पंक्तियाँ
@plural {n} column|{n} columns {n} कॉलम|{n} कॉलम
Pattern Brush Options पैटर्न ब्रश ऑप्शन्स Pattern Brush Options पैटर्न ब्रश ऑप्शन्स
Pattern Editing पैटर्न संपादन Pattern Editing पैटर्न संपादन
Pattern Options पैटर्न ऑप्शन्स Pattern Options पैटर्न ऑप्शन्स
Can't render this file because it is too large.
+23
View File
@@ -3009,6 +3009,29 @@ file-filter Templates Sablonok
Pattern Mintázat Pattern Mintázat
Pattern Brush Mintázatecset Pattern Brush Mintázatecset
missing from the Brushes panel hiányzik az Ecsetek panelről missing from the Brushes panel hiányzik az Ecsetek panelről
built in beépített
embedding not allowed beágyazás nem engedélyezett
embedding for editing beágyazás szerkesztéshez
embedding for preview and print beágyazás előnézethez és nyomtatáshoz
embedding allowed beágyazás engedélyezett
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) helyettesítve: megjelenítés ezzel: {family} {style} ({detail})
missing: shown in {family} {style} hiányzik: megjelenítés ezzel: {family} {style}
missing hiányzik
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Lineáris színátmenet
Radial gradient Körkörös színátmenet
Conic gradient Kúpos színátmenet
Diamond gradient Rombusz színátmenet
Freeform gradient Szabad formájú színátmenet
@plural {n} instance|{n} instances {n} példány
@plural {n} row|{n} rows {n} sor
@plural {n} column|{n} columns {n} oszlop
Pattern Brush Options Mintázatecset beállításai Pattern Brush Options Mintázatecset beállításai
Pattern Editing Mintázatszerkesztés Pattern Editing Mintázatszerkesztés
Pattern Options Mintázat beállításai Pattern Options Mintázat beállításai
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3139,6 +3139,29 @@ theme Appearance Tampilan
Bristle Brush Kuas bristle Bristle Brush Kuas bristle
Pattern Brush Kuas pola Pattern Brush Kuas pola
missing from the Brushes panel tidak ada di panel Kuas missing from the Brushes panel tidak ada di panel Kuas
built in bawaan
embedding not allowed penyematan tidak diizinkan
embedding for editing penyematan untuk pengeditan
embedding for preview and print penyematan untuk pratinjau dan cetak
embedding allowed penyematan diizinkan
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) diganti: ditampilkan dengan {family} {style} ({detail})
missing: shown in {family} {style} tidak ada: ditampilkan dengan {family} {style}
missing tidak ada
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Gradien linear
Radial gradient Gradien radial
Conic gradient Gradien kerucut
Diamond gradient Gradien wajik
Freeform gradient Gradien bentuk bebas
@plural {n} instance|{n} instances {n} instans
@plural {n} row|{n} rows {n} baris
@plural {n} column|{n} columns {n} kolom
Select art to make a scatter, art or pattern brush. Pilih karya seni untuk membuat kuas sebar, seni, atau pola. Select art to make a scatter, art or pattern brush. Pilih karya seni untuk membuat kuas sebar, seni, atau pola.
Brush Art Karya seni kuas Brush Art Karya seni kuas
New Symbol Simbol baru New Symbol Simbol baru
Can't render this file because it contains an unexpected character in line 2429 and column 5.
+23
View File
@@ -2230,6 +2230,29 @@ file-filter Templates Modelli
Bristle Brush Pennello a setole Bristle Brush Pennello a setole
Pattern Brush Pennello motivo Pattern Brush Pennello motivo
missing from the Brushes panel mancante nel pannello Pennelli missing from the Brushes panel mancante nel pannello Pennelli
built in integrato
embedding not allowed incorporamento non consentito
embedding for editing incorporamento per la modifica
embedding for preview and print incorporamento per anteprima e stampa
embedding allowed incorporamento consentito
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) sostituito: mostrato con {family} {style} ({detail})
missing: shown in {family} {style} mancante: mostrato con {family} {style}
missing mancante
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Sfumatura lineare
Radial gradient Sfumatura radiale
Conic gradient Sfumatura conica
Diamond gradient Sfumatura a rombo
Freeform gradient Sfumatura libera
@plural {n} instance|{n} instances {n} istanza|{n} istanze
@plural {n} row|{n} rows {n} riga|{n} righe
@plural {n} column|{n} columns {n} colonna|{n} colonne
Select art to make a scatter, art or pattern brush. Seleziona la grafica per creare un pennello a dispersione, artistico o motivo. Select art to make a scatter, art or pattern brush. Seleziona la grafica per creare un pennello a dispersione, artistico o motivo.
Brush Art Grafica del pennello Brush Art Grafica del pennello
New Calligraphic Brush Nuovo pennello calligrafico New Calligraphic Brush Nuovo pennello calligrafico
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -2439,6 +2439,29 @@ antiAlias Sharp シャープ
Bristle Brush 剛毛ブラシ Bristle Brush 剛毛ブラシ
Pattern Brush パターンブラシ Pattern Brush パターンブラシ
missing from the Brushes panel ブラシパネルにありません missing from the Brushes panel ブラシパネルにありません
built in 内蔵
embedding not allowed 埋め込み不可
embedding for editing 編集用に埋め込み可
embedding for preview and print プレビューと印刷用に埋め込み可
embedding allowed 埋め込み可
{file}; {embedding} {file}、{embedding}
substituted: shown in {family} {style} ({detail}) 代替: {family} {style} で表示 ({detail})
missing: shown in {family} {style} 見つかりません: {family} {style} で表示
missing 見つかりません
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}、{instances}
{kind}, {rows} × {cols} {kind}、{rows} × {cols}
{grid}, {instances} {grid}、{instances}
Linear gradient 線形グラデーション
Radial gradient 円形グラデーション
Conic gradient 円錐グラデーション
Diamond gradient ひし形グラデーション
Freeform gradient フリーグラデーション
@plural {n} instance|{n} instances インスタンス {n} 個
@plural {n} row|{n} rows {n} 行
@plural {n} column|{n} columns {n} 列
Select art to make a scatter, art or pattern brush. 散布ブラシ、アートブラシ、パターンブラシを作成するには、アートを選択します。 Select art to make a scatter, art or pattern brush. 散布ブラシ、アートブラシ、パターンブラシを作成するには、アートを選択します。
Brush Art ブラシのアート Brush Art ブラシのアート
New Calligraphic Brush 新規絵筆ブラシ New Calligraphic Brush 新規絵筆ブラシ
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -2982,6 +2982,29 @@ theme Appearance იერსახე
Bristle Brush ჯაგრის ფუნჯი Bristle Brush ჯაგრის ფუნჯი
Pattern Brush პატერნის ფუნჯი Pattern Brush პატერნის ფუნჯი
missing from the Brushes panel ფუნჯების პანელში არ არის missing from the Brushes panel ფუნჯების პანელში არ არის
built in ჩაშენებული
embedding not allowed ჩაშენება არ არის დაშვებული
embedding for editing ჩაშენება რედაქტირებისთვის
embedding for preview and print ჩაშენება გადახედვისა და ბეჭდვისთვის
embedding allowed ჩაშენება დაშვებულია
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) ჩანაცვლებულია: ნაჩვენებია შრიფტით {family} {style} ({detail})
missing: shown in {family} {style} აკლია: ნაჩვენებია შრიფტით {family} {style}
missing აკლია
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient წრფივი გრადიენტი
Radial gradient რადიალური გრადიენტი
Conic gradient კონუსური გრადიენტი
Diamond gradient რომბისებრი გრადიენტი
Freeform gradient თავისუფალი ფორმის გრადიენტი
@plural {n} instance|{n} instances {n} ეგზემპლარი
@plural {n} row|{n} rows {n} მწკრივი
@plural {n} column|{n} columns {n} სვეტი
Select art to make a scatter, art or pattern brush. მონიშნეთ ნახატი გაფანტვის, მხატვრული ან პატერნის ფუნჯის შესაქმნელად. Select art to make a scatter, art or pattern brush. მონიშნეთ ნახატი გაფანტვის, მხატვრული ან პატერნის ფუნჯის შესაქმნელად.
Brush Art ფუნჯის ნახატი Brush Art ფუნჯის ნახატი
New Symbol ახალი სიმბოლო New Symbol ახალი სიმბოლო
Can't render this file because it is too large.
+23
View File
@@ -2429,6 +2429,29 @@ antiAlias Sharp 날카롭게
Bristle Brush 강모 브러시 Bristle Brush 강모 브러시
Pattern Brush 패턴 브러시 Pattern Brush 패턴 브러시
missing from the Brushes panel 브러시 패널에 없음 missing from the Brushes panel 브러시 패널에 없음
built in 내장
embedding not allowed 포함 허용 안 됨
embedding for editing 편집용 포함
embedding for preview and print 미리 보기 및 인쇄용 포함
embedding allowed 포함 허용
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) 대체됨: {family} {style}(으)로 표시({detail})
missing: shown in {family} {style} 없음: {family} {style}(으)로 표시
missing 없음
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient 선형 그레이디언트
Radial gradient 방사형 그레이디언트
Conic gradient 원뿔형 그레이디언트
Diamond gradient 다이아몬드 그레이디언트
Freeform gradient 자유 형태 그레이디언트
@plural {n} instance|{n} instances 인스턴스 {n}개
@plural {n} row|{n} rows {n}행
@plural {n} column|{n} columns {n}열
Select art to make a scatter, art or pattern brush. 흩뿌리기, 아트 또는 패턴 브러시를 만들려면 아트웍을 선택하세요. Select art to make a scatter, art or pattern brush. 흩뿌리기, 아트 또는 패턴 브러시를 만들려면 아트웍을 선택하세요.
Brush Art 브러시 아트 Brush Art 브러시 아트
New Calligraphic Brush 새 캘리그래피 브러시 New Calligraphic Brush 새 캘리그래피 브러시
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3656,6 +3656,29 @@ theme Appearance Vormgeving
Bristle Brush Borstelpenseel Bristle Brush Borstelpenseel
Pattern Brush Patroonpenseel Pattern Brush Patroonpenseel
missing from the Brushes panel ontbreekt in het deelvenster Penselen missing from the Brushes panel ontbreekt in het deelvenster Penselen
built in ingebouwd
embedding not allowed insluiten niet toegestaan
embedding for editing insluiten voor bewerken
embedding for preview and print insluiten voor voorvertoning en afdrukken
embedding allowed insluiten toegestaan
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) vervangen: weergegeven in {family} {style} ({detail})
missing: shown in {family} {style} ontbreekt: weergegeven in {family} {style}
missing ontbreekt
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Lineair verloop
Radial gradient Radiaal verloop
Conic gradient Conisch verloop
Diamond gradient Ruitverloop
Freeform gradient Vrijvormverloop
@plural {n} instance|{n} instances {n} exemplaar|{n} exemplaren
@plural {n} row|{n} rows {n} rij|{n} rijen
@plural {n} column|{n} columns {n} kolom|{n} kolommen
Select art to make a scatter, art or pattern brush. Selecteer illustratie om een verspreidings-, art- of patroonpenseel te maken. Select art to make a scatter, art or pattern brush. Selecteer illustratie om een verspreidings-, art- of patroonpenseel te maken.
Brush Art Penseelillustratie Brush Art Penseelillustratie
New Symbol Nieuw symbool New Symbol Nieuw symbool
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3285,6 +3285,29 @@ theme Appearance Wygląd
Bristle Brush Pędzel włosiankowy Bristle Brush Pędzel włosiankowy
Pattern Brush Pędzel wzorka Pattern Brush Pędzel wzorka
missing from the Brushes panel brak w panelu Pędzle missing from the Brushes panel brak w panelu Pędzle
built in wbudowany
embedding not allowed osadzanie niedozwolone
embedding for editing osadzanie do edycji
embedding for preview and print osadzanie do podglądu i drukowania
embedding allowed osadzanie dozwolone
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) zastąpiony: wyświetlany krojem {family} {style} ({detail})
missing: shown in {family} {style} brak: wyświetlany krojem {family} {style}
missing brak
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Gradient liniowy
Radial gradient Gradient promienisty
Conic gradient Gradient stożkowy
Diamond gradient Gradient rombowy
Freeform gradient Gradient dowolny
@plural {n} instance|{n} instances {n} wystąpienie|{n} wystąpienia|{n} wystąpień
@plural {n} row|{n} rows {n} wiersz|{n} wiersze|{n} wierszy
@plural {n} column|{n} columns {n} kolumna|{n} kolumny|{n} kolumn
Select art to make a scatter, art or pattern brush. Zaznacz kompozycję, aby utworzyć pędzel rozproszony, artystyczny lub wzorka. Select art to make a scatter, art or pattern brush. Zaznacz kompozycję, aby utworzyć pędzel rozproszony, artystyczny lub wzorka.
Brush Art Kompozycja pędzla Brush Art Kompozycja pędzla
New Symbol Nowy symbol New Symbol Nowy symbol
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -1801,6 +1801,29 @@ antiAlias Sharp Nítido
Bristle Brush Pincel de Cerdas Bristle Brush Pincel de Cerdas
Pattern Brush Pincel de Padrão Pattern Brush Pincel de Padrão
missing from the Brushes panel ausente do painel Pincéis missing from the Brushes panel ausente do painel Pincéis
built in integrada
embedding not allowed incorporação não permitida
embedding for editing incorporação para edição
embedding for preview and print incorporação para visualização e impressão
embedding allowed incorporação permitida
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) substituída: exibida em {family} {style} ({detail})
missing: shown in {family} {style} ausente: exibida em {family} {style}
missing ausente
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppp
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Gradiente linear
Radial gradient Gradiente radial
Conic gradient Gradiente cônico
Diamond gradient Gradiente losango
Freeform gradient Gradiente de forma livre
@plural {n} instance|{n} instances {n} instância|{n} instâncias
@plural {n} row|{n} rows {n} linha|{n} linhas
@plural {n} column|{n} columns {n} coluna|{n} colunas
Select art to make a scatter, art or pattern brush. Selecione arte para criar um pincel de dispersão, de arte ou de padrão. Select art to make a scatter, art or pattern brush. Selecione arte para criar um pincel de dispersão, de arte ou de padrão.
Brush Art Arte do Pincel Brush Art Arte do Pincel
New Calligraphic Brush Novo Pincel Caligráfico New Calligraphic Brush Novo Pincel Caligráfico
Can't render this file because it contains an unexpected character in line 6 and column 47.
+23
View File
@@ -2217,6 +2217,29 @@ file-filter Templates Шаблоны
Bristle Brush Щетинная кисть Bristle Brush Щетинная кисть
Pattern Brush Узорная кисть Pattern Brush Узорная кисть
missing from the Brushes panel отсутствует в панели «Кисти» missing from the Brushes panel отсутствует в панели «Кисти»
built in встроенный
embedding not allowed внедрение запрещено
embedding for editing внедрение для редактирования
embedding for preview and print внедрение для просмотра и печати
embedding allowed внедрение разрешено
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) заменен: показан шрифтом {family} {style} ({detail})
missing: shown in {family} {style} отсутствует: показан шрифтом {family} {style}
missing отсутствует
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Линейный градиент
Radial gradient Радиальный градиент
Conic gradient Конический градиент
Diamond gradient Ромбовидный градиент
Freeform gradient Произвольный градиент
@plural {n} instance|{n} instances {n} экземпляр|{n} экземпляра|{n} экземпляров
@plural {n} row|{n} rows {n} строка|{n} строки|{n} строк
@plural {n} column|{n} columns {n} столбец|{n} столбца|{n} столбцов
Select art to make a scatter, art or pattern brush. Выделите рисунок, чтобы создать кисть-распыление, художественную или узорную кисть. Select art to make a scatter, art or pattern brush. Выделите рисунок, чтобы создать кисть-распыление, художественную или узорную кисть.
Brush Art Рисунок кисти Brush Art Рисунок кисти
New Calligraphic Brush Новая каллиграфическая кисть New Calligraphic Brush Новая каллиграфическая кисть
Can't render this file because it is too large.
+23
View File
@@ -2131,6 +2131,29 @@ font-technology Variable Variabla
Bristle Brush Borstpensel Bristle Brush Borstpensel
Pattern Brush Mönsterpensel Pattern Brush Mönsterpensel
missing from the Brushes panel saknas i panelen Penslar missing from the Brushes panel saknas i panelen Penslar
built in inbyggt
embedding not allowed inbäddning inte tillåten
embedding for editing inbäddning för redigering
embedding for preview and print inbäddning för förhandsvisning och utskrift
embedding allowed inbäddning tillåten
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) ersatt: visas med {family} {style} ({detail})
missing: shown in {family} {style} saknas: visas med {family} {style}
missing saknas
{width} × {height} px {width} × {height} px
{size} — {file} {size} – {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Linjär gradient
Radial gradient Radiell gradient
Conic gradient Konisk gradient
Diamond gradient Rombgradient
Freeform gradient Fri gradient
@plural {n} instance|{n} instances {n} instans|{n} instanser
@plural {n} row|{n} rows {n} rad|{n} rader
@plural {n} column|{n} columns {n} kolumn|{n} kolumner
Select art to make a scatter, art or pattern brush. Markera grafik för att skapa en spridnings-, konst- eller mönsterpensel. Select art to make a scatter, art or pattern brush. Markera grafik för att skapa en spridnings-, konst- eller mönsterpensel.
Brush Art Penselgrafik Brush Art Penselgrafik
New Calligraphic Brush Ny kalligrafisk pensel New Calligraphic Brush Ny kalligrafisk pensel
Can't render this file because it is too large.
+40
View File
@@ -463,6 +463,46 @@ fn document_info_labels_are_in_complete_catalogs() {
} }
} }
/// The values Document Info makes and shows in the interface language: the font states and
/// embedding permissions, the templates of the font, image, raster effects and Repeat object
/// values, the names of gradients named by their kind, and the counted instances, rows and columns.
#[test]
fn document_info_values_are_in_complete_catalogs() {
const VALUES: [&str; 20] = [
"built in",
"embedding not allowed",
"embedding for editing",
"embedding for preview and print",
"embedding allowed",
"{file}; {embedding}",
"substituted: shown in {family} {style} ({detail})",
"missing: shown in {family} {style}",
"missing",
"{width} × {height} px",
"{size} — {file}",
"{n} ppi",
"{kind}, {instances}",
"{kind}, {rows} × {cols}",
"{grid}, {instances}",
"Linear gradient",
"Radial gradient",
"Conic gradient",
"Diamond gradient",
"Freeform gradient",
];
const COUNTED: [(&str, &str); 3] = [("{n} instance", "{n} instances"), ("{n} row", "{n} rows"), ("{n} column", "{n} columns")];
let gradient_kinds: Vec<String> = vectorcraft_color::GradientKind::ALL.iter().map(|k| format!("{} gradient", k.label())).collect();
assert!(gradient_kinds.iter().all(|k| VALUES.contains(&k.as_str())), "{gradient_kinds:?}");
for lang in LANGUAGES.iter().filter(|lang| lang.complete_menus) {
for value in VALUES {
assert!(lang.catalog().plain(value).is_some(), "{} is missing {value}", lang.code);
}
for (one, other) in COUNTED {
assert!(lang.catalog().plural(one, other, 0).is_some(), "{} is missing the row for {other}", lang.code);
}
}
}
/// The Character panel's Anti-aliasing Method label and its choices, with the Sharp method's /// The Character panel's Anti-aliasing Method label and its choices, with the Sharp method's
/// own `antiAlias` row apart from the plain "Sharp" corner row. /// own `antiAlias` row apart from the plain "Sharp" corner row.
#[test] #[test]
+23
View File
@@ -6137,6 +6137,29 @@ theme Appearance Görünüm
New Brush Yeni Fırça New Brush Yeni Fırça
Pattern Brush Desen Fırçası Pattern Brush Desen Fırçası
missing from the Brushes panel Fırçalar panelinde yok missing from the Brushes panel Fırçalar panelinde yok
built in yerleşik
embedding not allowed gömmeye izin verilmiyor
embedding for editing düzenleme için gömme
embedding for preview and print önizleme ve yazdırma için gömme
embedding allowed gömmeye izin veriliyor
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) yerine konan: {family} {style} ile gösteriliyor ({detail})
missing: shown in {family} {style} eksik: {family} {style} ile gösteriliyor
missing eksik
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Doğrusal degrade
Radial gradient Radyal degrade
Conic gradient Konik degrade
Diamond gradient Elmas degrade
Freeform gradient Serbest biçimli degrade
@plural {n} instance|{n} instances {n} örnek
@plural {n} row|{n} rows {n} satır
@plural {n} column|{n} columns {n} sütun
Pick Color From: Rengi Buradan Al: Pick Color From: Rengi Buradan Al:
Scatter Brush Serpme Fırçası Scatter Brush Serpme Fırçası
Select art to make a scatter, art or pattern brush. Bir serpme, resim veya desen fırçası oluşturmak için çizim seçin. Select art to make a scatter, art or pattern brush. Bir serpme, resim veya desen fırçası oluşturmak için çizim seçin.
Can't render this file because it contains an unexpected character in line 5 and column 25.
+23
View File
@@ -2222,6 +2222,29 @@ file-filter Templates Шаблони
Bristle Brush Щетинний пензель Bristle Brush Щетинний пензель
Pattern Brush Візерунковий пензель Pattern Brush Візерунковий пензель
missing from the Brushes panel відсутній у панелі «Пензлі» missing from the Brushes panel відсутній у панелі «Пензлі»
built in вбудований
embedding not allowed вбудовування заборонено
embedding for editing вбудовування для редагування
embedding for preview and print вбудовування для перегляду й друку
embedding allowed вбудовування дозволено
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) замінено: показано шрифтом {family} {style} ({detail})
missing: shown in {family} {style} відсутній: показано шрифтом {family} {style}
missing відсутній
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Лінійний градієнт
Radial gradient Радіальний градієнт
Conic gradient Конічний градієнт
Diamond gradient Ромбоподібний градієнт
Freeform gradient Довільний градієнт
@plural {n} instance|{n} instances {n} екземпляр|{n} екземпляри|{n} екземплярів
@plural {n} row|{n} rows {n} рядок|{n} рядки|{n} рядків
@plural {n} column|{n} columns {n} стовпець|{n} стовпці|{n} стовпців
Select art to make a scatter, art or pattern brush. Виділіть малюнок, щоб створити пензель-розпорошення, художній або візерунковий пензель. Select art to make a scatter, art or pattern brush. Виділіть малюнок, щоб створити пензель-розпорошення, художній або візерунковий пензель.
Brush Art Малюнок пензля Brush Art Малюнок пензля
New Calligraphic Brush Новий каліграфічний пензель New Calligraphic Brush Новий каліграфічний пензель
Can't render this file because it is too large.
+23
View File
@@ -2826,6 +2826,29 @@ digits Hindi Hindi
Pattern Họa tiết Pattern Họa tiết
Pattern Brush Cọ họa tiết Pattern Brush Cọ họa tiết
missing from the Brushes panel không có trong bảng Cọ missing from the Brushes panel không có trong bảng Cọ
built in tích hợp sẵn
embedding not allowed không cho phép nhúng
embedding for editing nhúng để chỉnh sửa
embedding for preview and print nhúng để xem trước và in
embedding allowed cho phép nhúng
{file}; {embedding} {file}; {embedding}
substituted: shown in {family} {style} ({detail}) đã thay thế: hiển thị bằng {family} {style} ({detail})
missing: shown in {family} {style} thiếu: hiển thị bằng {family} {style}
missing thiếu
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind}, {instances}
{kind}, {rows} × {cols} {kind}, {rows} × {cols}
{grid}, {instances} {grid}, {instances}
Linear gradient Chuyển sắc tuyến tính
Radial gradient Chuyển sắc hướng tâm
Conic gradient Chuyển sắc hình nón
Diamond gradient Chuyển sắc hình thoi
Freeform gradient Chuyển sắc tự do
@plural {n} instance|{n} instances {n} phiên bản
@plural {n} row|{n} rows {n} hàng
@plural {n} column|{n} columns {n} cột
Pattern Brush Options Tùy chọn cọ họa tiết Pattern Brush Options Tùy chọn cọ họa tiết
Pattern Editing Chỉnh sửa họa tiết Pattern Editing Chỉnh sửa họa tiết
Pattern Options Tùy chọn họa tiết Pattern Options Tùy chọn họa tiết
Can't render this file because it contains an unexpected character in line 204 and column 5.
+23
View File
@@ -2087,6 +2087,29 @@ file-filter Templates 模板
Bristle Brush 鬃毛画笔 Bristle Brush 鬃毛画笔
Pattern Brush 图案画笔 Pattern Brush 图案画笔
missing from the Brushes panel “画笔”面板中没有 missing from the Brushes panel “画笔”面板中没有
built in 内置
embedding not allowed 不允许嵌入
embedding for editing 允许嵌入以供编辑
embedding for preview and print 允许嵌入以供预览和打印
embedding allowed 允许嵌入
{file}; {embedding} {file};{embedding}
substituted: shown in {family} {style} ({detail}) 已替换:以 {family} {style} 显示 ({detail})
missing: shown in {family} {style} 缺失:以 {family} {style} 显示
missing 缺失
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind},{instances}
{kind}, {rows} × {cols} {kind},{rows} × {cols}
{grid}, {instances} {grid},{instances}
Linear gradient 线性渐变
Radial gradient 径向渐变
Conic gradient 锥形渐变
Diamond gradient 菱形渐变
Freeform gradient 自由渐变
@plural {n} instance|{n} instances {n} 个实例
@plural {n} row|{n} rows {n} 行
@plural {n} column|{n} columns {n} 列
Select art to make a scatter, art or pattern brush. 选择图稿以创建散布画笔、艺术画笔或图案画笔。 Select art to make a scatter, art or pattern brush. 选择图稿以创建散布画笔、艺术画笔或图案画笔。
Brush Art 画笔图稿 Brush Art 画笔图稿
New Calligraphic Brush 新建书法画笔 New Calligraphic Brush 新建书法画笔
Can't render this file because it contains an unexpected character in line 2 and column 73.
+23
View File
@@ -3295,6 +3295,29 @@ file-filter Templates 範本
Bristle Brush 鬃毛筆刷 Bristle Brush 鬃毛筆刷
Pattern Brush 圖樣筆刷 Pattern Brush 圖樣筆刷
missing from the Brushes panel 「筆刷」面板中沒有 missing from the Brushes panel 「筆刷」面板中沒有
built in 內建
embedding not allowed 不允許內嵌
embedding for editing 允許內嵌以供編輯
embedding for preview and print 允許內嵌以供預覽和列印
embedding allowed 允許內嵌
{file}; {embedding} {file};{embedding}
substituted: shown in {family} {style} ({detail}) 已替代:以 {family} {style} 顯示 ({detail})
missing: shown in {family} {style} 遺失:以 {family} {style} 顯示
missing 遺失
{width} × {height} px {width} × {height} px
{size} — {file} {size} — {file}
{n} ppi {n} ppi
{kind}, {instances} {kind},{instances}
{kind}, {rows} × {cols} {kind},{rows} × {cols}
{grid}, {instances} {grid},{instances}
Linear gradient 線性漸層
Radial gradient 放射狀漸層
Conic gradient 錐形漸層
Diamond gradient 菱形漸層
Freeform gradient 自由漸層
@plural {n} instance|{n} instances {n} 個實例
@plural {n} row|{n} rows {n} 列
@plural {n} column|{n} columns {n} 欄
Select art to make a scatter, art or pattern brush. 選取圖稿以製作散佈筆刷、藝術筆刷或圖樣筆刷。 Select art to make a scatter, art or pattern brush. 選取圖稿以製作散佈筆刷、藝術筆刷或圖樣筆刷。
Brush Art 筆刷圖稿 Brush Art 筆刷圖稿
New Calligraphic Brush 新增書法筆刷 New Calligraphic Brush 新增書法筆刷
Can't render this file because it contains an unexpected character in line 6 and column 47.
+252 -6
View File
@@ -72,16 +72,15 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
if rows.is_empty() { if rows.is_empty() {
widgets::dim_label(ui, tl!("None")); widgets::dim_label(ui, tl!("None"));
} }
let named = named_rows(s["id"].as_str().unwrap_or_default(), rows); let id = s["id"].as_str().unwrap_or_default();
let named = named_rows(id, rows);
for (k, r) in rows.iter().enumerate() { for (k, r) in rows.iter().enumerate() {
let (label, value) = (r[0].as_str().unwrap_or_default(), r[1].as_str().unwrap_or_default()); let (label, value) = (r[0].as_str().unwrap_or_default(), r[1].as_str().unwrap_or_default());
let label = super::label_or_name(label, !named.contains(&k));
if value.is_empty() { if value.is_empty() {
widgets::dim_name(ui, label); widgets::dim_name(ui, super::label_or_name(label, !named.contains(&k)));
} else if s["id"] == "brushes" {
row(ui, label, brush_value(value).into());
} else { } else {
row(ui, label, value.into()); let shown = shown_value(&i, id, k, (label, value), named.contains(&k)).unwrap_or_else(|| value.to_string());
row(ui, super::label_or_name(label, !named.contains(&k)), shown);
} }
} }
} }
@@ -102,6 +101,127 @@ fn brush_value(value: &str) -> &str {
} }
} }
/// Row `k` of Document Info section `id` (`label` and `value` as `document.info` gives them, the
/// label a name when `named`) with its value in the interface language, built from what
/// `document.info` (`info`) gives as values: the
/// units, the raster effects resolution, each brush's type, each font's file, match and
/// embedding permission, each image's size and file, each Repeat object's kind and counts, and the
/// kinds of the gradients no swatch names. Names (fonts, files, faces, swatches) stay as they are.
/// None when the row's value is shown as `document.info` gives it, or the values do not match the
/// row.
fn shown_value(info: &Value, id: &str, k: usize, (label, value): (&str, &str), named: bool) -> Option<String> {
let entry = |list: &str| info[list].get(k).filter(|e| e["name"] == label || e["font"] == label);
match id {
"document" if !named => match label {
"Units" => {
let unit = info["document"]["unitId"].as_str()?;
vectorcraft_doc::Unit::EAST_ASIAN.into_iter().find(|u| u.key() == unit).map(|u| tl!(u.label()).to_string())
}
"Raster Effects" => {
let ppi = info["document"]["rasterEffectsPpi"].as_f64()?;
Some(fill(tl!("{n} ppi"), &[("n", &ppi.to_string())]))
}
_ => None,
},
"brushes" => Some(brush_value(value).to_string()),
"fontDetails" => font_detail_value(entry("fontDetails")?),
"linkedImages" | "embeddedImages" => {
let linked = id == "linkedImages";
let image = info["imageObjects"].as_array()?.iter().filter(|o| o["linked"] == linked).nth(k).filter(|o| o["name"] == label)?;
image_value(image)
}
"repeats" => repeat_value(entry("repeatObjects")?),
"gradients" => gradient_value(entry("gradientObjects")?),
_ => None,
}
}
/// [`crate::i18n::fill_once`]: a name that holds `{…}` text stays as it is.
fn fill(template: &str, args: &[(&str, &str)]) -> String {
crate::i18n::fill_once(template, args)
}
/// A `fontDetails` entry as Font Details shows it: the font's file (or that it is built in) and
/// what its license allows, the face shown in place of a style the family lacks, the face shown in
/// place of a font that is not installed, or that it is missing.
fn font_detail_value(e: &Value) -> Option<String> {
let found = || {
let embedding = match e["embedding"].as_str()? {
"forbidden" => tl!("embedding not allowed"),
"editing" => tl!("embedding for editing"),
"previewPrint" => tl!("embedding for preview and print"),
"allowed" => tl!("embedding allowed"),
_ => return None,
};
let file = e["file"].as_str().unwrap_or(tl!("built in"));
Some(fill(tl!("{file}; {embedding}"), &[("file", file), ("embedding", embedding)]))
};
let face = || Some((e["family"].as_str()?, e["style"].as_str()?));
match e["match"].as_str()? {
"exact" => found(),
"style" => {
let ((family, style), detail) = (face()?, found()?);
Some(fill(tl!("substituted: shown in {family} {style} ({detail})"), &[("family", family), ("style", style), ("detail", &detail)]))
}
"missingWithFallback" => {
let (family, style) = face()?;
Some(fill(tl!("missing: shown in {family} {style}"), &[("family", family), ("style", style)]))
}
"missing" => Some(tl!("missing").to_string()),
_ => None,
}
}
/// An `imageObjects` entry as Linked Images and Embedded Images show it: its size in pixels, and
/// for a linked image the file it shows.
fn image_value(o: &Value) -> Option<String> {
let size = fill(tl!("{width} × {height} px"), &[("width", &o["width"].as_u64()?.to_string()), ("height", &o["height"].as_u64()?.to_string())]);
if o["linked"] == true { Some(fill(tl!("{size} — {file}"), &[("size", &size), ("file", o["file"].as_str()?)])) } else { Some(size) }
}
/// A `repeatObjects` entry as Repeat Objects shows it: its kind and the number of instances it
/// draws, or for a grid its rows and columns, followed by the number it draws when that is fewer.
fn repeat_value(e: &Value) -> Option<String> {
let kind = match e["type"].as_str()? {
"radial" => tl!("Radial Repeat"),
"grid" => tl!("Grid Repeat"),
"mirror" => tl!("Mirror Repeat"),
_ => return None,
};
let n = e["instances"].as_u64()?;
let instances = crate::i18n::tn(n, "{n} instance", "{n} instances");
if e["type"] != "grid" {
return Some(fill(tl!("{kind}, {instances}"), &[("kind", kind), ("instances", &instances)]));
}
let (rows, cols) = (e["rows"].as_u64()?, e["cols"].as_u64()?);
let (shown_rows, shown_cols) = (crate::i18n::tn(rows, "{n} row", "{n} rows"), crate::i18n::tn(cols, "{n} column", "{n} columns"));
let grid = fill(tl!("{kind}, {rows} × {cols}"), &[("kind", kind), ("rows", &shown_rows), ("cols", &shown_cols)]);
Some(if n < rows.saturating_mul(cols) { fill(tl!("{grid}, {instances}"), &[("grid", &grid), ("instances", &instances)]) } else { grid })
}
/// A `gradientObjects` entry as Gradient Objects shows it: its gradients, a swatch's name as it
/// is and a gradient named by its kind in the interface language.
fn gradient_value(e: &Value) -> Option<String> {
let (names, kinds) = (e["gradients"].as_array()?, e["kinds"].as_array()?);
if names.len() != kinds.len() {
return None;
}
let shown: Option<Vec<&str>> = names
.iter()
.zip(kinds)
.map(|(name, kind)| match kind.as_str() {
None => name.as_str(),
Some("linear") => Some(tl!("Linear gradient")),
Some("radial") => Some(tl!("Radial gradient")),
Some("conic") => Some(tl!("Conic gradient")),
Some("diamond") => Some(tl!("Diamond gradient")),
Some("freeform") => Some(tl!("Freeform gradient")),
Some(_) => name.as_str(),
})
.collect();
Some(shown?.join(", "))
}
/// The rows of Document Info section `id` labelled by a name (an artboard, a style, a swatch, a /// The rows of Document Info section `id` labelled by a name (an artboard, a style, a swatch, a
/// font, an image, a repeat object) rather than an interface label: every row of the lists, and in Document the /// font, an image, a repeat object) rather than an interface label: every row of the lists, and in Document the
/// artboards, which follow the Artboards count. Names are shown as they are. /// artboards, which follow the Artboards count. Names are shown as they are.
@@ -337,4 +457,130 @@ mod tests {
let report = app.session.execute("document.info", &json!({"category": "brushes", "format": "text"})).unwrap()["text"].to_string(); let report = app.session.execute("document.info", &json!({"category": "brushes", "format": "text"})).unwrap()["text"].to_string();
assert!(report.contains("Charcoal: Art Brush"), "{report}"); assert!(report.contains("Charcoal: Art Brush"), "{report}");
} }
/// Section `id`'s rows as the panel shows them for `info`: each label and its shown value.
fn shown_rows(info: &Value, id: &str) -> Vec<(String, String)> {
let section = info["sections"].as_array().unwrap().iter().find(|s| s["id"] == id).unwrap_or_else(|| panic!("no {id}"));
let rows = section["rows"].as_array().unwrap();
let named = named_rows(id, rows);
rows.iter()
.enumerate()
.map(|(k, r)| {
let (label, value) = (r[0].as_str().unwrap(), r[1].as_str().unwrap());
(label.to_string(), shown_value(info, id, k, (label, value), named.contains(&k)).unwrap_or_else(|| value.to_string()))
})
.collect()
}
/// The values Document Info makes (the units, the raster effects resolution, each font's file
/// and state, each image's size, each Repeat object's kind and counts, and gradients named by
/// their kind) show in the interface language, from the same `document.info` after a language
/// change; font, face, file and swatch names stay as they are, also with Selection Only, and
/// `document.info` and the saved report stay in English.
#[test]
fn values_follow_interface_language_and_keep_names() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 400, "height": 400, "units": "Millimeters"})).unwrap();
app.session.execute("text.create", &json!({"x": 10, "y": 40, "text": "Hi"})).unwrap();
app.session.execute("text.create", &json!({"x": 10, "y": 80, "text": "Heavy", "font": "Source Sans 3", "style": "Black Wide"})).unwrap();
app.session.execute("text.create", &json!({"x": 10, "y": 120, "text": "Gone", "font": "Qx Absent Family"})).unwrap();
let rect = |app: &mut VectorcraftApp, x: f64| {
app.session.execute("shape.rectangle", &json!({"x": x, "y": 200, "width": 20, "height": 10})).unwrap()["id"].clone()
};
let ramp = |kind: &str| json!({"kind": kind, "stops": [{"offset": 0, "color": "#000"}, {"offset": 1, "color": "#fff"}]});
app.session.execute("swatch.new", &json!({"name": "Linear gradient", "gradient": ramp("radial")})).unwrap();
let glow = rect(&mut app, 0.0);
app.session.execute("paint.setFill", &json!({"ids": [glow], "swatch": "Linear gradient"})).unwrap();
app.session.execute("paint.setStroke", &json!({"ids": [glow], "gradient": ramp("radial")})).unwrap();
app.session.execute("layer.setProps", &json!({"id": glow, "name": "Glow"})).unwrap();
let star = rect(&mut app, 50.0);
app.session.execute("object.repeat.radial", &json!({"ids": [star], "instances": 6})).unwrap();
let tile = rect(&mut app, 100.0);
app.session.execute("object.repeat.grid", &json!({"ids": [tile], "rows": 2, "cols": 1})).unwrap();
let pic = rect(&mut app, 150.0);
app.session.execute("select.set", &json!({"ids": [pic]})).unwrap();
app.session.execute("object.rasterize", &json!({"ppi": 72})).unwrap();
app.session.execute("select.set", &json!({"ids": []})).unwrap();
let info = app.session.execute("document.info", &json!({})).unwrap();
let value = |rows: &[(String, String)], label: &str| {
rows.iter().find(|(l, _)| l == label).unwrap_or_else(|| panic!("no {label} in {rows:?}")).1.clone()
};
let english = |id: &str| shown_rows(&info, id);
assert_eq!((value(&english("document"), "Units"), value(&english("document"), "Raster Effects")), ("Millimeters".into(), "72 ppi".into()));
assert_eq!(value(&english("fontDetails"), "Source Sans 3 Regular"), "built in; embedding allowed");
assert_eq!(value(&english("gradients"), "Glow"), "Linear gradient, Radial gradient");
let repeats = english("repeats");
assert_eq!((repeats[0].1.as_str(), repeats[1].1.as_str()), ("Radial Repeat, 6 instances", "Grid Repeat, 2 rows × 1 column"));
// Every value the panel builds reads as `document.info`'s English row.
for s in info["sections"].as_array().unwrap() {
let id = s["id"].as_str().unwrap();
let rows: Vec<(String, String)> =
s["rows"].as_array().unwrap().iter().map(|r| (r[0].as_str().unwrap().into(), r[1].as_str().unwrap().into())).collect();
assert_eq!(shown_rows(&info, id), rows, "{id}");
}
let Some(de) = crate::i18n::Lang::from_code("de") else { panic!("no German catalog") };
crate::i18n::set_current(de);
let german = |id: &str| shown_rows(&info, id);
let (document, fonts, images, gradients, repeats) =
(german("document"), german("fontDetails"), german("embeddedImages"), german("gradients"), german("repeats"));
let painted = crate::tests_labels::painted_text(&mut app, show);
app.session.execute("select.set", &json!({"ids": [glow]})).unwrap();
let selected = shown_rows(&app.session.execute("document.info", &json!({"selectionOnly": true})).unwrap(), "gradients");
let written: std::rc::Rc<std::cell::RefCell<Vec<u8>>> = Default::default();
let w = written.clone();
app.services.write = Some(Box::new(move |_: &str, b: &[u8]| {
w.borrow_mut().extend_from_slice(b);
Ok(())
}));
app.run("docInfo.save", json!({"path": "/tmp/info.txt", "category": "gradients", "selectionOnly": true})).unwrap();
crate::i18n::set_current(crate::i18n::Lang::EN);
let saved = String::from_utf8(written.borrow().clone()).unwrap();
assert_eq!((value(&document, "Units"), value(&document, "Raster Effects")), ("Millimeter".into(), "72 ppi".into()));
assert!(painted.contains("Millimeter") && !painted.contains("Millimeters"), "{painted}");
assert_eq!(value(&fonts, "Source Sans 3 Regular"), "integriert; Einbetten erlaubt");
let wide = value(&fonts, "Source Sans 3 Black Wide");
assert!(wide.starts_with("ersetzt: angezeigt in Source Sans 3 ") && wide.ends_with("Einbetten erlaubt)"), "{wide}");
let gone = fonts.iter().find(|(l, _)| l.starts_with("Qx Absent Family")).unwrap_or_else(|| panic!("{fonts:?}")).1.clone();
assert!(gone == "fehlt" || gone.starts_with("fehlt: angezeigt in "), "{gone}");
assert_eq!(images.len(), 1);
assert!(images[0].1.ends_with(" px") && images[0].1.contains(" × "), "{images:?}");
// A swatch named like a kind keeps its name; the gradient named by its kind is translated.
assert_eq!(value(&gradients, "Glow"), "Linear gradient, Radialer Verlauf");
assert_eq!(value(&selected, "Glow"), "Linear gradient, Radialer Verlauf");
assert_eq!((repeats[0].1.as_str(), repeats[1].1.as_str()), ("Radiale Wiederholung, 6 Instanzen", "Rasterwiederholung, 2 Zeilen × 1 Spalte"));
assert!(saved.contains("(the selection only)") && saved.contains("\nGRADIENT OBJECTS\nGlow: Linear gradient, Radial gradient\n"), "{saved}");
let report = app.session.execute("document.info", &json!({"format": "text"})).unwrap()["text"].as_str().unwrap().to_string();
assert!(report.contains("Units: Millimeters\n") && report.contains("Radial Repeat, 6 instances"), "{report}");
}
/// A Repeat object's counts take each language's plural forms, and a grid that draws fewer
/// instances than its rows and columns give is followed by the number it draws.
#[test]
fn repeat_value_uses_locale_plural_forms_and_drawn_count() {
let radial = |n: u64| json!({"name": "R", "type": "radial", "instances": n});
let grid = json!({"name": "G", "type": "grid", "rows": 500, "cols": 500, "instances": 125_000});
let in_lang = |code: &str, e: &Value| {
let Some(lang) = crate::i18n::Lang::from_code(code) else { panic!("no {code} catalog") };
crate::i18n::set_current(lang);
let shown = repeat_value(e);
crate::i18n::set_current(crate::i18n::Lang::EN);
shown.unwrap_or_else(|| panic!("{e}"))
};
assert_eq!(in_lang("en", &radial(1)), "Radial Repeat, 1 instance");
assert_eq!(in_lang("en", &grid), "Grid Repeat, 500 rows × 500 columns, 125000 instances");
assert_eq!(
[1, 3, 5, 21].map(|n| in_lang("ru", &radial(n))),
[
"Радиальный повтор, 1 экземпляр",
"Радиальный повтор, 3 экземпляра",
"Радиальный повтор, 5 экземпляров",
"Радиальный повтор, 21 экземпляр"
]
);
assert_eq!(in_lang("ru", &grid), "Повтор сеткой, 500 строк × 500 столбцов, 125000 экземпляров");
assert_eq!([1, 2].map(|n| in_lang("ja", &radial(n))), ["ラジアルリピート、インスタンス 1 個", "ラジアルリピート、インスタンス 2 個"]);
// A full grid shows its rows and columns alone; an unknown kind is shown as `document.info` gives it.
assert_eq!(in_lang("de", &json!({"type": "grid", "rows": 1, "cols": 3, "instances": 3})), "Rasterwiederholung, 1 Zeile × 3 Spalten");
assert_eq!(repeat_value(&json!({"type": "spiral", "instances": 2})), None);
}
} }
+12 -3
View File
@@ -7249,7 +7249,14 @@ result carries the same files as a zip (`{name: "<name>.zip", dataBase64}`, entr
document, objects, graphicStyles, brushes, spotColors, patterns, gradients, symbols, fonts, fontDetails, linkedImages, document, objects, graphicStyles, brushes, spotColors, patterns, gradients, symbols, fonts, fontDetails, linkedImages,
embeddedImages, repeats), `fonts` (the fonts the document's type uses, the type in opacity masks and in the art of embeddedImages, repeats), `fonts` (the fonts the document's type uses, the type in opacity masks and in the art of
its symbols and pattern swatches included, which the fonts and fontDetails categories list; with `selectionOnly`, the its symbols and pattern swatches included, which the fonts and fontDetails categories list; with `selectionOnly`, the
fonts of the selected type), `brushNames` (the brushes the art's strokes use, each once, hidden strokes, strokes at an opacity of 0, strokes with no fonts of the selected type), `fontDetails` (`[{font, match, family, style, file, embedding}]`: each font of `fonts`,
in order, as the fontDetails category's rows give it: `match` is `exact`, `style` (a style the family lacks, shown in
`family` `style`), `missingWithFallback` (not installed, shown in `family` `style`) or `missing`; `file` is the face's
file name, null for a built-in face; `embedding` is `allowed`, `editing`, `previewPrint` or `forbidden`, null when no
face is found), `imageObjects` (`[{name, width, height, linked, file}]`: the image objects in the order the
linkedImages and embeddedImages categories list them, with the file a linked image's row shows and null for an
embedded one), `document.unitId` (the document's units as Preferences › Units names them, such as `millimeters`, beside
the English `document.units`), `brushNames` (the brushes the art's strokes use, each once, hidden strokes, strokes at an opacity of 0, strokes with no
paint or width, guides, paths with no segment, type that draws no characters, a group's or layer's own strokes, a compound path member's own strokes and an image's strokes without a paint or width, guides, paths with no segment, type that draws no characters, a group's or layer's own strokes, a compound path member's own strokes and an image's strokes without a
shape left out; with `selectionOnly`, the brushes the selected shape left out; with `selectionOnly`, the brushes the selected
objects' strokes use), `brushes` (`[{name, type}]`: those brushes with their types, `calligraphic`, `scatter`, `art`, objects' strokes use), `brushes` (`[{name, type}]`: those brushes with their types, `calligraphic`, `scatter`, `art`,
@@ -7262,9 +7269,11 @@ whose drawn characters use a pattern included, hidden fills and strokes, fills a
member's own fills and strokes, an image's fills and strokes that no Convert to Shape or Outline Object effect gives a member's own fills and strokes, an image's fills and strokes that no Convert to Shape or Outline Object effect gives a
shape, and characters that draw nothing (spaces and line breaks without an underline or strikethrough) left out, with shape, and characters that draw nothing (spaces and line breaks without an underline or strikethrough) left out, with
the patterns it uses, which the patterns category lists as Pattern Objects and `objects.patterns` counts; with `selectionOnly`, those in the selection), the patterns it uses, which the patterns category lists as Pattern Objects and `objects.patterns` counts; with `selectionOnly`, those in the selection),
`patternNames` (the patterns those objects use), `gradientObjects` (`[{id, name, gradients}]`: every object filled or `patternNames` (the patterns those objects use), `gradientObjects` (`[{id, name, gradients, kinds}]`: every object filled or
stroked with a gradient, by the same rules as `patternObjects`, with the gradients it uses: the name of the gradient stroked with a gradient, by the same rules as `patternObjects`, with the gradients it uses: the name of the gradient
swatch a gradient is linked to, else `Linear gradient`, `Radial gradient` or `Freeform gradient`; the gradients category swatch a gradient is linked to, else its kind's name, such as `Linear gradient`, `Radial gradient` or `Freeform gradient`;
`kinds` gives, for each of `gradients`, the kind (`linear`, `radial`, `conic`, `diamond` or `freeform`) of a gradient
named by its kind and null for a swatch's name; the gradients category
lists them as Gradient Objects and `objects.gradients` counts them; gradient swatches no art uses are not listed; with lists them as Gradient Objects and `objects.gradients` counts them; gradient swatches no art uses are not listed; with
`selectionOnly`, those in the selection), `gradientNames` (the gradients those objects use), `spotObjects` `selectionOnly`, those in the selection), `gradientNames` (the gradients those objects use), `spotObjects`
(`[{id, name, spotColors}]`: every object filled or stroked with a spot color swatch, as a solid color or in the (`[{id, name, spotColors}]`: every object filled or stroked with a spot color swatch, as a solid color or in the
+1
View File
@@ -1513,6 +1513,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
Document Info's Brushes, Spot Color Objects, Pattern Objects and Gradient Objects categories leave out hidden objects, objects at an opacity of 0 and the objects inside a hidden group or layer or one at an opacity of 0, their characters' paints and colorized images included. With Selection Only an object selected inside such a group or layer is left out too, and Objects still counts every object as before. This is VectorCraft's own rule. Test: `cmd::docinfo::tests::info_skips_paints_on_hidden_and_clear_objects_and_their_members` (vectorcraft-engine). Document Info's Brushes, Spot Color Objects, Pattern Objects and Gradient Objects categories leave out hidden objects, objects at an opacity of 0 and the objects inside a hidden group or layer or one at an opacity of 0, their characters' paints and colorized images included. With Selection Only an object selected inside such a group or layer is left out too, and Objects still counts every object as before. This is VectorCraft's own rule. Test: `cmd::docinfo::tests::info_skips_paints_on_hidden_and_clear_objects_and_their_members` (vectorcraft-engine).
Document Info's Save… writes the category the panel shows, with Selection Only applied, as the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes ("Exports the selected category's visible information to a text file"). With All Categories shown it writes every category, which is VectorCraft's own rule (`docInfo.save {category}`). Test: `panels::doc_info::tests::save_writes_the_category_the_panel_shows`. Document Info's Save… writes the category the panel shows, with Selection Only applied, as the [Document Info panel](https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/document-info-panel.html) page describes ("Exports the selected category's visible information to a text file"). With All Categories shown it writes every category, which is VectorCraft's own rule (`docInfo.save {category}`). Test: `panels::doc_info::tests::save_writes_the_category_the_panel_shows`.
Document Info's category menu and headings show every category title in the interface language, Font Details, Linked Images and Embedded Images included, and the Objects category shows every count label in it too: Compound Paths, Text Objects, Placed Documents, Symbol Instances, Gradient Meshes, Blends, Envelopes, Intertwine Groups, Opacity Masks and Objects with Effects. The Document category's Pattern Swatches row and the Objects category's Path Length row are translated in every catalog that has every menu label. `document.info` and the saved report keep the English labels. Showing these labels in the interface language is VectorCraft's own rule. Test: `i18n::tests::document_info_labels_are_in_complete_catalogs` (vectorcraft-ui-egui). Document Info's category menu and headings show every category title in the interface language, Font Details, Linked Images and Embedded Images included, and the Objects category shows every count label in it too: Compound Paths, Text Objects, Placed Documents, Symbol Instances, Gradient Meshes, Blends, Envelopes, Intertwine Groups, Opacity Masks and Objects with Effects. The Document category's Pattern Swatches row and the Objects category's Path Length row are translated in every catalog that has every menu label. `document.info` and the saved report keep the English labels. Showing these labels in the interface language is VectorCraft's own rule. Test: `i18n::tests::document_info_labels_are_in_complete_catalogs` (vectorcraft-ui-egui).
Document Info shows the values it makes in the interface language too: the Units, the Raster Effects resolution, each font's file, substitution and embedding permission (or that it is built in or missing), each image's size and linked file, each Repeat object's kind with its instances, rows and columns in the language's plural forms, and gradients named by their kind. Font, face, file, swatch and other names stay as they are, a gradient swatch named like a kind included, and `document.info`, Save… and Package's report keep the English values (`document.unitId`, `fontDetails`, `imageObjects`, `gradientObjects` `kinds`). Showing these values in the interface language is VectorCraft's own rule. Tests: `panels::doc_info::tests::values_follow_interface_language_and_keep_names`, `repeat_value_uses_locale_plural_forms_and_drawn_count`, `i18n::tests::document_info_values_are_in_complete_catalogs` (vectorcraft-ui-egui), `cmd::docinfo::tests::info_exposes_structured_font_details_without_changing_english_rows`, `info_exposes_ordered_images_and_unit_id` and `gradient_kinds_tell_kinds_from_swatch_names` (vectorcraft-engine).
The Brushes panel menu's Select All Unused selects the brushes that are not used in the document, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. A brush counts as used when a stroke paints with it in the art (opacity masks included), in symbols, graph designs, pattern tiles or graphic styles, or in the art of a used brush (`brush.unused`). The Brushes panel menu's Select All Unused selects the brushes that are not used in the document, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. A brush counts as used when a stroke paints with it in the art (opacity masks included), in symbols, graph designs, pattern tiles or graphic styles, or in the art of a used brush (`brush.unused`).
A rename in Brush Options and Delete Brush change every stroke that paints with the brush: in the layers and opacity masks, symbols, graph designs, pattern tiles, graphic styles and the art of other brushes. A rename gives those strokes the new name, and Delete Brush makes them plain strokes, in one undo step. A rename in Brush Options and Delete Brush change every stroke that paints with the brush: in the layers and opacity masks, symbols, graph designs, pattern tiles, graphic styles and the art of other brushes. A rename gives those strokes the new name, and Delete Brush makes them plain strokes, in one undo step.
A scatter, art or pattern brush dragged from the Brushes panel onto an empty part of the canvas places a copy of its art there to edit, as the [Modify brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/modify-brushes.html) page describes (`brush.placeArt`). The copy is centered where it is dropped. A pattern brush comes out as its tiles in a row, 10 pt apart, each in a group named for its tile, with an unpainted rectangle at the back of a tile that has a repeat frame. The centering and the tile layout are VectorCraft's own rules. Evidence: `tests::brushsym::place_art_puts_a_copy_of_the_brush_art_where_it_is_dropped`, `tests::brushsym::pattern_brushes_place_their_tiles_in_a_row` (vectorcraft-engine), `canvas::tests::a_brush_with_art_dropped_off_the_art_places_a_copy_of_its_art_there` (vectorcraft-ui-egui). A scatter, art or pattern brush dragged from the Brushes panel onto an empty part of the canvas places a copy of its art there to edit, as the [Modify brushes](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/apply-and-edit-strokes/modify-brushes.html) page describes (`brush.placeArt`). The copy is centered where it is dropped. A pattern brush comes out as its tiles in a row, 10 pt apart, each in a group named for its tile, with an unpainted rectangle at the back of a tile that has a repeat frame. The centering and the tile layout are VectorCraft's own rules. Evidence: `tests::brushsym::place_art_puts_a_copy_of_the_brush_art_where_it_is_dropped`, `tests::brushsym::pattern_brushes_place_their_tiles_in_a_row` (vectorcraft-engine), `canvas::tests::a_brush_with_art_dropped_off_the_art_places_a_copy_of_its_art_there` (vectorcraft-ui-egui).