mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-03 00:39:44 -06:00
* M6: a pasted image the clipboard doesn't carry shows as missing and is named (#1646) The placeholder an import stores for an image it lacks is now striped, not flat grey; the fill keeps the image's name; .fig and paste give a fixed, translated note per image; edit.pasteDesignLayers returns missingImages, the paste's message names those layers, and a paste with notes opens the import report. * M6: the missing-image placeholder's stripes stay clean when scaled, and a .fig gives one note (#1646)
55 lines
2.4 KiB
Rust
55 lines
2.4 KiB
Rust
//! Layers pasted from another design app whose image the clipboard doesn't carry (#1646): the
|
|
//! paste names them (`missingImages`), and they keep their image's name. Design data made by our
|
|
//! own `.fig` export: its canvas alone, as a clipboard carries it, and the whole file with images.
|
|
|
|
use std::io::Read;
|
|
|
|
use base64::Engine as _;
|
|
use serde_json::{Value, json};
|
|
|
|
use crate::Session;
|
|
use crate::params::id_of;
|
|
|
|
/// A `.fig` export of a page with "Photo" (an image fill) and "Plain" (a solid fill).
|
|
fn exported() -> Vec<u8> {
|
|
let mut s = Session::new();
|
|
let photo = id_of(&s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 80, "height": 60})).unwrap()["id"]).unwrap();
|
|
let plain = id_of(&s.execute("shape.rectangle", &json!({"x": 100, "y": 0, "width": 80, "height": 60})).unwrap()["id"]).unwrap();
|
|
let mut png = Vec::new();
|
|
image::RgbaImage::from_pixel(4, 4, image::Rgba([200, 40, 40, 255]))
|
|
.write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png)
|
|
.unwrap();
|
|
let doc = s.doc_mut().unwrap();
|
|
let (hash, _, _) = figcraft_format::add_image(doc, png).unwrap();
|
|
let n = doc.get_mut(photo).unwrap();
|
|
n.name = "Photo".into();
|
|
n.fills = vec![figcraft_color::Paint::image(hash, figcraft_color::ImageScaleMode::Fill)];
|
|
doc.get_mut(plain).unwrap().name = "Plain".into();
|
|
let r = s.execute("file.export", &json!({"format": "fig"})).unwrap();
|
|
base64::engine::general_purpose::STANDARD.decode(r["data"].as_str().unwrap()).unwrap()
|
|
}
|
|
|
|
/// Just the canvas of a `.fig` file: the design data a clipboard carries, without the images.
|
|
fn canvas_only(fig: &[u8]) -> Vec<u8> {
|
|
let mut z = zip::ZipArchive::new(std::io::Cursor::new(fig)).unwrap();
|
|
let mut out = Vec::new();
|
|
z.by_name("canvas.fig").unwrap().read_to_end(&mut out).unwrap();
|
|
out
|
|
}
|
|
|
|
fn paste(data: &[u8]) -> Value {
|
|
let mut s = Session::new();
|
|
s.execute("edit.pasteDesignLayers", &json!({"data": base64::engine::general_purpose::STANDARD.encode(data)})).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn layers_whose_image_the_clipboard_lacks_are_named() {
|
|
let fig = exported();
|
|
let r = paste(&canvas_only(&fig));
|
|
assert_eq!(r["missingImages"], json!(["Photo"]), "{r}");
|
|
assert!(r["warnings"].as_array().unwrap().iter().any(|w| w.as_str().unwrap().contains("missing-image placeholder")), "{r}");
|
|
// With its images, nothing is missing.
|
|
let r = paste(&fig);
|
|
assert!(r.get("missingImages").is_none(), "{r}");
|
|
}
|