mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 20:27:05 -06:00
451 lines
20 KiB
Rust
451 lines
20 KiB
Rust
//! Export for Screens, drawn headlessly: the fields it opens on (defaults, then the document's last
|
||
//! settings), the artboard choice, Format Settings, desktop folders and web downloads.
|
||
|
||
use std::cell::RefCell;
|
||
use std::rc::Rc;
|
||
|
||
use serde_json::{Value, json};
|
||
use vectorcraft_engine::Session;
|
||
use vectorcraft_engine::cmd::fileio;
|
||
|
||
use super::tests_export::{frame, kind, set};
|
||
use super::*;
|
||
use crate::Services;
|
||
|
||
type Log = Rc<RefCell<Vec<(String, Vec<u8>)>>>;
|
||
|
||
/// An app with `artboards` 60×40 pt artboards and an ellipse on the first; `web` gives it a
|
||
/// download service (recorded in the log) instead of a file manager (whose reveals are recorded).
|
||
fn app(artboards: usize, web: bool) -> (VectorcraftApp, Log) {
|
||
let log = Log::default();
|
||
let l = log.clone();
|
||
let mut services = Services::default();
|
||
if web {
|
||
services.download = Some(Box::new(move |name: &str, bytes: &[u8]| l.borrow_mut().push((name.to_string(), bytes.to_vec()))));
|
||
} else {
|
||
services.reveal = Some(Box::new(move |path: &str| {
|
||
l.borrow_mut().push((path.to_string(), vec![]));
|
||
Ok(())
|
||
}));
|
||
services.pick_folder = Some(Box::new(|| None));
|
||
}
|
||
let mut app = VectorcraftApp::new(Session::new(), services);
|
||
app.run("file.new", json!({"width": 60, "height": 40, "artboards": artboards})).unwrap();
|
||
app.run("shape.ellipse", json!({"x": 5, "y": 4, "width": 30, "height": 20})).unwrap();
|
||
(app, log)
|
||
}
|
||
|
||
fn field(app: &VectorcraftApp, k: &str) -> Value {
|
||
app.ui.dialog.as_ref().expect("a dialog is open").fields.get(k).cloned().unwrap_or(Value::Null)
|
||
}
|
||
|
||
fn open(app: &mut VectorcraftApp) {
|
||
app.run("file.exportForScreens", json!({})).unwrap();
|
||
assert_eq!(kind(app), Some(export_for_screens::KIND));
|
||
}
|
||
|
||
fn temp_folder(tag: &str) -> std::path::PathBuf {
|
||
std::env::temp_dir().join(format!("vc-efs-{tag}-{}", std::process::id()))
|
||
}
|
||
|
||
#[test]
|
||
fn opens_on_defaults_and_draws_every_view() {
|
||
let (mut app, _) = app(3, false);
|
||
open(&mut app);
|
||
assert_eq!((field(&app, "select"), field(&app, "range"), field(&app, "boards")), (json!("all"), json!("1-3"), json!([true, true, true])));
|
||
assert_eq!(field(&app, "formats"), json!([{"format": "png", "scale": "1x", "suffix": ""}]));
|
||
assert_eq!(field(&app, "folder"), json!(fileio::export_folder().unwrap_or_default()), "the Desktop, else home");
|
||
assert_eq!((field(&app, "openLocation"), field(&app, "subfolders"), field(&app, "preset")), (json!(true), json!(false), json!("")));
|
||
let settings = field(&app, "settings");
|
||
for f in ["png", "png8", "jpg", "webp", "gif", "tiff", "svg", "pdf"] {
|
||
assert!(settings[f].is_object(), "{f} has settings");
|
||
}
|
||
assert!(settings["jpg"].get("quality").is_none() && settings["jpg"].get("imageMap").is_none(), "rows choose quality; no maps");
|
||
assert!(settings["svg"].get("images").is_none(), "no linked images");
|
||
assert_eq!(settings["pdf"]["preset"], json!(fileio::pdf::DEFAULT_PRESET));
|
||
frame(&mut app);
|
||
// Assets, Full Document, each format's Format Settings: all draw.
|
||
set(&mut app, "tab", json!("assets"));
|
||
frame(&mut app);
|
||
set(&mut app, "tab", json!("artboards"));
|
||
set(&mut app, "select", json!("full"));
|
||
for f in ["png", "png8", "jpg", "webp", "gif", "tiff", "svg", "pdf"] {
|
||
set(&mut app, "__settings", json!(f));
|
||
frame(&mut app);
|
||
assert_eq!(field(&app, "__settings"), json!(f));
|
||
}
|
||
assert_eq!(kind(&app), Some(export_for_screens::KIND));
|
||
assert_eq!((spec(export_for_screens::KIND).ok_label.unwrap())(&app), "Export Artboard");
|
||
}
|
||
|
||
#[test]
|
||
fn checked_artboards_read_as_a_range() {
|
||
let (mut app, _) = app(5, false);
|
||
open(&mut app);
|
||
set(&mut app, "boards", json!([true, false, true, true, false]));
|
||
frame(&mut app);
|
||
// Nothing clicked: the fields stay as set.
|
||
assert_eq!(field(&app, "boards"), json!([true, false, true, true, false]));
|
||
for (boards, text) in [
|
||
(vec![true, true, true], "1-3"),
|
||
(vec![false, true, false, true], "2, 4"),
|
||
(vec![true, false, true, true, false, true], "1, 3-4, 6"),
|
||
(vec![false], ""),
|
||
] {
|
||
assert_eq!(export_for_screens::range_text(&boards), text);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn exports_into_the_folder_and_shows_it() {
|
||
let (mut app, log) = app(2, false);
|
||
let dir = temp_folder("desk");
|
||
let folder = dir.to_string_lossy().replace('\\', "/");
|
||
open(&mut app);
|
||
set(&mut app, "folder", json!(folder));
|
||
set(&mut app, "subfolders", json!(true));
|
||
set(
|
||
&mut app,
|
||
"formats",
|
||
json!([{"format": "png", "scale": "1x", "suffix": ""}, {"format": "png", "scale": "120w", "suffix": "@120w"}, {"format": "svg", "scale": "1x", "suffix": ""}]),
|
||
);
|
||
// A bad range keeps the dialog open; so does no folder.
|
||
set(&mut app, "select", json!("range"));
|
||
set(&mut app, "range", json!("7"));
|
||
assert!(confirm(&mut app).is_err());
|
||
assert_eq!(kind(&app), Some(export_for_screens::KIND));
|
||
set(&mut app, "range", json!("2"));
|
||
set(&mut app, "folder", json!(" "));
|
||
assert!(confirm(&mut app).is_err());
|
||
set(&mut app, "folder", json!(folder));
|
||
let r = confirm(&mut app).unwrap();
|
||
assert!(app.ui.dialog.is_none());
|
||
let files: Vec<&str> = r["files"].as_array().unwrap().iter().map(|f| f.as_str().unwrap()).collect();
|
||
assert_eq!(files, [format!("{folder}/1x/Artboard-2.png"), format!("{folder}/120w/Artboard-2@120w.png"), format!("{folder}/SVG/Artboard-2.svg")]);
|
||
let png = image::load_from_memory(&std::fs::read(dir.join("120w").join("Artboard-2@120w.png")).unwrap()).unwrap();
|
||
assert_eq!((png.width(), png.height()), (120, 80));
|
||
assert_eq!(log.borrow().iter().map(|(p, _)| p.as_str()).collect::<Vec<_>>(), [files[0]], "Open Location shows the first file");
|
||
// It reopens on those settings.
|
||
open(&mut app);
|
||
assert_eq!((field(&app, "select"), field(&app, "range"), field(&app, "boards")), (json!("range"), json!("2"), json!([false, true])));
|
||
assert_eq!(field(&app, "formats")[1]["scale"], json!("120w"));
|
||
assert_eq!((field(&app, "subfolders"), field(&app, "folder")), (json!(true), json!(folder)));
|
||
let _ = std::fs::remove_dir_all(dir);
|
||
}
|
||
|
||
#[test]
|
||
fn the_web_downloads_one_zip_or_the_file() {
|
||
let (mut app, log) = app(2, true);
|
||
assert_eq!((spec(export_for_screens::KIND).ok_label.unwrap())(&app), "Download");
|
||
open(&mut app);
|
||
frame(&mut app);
|
||
set(
|
||
&mut app,
|
||
"formats",
|
||
json!([{"format": "png", "scale": "1x", "suffix": ""}, {"format": "jpg", "quality": 80, "scale": "2x", "suffix": "@2x"}]),
|
||
);
|
||
set(&mut app, "settings", json!({"png": {"background": "black"}}));
|
||
let r = confirm(&mut app).unwrap();
|
||
assert!(r.get("dataBase64").is_some(), "four files: one zip");
|
||
{
|
||
let log = log.borrow();
|
||
let [(name, zip)] = log.as_slice() else { panic!("one download: {:?}", log.iter().map(|(n, _)| n).collect::<Vec<_>>()) };
|
||
assert_eq!(name, "Untitled-1.zip");
|
||
assert_eq!(&vectorcraft_format::base64_decode(r["dataBase64"].as_str().unwrap()).unwrap(), zip);
|
||
}
|
||
assert_eq!(r["files"], json!(["Artboard-1.png", "Artboard-1@2x.jpg", "Artboard-2.png", "Artboard-2@2x.jpg"]));
|
||
// One file downloads as itself, with the format's settings.
|
||
log.borrow_mut().clear();
|
||
open(&mut app);
|
||
assert_eq!(field(&app, "settings")["png"]["background"], json!("black"), "remembered");
|
||
set(&mut app, "select", json!("range"));
|
||
set(&mut app, "range", json!("1"));
|
||
set(&mut app, "formats", json!([{"format": "png", "scale": "1x", "suffix": ""}]));
|
||
confirm(&mut app).unwrap();
|
||
let log = log.borrow();
|
||
assert_eq!(log.len(), 1);
|
||
assert_eq!(log[0].0, "Artboard-1.png");
|
||
assert_eq!(image::load_from_memory(&log[0].1).unwrap().to_rgba8().get_pixel(1, 1).0, [0, 0, 0, 255], "the PNG settings' background");
|
||
}
|
||
|
||
#[test]
|
||
fn a_preset_export_reopens_on_its_rows() {
|
||
let (mut app, _) = app(1, true);
|
||
app.run("file.exportForScreens", json!({"preset": "density", "fullDocument": true})).unwrap();
|
||
open(&mut app);
|
||
assert_eq!(field(&app, "preset"), json!("density"));
|
||
assert_eq!(field(&app, "select"), json!("full"));
|
||
assert_eq!(field(&app, "subfolders"), json!(true));
|
||
let rows = field(&app, "formats");
|
||
assert_eq!(rows.as_array().unwrap().len(), 6);
|
||
assert_eq!((rows[0]["scale"].clone(), rows[0]["folder"].clone()), (json!("0.75x"), json!("ldpi")));
|
||
frame(&mut app);
|
||
// Agents' numeric sizes show as text.
|
||
app.run(
|
||
"file.exportForScreens",
|
||
json!({"formats": [{"format": "png", "width": 64}, {"format": "png", "scale": 3}, {"format": "png", "ppi": 144}]}),
|
||
)
|
||
.unwrap();
|
||
open(&mut app);
|
||
let scales: Vec<Value> = field(&app, "formats").as_array().unwrap().iter().map(|r| r["scale"].clone()).collect();
|
||
assert_eq!(scales, [json!("64w"), json!("3x"), json!("144ppi")]);
|
||
}
|
||
|
||
/// Persistent frames keep dropdowns open between the pointer press and release.
|
||
struct ScreensFrame {
|
||
ctx: egui::Context,
|
||
time: f64,
|
||
}
|
||
|
||
impl ScreensFrame {
|
||
fn new() -> Self {
|
||
let ctx = egui::Context::default();
|
||
crate::theme::install_fonts(&ctx);
|
||
Self { ctx, time: 0.0 }
|
||
}
|
||
|
||
fn frame(&mut self, app: &mut VectorcraftApp, events: Vec<egui::Event>) -> egui::FullOutput {
|
||
self.time += 0.1;
|
||
let input = egui::RawInput {
|
||
events,
|
||
time: Some(self.time),
|
||
screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1100.0, 900.0))),
|
||
..Default::default()
|
||
};
|
||
let mut out = self.ctx.run_ui(input, |ui| super::show(app, ui.ctx()));
|
||
out.textures_delta.clear();
|
||
out
|
||
}
|
||
|
||
fn settle(&mut self, app: &mut VectorcraftApp) -> egui::FullOutput {
|
||
self.frame(app, vec![]);
|
||
self.frame(app, vec![])
|
||
}
|
||
|
||
fn asset_panel(&mut self, app: &mut VectorcraftApp, events: Vec<egui::Event>) -> egui::FullOutput {
|
||
self.time += 0.1;
|
||
let input = egui::RawInput {
|
||
events,
|
||
time: Some(self.time),
|
||
screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(236.0, 700.0))),
|
||
..Default::default()
|
||
};
|
||
let mut out = self.ctx.run_ui(input, |ui| crate::panels::show_icon_panel(app, ui, "assetExport"));
|
||
out.textures_delta.clear();
|
||
out
|
||
}
|
||
|
||
fn click(&mut self, app: &mut VectorcraftApp, at: egui::Pos2) -> egui::FullOutput {
|
||
let button = |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() };
|
||
self.frame(app, vec![egui::Event::PointerMoved(at)]);
|
||
self.frame(app, vec![button(true)]);
|
||
self.frame(app, vec![button(false)]);
|
||
self.settle(app)
|
||
}
|
||
|
||
fn click_label(&mut self, app: &mut VectorcraftApp, label: &str) -> egui::FullOutput {
|
||
let out = self.settle(app);
|
||
self.click(app, screen_text_at(&out, label))
|
||
}
|
||
|
||
fn choose(&mut self, app: &mut VectorcraftApp, current: &str, next: &str) {
|
||
let out = self.click_label(app, current);
|
||
self.click(app, screen_text_at(&out, next));
|
||
}
|
||
}
|
||
|
||
fn screen_text_at(out: &egui::FullOutput, label: &str) -> egui::Pos2 {
|
||
fn find(shape: &egui::Shape, label: &str) -> Option<egui::Pos2> {
|
||
match shape {
|
||
egui::Shape::Text(t) if t.galley.text().trim() == label => Some(t.visual_bounding_rect().center()),
|
||
egui::Shape::Vec(v) => v.iter().find_map(|s| find(s, label)),
|
||
_ => None,
|
||
}
|
||
}
|
||
out.shapes.iter().find_map(|s| find(&s.shape, label)).unwrap_or_else(|| panic!("missing {label}: {}", crate::tests_labels::shapes_text(out)))
|
||
}
|
||
|
||
/// First-directory entries of the big-endian TIFF chosen below: tag, type, count, inline value.
|
||
fn big_tiff_tags(bytes: &[u8]) -> Vec<(u16, u16, u32, u16)> {
|
||
assert_eq!(&bytes[..4], b"MM\0*");
|
||
let short = |at| u16::from_be_bytes(bytes[at..at + 2].try_into().unwrap());
|
||
let long = |at| u32::from_be_bytes(bytes[at..at + 4].try_into().unwrap());
|
||
let dir = long(4) as usize;
|
||
(0..usize::from(short(dir)))
|
||
.map(|i| {
|
||
let at = dir + 2 + 12 * i;
|
||
(short(at), short(at + 2), long(at + 4), short(at + 8))
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
/// Locate the gear from the painted Formats label and its production column width.
|
||
fn screen_format_gear(out: &egui::FullOutput, width: f32) -> egui::Pos2 {
|
||
fn label(shape: &egui::Shape) -> Option<egui::Rect> {
|
||
match shape {
|
||
egui::Shape::Text(t) if t.galley.text().trim() == "Formats:" => Some(t.visual_bounding_rect()),
|
||
egui::Shape::Vec(shapes) => shapes.iter().find_map(label),
|
||
_ => None,
|
||
}
|
||
}
|
||
let label = out.shapes.iter().find_map(|s| label(&s.shape)).expect("painted Formats row");
|
||
// The existing 24 pt gear is right-aligned in the 350 pt dialog or 236 pt panel.
|
||
egui::pos2(label.left() + width - 12.0, label.center().y)
|
||
}
|
||
|
||
fn saved_tiff_alias_gear(alias: &str, asset_panel: bool) {
|
||
let (mut app, log) = app(1, true);
|
||
app.run("paint.setStroke", json!({"none": true})).unwrap();
|
||
app.run("object.convertDocumentColorMode", json!({"mode": "cmyk"})).unwrap();
|
||
if asset_panel {
|
||
app.run("assets.add", json!({"name": "Badge"})).unwrap();
|
||
}
|
||
let rows = json!([{"format": alias, "scale": "2x", "suffix": "@2x", "antiAlias": "none"}]);
|
||
app.run(
|
||
"assets.settings.set",
|
||
json!({
|
||
"formats": rows.clone(),
|
||
"settings": {
|
||
"tiff": {"colorModel": "rgb", "byteOrder": "little", "lzw": false, "embedIcc": false, "antiAlias": "type"},
|
||
"png": {"interlaced": true}
|
||
}
|
||
}),
|
||
)
|
||
.unwrap();
|
||
let saved_before = app.session.active().unwrap().doc.export_settings.clone();
|
||
if !asset_panel {
|
||
open(&mut app);
|
||
}
|
||
let mut h = ScreensFrame::new();
|
||
let draw = |h: &mut ScreensFrame, app: &mut VectorcraftApp, events| {
|
||
if asset_panel { h.asset_panel(app, events) } else { h.frame(app, events) }
|
||
};
|
||
draw(&mut h, &mut app, vec![]);
|
||
let out = draw(&mut h, &mut app, vec![]);
|
||
let gear = screen_format_gear(&out, if asset_panel { 236.0 } else { 350.0 });
|
||
draw(&mut h, &mut app, vec![egui::Event::PointerMoved(gear)]);
|
||
for pressed in [true, false] {
|
||
draw(
|
||
&mut h,
|
||
&mut app,
|
||
vec![egui::Event::PointerButton { pos: gear, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }],
|
||
);
|
||
}
|
||
assert_eq!(field(&app, "__settings"), "tiff", "gear for {alias}, asset panel={asset_panel}");
|
||
assert_eq!(field(&app, "tab"), if asset_panel { "assets" } else { "artboards" });
|
||
let out = h.settle(&mut app);
|
||
for label in ["Format Settings", "TIFF", "Color Model:", "RGB", "Byte Order:", "Little-endian (II)", "LZW Compression", "Embed ICC Profile"] {
|
||
screen_text_at(&out, label);
|
||
}
|
||
assert_eq!(field(&app, "formats"), rows, "opening the gear preserves the saved spelling and row options");
|
||
assert_eq!(app.session.active().unwrap().doc.export_settings, saved_before, "opening settings does not save them");
|
||
let mut expected = field(&app, "settings");
|
||
assert_eq!(expected["tiff"]["colorModel"], "rgb", "saved RGB overrides the CMYK document default");
|
||
assert_eq!(expected["tiff"]["lzw"], false);
|
||
assert_eq!(expected["tiff"]["embedIcc"], false);
|
||
assert_eq!(expected["tiff"]["antiAlias"], "type");
|
||
assert_eq!(expected["png"]["interlaced"], true);
|
||
h.choose(&mut app, "Little-endian (II)", "Big-endian (MM)");
|
||
expected["tiff"]["byteOrder"] = json!("big");
|
||
assert_eq!(field(&app, "settings"), expected, "only the chosen TIFF option changes");
|
||
h.click_label(&mut app, "Done");
|
||
assert_eq!(field(&app, "formats"), rows);
|
||
h.click_label(&mut app, "Download");
|
||
assert!(app.ui.dialog.is_none(), "{}", app.ui.status);
|
||
{
|
||
let log = log.borrow();
|
||
let [(name, bytes)] = log.as_slice() else { panic!("expected one TIFF download for {alias}") };
|
||
assert_eq!(name, if asset_panel { "Badge@2x.tif" } else { "Artboard-1@2x.tif" });
|
||
let tags = big_tiff_tags(bytes); // The gear's edited canonical settings reach the aliased row.
|
||
assert!(tags.contains(&(259, 3, 1, 1)), "saved uncompressed option survives");
|
||
assert!(tags.contains(&(262, 3, 1, 2)), "saved RGB survives");
|
||
assert!(!tags.iter().any(|t| t.0 == 34675), "saved profile-off option survives");
|
||
let img = image::load_from_memory_with_format(bytes, image::ImageFormat::Tiff).unwrap();
|
||
assert_eq!((img.width(), img.height()), if asset_panel { (60, 40) } else { (120, 80) });
|
||
}
|
||
let saved = app.run("document.exportSettings", json!({})).unwrap()["settings"].clone();
|
||
assert_eq!(saved["formats"], rows, "export preserves the alias, scale, suffix and row override");
|
||
assert_eq!(saved["settings"], expected, "canonical TIFF edits and other format settings persist");
|
||
open(&mut app);
|
||
assert_eq!(field(&app, "formats"), rows);
|
||
assert_eq!(field(&app, "settings"), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn tiff_saved_alias_gear_opens_and_edits_settings_in_screens() {
|
||
for alias in ["tiff", "tif", "TIFF", ".tif"] {
|
||
saved_tiff_alias_gear(alias, false);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn tiff_saved_alias_gear_opens_and_edits_settings_in_asset_export() {
|
||
for alias in ["tiff", "tif", "TIFF", ".tif"] {
|
||
saved_tiff_alias_gear(alias, true);
|
||
}
|
||
}
|
||
|
||
#[test]
|
||
fn tiff_dropdown_exports_scaled_artboards_with_options_and_reopens() {
|
||
let (mut app, log) = app(2, true);
|
||
app.run("paint.setStroke", json!({"none": true})).unwrap();
|
||
app.run("paint.setFill", json!({"color": "#c03020"})).unwrap();
|
||
open(&mut app);
|
||
// Begin with an existing PNG row; TIFF must be chosen from the actual popup.
|
||
set(&mut app, "formats", json!([{"format": "png", "scale": "2x", "suffix": "@2x"}]));
|
||
set(&mut app, "prefix", json!("proof-"));
|
||
let mut h = ScreensFrame::new();
|
||
h.choose(&mut app, "PNG", "TIFF");
|
||
assert_eq!(field(&app, "formats"), json!([{"format": "tiff", "scale": "2x", "suffix": "@2x"}]));
|
||
// Leave only the first of two boards checked.
|
||
h.click_label(&mut app, "Artboard 2");
|
||
assert_eq!(field(&app, "boards"), json!([true, false]));
|
||
assert_eq!(field(&app, "range"), json!("1"));
|
||
|
||
// Exercise the settings dropdown independently of the row's format choice.
|
||
set(&mut app, "__settings", json!("png"));
|
||
h.choose(&mut app, "PNG", "TIFF");
|
||
assert_eq!(field(&app, "__settings"), json!("tiff"));
|
||
let settings = field(&app, "settings");
|
||
assert_eq!(settings["tiff"]["colorModel"], "rgb");
|
||
assert_eq!(settings["tiff"]["byteOrder"], "little");
|
||
assert_eq!(settings["tiff"]["lzw"], true);
|
||
assert_eq!(settings["tiff"]["embedIcc"], true);
|
||
h.choose(&mut app, "Little-endian (II)", "Big-endian (MM)");
|
||
h.click_label(&mut app, "LZW Compression");
|
||
h.click_label(&mut app, "Embed ICC Profile");
|
||
let settings = field(&app, "settings");
|
||
assert_eq!(settings["tiff"]["byteOrder"], "big");
|
||
assert_eq!(settings["tiff"]["lzw"], false);
|
||
assert_eq!(settings["tiff"]["embedIcc"], false);
|
||
h.click_label(&mut app, "Done");
|
||
h.click_label(&mut app, "Download");
|
||
assert!(app.ui.dialog.is_none(), "{}", app.ui.status);
|
||
|
||
{
|
||
let log = log.borrow();
|
||
let [(name, bytes)] = log.as_slice() else { panic!("expected one TIFF download") };
|
||
assert_eq!(name, "proof-Artboard-1@2x.tif");
|
||
let tags = big_tiff_tags(bytes);
|
||
assert!(tags.contains(&(259, 3, 1, 1)), "uncompressed as selected");
|
||
assert!(tags.contains(&(262, 3, 1, 2)), "RGB");
|
||
assert!(!tags.iter().any(|t| t.0 == 34675), "ICC profile disabled");
|
||
let img = image::load_from_memory_with_format(bytes, image::ImageFormat::Tiff).unwrap().to_rgba8();
|
||
assert_eq!(img.dimensions(), (120, 80));
|
||
assert_eq!(img.get_pixel(0, 0).0[3], 0, "transparent board corner");
|
||
let inside = img.get_pixel(40, 28).0;
|
||
assert!(inside[0] > inside[1] && inside[3] == 255, "ellipse at the scaled position: {inside:?}");
|
||
}
|
||
|
||
open(&mut app);
|
||
assert_eq!(field(&app, "formats"), json!([{"format": "tiff", "scale": "2x", "suffix": "@2x"}]));
|
||
assert_eq!(field(&app, "boards"), json!([true, false]));
|
||
assert_eq!(field(&app, "prefix"), "proof-");
|
||
assert_eq!(field(&app, "settings")["tiff"], settings["tiff"]);
|
||
// Existing vector and raster choices remain selectable.
|
||
h.choose(&mut app, "TIFF", "SVG");
|
||
assert_eq!(field(&app, "formats")[0]["format"], "svg");
|
||
h.choose(&mut app, "SVG", "PNG");
|
||
assert_eq!(field(&app, "formats")[0]["format"], "png");
|
||
}
|