Merge pull request #5147 from dotCooCoo/feat/save-variable-library

Variables: Save Variable Library… writes the variables and data sets as an XML library
This commit is contained in:
BFlat
2026-11-02 14:49:06 +07:00
committed by GitHub
33 changed files with 628 additions and 11 deletions
+12 -2
View File
@@ -10,7 +10,8 @@
//! `name` column, and header cells that define new variables (`#` for visibility, `@` for
//! image, no prefix for text). `variable.loadLibrary` replaces the variables and data sets with the
//! text variables and data sets of a variable library, the XML file of the reference app's Save
//! Variable Library. Agents reach every command through `run_command`, like all engine commands.
//! Variable Library, and `variable.saveLibrary` writes the text variables and data sets as one.
//! Agents reach every command through `run_command`, like all engine commands.
use std::collections::{BTreeMap, BTreeSet};
@@ -26,7 +27,7 @@ mod csv;
mod library;
use csv::{MAX_GRAPH_CSV, dataset_export, dataset_import};
use library::load_library;
use library::{load_library, save_library};
/// The longest variable or dataset name, in characters (names arrive from MCP and files).
const MAX_NAME: usize = 255;
@@ -837,5 +838,14 @@ pub fn specs() -> Vec<CommandSpec> {
has_doc,
load_library
),
cmd!(
query "variable.saveLibrary",
"Save Variable Library…",
["Window", "Variables"],
None,
"{path?} write the document's text variables and every data set as a variable library, the XML that variable.loadLibrary reads: a `variable` element with `varName` and `trait=\"textcontent\"` per text variable, and a `sampleDataSet` element with `dataSetName` per data set, holding an element named after each text variable it has a value for, with one `p` element per line of the value. Variables of other kinds are left out with their values and named in `skipped` and `warning`. Refused without a text variable, for a text variable whose name isn't an XML name (letters, digits, `-`, `_` and `.`, starting with a letter or `_`), for a data set name or value that holds a control character, for a data set name with white space at either end, for a name longer than 255 characters or used twice, and past variable.loadLibrary's caps (256 variables, 1,000 data sets, 8 MiB). Without path → {xml, variables, datasets, skipped, warning?}; with it → {path, variables, datasets, skipped, warning?}",
has_doc,
save_library
),
]
}
+200 -9
View File
@@ -1,8 +1,16 @@
use super::{
BTreeMap, BTreeSet, DataSet, DataValue, EngineError, MAX_NAME, Result, Session, Value, Variable, VariableKind, absolute_path, bad, json, named,
read_file_head, str_param, variables_unlocked,
read_file_head, str_param, variables_unlocked, write_file,
};
/// The namespace of a variable library's elements, the default namespace of its data sets' values,
/// and the `category` of its text variables.
const NS_VARIABLES: &str = "http://ns.adobe.com/Variables/1.0/"; // brand-ok: the namespace variable libraries use
const NS_CUSTOM: &str = "http://ns.adobe.com/GenericCustomNamespace/1.0/"; // brand-ok: the namespace variable libraries use
const NS_FLOWS: &str = "http://ns.adobe.com/Flows/1.0/"; // brand-ok: the namespace variable libraries use
/// Caps on a variable library (paths and MCP payloads are untrusted): its size, its XML nodes, how
/// deep its elements nest, and the entities, variables and data sets it declares.
const MAX_LIBRARY_BYTES: usize = 8 << 20;
@@ -285,17 +293,200 @@ pub(super) fn load_library(s: &mut Session, p: &Value) -> Result<Value> {
let mut out = json!({
"variables": lib.text, "deleted": deleted, "deletedDataSets": deleted_sets, "names": names, "skipped": skipped,
});
// Each `trait` once, in the order the library first uses it, with how many variables have it.
let mut kinds: Vec<(&str, usize)> = Vec::new();
for (_, t) in &lib.other {
match kinds.iter_mut().find(|(k, _)| k == t) {
if let Some(warning) = left_out_warning(lib.other.iter().map(|(_, t)| t.as_str())) {
out["warning"] = json!(warning);
}
Ok(out)
}
/// The warning about variables that aren't text and were left out, given the kind of each: every
/// kind once, in the order it first comes, with how many variables have it. `None` without any.
fn left_out_warning<'a>(kinds: impl Iterator<Item = &'a str>) -> Option<String> {
let mut counted: Vec<(&str, usize)> = Vec::new();
for t in kinds {
match counted.iter_mut().find(|(k, _)| *k == t) {
Some((_, n)) => *n += 1,
None => kinds.push((t, 1)),
None => counted.push((t, 1)),
}
}
if counted.is_empty() {
return None;
}
let kinds = counted.iter().map(|(k, n)| format!("{k} ({n})")).collect::<Vec<_>>().join(", ");
Some(format!("variables that aren't text were left out: {kinds}"))
}
/// Whether `name` can name an element: an XML name without a colon (XML 1.0, productions 4, 4a and
/// 5). A data set holds a text variable's value in an element named after the variable.
fn element_name(name: &str) -> bool {
let start = |c: char| {
matches!(c, 'A'..='Z' | '_' | 'a'..='z' | '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' | '\u{370}'..='\u{37D}'
| '\u{37F}'..='\u{1FFF}' | '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' | '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}'
| '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}' | '\u{10000}'..='\u{EFFFF}')
};
let more = |c: char| start(c) || matches!(c, '-' | '.' | '0'..='9' | '\u{B7}' | '\u{300}'..='\u{36F}' | '\u{203F}'..='\u{2040}');
let mut chars = name.chars();
chars.next().is_some_and(start) && chars.all(more)
}
/// Whether XML can hold `c` (XML 1.0, production 2): tab, line feed, carriage return, and the
/// characters from U+0020 on except U+FFFE and U+FFFF.
fn xml_char(c: char) -> bool {
matches!(c, '\t' | '\n' | '\r' | '\u{20}'..='\u{FFFD}' | '\u{10000}'..='\u{10FFFF}')
}
/// `s` escaped for XML text, or with `attribute` for a quoted attribute value. `&`, `<` and `>`
/// become entity references, and so do quotes in an attribute value. A carriage return, and a tab
/// or line feed in an attribute value, become character references, which the parser reads back
/// as they are.
fn escaped(s: &str, attribute: bool) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
push_escaped(&mut out, c, attribute);
}
out
}
/// `c` added to `out`, escaped as [`escaped`] says.
fn push_escaped(out: &mut String, c: char, attribute: bool) {
match c {
'&' => out.push_str("&amp;"),
'<' => out.push_str("&lt;"),
'>' => out.push_str("&gt;"),
'\r' => out.push_str("&#13;"),
'"' if attribute => out.push_str("&quot;"),
'\'' if attribute => out.push_str("&apos;"),
'\t' if attribute => out.push_str("&#9;"),
'\n' if attribute => out.push_str("&#10;"),
c => out.push(c),
}
}
/// Save Variable Library: the document's text variables and its data sets as a variable library in
/// the layout [`parse_library`] reads, so Load Variable Library reads back the same text variables,
/// data set names and text values. A text value is written as one `p` element per line. Variables
/// of other kinds are left out with their values, named in `skipped` and in a warning. A text
/// variable whose name can't name an element ([`element_name`]), a data set name or text value that
/// holds a character XML can't hold, a name that Load Variable Library would read differently or
/// refuse (white space at either end of a data set name, a name longer than `MAX_NAME` characters,
/// a name used twice), and a library past the caps Load Variable Library reads are refused. With `path` the library is written to the file; without it the reply carries it as
/// `xml`.
pub(super) fn save_library(s: &mut Session, p: &Value) -> Result<Value> {
const C: &str = "variable.saveLibrary";
let vars = &s.doc()?.doc.variables;
if vars.variables.is_empty() {
return Err(bad(C, "define a variable first"));
}
let (text, other): (Vec<&Variable>, Vec<&Variable>) = vars.variables.iter().partition(|v| v.kind == VariableKind::Text);
if text.is_empty() {
return Err(bad(C, "a variable library holds text variables, and the document has none"));
}
if let Some(name) = text.iter().map(|v| &v.name).find(|n| !element_name(n)) {
return Err(bad(
C,
format!(
"variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.`"
),
));
}
// Load Variable Library refuses a name longer than `MAX_NAME` characters and a name declared
// twice.
let mut declared = BTreeSet::new();
for v in &text {
named(Some(&v.name), "varName", C)?;
if !declared.insert(v.name.as_str()) {
return Err(bad(C, format!("variable `{}` is already defined", v.name)));
}
}
if !kinds.is_empty() {
let kinds = kinds.iter().map(|(k, n)| format!("{k} ({n})")).collect::<Vec<_>>().join(", ");
out["warning"] = json!(format!("variables that aren't text were left out: {kinds}"));
if text.len() > MAX_LIBRARY_VARIABLES {
return Err(bad(C, format!("the variable library declares more than {MAX_LIBRARY_VARIABLES} variables")));
}
if vars.datasets.len() > MAX_LIBRARY_DATASETS {
return Err(bad(C, format!("the variable library declares more than {MAX_LIBRARY_DATASETS} data sets")));
}
fn text_value<'a>(ds: &'a DataSet, var: &str) -> Option<&'a str> {
match ds.values.get(var) {
Some(DataValue::Text(t)) => Some(t.as_str()),
_ => None,
}
}
let mut xml = format!(
"<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\
<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" [\n\
\t<!ENTITY ns_vars \"{NS_VARIABLES}\">\n\
\t<!ENTITY ns_custom \"{NS_CUSTOM}\">\n\
\t<!ENTITY ns_flows \"{NS_FLOWS}\">\n\
]>\n\
<svg>\n\
<variableSets xmlns=\"&ns_vars;\">\n\
\t<variableSet varSetName=\"binding1\" locked=\"none\">\n\
\t\t<variables>\n"
);
for v in &text {
xml += &format!("\t\t\t<variable varName=\"{}\" trait=\"textcontent\" category=\"&ns_flows;\"></variable>\n", v.name);
}
xml += "\t\t</variables>\n\t\t<v:sampleDataSets xmlns:v=\"&ns_vars;\" xmlns=\"&ns_custom;\">\n";
// Load Variable Library caps the file and the text its entity references expand to: one
// `&ns_flows;` per variable, two `&ns_vars;` and one `&ns_custom;`. The library stays within
// the cap less what the references add.
let grows = |value: &str, reference: &str| value.len().saturating_sub(reference.len());
let growth = text.len() * grows(NS_FLOWS, "&ns_flows;") + 2 * grows(NS_VARIABLES, "&ns_vars;") + grows(NS_CUSTOM, "&ns_custom;");
let room = MAX_LIBRARY_BYTES.saturating_sub(growth);
let too_large = || bad(C, format!("the variable library is larger than {MAX_LIBRARY_BYTES} bytes"));
let mut seen = BTreeSet::new();
for ds in &vars.datasets {
let name = &ds.name;
// Load Variable Library reads a data set's name trimmed and at most `MAX_NAME` characters
// long ([`named`]), and refuses a name declared twice.
if named(Some(name), "dataSetName", C)? != *name {
return Err(bad(C, format!("data set `{name}` starts or ends with white space, which a variable library can't hold")));
}
if !seen.insert(name.as_str()) {
return Err(bad(C, format!("dataset `{name}` already exists")));
}
if !name.chars().all(xml_char) || text.iter().filter_map(|v| text_value(ds, &v.name)).any(|t| !t.chars().all(xml_char)) {
return Err(bad(C, format!("data set `{name}` holds a control character, which a variable library can't hold")));
}
xml += &format!("\t\t\t<v:sampleDataSet dataSetName=\"{}\">\n", escaped(name, true));
for v in &text {
let Some(t) = text_value(ds, &v.name) else { continue };
xml += &format!("\t\t\t\t<{}>\n", v.name);
for line in t.split('\n') {
xml += "\t\t\t\t\t<p>";
// A value can be larger than the cap, so the text stops growing once it passes it.
for c in line.chars() {
push_escaped(&mut xml, c, false);
if xml.len() > room {
return Err(too_large());
}
}
xml += "</p>\n";
}
xml += &format!("\t\t\t\t</{}>\n", v.name);
}
xml += "\t\t\t</v:sampleDataSet>\n";
if xml.len() > room {
return Err(too_large());
}
}
xml += "\t\t</v:sampleDataSets>\n\t</variableSet>\n</variableSets>\n</svg>\n";
if xml.len() > room {
return Err(too_large());
}
let skipped: Vec<Value> = other.iter().map(|v| json!({"name": v.name, "kind": v.kind.label()})).collect();
let mut out = json!({
"variables": text.iter().map(|v| v.name.as_str()).collect::<Vec<_>>(), "datasets": vars.datasets.len(), "skipped": skipped,
});
if let Some(warning) = left_out_warning(other.iter().map(|v| v.kind.label())) {
out["warning"] = json!(warning);
}
match str_param(p, "path") {
Some(path) => {
let path = absolute_path(path);
write_file(&path, xml.as_bytes())?;
out["path"] = json!(path);
}
None => out["xml"] = json!(xml),
}
Ok(out)
}
+224
View File
@@ -2096,6 +2096,230 @@ fn a_variable_library_expands_entities_of_plain_text() {
assert_eq!(value, Some(vectorcraft_doc::DataValue::Text("x-y \"z\"-x&a;".into())));
}
/// Save Variable Library writes what Load Variable Library reads back: the text variables in their
/// order, every data set by name and in order, and each text value as it was, with several lines,
/// markup characters, quotes, a carriage return and an empty value. Variables of other kinds are
/// left out with their values, named in `skipped` and in the warning. The library is written while
/// the variables are locked too, to a file with `path`.
#[test]
fn a_saved_variable_library_loads_back_the_text_variables_and_data_sets() {
let mut s = session();
let t = text(&mut s, "Alice");
for (name, kind) in [("Name", "text"), ("Shown", "visibility"), ("Note", "text"), ("Photo", "image"), ("Badge", "visibility")] {
s.execute("variable.define", &json!({"name": name, "kind": kind})).unwrap();
}
s.execute("variable.bind", &json!({"variable": "Name", "ids": [t.0]})).unwrap();
s.execute("dataset.new", &json!({"name": "First", "values": {"Name": "Bravo", "Note": "One\nTwo & <three>\r\n'four' \"five\"", "Shown": true}}))
.unwrap();
s.execute("dataset.new", &json!({"name": "Say \"hi\" & <go>\tnow", "values": {"Name": "", "Photo": "a.png"}})).unwrap();
s.execute("dataset.new", &json!({"name": "Empty"})).unwrap();
s.execute("variable.lock", &json!({"locked": true})).unwrap();
let before = s.doc().unwrap().doc.variables.clone();
let saved = s.execute("variable.saveLibrary", &json!({})).unwrap();
assert_eq!(saved["variables"], json!(["Name", "Note"]));
assert_eq!(saved["datasets"], json!(3));
assert_eq!(
saved["skipped"],
json!([{"name": "Shown", "kind": "visibility"}, {"name": "Photo", "kind": "image"}, {"name": "Badge", "kind": "visibility"}])
);
assert_eq!(saved["warning"], "variables that aren't text were left out: visibility (2), image (1)");
assert_eq!(s.doc().unwrap().doc.variables, before, "saving changes nothing");
let mut other = session();
let loaded = other.execute("variable.loadLibrary", &json!({"xml": saved["xml"]})).unwrap();
assert_eq!((loaded["skipped"].clone(), loaded.get("warning")), (json!([]), None));
let vars = &other.doc().unwrap().doc.variables;
assert_eq!(vars.variables.iter().map(|v| (v.name.as_str(), v.kind.label())).collect::<Vec<_>>(), [("Name", "text"), ("Note", "text")]);
let text_only: Vec<vectorcraft_doc::DataSet> = before
.datasets
.iter()
.map(|d| {
let mut d = d.clone();
d.values.retain(|k, _| k == "Name" || k == "Note");
d
})
.collect();
assert_eq!(vars.datasets, text_only);
let dir = Folder::new("vars-save-library");
let path = dir.file("library.xml");
let written = s.execute("variable.saveLibrary", &json!({"path": path})).unwrap();
assert_eq!(written["path"], json!(path));
assert!(written.get("xml").is_none());
assert_eq!(std::fs::read_to_string(&path).unwrap(), saved["xml"].as_str().unwrap());
let mut from_file = session();
from_file.execute("variable.loadLibrary", &json!({"path": path})).unwrap();
assert_eq!(from_file.doc().unwrap().doc.variables.datasets, text_only);
}
/// The library is the XML of the public grammar: a document type declaration with the namespace
/// entities, `variableSets` › `variableSet` › `variables` with a `textcontent` variable per text
/// variable, and `sampleDataSets` with a `sampleDataSet` per data set, holding an element per text
/// variable it has a value for, with a `p` element per line. Markup characters, a carriage return,
/// and a tab or quote in a data set name are escaped. A document with text variables and no data
/// sets writes an empty `sampleDataSets`.
#[test]
fn a_saved_variable_library_has_the_library_layout() {
let mut s = session();
for (name, kind) in [("Name", "text"), ("Shown", "visibility"), ("Note", "text")] {
s.execute("variable.define", &json!({"name": name, "kind": kind})).unwrap();
}
let head = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\
<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\" [\n\
\t<!ENTITY ns_vars \"http://ns.adobe.com/Variables/1.0/\">\n\
\t<!ENTITY ns_custom \"http://ns.adobe.com/GenericCustomNamespace/1.0/\">\n\
\t<!ENTITY ns_flows \"http://ns.adobe.com/Flows/1.0/\">\n\
]>\n\
<svg>\n\
<variableSets xmlns=\"&ns_vars;\">\n\
\t<variableSet varSetName=\"binding1\" locked=\"none\">\n\
\t\t<variables>\n\
\t\t\t<variable varName=\"Name\" trait=\"textcontent\" category=\"&ns_flows;\"></variable>\n\
\t\t\t<variable varName=\"Note\" trait=\"textcontent\" category=\"&ns_flows;\"></variable>\n\
\t\t</variables>\n\
\t\t<v:sampleDataSets xmlns:v=\"&ns_vars;\" xmlns=\"&ns_custom;\">\n";
let tail = "\t\t</v:sampleDataSets>\n\t</variableSet>\n</variableSets>\n</svg>\n";
let empty = s.execute("variable.saveLibrary", &json!({})).unwrap();
assert_eq!(empty["xml"].as_str().unwrap(), format!("{head}{tail}"));
assert_eq!(empty["datasets"], json!(0));
s.execute("dataset.new", &json!({"name": "A &\t\"B\"", "values": {"Name": "x\r\ny < z", "Shown": false}})).unwrap();
s.execute("dataset.new", &json!({"name": "C", "values": {"Note": ""}})).unwrap();
let xml = s.execute("variable.saveLibrary", &json!({})).unwrap()["xml"].as_str().unwrap().to_string();
let sets = "\t\t\t<v:sampleDataSet dataSetName=\"A &amp;&#9;&quot;B&quot;\">\n\
\t\t\t\t<Name>\n\
\t\t\t\t\t<p>x&#13;</p>\n\
\t\t\t\t\t<p>y &lt; z</p>\n\
\t\t\t\t</Name>\n\
\t\t\t</v:sampleDataSet>\n\
\t\t\t<v:sampleDataSet dataSetName=\"C\">\n\
\t\t\t\t<Note>\n\
\t\t\t\t\t<p></p>\n\
\t\t\t\t</Note>\n\
\t\t\t</v:sampleDataSet>\n";
assert_eq!(xml, format!("{head}{sets}{tail}"));
}
/// Save Variable Library refuses a document without variables or without text variables, a text
/// variable whose name can't name an element, a data set name or text value with a character XML
/// can't hold, a name that Load Variable Library would read differently or refuse, and a library
/// past the caps that Load Variable Library reads. A refused save writes no file.
#[test]
fn save_variable_library_refuses_what_a_library_cant_hold() {
let dir = Folder::new("vars-save-refused");
let path = dir.file("library.xml");
let refused = |s: &mut Session, why: &str| {
let e = s.execute("variable.saveLibrary", &json!({"path": path})).unwrap_err().to_string();
assert!(e.contains(why), "{why}: {e}");
assert!(!std::path::Path::new(&path).exists(), "{why}: a file was written");
};
let mut s = session();
refused(&mut s, "define a variable first");
s.execute("variable.define", &json!({"name": "Shown", "kind": "visibility"})).unwrap();
refused(&mut s, "holds text variables, and the document has none");
for name in ["First Name", "1st", "a:b", "x/y", "-dash"] {
let mut s = session();
s.execute("variable.define", &json!({"name": name, "kind": "text"})).unwrap();
refused(&mut s, &format!("variable `{name}` can't be saved in a variable library"));
}
for name in ["Ünïcode", "_x", "a.b-c_d", "名前"] {
let mut s = session();
s.execute("variable.define", &json!({"name": name, "kind": "text"})).unwrap();
s.execute("dataset.new", &json!({"name": "A", "values": {name: "v"}})).unwrap();
let xml = s.execute("variable.saveLibrary", &json!({})).unwrap()["xml"].clone();
let mut other = session();
other.execute("variable.loadLibrary", &json!({"xml": xml})).unwrap();
assert_eq!(other.doc().unwrap().doc.variables.datasets, s.doc().unwrap().doc.variables.datasets, "{name}");
}
let mut s = session();
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
s.execute("dataset.new", &json!({"name": "A", "values": {"Name": "bell \u{7}"}})).unwrap();
refused(&mut s, "data set `A` holds a control character");
let mut s = session();
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
s.execute("dataset.new", &json!({"name": "B\u{1}"})).unwrap();
refused(&mut s, "holds a control character");
// A document opened from a file can hold names that the commands don't make, and Load Variable
// Library would read them differently or refuse them.
let renamed = |dataset: Option<&str>, variable: Option<&str>| {
let mut s = session();
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
s.execute("variable.define", &json!({"name": "Other", "kind": "text"})).unwrap();
s.execute("dataset.new", &json!({"name": "A"})).unwrap();
s.execute("dataset.new", &json!({"name": "B"})).unwrap();
let vars = &mut std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc).variables;
if let Some(name) = dataset {
vars.datasets[1].name = name.to_string();
}
if let Some(name) = variable {
vars.variables[1].name = name.to_string();
}
s
};
for name in [" B", "B\t", "B\n"] {
refused(&mut renamed(Some(name), None), "starts or ends with white space");
}
refused(&mut renamed(Some(""), None), "missing `dataSetName`");
refused(&mut renamed(Some(&"d".repeat(256)), None), "`dataSetName` is longer than 255 characters");
refused(&mut renamed(Some("A"), None), "dataset `A` already exists");
refused(&mut renamed(None, Some(&"v".repeat(256))), "`varName` is longer than 255 characters");
refused(&mut renamed(None, Some("Name")), "variable `Name` is already defined");
let mut s = renamed(Some(&"d".repeat(255)), Some(&"v".repeat(255)));
let xml = s.execute("variable.saveLibrary", &json!({})).unwrap()["xml"].clone();
let mut other = session();
other.execute("variable.loadLibrary", &json!({"xml": xml})).unwrap();
assert_eq!(other.doc().unwrap().doc.variables.datasets, s.doc().unwrap().doc.variables.datasets);
let mut s = session();
for i in 0..257 {
s.execute("variable.define", &json!({"name": format!("V{i}"), "kind": "text"})).unwrap();
}
refused(&mut s, "more than 256 variables");
let mut s = session();
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
for i in 0..1001 {
s.execute("dataset.new", &json!({"name": format!("D{i}")})).unwrap();
}
refused(&mut s, "more than 1000 data sets");
let mut s = session();
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
s.execute("dataset.new", &json!({"name": "A", "values": {"Name": "x".repeat(9 << 20)}})).unwrap();
refused(&mut s, "larger than 8388608 bytes");
}
/// A library near the size cap is saved only when Load Variable Library reads it back, after its
/// entity references expand: each text variable's `&ns_flows;`, two `&ns_vars;` and one
/// `&ns_custom;` grow by 20, 25 and 36 bytes.
#[test]
fn a_saved_variable_library_near_the_size_cap_loads_back() {
let mut s = session();
for i in 0..200 {
s.execute("variable.define", &json!({"name": format!("V{i}"), "kind": "text"})).unwrap();
}
s.execute("dataset.new", &json!({"name": "A", "values": {"V0": ""}})).unwrap();
let base = s.execute("variable.saveLibrary", &json!({})).unwrap()["xml"].as_str().unwrap().len();
let room = (8 << 20) - (200 * 20 + 2 * 25 + 36);
s.execute("dataset.set", &json!({"name": "A", "values": {"V0": "x".repeat(room - base + 1)}})).unwrap();
let e = s.execute("variable.saveLibrary", &json!({})).unwrap_err().to_string();
assert!(e.contains("larger than 8388608 bytes"), "{e}");
s.execute("dataset.set", &json!({"name": "A", "values": {"V0": "x".repeat(room - base)}})).unwrap();
let xml = s.execute("variable.saveLibrary", &json!({})).unwrap()["xml"].clone();
assert_eq!(xml.as_str().map(str::len), Some(room));
let mut other = session();
other.execute("variable.loadLibrary", &json!({"xml": xml})).unwrap();
assert_eq!(other.doc().unwrap().doc.variables.datasets, s.doc().unwrap().doc.variables.datasets);
// The same library with the value grown to a file of exactly 8 MiB doesn't load back.
let full = xml.as_str().unwrap().replacen("<p>x", &format!("<p>{}x", "x".repeat((8 << 20) - room)), 1);
assert_eq!(full.len(), 8 << 20);
let e = other.execute("variable.loadLibrary", &json!({"xml": full})).unwrap_err().to_string();
assert!(e.contains("entities expand to more than"), "{e}");
}
/// While the variables are locked, a library that would delete or define a variable is refused, as
/// Delete Variable and New Variable are, and the document keeps its variables and data sets. That
/// covers a library whose only variable is a text variable the document already has, and one
+5
View File
@@ -5367,6 +5367,11 @@ theme Appearance Външен вид
Import Data Sets… Импортиране на набори от данни…
Export Data Sets… Експортиране на набори от данни…
Load Variable Library… Зареждане на библиотека с променливи…
Save Variable Library… Запазване на библиотека с променливи…
@msg a variable library holds text variables, and the document has none библиотеката с променливи съдържа текстови променливи, а документът няма такива
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` променливата `{name}` не може да бъде запазена в библиотека с променливи: преименувайте я така, че да започва с буква или `_`, без интервали и символи, различни от `-`, `_` и `.`
@msg data set `{name}` holds a control character, which a variable library can't hold наборът от данни `{name}` съдържа управляващ знак, който библиотеката с променливи не може да съдържа
@msg data set `{name}` starts or ends with white space, which a variable library can't hold наборът от данни `{name}` започва или завършва с интервал, който библиотеката с променливи не може да съдържа
Make Graph Dynamic Динамична диаграма
Batch… Пакет…
Source Folder: Изходна папка:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5391,6 +5391,11 @@ mark-style Japanese Japanski
Import Data Sets… Uvezi skupove podataka…
Export Data Sets… Izvezi skupove podataka…
Load Variable Library… Učitaj biblioteku varijabli…
Save Variable Library… Sačuvaj biblioteku varijabli…
@msg a variable library holds text variables, and the document has none biblioteka varijabli sadrži tekstualne varijable, a dokument ih nema
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` varijabla `{name}` ne može se sačuvati u biblioteci varijabli: preimenujte je tako da počinje slovom ili sa `_`, bez razmaka i simbola osim `-`, `_` i `.`
@msg data set `{name}` holds a control character, which a variable library can't hold skup podataka `{name}` sadrži kontrolni znak, koji biblioteka varijabli ne može sadržavati
@msg data set `{name}` starts or ends with white space, which a variable library can't hold skup podataka `{name}` počinje ili završava razmakom, koji biblioteka varijabli ne može sadržavati
Make Graph Dynamic Učini grafikon dinamičkim
Batch… Paketna obrada…
Source Folder: Izvorni folder:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -2240,6 +2240,11 @@ digits Malayalam Malaiàlam
Load Texture… Carrega una textura…
Load Variable Library Carrega la biblioteca de variables
Load Variable Library… Carrega la biblioteca de variables…
Save Variable Library… Desa la biblioteca de variables…
@msg a variable library holds text variables, and the document has none una biblioteca de variables conté variables de text, i el document no en té cap
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` la variable `{name}` no es pot desar en una biblioteca de variables: canvieu-li el nom perquè comenci amb una lletra o `_`, sense espais ni símbols que no siguin `-`, `_` i `.`
@msg data set `{name}` holds a control character, which a variable library can't hold el conjunt de dades `{name}` conté un caràcter de control, que una biblioteca de variables no pot contenir
@msg data set `{name}` starts or ends with white space, which a variable library can't hold el conjunt de dades `{name}` comença o acaba amb un espai en blanc, que una biblioteca de variables no pot contenir
Loaded Texture Textura carregada
Loads native code Carrega codi natiu
Local Libraries Biblioteques locals
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5468,6 +5468,11 @@ mark-style Japanese Japonské
Import Data Sets… Importovat sady dat…
Export Data Sets… Exportovat sady dat…
Load Variable Library… Načíst knihovnu proměnných…
Save Variable Library… Uložit knihovnu proměnných…
@msg a variable library holds text variables, and the document has none knihovna proměnných obsahuje textové proměnné a dokument žádné nemá
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` proměnnou `{name}` nelze uložit do knihovny proměnných: přejmenujte ji tak, aby začínala písmenem nebo `_` a neobsahovala mezery ani jiné symboly než `-`, `_` a `.`
@msg data set `{name}` holds a control character, which a variable library can't hold sada dat `{name}` obsahuje řídicí znak, který knihovna proměnných nemůže obsahovat
@msg data set `{name}` starts or ends with white space, which a variable library can't hold sada dat `{name}` začíná nebo končí prázdným znakem, který knihovna proměnných nemůže obsahovat
Make Graph Dynamic Nastavit graf jako dynamický
Batch… Dávka…
Source Folder: Zdrojová složka:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5129,6 +5129,11 @@ mark-style Japanese Japansk
Import Data Sets… Importér datasæt…
Export Data Sets… Eksportér datasæt…
Load Variable Library… Indlæs variabelbibliotek…
Save Variable Library… Gem variabelbibliotek…
@msg a variable library holds text variables, and the document has none et variabelbibliotek indeholder tekstvariabler, og dokumentet har ingen
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` variablen `{name}` kan ikke gemmes i et variabelbibliotek: omdøb den, så den begynder med et bogstav eller `_`, uden mellemrum eller andre symboler end `-`, `_` og `.`
@msg data set `{name}` holds a control character, which a variable library can't hold datasættet `{name}` indeholder et kontroltegn, som et variabelbibliotek ikke kan indeholde
@msg data set `{name}` starts or ends with white space, which a variable library can't hold datasættet `{name}` begynder eller slutter med mellemrum, som et variabelbibliotek ikke kan indeholde
@msg variables that aren't text were left out: {kinds} variabler, der ikke er tekst, blev udeladt: {kinds}
@msg a new document won't read these variables back with their kind: {names} et nyt dokument læser ikke disse variabler tilbage med deres type: {names}
@msg a new document refuses this file because of these variables: {names} et nyt dokument afviser denne fil på grund af disse variabler: {names}
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5045,6 +5045,11 @@ effect Soft Weich
Import Data Sets… Datensätze importieren…
Export Data Sets… Datensätze exportieren…
Load Variable Library… Variablenbibliothek laden…
Save Variable Library… Variablenbibliothek speichern…
@msg a variable library holds text variables, and the document has none eine Variablenbibliothek enthält Textvariablen, und das Dokument hat keine
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` die Variable `{name}` kann nicht in einer Variablenbibliothek gespeichert werden: Benennen Sie sie so um, dass sie mit einem Buchstaben oder `_` beginnt, ohne Leerzeichen oder andere Symbole als `-`, `_` und `.`
@msg data set `{name}` holds a control character, which a variable library can't hold der Datensatz `{name}` enthält ein Steuerzeichen, das eine Variablenbibliothek nicht enthalten kann
@msg data set `{name}` starts or ends with white space, which a variable library can't hold der Datensatz `{name}` beginnt oder endet mit Leerraum, den eine Variablenbibliothek nicht enthalten kann
Make Graph Dynamic Diagramm dynamisch machen
Batch… Stapelverarbeitung…
Source Folder: Quellordner:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5326,6 +5326,11 @@ effect Points Σημεία
Editable Επεξεργάσιμο
Export Data Sets… Εξαγωγή συνόλων δεδομένων…
Load Variable Library… Φόρτωση βιβλιοθήκης μεταβλητών…
Save Variable Library… Αποθήκευση βιβλιοθήκης μεταβλητών…
@msg a variable library holds text variables, and the document has none μια βιβλιοθήκη μεταβλητών περιέχει μεταβλητές κειμένου και το έγγραφο δεν έχει καμία
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` η μεταβλητή `{name}` δεν μπορεί να αποθηκευτεί σε βιβλιοθήκη μεταβλητών: μετονομάστε την ώστε να ξεκινά με γράμμα ή `_`, χωρίς κενά ή σύμβολα εκτός από `-`, `_` και `.`
@msg data set `{name}` holds a control character, which a variable library can't hold το σύνολο δεδομένων `{name}` περιέχει χαρακτήρα ελέγχου, τον οποίο δεν μπορεί να περιέχει μια βιβλιοθήκη μεταβλητών
@msg data set `{name}` starts or ends with white space, which a variable library can't hold το σύνολο δεδομένων `{name}` ξεκινά ή τελειώνει με κενό διάστημα, το οποίο δεν μπορεί να περιέχει μια βιβλιοθήκη μεταβλητών
Filter Φίλτρο
Filter Layers Panel Rows Φιλτράρισμα γραμμών πίνακα επιπέδων
Flatten Ισοπέδωση
Can't render this file because it contains an unexpected character in line 163 and column 5.
+5
View File
@@ -5409,6 +5409,11 @@ mark-style Japanese Japonesas
Import Data Sets… Importar conjuntos de datos…
Export Data Sets… Exportar conjuntos de datos…
Load Variable Library… Cargar biblioteca de variables…
Save Variable Library… Guardar biblioteca de variables…
@msg a variable library holds text variables, and the document has none una biblioteca de variables contiene variables de texto y el documento no tiene ninguna
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` la variable `{name}` no se puede guardar en una biblioteca de variables: cámbiele el nombre para que empiece por una letra o `_`, sin espacios ni símbolos distintos de `-`, `_` y `.`
@msg data set `{name}` holds a control character, which a variable library can't hold el conjunto de datos `{name}` contiene un carácter de control, que una biblioteca de variables no puede contener
@msg data set `{name}` starts or ends with white space, which a variable library can't hold el conjunto de datos `{name}` empieza o termina con espacio en blanco, que una biblioteca de variables no puede contener
Make Graph Dynamic Hacer gráfico dinámico
Batch… Lote…
Source Folder: Carpeta de origen:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5338,6 +5338,11 @@ mark-style Japanese Japanilainen
Editable Muokattava
Export Data Sets… Vie tietojoukot…
Load Variable Library… Lataa muuttujakirjasto…
Save Variable Library… Tallenna muuttujakirjasto…
@msg a variable library holds text variables, and the document has none muuttujakirjasto sisältää tekstimuuttujia, eikä asiakirjassa ole yhtään
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` muuttujaa `{name}` ei voi tallentaa muuttujakirjastoon: nimeä se uudelleen niin, että se alkaa kirjaimella tai merkillä `_` eikä sisällä välilyöntejä tai muita symboleja kuin `-`, `_` ja `.`
@msg data set `{name}` holds a control character, which a variable library can't hold tietojoukko `{name}` sisältää ohjausmerkin, jota muuttujakirjasto ei voi sisältää
@msg data set `{name}` starts or ends with white space, which a variable library can't hold tietojoukko `{name}` alkaa tai päättyy tyhjeeseen, jota muuttujakirjasto ei voi sisältää
Filter Suodata
Filter Layers Panel Rows Suodata Tasot-paneelin rivit
Flatten Litistä
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5465,6 +5465,11 @@ theme Appearance Apparence
Import Data Sets… Importer des jeux de données…
Export Data Sets… Exporter des jeux de données…
Load Variable Library… Charger une bibliothèque de variables…
Save Variable Library… Enregistrer une bibliothèque de variables…
@msg a variable library holds text variables, and the document has none une bibliothèque de variables contient des variables de texte, et le document n’en a aucune
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` la variable `{name}` ne peut pas être enregistrée dans une bibliothèque de variables : renommez-la pour qu’elle commence par une lettre ou `_`, sans espaces ni symboles autres que `-`, `_` et `.`
@msg data set `{name}` holds a control character, which a variable library can't hold le jeu de données `{name}` contient un caractère de contrôle, qu’une bibliothèque de variables ne peut pas contenir
@msg data set `{name}` starts or ends with white space, which a variable library can't hold le jeu de données `{name}` commence ou se termine par une espace, qu’une bibliothèque de variables ne peut pas contenir
Make Graph Dynamic Rendre le graphe dynamique
Batch… Traitement par lots…
Source Folder: Dossier source :
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5453,6 +5453,11 @@ imageTrace Outline Körvonal
Import Data Sets… Adatkészletek importálása…
Export Data Sets… Adatkészletek exportálása…
Load Variable Library… Változókönyvtár betöltése…
Save Variable Library… Változókönyvtár mentése…
@msg a variable library holds text variables, and the document has none a változókönyvtár szöveges változókat tartalmaz, a dokumentumban pedig nincs ilyen
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` a(z) `{name}` változó nem menthető változókönyvtárba: nevezze át úgy, hogy betűvel vagy `_` jellel kezdődjön, szóközök és a `-`, `_` és `.` jeltől eltérő szimbólumok nélkül
@msg data set `{name}` holds a control character, which a variable library can't hold a(z) `{name}` adatkészlet vezérlőkaraktert tartalmaz, amelyet a változókönyvtár nem tartalmazhat
@msg data set `{name}` starts or ends with white space, which a variable library can't hold a(z) `{name}` adatkészlet szóközzel kezdődik vagy végződik, amelyet a változókönyvtár nem tartalmazhat
Make Graph Dynamic Diagram dinamikussá tétele
Batch… Kötegelt feldolgozás…
Source Folder: Forrásmappa:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5129,6 +5129,11 @@ mark-style Japanese Jepang
Import Data Sets… Impor himpunan data…
Export Data Sets… Ekspor himpunan data…
Load Variable Library… Muat pustaka variabel…
Save Variable Library… Simpan pustaka variabel…
@msg a variable library holds text variables, and the document has none pustaka variabel berisi variabel teks, dan dokumen tidak memilikinya
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` variabel `{name}` tidak dapat disimpan dalam pustaka variabel: ganti namanya agar diawali huruf atau `_`, tanpa spasi atau simbol selain `-`, `_`, dan `.`
@msg data set `{name}` holds a control character, which a variable library can't hold himpunan data `{name}` berisi karakter kontrol, yang tidak dapat dimuat dalam pustaka variabel
@msg data set `{name}` starts or ends with white space, which a variable library can't hold himpunan data `{name}` diawali atau diakhiri spasi, yang tidak dapat dimuat dalam pustaka variabel
@msg variables that aren't text were left out: {kinds} variabel yang bukan teks dihilangkan: {kinds}
@msg a new document won't read these variables back with their kind: {names} dokumen baru tidak akan membaca kembali variabel ini dengan jenisnya: {names}
@msg a new document refuses this file because of these variables: {names} dokumen baru menolak file ini karena variabel berikut: {names}
Can't render this file because it contains an unexpected character in line 2194 and column 5.
+5
View File
@@ -5080,6 +5080,11 @@ lighting Light Luce
Import Data Sets… Importa set di dati…
Export Data Sets… Esporta set di dati…
Load Variable Library… Carica libreria di variabili…
Save Variable Library… Salva libreria di variabili…
@msg a variable library holds text variables, and the document has none una libreria di variabili contiene variabili di testo e il documento non ne ha
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` la variabile `{name}` non può essere salvata in una libreria di variabili: rinominala in modo che inizi con una lettera o `_`, senza spazi né simboli diversi da `-`, `_` e `.`
@msg data set `{name}` holds a control character, which a variable library can't hold il set di dati `{name}` contiene un carattere di controllo, che una libreria di variabili non può contenere
@msg data set `{name}` starts or ends with white space, which a variable library can't hold il set di dati `{name}` inizia o finisce con uno spazio, che una libreria di variabili non può contenere
Make Graph Dynamic Rendi grafico dinamico
Batch… Elaborazione batch…
Source Folder: Cartella di origine:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5125,6 +5125,11 @@ lighting Light ライト
Import Data Sets… データセットを読み込み…
Export Data Sets… データセットを書き出し…
Load Variable Library… 変数ライブラリを読み込み…
Save Variable Library… 変数ライブラリを保存…
@msg a variable library holds text variables, and the document has none 変数ライブラリにはテキスト変数を保存しますが、このドキュメントにはテキスト変数がありません
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` 変数 `{name}` は変数ライブラリに保存できません: 英字または `_` で始まり、スペースや `-`、`_`、`.` 以外の記号を含まない名前に変更してください
@msg data set `{name}` holds a control character, which a variable library can't hold データセット `{name}` に変数ライブラリに保存できない制御文字が含まれています
@msg data set `{name}` starts or ends with white space, which a variable library can't hold データセット `{name}` の先頭または末尾に変数ライブラリに保存できない空白があります
Make Graph Dynamic グラフを動的にする
Batch… バッチ…
Source Folder: ソースフォルダー:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5112,6 +5112,11 @@ imageTrace Outline კონტური
Import Data Sets… მონაცემთა ნაკრებების იმპორტი…
Export Data Sets… მონაცემთა ნაკრებების ექსპორტი…
Load Variable Library… ცვლადების ბიბლიოთეკის ჩატვირთვა…
Save Variable Library… ცვლადების ბიბლიოთეკის შენახვა…
@msg a variable library holds text variables, and the document has none ცვლადების ბიბლიოთეკა ტექსტურ ცვლადებს შეიცავს, დოკუმენტს კი არცერთი არ აქვს
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` ცვლადი `{name}` ვერ შეინახება ცვლადების ბიბლიოთეკაში: გადაარქვით სახელი ისე, რომ იწყებოდეს ასოთი ან `_`-ით, ჰარეების და `-`, `_` და `.`-ის გარდა სხვა სიმბოლოების გარეშე
@msg data set `{name}` holds a control character, which a variable library can't hold მონაცემთა ნაკრები `{name}` შეიცავს მმართველ სიმბოლოს, რომელსაც ცვლადების ბიბლიოთეკა ვერ შეინახავს
@msg data set `{name}` starts or ends with white space, which a variable library can't hold მონაცემთა ნაკრები `{name}` იწყება ან მთავრდება ჰარით, რომელსაც ცვლადების ბიბლიოთეკა ვერ შეინახავს
Make Graph Dynamic დიაგრამის დინამიკურად ქცევა
Batch… პაკეტური დამუშავება…
Source Folder: წყარო საქაღალდე:
Can't render this file because it is too large.
+5
View File
@@ -5043,6 +5043,11 @@ lighting Light 광원
Import Data Sets… 데이터 세트 가져오기…
Export Data Sets… 데이터 세트 내보내기…
Load Variable Library… 변수 라이브러리 불러오기…
Save Variable Library… 변수 라이브러리 저장…
@msg a variable library holds text variables, and the document has none 변수 라이브러리에는 텍스트 변수가 들어가는데 문서에 텍스트 변수가 없습니다
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` 변수 `{name}`은(는) 변수 라이브러리에 저장할 수 없습니다: 문자나 `_`로 시작하고 공백이나 `-`, `_`, `.` 이외의 기호가 없도록 이름을 바꾸세요
@msg data set `{name}` holds a control character, which a variable library can't hold 데이터 세트 `{name}`에 변수 라이브러리에 담을 수 없는 제어 문자가 있습니다
@msg data set `{name}` starts or ends with white space, which a variable library can't hold 데이터 세트 `{name}`의 앞이나 뒤에 변수 라이브러리에 담을 수 없는 공백이 있습니다
Make Graph Dynamic 그래프를 동적으로 만들기
Batch… 일괄 처리…
Source Folder: 소스 폴더:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5141,6 +5141,11 @@ mark-style Japanese Japans
Import Data Sets… Gegevenssets importeren…
Export Data Sets… Gegevenssets exporteren…
Load Variable Library… Variabelenbibliotheek laden…
Save Variable Library… Variabelenbibliotheek opslaan…
@msg a variable library holds text variables, and the document has none een variabelenbibliotheek bevat tekstvariabelen, en het document heeft er geen
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` variabele `{name}` kan niet in een variabelenbibliotheek worden opgeslagen: geef haar een naam die met een letter of `_` begint, zonder spaties of andere symbolen dan `-`, `_` en `.`
@msg data set `{name}` holds a control character, which a variable library can't hold gegevensset `{name}` bevat een stuurteken, dat een variabelenbibliotheek niet kan bevatten
@msg data set `{name}` starts or ends with white space, which a variable library can't hold gegevensset `{name}` begint of eindigt met witruimte, die een variabelenbibliotheek niet kan bevatten
Make Graph Dynamic Grafiek dynamisch maken
Batch… Batch…
Source Folder: Bronmap:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5320,6 +5320,11 @@ mark-style Japanese Japońskie
Import Data Sets… Importuj zestawy danych…
Export Data Sets… Eksportuj zestawy danych…
Load Variable Library… Wczytaj bibliotekę zmiennych…
Save Variable Library… Zapisz bibliotekę zmiennych…
@msg a variable library holds text variables, and the document has none biblioteka zmiennych zawiera zmienne tekstowe, a dokument nie ma żadnej
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` nie można zapisać zmiennej `{name}` w bibliotece zmiennych: zmień jej nazwę tak, aby zaczynała się literą lub `_`, bez spacji i symboli innych niż `-`, `_` i `.`
@msg data set `{name}` holds a control character, which a variable library can't hold zestaw danych `{name}` zawiera znak sterujący, którego biblioteka zmiennych nie może zawierać
@msg data set `{name}` starts or ends with white space, which a variable library can't hold zestaw danych `{name}` zaczyna się lub kończy białym znakiem, którego biblioteka zmiennych nie może zawierać
Make Graph Dynamic Uczyń wykres dynamicznym
Batch… Przetwarzanie wsadowe…
Source Folder: Folder źródłowy:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -951,6 +951,11 @@
Export CSS… Exportar CSS…
Export Data Sets… Exportar Conjuntos de Dados…
Load Variable Library… Carregar Biblioteca de Variáveis…
Save Variable Library… Salvar Biblioteca de Variáveis…
@msg a variable library holds text variables, and the document has none uma biblioteca de variáveis contém variáveis de texto, e o documento não tem nenhuma
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` a variável `{name}` não pode ser salva em uma biblioteca de variáveis: renomeie-a para começar com uma letra ou `_`, sem espaços nem símbolos além de `-`, `_` e `.`
@msg data set `{name}` holds a control character, which a variable library can't hold o conjunto de dados `{name}` contém um caractere de controle, que uma biblioteca de variáveis não pode conter
@msg data set `{name}` starts or ends with white space, which a variable library can't hold o conjunto de dados `{name}` começa ou termina com espaço em branco, que uma biblioteca de variáveis não pode conter
Export DXF Exportar DXF
Export Document Exportar Documento
Export EPS Exportar EPS
Can't render this file because it contains an unexpected character in line 6 and column 47.
+5
View File
@@ -5075,6 +5075,11 @@ lighting Light Свет
Import Data Sets… Импортировать наборы данных…
Export Data Sets… Экспортировать наборы данных…
Load Variable Library… Загрузить библиотеку переменных…
Save Variable Library… Сохранить библиотеку переменных…
@msg a variable library holds text variables, and the document has none библиотека переменных содержит текстовые переменные, а в документе их нет
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` переменную `{name}` нельзя сохранить в библиотеке переменных: переименуйте её так, чтобы имя начиналось с буквы или `_` и не содержало пробелов и символов, кроме `-`, `_` и `.`
@msg data set `{name}` holds a control character, which a variable library can't hold набор данных `{name}` содержит управляющий символ, который не может храниться в библиотеке переменных
@msg data set `{name}` starts or ends with white space, which a variable library can't hold набор данных `{name}` начинается или заканчивается пробелом, который не может храниться в библиотеке переменных
Make Graph Dynamic Сделать диаграмму динамической
Batch… Пакетная обработка…
Source Folder: Исходная папка:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5330,6 +5330,11 @@ theme Appearance Utseende
Editable Redigerbar
Export Data Sets… Exportera datauppsättningar…
Load Variable Library… Läs in variabelbibliotek…
Save Variable Library… Spara variabelbibliotek…
@msg a variable library holds text variables, and the document has none ett variabelbibliotek innehåller textvariabler, och dokumentet har inga
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` variabeln `{name}` kan inte sparas i ett variabelbibliotek: byt namn på den så att den börjar med en bokstav eller `_`, utan mellanslag eller andra symboler än `-`, `_` och `.`
@msg data set `{name}` holds a control character, which a variable library can't hold datauppsättningen `{name}` innehåller ett kontrolltecken, som ett variabelbibliotek inte kan innehålla
@msg data set `{name}` starts or ends with white space, which a variable library can't hold datauppsättningen `{name}` börjar eller slutar med blanksteg, som ett variabelbibliotek inte kan innehålla
Filter Filter
Filter Layers Panel Rows Filtrera rader i panelen Lager
Flatten Platta till
Can't render this file because it contains an unexpected character in line 5 and column 25.
+5
View File
@@ -5463,6 +5463,11 @@ theme Appearance Görünüm
In Merge Mode, Clicking Stroke Splits the Path Birleştirme modunda kontura tıklamak yolu böler
Insert Kashidas Keşide Ekle
Load Variable Library… Değişken Kitaplığı Yükle…
Save Variable Library… Değişken Kitaplığını Kaydet…
@msg a variable library holds text variables, and the document has none değişken kitaplığı metin değişkenleri içerir ve belgede hiç metin değişkeni yok
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` `{name}` değişkeni değişken kitaplığına kaydedilemiyor: adını bir harf veya `_` ile başlayacak, boşluk ya da `-`, `_` ve `.` dışında simge içermeyecek şekilde değiştirin
@msg data set `{name}` holds a control character, which a variable library can't hold `{name}` veri kümesi, değişken kitaplığının tutamayacağı bir denetim karakteri içeriyor
@msg data set `{name}` starts or ends with white space, which a variable library can't hold `{name}` veri kümesi, değişken kitaplığının tutamayacağı bir boşlukla başlıyor veya bitiyor
Long Uzun
New Brush Yeni Fırça
Pattern Brush Desen Fırçası
Can't render this file because it contains an unexpected character in line 5 and column 25.
+5
View File
@@ -5060,6 +5060,11 @@ lighting Light Світло
Import Data Sets… Імпортувати набори даних…
Export Data Sets… Експортувати набори даних…
Load Variable Library… Завантажити бібліотеку змінних…
Save Variable Library… Зберегти бібліотеку змінних…
@msg a variable library holds text variables, and the document has none бібліотека змінних містить текстові змінні, а в документі їх немає
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` змінну `{name}` не можна зберегти в бібліотеці змінних: перейменуйте її так, щоб ім’я починалося з літери або `_` і не містило пробілів і символів, крім `-`, `_` і `.`
@msg data set `{name}` holds a control character, which a variable library can't hold набір даних `{name}` містить керівний символ, який бібліотека змінних не може містити
@msg data set `{name}` starts or ends with white space, which a variable library can't hold набір даних `{name}` починається або закінчується пробілом, який бібліотека змінних не може містити
Make Graph Dynamic Зробити діаграму динамічною
Batch… Пакетна обробка…
Source Folder: Вихідна папка:
Can't render this file because it contains an unexpected character in line 177 and column 5.
+5
View File
@@ -5063,6 +5063,11 @@ theme Appearance Giao diện
Import Data Sets… Nhập tập dữ liệu…
Export Data Sets… Xuất tập dữ liệu…
Load Variable Library… Tải thư viện biến số…
Save Variable Library… Lưu thư viện biến số…
@msg a variable library holds text variables, and the document has none thư viện biến số chứa các biến số văn bản, và tài liệu không có biến số văn bản nào
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` không thể lưu biến số `{name}` vào thư viện biến số: hãy đổi tên để bắt đầu bằng một chữ cái hoặc `_`, không có khoảng trắng hay ký hiệu nào ngoài `-`, `_` và `.`
@msg data set `{name}` holds a control character, which a variable library can't hold tập dữ liệu `{name}` chứa ký tự điều khiển mà thư viện biến số không thể chứa
@msg data set `{name}` starts or ends with white space, which a variable library can't hold tập dữ liệu `{name}` bắt đầu hoặc kết thúc bằng khoảng trắng mà thư viện biến số không thể chứa
Make Graph Dynamic Tạo biểu đồ động
Batch… Hàng loạt…
Source Folder: Thư mục nguồn:
Can't render this file because it contains an unexpected character in line 199 and column 5.
+5
View File
@@ -5310,6 +5310,11 @@ lighting Light 光源
Import Data Sets… 导入数据集…
Export Data Sets… 导出数据集…
Load Variable Library… 载入变量库…
Save Variable Library… 保存变量库…
@msg a variable library holds text variables, and the document has none 变量库包含文本变量,而文档中没有文本变量
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` 无法将变量 `{name}` 保存到变量库中:请重命名,使其以字母或 `_` 开头,且不含空格或 `-`、`_`、`.` 以外的符号
@msg data set `{name}` holds a control character, which a variable library can't hold 数据集 `{name}` 包含变量库无法保存的控制字符
@msg data set `{name}` starts or ends with white space, which a variable library can't hold 数据集 `{name}` 的开头或结尾有变量库无法保存的空白字符
Make Graph Dynamic 使图表动态化
Batch… 批处理…
Source Folder: 源文件夹:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+5
View File
@@ -5124,6 +5124,11 @@ lighting Light 光源
Import Data Sets… 匯入資料集…
Export Data Sets… 匯出資料集…
Load Variable Library… 載入變數資料庫…
Save Variable Library… 儲存變數資料庫…
@msg a variable library holds text variables, and the document has none 變數資料庫包含文字變數,但文件中沒有文字變數
@msg variable `{name}` can't be saved in a variable library: rename it to start with a letter or `_`, without spaces or symbols other than `-`, `_` and `.` 無法將變數 `{name}` 儲存到變數資料庫:請重新命名,使其以字母或 `_` 開頭,且不含空格或 `-`、`_`、`.` 以外的符號
@msg data set `{name}` holds a control character, which a variable library can't hold 資料集 `{name}` 包含變數資料庫無法儲存的控制字元
@msg data set `{name}` starts or ends with white space, which a variable library can't hold 資料集 `{name}` 的開頭或結尾有變數資料庫無法儲存的空白字元
Make Graph Dynamic 使圖表動態化
Batch… 批次處理…
Source Folder: 來源資料夾:
Can't render this file because it contains an unexpected character in line 6 and column 47.
+16
View File
@@ -263,6 +263,8 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
let highlighted_bound: Vec<u64> = highlighted.as_ref().map(|h| bound_objects(st, h)).unwrap_or_default();
let Picked { selected, has_selection, selection_bound, single_type, single_image, selection_graph } = picked(st);
let locked = st.doc.variables.locked;
// Save Variable Library writes text variables only.
let has_text = st.doc.variables.variables.iter().any(|v| v.kind == VariableKind::Text);
if widgets::menu_item(ui, tl!("New Variable…"), !locked, false) {
crate::menus::invoke(app, "variable.define", json!({}));
@@ -326,6 +328,9 @@ pub fn menu(app: &mut VectorcraftApp, ui: &mut Ui) {
if widgets::menu_item(ui, tl!("Load Variable Library…"), true, false) {
crate::picks::button(app, load_library);
}
if widgets::menu_item(ui, tl!("Save Variable Library…"), has_text, false) {
crate::picks::button(app, save_library);
}
ui.separator();
// What the highlighted variable drives, selected — its bindings are left alone.
if widgets::menu_item(ui, tl!("Select Bound Object"), !highlighted_bound.is_empty(), false) {
@@ -364,6 +369,16 @@ fn load_library(app: &mut VectorcraftApp) {
}
}
/// Variables panel: Save Variable Library… opens a save dialog for an XML file and runs
/// `variable.saveLibrary` with the chosen path.
fn save_library(app: &mut VectorcraftApp) {
let pick = crate::FilePick { name: "variables.xml".into(), filters: vec![crate::FilePick::filter("XML", &["xml"])], ..Default::default() };
let Some(path) = crate::picks::save(app, &pick) else { return };
if let Err(e) = app.run("variable.saveLibrary", json!({"path": path})) {
app.status(e);
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -451,6 +466,7 @@ mod tests {
"Rename Data Set…",
"Delete Data Set",
"Load Variable Library…",
"Save Variable Library…",
"Select Bound Object",
"Select All Bound Objects",
] {
+33
View File
@@ -334,6 +334,39 @@ fn load_variable_library_asks_off_the_ui_thread() {
let _ = std::fs::remove_dir_all(dir);
}
/// The Variables panel menu's Save Variable Library… is enabled once the document has a text
/// variable, and then asks off the UI thread: the library is written
/// to the path the dialog answers, Load Variable Library reads it back, and the warning about the
/// visibility variable it left out shows in the status bar.
#[test]
fn save_variable_library_asks_off_the_ui_thread() {
let (mut app, shown, _) = app();
app.run("file.new", json!({"width": 100, "height": 80})).unwrap();
app.run("variable.define", json!({"name": "Shown", "kind": "visibility"})).unwrap();
// Without a text variable the item is disabled and asks for no file.
click(&mut app, "Save Variable Library…", crate::panels::variables::menu);
assert!(shown.requests().is_empty(), "{:?}", shown.requests());
app.run("variable.define", json!({"name": "Name", "kind": "text"})).unwrap();
app.run("dataset.new", json!({"name": "A", "values": {"Name": "x", "Shown": true}})).unwrap();
let dir = vectorcraft_testkit::temp_dir("picks-save-variable-library");
if let Err(e) = std::fs::remove_dir_all(&dir) {
assert_eq!(e.kind(), std::io::ErrorKind::NotFound, "cannot empty {}: {e}", dir.display());
}
std::fs::create_dir_all(&dir).unwrap();
let library = dir.join("library.xml");
click(&mut app, "Save Variable Library…", crate::panels::variables::menu);
assert!(matches!(shown.requests().as_slice(), [PickRequest::Save(p)] if p.name == "variables.xml"));
assert!(!library.exists(), "nothing is written before the dialog answers");
shown.answer(&[&library.to_string_lossy()]);
poll(&mut app);
assert_eq!(app.ui.status, "variables that aren't text were left out: visibility (1)");
let xml = std::fs::read_to_string(&library).unwrap_or_else(|e| panic!("{e}: {}", app.ui.status));
let loaded = app.run("variable.loadLibrary", json!({"xml": xml})).unwrap();
assert_eq!((loaded["variables"].clone(), loaded["names"].clone()), (json!(["Name"]), json!(["A"])));
let _ = std::fs::remove_dir_all(dir);
}
/// The Keyboard Shortcuts dialog's Import… and Export… ask off the UI thread: the set imported
/// replaces the dialog's working set (kept on OK), Export… writes it where the dialog answers, and
/// an answer once the dialog is closed does nothing.
+12
View File
@@ -1679,6 +1679,18 @@ already walks Image Trace. Short agent entry points:
comments, processing instructions and up to 64 entity declarations whose values are plain text (no `<`, `&` or
`%`). A declaration that holds anything else, and entity references that expand the library past 8 MiB, are
refused. So are a variable or data set declared twice and a value for a variable the library doesn't declare.
`variable.saveLibrary {path?}` (the panel menu's Save Variable Library…) writes the document's text variables and
every data set as a variable library in the layout `variable.loadLibrary` reads: a `variable` element with
`varName` and `trait="textcontent"` for each text variable, and a `sampleDataSet` element with `dataSetName` for
each data set, holding an element named after each text variable it has a value for, with one `p` element per line
of the value. Loading the file gives back the same text variables, data set names and text values. Variables of
other kinds are left out with their values: the reply lists them in `skipped` (`name`, `kind`), and its `warning`
names each kind with its count. Saving is refused for a document without a text variable, for a text variable whose
name isn't an XML name (it starts with a letter or `_` and holds only letters, digits, `-`, `_` and `.`), for a
data set whose name or text value holds a control character, for a data set name with white space at either end,
for a name longer than 255 characters or used twice, and past `variable.loadLibrary`'s caps (256 variables, 1,000
data sets, 8 MiB). Without `path` the reply is `{xml, variables, datasets, skipped, warning?}`;
with it, the file is written and the reply is `{path, variables, datasets, skipped, warning?}`.
- Batch: `action.batch {commands, source?: folder|datasets, folder?, destination?: none|saveAndClose|folder,
destinationFolder?, overrideOpen?, overrideSave?, overrideExport?, includeSubdirs?, stopOnError?}` plays an
action on every file in a folder or every data set (the Actions panel menu's Batch…). Override Save and Override
+1
View File
@@ -565,6 +565,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- Variables: Import Data Sets… (`dataset.import`) reads tab-separated files as well as comma-separated ones, and files without a `name` column, whose rows take the first free names Data Set 1, Data Set 2 and so on. An import error about a row's values or name gives the file line the row starts on.
- Variables: Import Data Sets… (`dataset.import`) defines the variables a file's header names that the document lacks, unbound. `#Shown` defines a visibility variable, `@Photo` defines an image variable, and a header without a prefix defines a text variable. A header that is a variable's exact name keeps naming it, and any other `%` header (graph data files) is refused.
- Variables: Load Variable Library… in the Variables panel menu (`variable.loadLibrary`) replaces the document's variables and data sets with a variable library's, read from its XML file, in one undo step: every variable is deleted with its bindings and values, and every data set is deleted; then the library's text variables are defined, unbound, and its data sets take the text of each value's `p` elements, one paragraph each. Variables of other kinds are left out with a warning that names their kinds.
- Variables: Save Variable Library… in the Variables panel menu (`variable.saveLibrary`) writes the document's text variables and data sets as a variable library's XML file, which Load Variable Library reads back with the same text variables, data set names and text values. Variables of other kinds are left out with a warning that names their kinds.
- JSON-lines control channel with real egui pointer and keyboard injection.
- Synthetic input validates complete gestures before queue mutation: at most 256 drag steps, 16 clicks and 1,024 pending events; up to 64 control requests per frame, with later requests retained for the next frame (#1709).
- The control server writes each reply through a 64 KiB buffer. On Windows 11 it sends the 17 MB `document.inspect` reply of a document with 50,000 objects on one layer to a client over loopback in 0.09 s instead of 34 s.