Files
vectorcraft/crates/ui-egui/src/dialogs/eps_options.rs
T
dotCooCoo 0641412cce Dialogs: ui.dialog.set returns an error for a field the open dialog does not read
ui.dialog.set stored any field name it was given, so a misspelled field
(`discrad` for `discard`) was kept without effect and the dialog then
ran with its old value. It now answers such a field with an error that
lists the dialog's fields, and leaves the dialog unchanged.

A dialog accepts the fields it holds, `__` fields, `discard` with a
discard button, `preview` with a live preview, the fields it reads
before a control adds them (each spec's `inputs`, for example Copy and
the object options of the Transform dialogs, Move's `distance` and
`angle`, the reference point, `path` of the export options dialogs,
`searchFolder`, Brush Options' `tips`, the Justification and
Hyphenation values that open blank, the Corners `kind` and `radius` of
corners that differ, the Gradient Stop popover's `color`, `opacity`,
`location` and `spread`, and a new artboard's `color` and display
marks), and the fields it passes to the command it runs: every
parameter of the generic `command` dialog's command, and the
`textureImage` that Load Texture… sets in the Glass and Texturizer
effects. Edit Selection now opens holding `deleted<i>` for each of its
rows, so setting a row number it does not have returns an error, and
Transparency Flattener Presets opens holding the selected preset's name
and options instead of adding them on its first frame.
2026-10-26 09:09:45 +07:00

138 lines
6.2 KiB
Rust

//! EPS Options (Export As → EPS): the preview (none, TIFF black and white or colour, transparent
//! or opaque), overprints and the flattener preset transparency is flattened with, fonts, the
//! options (linked files, thumbnails, CMYK PostScript, compatible gradients) and the PostScript
//! language level.
//!
//! Fields: `document.export` params (`format: eps`, `path?`, the artboard choice) and the EPS
//! options of `document.exportEps`. OK remembers the options for next time and writes the file
//! (asking where when there is no path); bad options keep the dialog open.
use serde_json::{Map, Value, json};
use vectorcraft_engine::cmd::{FlattenOptions, fileio};
use vectorcraft_eps::{Level, Overprint, Preview};
use super::DialogSpec;
use super::document_setup::{LABEL, check, choice};
use crate::state::Dialog;
use crate::{VectorcraftApp, io, widgets};
pub(super) const KIND: &str = "epsOptions";
pub(super) const SPEC: DialogSpec = DialogSpec {
heading: |_| tl!("EPS Options").into(),
body,
confirm,
ok: Some("Export"),
min_width: 420.0,
max_width: Some(460.0),
inputs: |_| &["path"],
..DialogSpec::FORM
};
/// Fields that are not remembered between exports (they belong to one export).
const PER_EXPORT: [&str; 7] = ["format", "path", "useArtboards", "range", "artboard", "artboards", "selectedOnly"];
/// The options a first EPS Options dialog starts from (the export's defaults).
fn defaults() -> Map<String, Value> {
let medium = FlattenOptions::preset_label(FlattenOptions::PRESETS[1]).unwrap_or_default();
let mut m = Map::new();
for (k, v) in [
("previewFormat", json!(Preview::default().id())),
("transparentPreview", json!(true)),
("overprints", json!(Overprint::default().id())),
("flattenerPreset", json!(medium)),
("includeLinkedFiles", json!(false)),
("thumbnails", json!(true)),
("cmykPostScript", json!(true)),
("compatibleGradients", json!(false)),
("level", json!(Level::default().id())),
] {
m.insert(k.into(), v);
}
m
}
/// Open EPS Options for an export whose params (path, artboard choice, any EPS option) are
/// `params`, over the options used last.
pub fn open(app: &mut VectorcraftApp, params: &Value) {
let mut fields = defaults();
for given in [&app.ui.eps_options, params] {
if let Some(o) = given.as_object() {
fields.extend(o.iter().map(|(k, v)| (k.clone(), v.clone())));
}
}
fields.insert("format".into(), json!("eps"));
// A built-in preset named by id (`medium`) shows as its name.
if let Some(label) = fields.get("flattenerPreset").and_then(Value::as_str).and_then(|p| FlattenOptions::preset_label(&p.to_ascii_lowercase())) {
fields.insert("flattenerPreset".into(), json!(label));
}
app.ui.dialog = Some(Dialog::new(KIND, Value::Object(fields)));
}
fn body(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) -> bool {
widgets::subheader(ui, tl!("Preview"));
let previews: Vec<(&str, &str)> = Preview::ALL.iter().map(|p| (p.id(), p.label())).collect();
choice(ui, d, "previewFormat", tl!("Format:"), &previews);
// Only the colour preview can keep transparency.
let color = Preview::from_id(&d.str("previewFormat")) == Some(Preview::TiffColor);
let transparent = d.bool("transparentPreview");
ui.horizontal(|ui| {
ui.add_space(LABEL + 8.0);
for (label, on) in [(tl!("Transparent"), true), (tl!("Opaque"), false)] {
if widgets::radio(ui, label, color && transparent == on, color) {
d.fields.insert("transparentPreview".into(), json!(on));
}
ui.add_space(12.0);
}
});
ui.add_space(8.0);
widgets::subheader(ui, tl!("Transparency"));
let overprints: Vec<(&str, &str)> = Overprint::ALL.iter().map(|o| (o.id(), o.label())).collect();
choice(ui, d, "overprints", tl!("Overprints:"), &overprints);
let presets = app.session.flattener_presets();
let names: Vec<(&str, &str)> = presets.iter().map(|p| (p.name.as_str(), p.name.as_str())).collect();
choice(ui, d, "flattenerPreset", tl!("Preset:"), &names);
ui.add_space(8.0);
widgets::subheader(ui, tl!("Fonts"));
// Type is written as glyph outlines, which need no fonts.
widgets::check(ui, tl!("Embed Fonts (for other applications)"), true, false);
ui.add_space(8.0);
widgets::subheader(ui, tl!("Options"));
check(ui, d, "includeLinkedFiles", tl!("Include Linked Files"));
check(ui, d, "thumbnails", tl!("Include Document Thumbnails"));
check(ui, d, "cmykPostScript", tl!("Include CMYK PostScript in RGB Files"));
check(ui, d, "compatibleGradients", tl!("Compatible Gradient and Gradient Mesh Printing"));
ui.add_space(4.0);
let levels: Vec<(&str, &str)> = Level::ALL.iter().map(|l| (l.id(), l.label())).collect();
// A level set as a number (`level: 2`) reads as its id.
if let Some(Value::Number(n)) = d.fields.get("level") {
let id = n.to_string();
d.fields.insert("level".into(), json!(id));
}
choice(ui, d, "level", tl!("PostScript:"), &levels);
ui.add_space(8.0);
super::form::caption(
ui,
if Level::from_id(&d.str("level")) == Some(Level::Two) || d.bool("compatibleGradients") {
tl!("Transparency is flattened with the preset. Type is written as outlines; gradients as bands of colour.")
} else {
tl!("Transparency is flattened with the preset. Type is written as outlines; gradients stay smooth.")
},
);
false
}
/// Check the options, remember them, and write the file(s).
fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
let params = fileio::eps::resolve(&super::form::params(d), &app.session.prefs.flattener_presets)?;
// Nothing selected keeps the dialog open too.
if let Some(st) = app.session.active() {
fileio::export_source(st, &params).map_err(|e| e.to_string())?;
}
app.ui.eps_options =
Value::Object(d.fields.iter().filter(|(k, _)| !PER_EXPORT.contains(&k.as_str())).map(|(k, v)| (k.clone(), v.clone())).collect());
app.ui.dialog = None;
let path = d.fields.get("path").and_then(Value::as_str).map(str::to_string);
io::export(app, Some("eps"), path, &params)
}