diff --git a/crates/ui-egui/src/dialogs/export_as.rs b/crates/ui-egui/src/dialogs/export_as.rs index 246aaf23f..c0ed169e9 100644 --- a/crates/ui-egui/src/dialogs/export_as.rs +++ b/crates/ui-egui/src/dialogs/export_as.rs @@ -103,7 +103,8 @@ fn body(_: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { d.fields.insert("all".into(), json!(all)); } let mut range = d.str("range"); - if ui.add_enabled(!all, egui::TextEdit::singleline(&mut range).align(egui::Align2::LEFT_CENTER).desired_width(90.0)).changed() { + let field = egui::TextEdit::singleline(&mut range).align(egui::Align2::LEFT_CENTER).desired_width(90.0).hint_text("1-3, 5"); + if ui.add_enabled(!all, field).changed() { d.fields.insert("range".into(), json!(range)); } }); diff --git a/crates/ui-egui/src/dialogs/save_options.rs b/crates/ui-egui/src/dialogs/save_options.rs index 6381f1e86..5dfb88604 100644 --- a/crates/ui-egui/src/dialogs/save_options.rs +++ b/crates/ui-egui/src/dialogs/save_options.rs @@ -9,7 +9,10 @@ //! artboard to a separate file, with `all` (true: every artboard) or `range`, Include Linked Files, //! Embed ICC Profiles, Create PDF-Compatible File and Use Compression. EPS shows Use Artboards //! (`useArtboards`, off by default: the bounding box fits the visible art) with the same `all` or -//! `range` under it. A native file's Version is a menu of the versions VectorCraft writes +//! `range` under it, then the options Export's EPS Options shows: Preview, Overprints, Flattener +//! preset and PostScript are menus that write the option's value (`previewFormat: "tiffColor"`, +//! `overprints: "preserve"`, `flattenerPreset: "medium"` or a saved preset's name, `level: 3`). +//! A native file's Version is a menu of the versions VectorCraft writes //! (`version`, named by the releases that read each). A //! `.ai` file's Version comes first: PDF-compatible, or legacy version 7 (`aiVersion: "7"`, a file //! per artboard), which takes only Transparent Images (`transparentImages`), Hidden Objects @@ -20,7 +23,9 @@ use std::sync::OnceLock; use serde_json::{Map, Value, json}; +use vectorcraft_engine::cmd::FlattenOptions; use vectorcraft_engine::cmd::fileio::{self, ArtboardPick, Format, FormatOption, SAVE_FORMATS, SaveMode, TEMPLATE_FORMATS}; +use vectorcraft_eps::{Level, Overprint, Preview}; use super::{DialogResult, DialogSpec, form}; use crate::state::Dialog; @@ -109,19 +114,87 @@ fn shown(f: &Format) -> impl Iterator { }) } -/// An option's label: the save options as the reference app's options dialog names them, the -/// others from their names. +/// An option's label: its written label, or for an option without one, its name split into words. fn label(o: &FormatOption) -> String { - match o.name { + written_label(o).unwrap_or_else(|| form::humanize(o.name)) +} + +/// The written label of option `o`, translated. +fn written_label(o: &FormatOption) -> Option { + Some(match o.name { "separateArtboards" => tl!("Save each artboard to a separate file").into(), "useArtboards" => tl!("Use Artboards").into(), - "includeLinked" => tl!("Include Linked Files").into(), + "includeLinked" | "includeLinkedFiles" => tl!("Include Linked Files").into(), "embedProfiles" => tl!("Embed ICC Profiles").into(), "pdfCompatible" => tl!("Create PDF-Compatible File").into(), "compress" => tl!("Use Compression").into(), "editingData" => tl!("Include Editing Data for Other Apps").into(), - _ => form::humanize(o.name), + "version" | "aiVersion" => tl!("Version:").into(), + "transparentImages" => tl!("Transparent Images:").into(), + "hiddenObjects" => tl!("Hidden Objects:").into(), + "outlineText" => tl!("Text:").into(), + "preview" => tl!("Preview").into(), + "thumbnails" => tl!("Include Document Thumbnails").into(), + "previewFormat" => tl!("Preview:").into(), + "transparentPreview" => tl!("Transparent").into(), + "overprints" => tl!("Overprints:").into(), + "flattenerPreset" => tl!("Flattener preset:").into(), + "embedFonts" => tl!("Embed Fonts (for other applications)").into(), + "cmykPostScript" => tl!("Include CMYK PostScript in RGB Files").into(), + "compatibleGradients" => tl!("Compatible Gradient and Gradient Mesh Printing").into(), + "level" => tl!("PostScript:").into(), + _ => return None, + }) +} + +/// The values of an option that takes one of a fixed set, each with the name its menu shows, and +/// `at`, the entry the option's value picks as the save reads it (none when the save would reject +/// the value). The first `builtins` names are VectorCraft's own (translated where they are shown); +/// the others are names the user gave (saved flattener presets), shown as they are. +struct Choices { + values: Vec, + names: Vec, + builtins: usize, + at: Option, +} + +/// The choices of `f`'s option `o`, whose value is `value`, when it takes one of a fixed set of +/// values: EPS's preview format, overprints, PostScript level and flattener preset (the built-in +/// presets by id, then the saved ones by name). +fn choices(app: &VectorcraftApp, f: &Format, o: &FormatOption, value: &Value) -> Option { + if f.id != "eps" { + return None; } + let text = value.as_str().map_or_else(|| value.to_string(), str::to_string); + let (pairs, builtins, at): (Vec<(Value, String)>, Option, Option) = match o.name { + "previewFormat" => ( + Preview::ALL.iter().map(|p| (json!(p.id()), p.label().to_string())).collect(), + None, + Preview::from_id(&text).and_then(|p| Preview::ALL.iter().position(|q| *q == p)), + ), + "overprints" => ( + Overprint::ALL.iter().map(|p| (json!(p.id()), p.label().to_string())).collect(), + None, + Overprint::from_id(&text).and_then(|p| Overprint::ALL.iter().position(|q| *q == p)), + ), + // The level is a number in the save options. + "level" => ( + Level::ALL.iter().filter_map(|l| Some((json!(l.id().parse::().ok()?), l.label().to_string()))).collect(), + None, + Level::from_id(&text).and_then(|l| Level::ALL.iter().position(|q| *q == l)), + ), + "flattenerPreset" => { + let presets = app.session.flattener_presets(); + let id = |name: &str| FlattenOptions::PRESETS.into_iter().find(|id| FlattenOptions::preset_label(id) == Some(name)); + let pairs = presets.iter().map(|p| (id(&p.name).map_or_else(|| json!(p.name), |id| json!(id)), p.name.clone())).collect(); + let at = app.session.flattener_preset(&text).and_then(|p| presets.iter().position(|q| q.name == p.name)); + (pairs, Some(FlattenOptions::builtin_presets().len()), at) + } + _ => return None, + }; + let builtins = builtins.unwrap_or(pairs.len()); + let (values, names): (Vec, Vec) = pairs.into_iter().unzip(); + Some(Choices { values, names, builtins, at }) } /// Every artboard (the All choice under Save each artboard to a separate file or Use Artboards): @@ -181,9 +254,10 @@ fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool { // Version first; the legacy version takes only its own options. let legacy = legacy_ai(d); let mut rows: Vec<&FormatOption> = shown(f).filter(|o| LEGACY_ONLY.contains(&o.name) == legacy || o.name == "aiVersion").collect(); - rows.sort_by_key(|o| o.name != "aiVersion"); + // EPS's PostScript level comes last, as in Export's EPS Options. + rows.sort_by_key(|o| (o.name != "aiVersion", f.id == "eps" && o.name == "level")); for o in rows { - option_row(app, ui, d, o); + option_row(app, ui, d, f, o); } }); if legacy_ai(d) && app.session.active().is_some_and(|st| st.doc.artboards.len() > 1) { @@ -344,8 +418,9 @@ fn artboard_picker(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) } } -/// One option: a checkbox, an artboard menu, a version menu, a number or a text field. -fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &FormatOption) { +/// One option of format `f`: a checkbox, an artboard menu, a version menu, a menu of its +/// [`choices`], a number or a text field. +fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, f: &Format, o: &FormatOption) { let t = Tokens::get(ui.ctx()); let label = label(o); let value = d.fields.get(o.name).cloned().unwrap_or(Value::Null); @@ -397,6 +472,40 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma ui.end_row(); return; } + if let Some(c) = choices(app, f, o, &value) { + ui.label(egui::RichText::new(&label).color(t.text_dim)).on_hover_text(o.description); + // A value none of them names shows as it is. + let current = c.at.and_then(|i| c.names.get(i)).cloned().unwrap_or_else(|| value.as_str().map_or_else(|| value.to_string(), str::to_string)); + let names: Vec<&str> = c.names.iter().map(String::as_str).collect(); + if let Some(i) = super::mixed_dropdown(ui, ("save-option", o.name), ¤t, &names, 254.0, |k| k < c.builtins) { + choose(d, o, &c, i); + } + ui.end_row(); + return; + } + if f.id == "eps" && o.name == "transparentPreview" { + // Only the color preview can keep transparency. + ui.label(""); + let color = Preview::from_id(&d.str("previewFormat")) == Some(Preview::TiffColor); + let transparent = value.as_bool().unwrap_or(true); + ui.horizontal(|ui| { + for (name, on) in [(tl!("Transparent"), true), (tl!("Opaque"), false)] { + if widgets::radio(ui, name, color && transparent == on, color) { + d.fields.insert(o.name.into(), json!(on)); + } + ui.add_space(12.0); + } + }); + ui.end_row(); + return; + } + if f.id == "eps" && o.name == "embedFonts" { + // Type is written as glyph outlines, which need no fonts. + ui.label(""); + widgets::check_tip(ui, &label, true, false, o.description); + ui.end_row(); + return; + } if o.ty == "boolean" { ui.label(""); let on = value.as_bool().unwrap_or(false); @@ -435,6 +544,13 @@ fn option_row(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, o: &Forma ui.end_row(); } +/// Option `o` set to the value of entry `i` of its menu `c`. +fn choose(d: &mut Dialog, o: &FormatOption, c: &Choices, i: usize) { + if let Some(v) = c.values.get(i) { + d.fields.insert(o.name.into(), v.clone()); + } +} + /// The native versions VectorCraft writes, newest first, each with the first release that reads /// it (the format version each release tag writes: 2 in 0.1, 3 from 0.2, 4 from 0.8). const NATIVE_VERSIONS: [(u32, &str); 4] = [(4, "0.8"), (3, "0.2"), (2, "0.1"), (1, "0.1")]; diff --git a/crates/ui-egui/src/dialogs/save_options/tests.rs b/crates/ui-egui/src/dialogs/save_options/tests.rs index 68a88eaf1..968d79568 100644 --- a/crates/ui-egui/src/dialogs/save_options/tests.rs +++ b/crates/ui-egui/src/dialogs/save_options/tests.rs @@ -407,6 +407,154 @@ fn eps_save_as_shows_use_artboards_off_and_writes_the_art_or_the_artboard() { assert_eq!(bounding_box(&saved[2].1), "%%BoundingBox: 0 0 100 80"); } +/// Save As › EPS shows the options of Export's EPS Options: Preview, Overprints, Flattener preset +/// and PostScript as menus of readable names (not fields holding `tiffColor`, `preserve` and +/// `medium`), the checkboxes under written labels, and the PostScript level last. +#[test] +fn eps_save_options_are_menus_with_readable_names() { + let (mut app, _, _) = desktop("art.eps"); + app.run("file.saveAs", json!({})).unwrap(); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| super::super::show(app, ui.ctx())); + let lines: Vec<&str> = text.lines().collect(); + for shown in [ + "Preview:", + "TIFF (8-bit Color)", + "Transparent", + "Opaque", + "Overprints:", + "Preserve", + "Flattener preset:", + "Medium Resolution", + "Embed Fonts (for other applications)", + "Include Linked Files", + "Include Document Thumbnails", + "Include CMYK PostScript in RGB Files", + "Compatible Gradient and Gradient Mesh Printing", + "PostScript:", + "Language Level 3", + ] { + assert!(lines.contains(&shown), "{shown:?} in {text}"); + } + for id in ["tiffColor", "preserve", "medium", "Cmyk Post Script", "Preview Format:", "Level:", "Thumbnails"] { + assert!(!lines.contains(&id), "{id:?} in {text}"); + } + let at = |s: &str| lines.iter().position(|l| *l == s).unwrap(); + assert!(at("PostScript:") > at("Compatible Gradient and Gradient Mesh Printing"), "{text}"); +} + +/// Every option the save options dialog shows, in every format it serves, has a written label: +/// none is painted as its name split into words. +#[test] +fn save_options_have_written_labels() { + // The web's Save As and Save as Template dialogs show the options of every format they pick. + let formats = SAVE_FORMATS.iter().map(|id| (*id, "file.saveAs")).chain(TEMPLATE_FORMATS.iter().map(|id| (*id, "file.saveAsTemplate"))); + for (id, command) in formats { + let (mut app, _) = web(); + app.run(command, json!({})).unwrap(); + let mut d = app.ui.dialog.take().unwrap(); + switch_format(&mut app, &mut d, id); + app.ui.dialog = Some(d); + assert_eq!((dialog(&app).kind.as_str(), dialog(&app).str("format").as_str()), (KIND, id)); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| super::super::show(app, ui.ctx())); + let f = fileio::format(id).unwrap(); + for o in shown(f) { + let written = written_label(o); + assert!(written.is_some(), "{id}: {} has no written label", o.name); + let split = form::humanize(o.name); + let split = split.trim_end_matches(':'); + assert!( + written.as_deref().map(|w| w.trim_end_matches(':')) == Some(split) || !text.lines().any(|l| l == split), + "{id}: {split:?} in {text}" + ); + } + } +} + +/// A pick from an EPS menu saves the option's value: the id, the level as a number, a saved +/// flattener preset by its name. The file is written with them. +#[test] +fn picking_eps_menu_items_saves_their_values() { + let (mut app, written, _) = desktop("art.eps"); + app.session.prefs.flattener_presets.push(vectorcraft_engine::cmd::FlattenerPreset { + name: "Mine".into(), + options: vectorcraft_engine::cmd::FlattenOptions::preset("low").unwrap(), + }); + app.run("file.saveAs", json!({})).unwrap(); + let f = fileio::format("eps").unwrap(); + let mut d = app.ui.dialog.take().unwrap(); + for (name, pick, value) in [ + ("previewFormat", "None", json!("none")), + ("overprints", "Discard", json!("discard")), + ("flattenerPreset", "Mine", json!("Mine")), + ("level", "Language Level 2", json!(2)), + ] { + let o = f.options.iter().find(|o| o.name == name).unwrap(); + let c = choices(&app, f, o, &Value::Null).unwrap_or_else(|| panic!("{name} is a menu")); + let i = c.names.iter().position(|n| n == pick).unwrap_or_else(|| panic!("{pick} in {name}: {:?}", c.names)); + choose(&mut d, o, &c, i); + assert_eq!(d.fields[name], value, "{name}"); + assert_eq!(choices(&app, f, o, &value).unwrap().at, Some(i), "{name}: the menu shows the pick"); + } + // The built-in presets come first, by id. A value picks the entry the save reads it as: a + // preset by id or name in any case, a level by its digits, a preview or overprint by id only. + let at = |name: &str, value: Value| { + let o = f.options.iter().find(|o| o.name == name).unwrap(); + choices(&app, f, o, &value).unwrap().at + }; + let o = f.options.iter().find(|o| o.name == "flattenerPreset").unwrap(); + let c = choices(&app, f, o, &Value::Null).unwrap(); + assert_eq!(c.values, [json!("high"), json!("medium"), json!("low"), json!("Mine")]); + assert_eq!((c.builtins, c.at), (3, None)); + assert_eq!( + (at("flattenerPreset", json!("MEDIUM RESOLUTION")), at("flattenerPreset", json!("mine")), at("flattenerPreset", json!("Other"))), + (Some(1), Some(3), None) + ); + assert_eq!((at("level", json!("level2")), at("level", json!(3)), at("level", json!(4))), (Some(0), Some(1), None)); + assert_eq!((at("previewFormat", json!("TIFFBW")), at("previewFormat", json!("TIFF (8-bit Color)"))), (Some(1), None)); + assert_eq!((at("overprints", json!("Discard")), at("overprints", json!("keep"))), (Some(1), None)); + app.ui.dialog = Some(d); + super::super::confirm(&mut app).unwrap(); + assert!(app.ui.dialog.is_none(), "saved: {}", app.ui.status); + let saved = &app.session.active().unwrap().save_options; + for (name, value) in [("previewFormat", json!("none")), ("overprints", json!("discard")), ("flattenerPreset", json!("Mine")), ("level", json!(2))] + { + assert_eq!(saved[name], value, "{name}"); + } + let bytes = written.borrow()[0].1.clone(); + let (ps, preview) = vectorcraft_eps::sections(&bytes).unwrap(); + assert!(preview.is_none(), "no preview"); + assert!(String::from_utf8_lossy(ps).contains("%%LanguageLevel: 2")); +} + +/// Save As › EPS: Transparent and Opaque apply to the color TIFF preview only, and are disabled +/// with any other preview. Embed Fonts shows checked and disabled. A flattener preset that no +/// entry of the menu names shows as it is. +#[test] +fn eps_save_transparency_follows_the_color_preview() { + use crate::widgets::accessibility_tests::{named, role_and_state, tree}; + use egui::accesskit::Toggled; + let (mut app, _, _) = desktop("art.eps"); + app.run("file.saveAs", json!({})).unwrap(); + let set = |app: &mut VectorcraftApp, k: &str, v: Value| { + app.ui.dialog.as_mut().unwrap().fields.insert(k.into(), v); + }; + set(&mut app, "previewFormat", json!("tiffColor")); + set(&mut app, "transparentPreview", json!(false)); + let t = tree(|ui| super::super::show(&mut app, ui.ctx())); + assert!(!named(&t, "Transparent").is_disabled() && !named(&t, "Opaque").is_disabled()); + assert_eq!((role_and_state(&t, "Transparent").1, role_and_state(&t, "Opaque").1), (Some(Toggled::False), Some(Toggled::True))); + let fonts = named(&t, "Embed Fonts (for other applications)"); + assert_eq!((fonts.is_disabled(), fonts.toggled()), (true, Some(Toggled::True))); + for preview in ["none", "tiffBw"] { + set(&mut app, "previewFormat", json!(preview)); + let t = tree(|ui| super::super::show(&mut app, ui.ctx())); + assert!(named(&t, "Transparent").is_disabled() && named(&t, "Opaque").is_disabled(), "{preview}"); + } + set(&mut app, "flattenerPreset", json!("Gone")); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| super::super::show(app, ui.ctx())); + assert!(text.lines().any(|l| l == "Gone"), "{text}"); +} + /// #4931: a native file's Version is a menu of the versions VectorCraft writes, the current one /// first and chosen, each named by the releases that read it. An older version without /// compression saves plain, whatever Use Compression held. diff --git a/crates/ui-egui/src/dialogs/tests_export.rs b/crates/ui-egui/src/dialogs/tests_export.rs index 891fb586b..93bb5dec7 100644 --- a/crates/ui-egui/src/dialogs/tests_export.rs +++ b/crates/ui-egui/src/dialogs/tests_export.rs @@ -180,6 +180,15 @@ fn a_bad_range_keeps_export_as_open() { assert!(written.borrow().is_empty()); } +/// An emptied Range field shows an example of the artboard numbers it takes, as Save As's does. +#[test] +fn an_empty_export_as_range_shows_an_example() { + let (mut app, _) = app(2); + app.run("file.exportAs", json!({"useArtboards": true, "range": ""})).unwrap(); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| show(app, ui.ctx())); + assert!(text.lines().any(|l| l == "1-3, 5"), "{text}"); +} + #[test] fn svg_continues_in_svg_options_and_pdf_art_is_written_directly() { let (mut app, written) = app(2); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 2d8bffbf9..53e27cc3a 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -2461,6 +2461,7 @@ font-technology Variable Променливи Presets: Предварителни настройки: Press Натиснете Preview Преглед + Preview: Преглед: Preview All Layers Преглед на всички слоеве Preview in Browser Преглед в браузър Preview in Browser needs the desktop app (the web downloads files instead) Прегледът в браузър изисква настолното приложение (уеб версията изтегля файловете вместо това) diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index e5b3c1276..0a52c55f8 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -544,6 +544,7 @@ Gaussian Blur… Gaussovo zamućenje… View Prikaz Preview Pregled + Preview: Pregled: Overprint Preview Pregled pretiska Pixel Preview Pregled piksela Trim View Prikaz obrezivanja diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 298f10b5f..be89adc5f 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -3194,6 +3194,7 @@ digits Malayalam Malaiàlam Press Quality Qualitat d’impressió Pressure Pressió Preview Previsualització + Preview: Previsualització: Preview (1 inch on the page) Previsualització (1 polzada a la pàgina) Preview All Layers Previsualitza totes les capes Preview in Browser Previsualitza al navegador diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 7517fc8ae..8857c933f 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -552,6 +552,7 @@ Gaussian Blur… Gaussovské rozostření… View Zobrazení Preview Náhled + Preview: Náhled: Overprint Preview Náhled přetisků Pixel Preview Náhled obrazových bodů Trim View Oříznuté zobrazení diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 0d42626aa..e92cbc29d 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -508,6 +508,7 @@ Gaussian Blur… Gaussisk sløring… View Vis Preview Forhåndsvisning + Preview: Forhåndsvisning: Overprint Preview Overprint-forhåndsvisning Pixel Preview Pixel-forhåndsvisning Trim View Beskåret visning diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 27fc28e49..bf1b0a06a 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -798,6 +798,14 @@ font-technology Variable Variabel Disable Auto Add/Delete Automatisches Hinzufügen/Löschen deaktivieren Disable Opacity Mask Deckkraftmaske deaktivieren Discard Verwerfen + Preserve Beibehalten + TIFF (Black & White) TIFF (Schwarzweiß) + TIFF (8-bit Color) TIFF (8-Bit-Farbe) + High Resolution Hohe Auflösung + Medium Resolution Mittlere Auflösung + Low Resolution Niedrige Auflösung + Language Level 2 Sprachebene 2 + Language Level 3 Sprachebene 3 Discard Empty Type Leeren Text verwerfen Discard Recovered Documents Wiederhergestellte Dokumente verwerfen Discard White Overprint in Output Weißes Überdrucken in der Ausgabe verwerfen @@ -2490,6 +2498,7 @@ diagnostics ui Oberfläche Presets: Vorgaben: Press Mit Preview Vorschau + Preview: Vorschau: Preview All Layers Vorschau aller Ebenen Preview in Browser Vorschau im Browser Preview in Browser needs the desktop app (the web downloads files instead) „Vorschau im Browser“ erfordert die Desktop-App (im Web werden stattdessen Dateien heruntergeladen) diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index c5b6c8023..ff22fce80 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1879,6 +1879,7 @@ font-technology Variable Μεταβλητές Preset: Προεπιλογή: Presets: Προεπιλογές: Preview Προεπισκόπηση + Preview: Προεπισκόπηση: Previous Προηγούμενο Print Εκτύπωση Printer Εκτυπωτής diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 8ae685003..454bd9328 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -2534,6 +2534,7 @@ font-technology Variable Variables Presets: Preajustes: Press Presiona Preview Vista previa + Preview: Vista previa: Preview All Layers Previsualizar todas las capas Preview in Browser Previsualizar en el navegador Preview in Browser needs the desktop app (the web downloads files instead) Previsualizar en el navegador requiere la aplicación de escritorio (la versión web descarga los archivos) diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index cb3c99ced..22477cb3a 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -546,6 +546,7 @@ Gaussian Blur… Gaussin sumennus… View Näytä Preview Esikatselu + Preview: Esikatselu: Overprint Preview Päällepainatuksen esikatselu Pixel Preview Pikseliesikatselu Trim View Rajattu näkymä diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 18e3e1d49..123152dbf 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -2526,6 +2526,7 @@ font-technology Variable Variables Presets: Paramètres prédéfinis : Press Appuyez sur Preview Aperçu + Preview: Aperçu : Preview All Layers Aperçu de tous les calques Preview in Browser Aperçu dans le navigateur Preview in Browser needs the desktop app (the web downloads files instead) L’aperçu dans le navigateur nécessite l’application de bureau (la version web télécharge les fichiers à la place) diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index f07175090..da99be150 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -3487,6 +3487,7 @@ antiAlias Sharp शार्प Press Quality प्रेस गुणवत्ता Pressure दबाव Preview प्रीव्यू + Preview: प्रीव्यू: Preview (1 inch on the page) प्रीव्यू (1 इंच पृष्ठ पर) Preview All Layers सभी लेयर्स का प्रीव्यू करें Preview in Browser ब्राउज़र में प्रीव्यू diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 7b7d200d8..169e4c222 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -3082,6 +3082,7 @@ diagnostics ui felület Press keys… Nyomd le a billentyűket… Pressure Nyomás Preview Előnézet + Preview: Előnézet: Preview All Layers Összes réteg előnézetben Preview in Browser Előnézet böngészőben Preview in Browser needs the desktop app (the web downloads files instead) Az Előnézet böngészőben az asztali alkalmazásban érhető el (a webes változat helyette letölti a fájlokat) diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index d5d37dac4..c70b8f99b 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -508,6 +508,7 @@ Gaussian Blur… Kabur Gaussian… View Tampilan Preview Pratinjau + Preview: Pratinjau: Overprint Preview Pratinjau overprint Pixel Preview Pratinjau piksel Trim View Tampilan terpangkas diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 8f12d5dfb..0d478f9e6 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -819,6 +819,14 @@ font-technology Variable Variabili Disable Auto Add/Delete Disattiva aggiunta/eliminazione automatica Disable Opacity Mask Disattiva maschera di opacità Discard Scarta + Preserve Mantieni + TIFF (Black & White) TIFF (bianco e nero) + TIFF (8-bit Color) TIFF (colore a 8 bit) + High Resolution Alta risoluzione + Medium Resolution Media risoluzione + Low Resolution Bassa risoluzione + Language Level 2 Livello di linguaggio 2 + Language Level 3 Livello di linguaggio 3 Discard Empty Type Scarta testo vuoto Discard Recovered Documents Scarta documenti recuperati Discard White Overprint in Output Scarta sovrastampa del bianco nell'output @@ -2566,6 +2574,7 @@ diagnostics ui interfaccia Presets: Preset: Press Premi Preview Anteprima + Preview: Anteprima: Preview All Layers Anteprima di tutti i livelli Preview in Browser Anteprima nel browser Preview in Browser needs the desktop app (the web downloads files instead) Anteprima nel browser richiede l'app desktop (la versione web scarica invece i file) diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index e26af9850..525352f47 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -848,6 +848,7 @@ digits Malayalam マラヤーラム数字 Search Commands… コマンドを検索… VectorCraft Help… VectorCraft ヘルプ… Preview プレビュー + Preview: プレビュー: Show Edges エッジを表示 Show Corner Widget コーナーウィジェットを表示 Show Template テンプレートを表示 @@ -1437,6 +1438,14 @@ antiAlias Sharp シャープ Disable Auto Add/Delete 自動追加/削除機能を使用しない Disable Opacity Mask 不透明マスクを無効にする Discard 破棄 + Preserve 保持 + TIFF (Black & White) TIFF (白黒) + TIFF (8-bit Color) TIFF (8 bit カラー) + High Resolution 高解像度 + Medium Resolution 中解像度 + Low Resolution 低解像度 + Language Level 2 言語レベル 2 + Language Level 3 言語レベル 3 Discard Empty Type 空のテキストを破棄 Discard Recovered Documents 復帰したドキュメントを破棄 Discard White Overprint in Output 出力時に白のオーバープリントを破棄 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 9eb24e51d..d094007fb 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -482,6 +482,7 @@ Gaussian Blur… გაუსის გაბუნდოვნება… View ხედი Preview წინასწარი ნახვა + Preview: წინასწარი ნახვა: Overprint Preview ზედდაბეჭდვის წინასწარი ნახვა Pixel Preview პიქსელების წინასწარი ნახვა Trim View მოჭრილი ხედი diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 59e5ab62a..9e28f0595 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -841,6 +841,7 @@ digits Malayalam 말라얄람어 Search Commands… 명령 검색… VectorCraft Help… VectorCraft 도움말… Preview 미리 보기 + Preview: 미리 보기: Show Edges 가장자리 표시 Show Corner Widget 모퉁이 위젯 표시 Show Template 템플릿 표시 @@ -1427,6 +1428,14 @@ antiAlias Sharp 날카롭게 Disable Auto Add/Delete 자동 추가/삭제 사용 안 함 Disable Opacity Mask 불투명 마스크 사용 안 함 Discard 삭제 + Preserve 보존 + TIFF (Black & White) TIFF (흑백) + TIFF (8-bit Color) TIFF (8비트 색상) + High Resolution 고해상도 + Medium Resolution 중간 해상도 + Low Resolution 저해상도 + Language Level 2 언어 수준 2 + Language Level 3 언어 수준 3 Discard Empty Type 빈 문자 삭제 Discard Recovered Documents 복구된 문서 삭제 Discard White Overprint in Output 출력에서 흰색 오버프린트 삭제 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 3bed722a2..e2937d793 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -522,6 +522,7 @@ Gaussian Blur… Gaussiaanse vervaging… View Beeld Preview Voorbeeld + Preview: Voorbeeld: Overprint Preview Overprintvoorbeeld Pixel Preview Pixelvoorbeeld Trim View Bijsnijweergave diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index baea8e14a..f99072bb1 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -511,6 +511,7 @@ Gaussian Blur… Rozmycie gaussowskie… View Widok Preview Podgląd + Preview: Podgląd: Overprint Preview Podgląd nadruku Pixel Preview Podgląd pikseli Trim View Widok przycięty diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 0155affc0..58616a44c 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -2107,6 +2107,7 @@ antiAlias Sharp Nítido Presets: Predefinidos: Press Pressione Preview Visualização + Preview: Visualização: Preview Mode Modo de Visualização Preview in Browser Visualizar no Navegador Preview the document as it is now Visualizar o documento como está agora diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index f346b99a4..fe81696d6 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -2561,6 +2561,7 @@ diagnostics ui интерфейс Presets: Наборы: Press Нажмите Preview Просмотр + Preview: Просмотр: Preview All Layers Просмотреть все слои Preview in Browser Просмотреть в браузере Preview in Browser needs the desktop app (the web downloads files instead) Просмотр в браузере требует настольного приложения (web-версия скачивает файлы) diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index bff5500ad..3af639518 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -2471,6 +2471,7 @@ font-technology Variable Variabla Presets: Förinställningar: Press Tryck på Preview Förhandsgranskning + Preview: Förhandsgranskning: Preview All Layers Förhandsgranska alla lager Preview in Browser Förhandsgranska i webbläsare Preview in Browser needs the desktop app (the web downloads files instead) Förhandsgranska i webbläsare kräver skrivbordsappen (webbversionen hämtar filerna i stället) diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index c63e00079..1874e1f28 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -238,6 +238,27 @@ fn anti_aliasing_method_labels_are_in_complete_catalogs() { } } +/// The names the EPS options' menus show (preview, overprints, flattener preset and PostScript +/// level), and Save As's Preview label. +#[test] +fn eps_option_menu_names_are_in_complete_catalogs() { + use vectorcraft_engine::cmd::FlattenOptions; + use vectorcraft_eps::{Level, Overprint, Preview}; + let names: Vec<&str> = Preview::ALL + .map(Preview::label) + .into_iter() + .chain(Overprint::ALL.map(Overprint::label)) + .chain(Level::ALL.map(Level::label)) + .chain(FlattenOptions::PRESETS.into_iter().filter_map(FlattenOptions::preset_label)) + .chain(["Preview:"]) + .collect(); + for lang in LANGUAGES.iter().filter(|lang| lang.complete_menus) { + for label in &names { + assert!(lang.catalog().plain(label).is_some(), "{} is missing {label}", lang.code); + } + } +} + /// Each language counts with its own plural rule: the form a count picks (`one`, then `few`, then /// `other`/`many`). #[test] diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 1ee0a6410..7e9ce4fc4 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -2344,6 +2344,7 @@ shape Open Açık Presets: Ön ayarlar: Press Basın Preview Önizleme + Preview: Önizleme: Preview All Layers Tüm Katmanları Önizle Preview in Browser Tarayıcıda Önizleme Preview in Browser needs the desktop app (the web downloads files instead) Tarayıcıdaki önizleme için masaüstü uygulaması gerekir (bunun yerine web, dosyaları indirir) diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index deff98d6d..31629f737 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -813,6 +813,14 @@ font-technology Variable Варіативні Disable Auto Add/Delete Вимкнути авто-додавання/видалення Disable Opacity Mask Вимкнути маску непрозорості Discard Відкинути + Preserve Зберігати + TIFF (Black & White) TIFF (чорно-білий) + TIFF (8-bit Color) TIFF (8-бітний колір) + High Resolution Висока роздільна здатність + Medium Resolution Середня роздільна здатність + Low Resolution Низька роздільна здатність + Language Level 2 Рівень мови 2 + Language Level 3 Рівень мови 3 Discard Empty Type Видалити порожній текст Discard Recovered Documents Відкинути відновлені документи Discard White Overprint in Output Скасувати накладення білого під час виведення @@ -2558,6 +2566,7 @@ diagnostics ui інтерфейс Presets: Набори: Press Натисніть Preview Перегляд + Preview: Перегляд: Preview All Layers Переглянути всі шари Preview in Browser Переглянути у браузері Preview in Browser needs the desktop app (the web downloads files instead) Перегляд у браузері вимагає настільної програми (web-версія завантажує файли) diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 575507292..aaf741467 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -954,6 +954,14 @@ font-technology Variable Biến thiên Disable Auto Add/Delete Tắt tự động thêm/xóa Disable Opacity Mask Tắt mặt nạ độ mờ Discard Loại bỏ + Preserve Bảo lưu + TIFF (Black & White) TIFF (Đen trắng) + TIFF (8-bit Color) TIFF (Màu 8 bit) + High Resolution Độ phân giải cao + Medium Resolution Độ phân giải vừa + Low Resolution Độ phân giải thấp + Language Level 2 Cấp ngôn ngữ 2 + Language Level 3 Cấp ngôn ngữ 3 Discard Empty Type Loại bỏ chữ trống Discard Recovered Documents Loại bỏ tài liệu đã phục hồi Discard White Overprint in Output Loại bỏ in chồng màu trắng khi xuất @@ -2867,6 +2875,7 @@ diagnostics ui giao diện Press Nhấn Pressure Áp lực Preview Xem trước + Preview: Xem trước: Preview All Layers Xem trước tất cả lớp Preview in Browser Xem trước trong trình duyệt Preview in Browser needs the desktop app (the web downloads files instead) Xem trước trong trình duyệt cần ứng dụng máy tính để bàn (web sẽ tải tệp xuống thay thế) diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index ddc308ee7..ed3481d89 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -2432,6 +2432,7 @@ diagnostics ui 界面 Presets: 预设: Press 按 Preview 预览 + Preview: 预览: Preview in Browser 在浏览器中预览 Preview in Browser needs the desktop app (the web downloads files instead) 在浏览器中预览需要桌面版应用程序 (网页版会改为下载文件) Preview Mode 预览模式 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index a2726f2d2..373b5c135 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -781,6 +781,14 @@ font-technology Variable 可變 Disable Auto Add/Delete 停用自動增加/刪除 Disable Opacity Mask 停用不透明度遮色片 Discard 捨棄 + Preserve 保留 + TIFF (Black & White) TIFF (黑白) + TIFF (8-bit Color) TIFF (8 位元色彩) + High Resolution 高解析度 + Medium Resolution 中解析度 + Low Resolution 低解析度 + Language Level 2 語言層級 2 + Language Level 3 語言層級 3 Discard Empty Type 捨棄空白文字 Discard Recovered Documents 捨棄已復原的文件 Discard White Overprint in Output 輸出時捨棄白色疊印 @@ -3515,6 +3523,7 @@ diagnostics ui 介面 Presets: 預設集: Press 按下 Preview 預覽 + Preview: 預覽: Preview All Layers 預覽所有圖層 Preview in Browser 在瀏覽器中預覽 Preview in Browser needs the desktop app (the web downloads files instead) 在瀏覽器中預覽需要桌面版應用程式 (網頁版會改為下載檔案) diff --git a/docs/control-protocol.md b/docs/control-protocol.md index e302ce633..2c4e4a5ff 100644 --- a/docs/control-protocol.md +++ b/docs/control-protocol.md @@ -787,7 +787,11 @@ artboards' files. A `.ai` file's `aiVersion` comes first: `"pdf"` (the default) Options"), which takes only `transparentImages` (`"flatten"` or `"omit"`), `hiddenObjects` (`"layer"`, `"omit"` or `"visible"`, the Hidden Objects menu), `outlineText` (the Text menu: false Editable, true Outlines) and, with several artboards, the artboard picker: `boards`, one bool per artboard (default: those last saved, else all), saved as `range`, one file per checked -artboard. OK is disabled with none checked. +artboard. OK is disabled with none checked. Save As to an EPS file opens the same dialog after the save panel (heading +"EPS Options") with `useArtboards`, `all` and `range`, then the options Export's EPS Options shows. Preview +(`previewFormat`), Overprints (`overprints`), Flattener preset (`flattenerPreset`) and PostScript (`level`) are menus: +a pick writes the option's value (`"tiffColor"`, `"preserve"`, a built-in preset's id such as `"medium"` or a saved +preset's name, the level as a number), and a value none of them names shows as it is. EMF and WMF: Export As… lists both formats (`file.exportAs {format: "emf"}` or `"wmf"`); they have no options dialog, so `ui.dialog.confirm` asks for the path and writes the file (one per artboard with Use Artboards). Open and