mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-12 00:40:57 -06:00
crates/metafile import: EMF and WMF (placeable or not) records play on a small device-context model (objects numbered as GDI does, saved DCs, world transforms and mapping modes, path brackets, the current position) into one artboard (the picture's frame) and one layer: paths with fills and strokes (pens with caps, joins, dashes), clipping groups for clips, images from DIBs (StretchDIBits, BitBlt, StretchBlt, AlphaBlend, StretchDIB, DIBBitBlt) and point type in the font the file names. Lines drawn one after another join into one path; a clip around the whole picture is dropped; objects far outside it are left out; unknown records are skipped with one warning. document.open and file.place read .emf and .wmf through the shared loader (Open and Place filters list them). clipboard.importEmf loads a picture into the clipboard, and Paste takes image/emf from a system clipboard service ahead of text and bitmaps. The import fuzzer covers mutated and random-record metafiles opened, placed and pasted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
53 lines
2.3 KiB
Rust
53 lines
2.3 KiB
Rust
//! EMF and WMF for every path that writes or reads Windows metafiles (`vectorcraft_metafile`):
|
|
//! `document.export {format: emf|wmf}` and Export As (one picture of an artboard, or one per
|
|
//! artboard with Use Artboards; raster effects as images, rendered at the document's raster
|
|
//! effects resolution), `document.open` and File → Place (one artboard, the picture's frame).
|
|
|
|
use serde_json::Value;
|
|
use vectorcraft_doc::Document;
|
|
use vectorcraft_metafile::Kind;
|
|
|
|
use super::super::*;
|
|
use super::{ArtboardPick, Encoded, FormatOption};
|
|
|
|
const C: &str = "document.export";
|
|
|
|
/// The options `document.formats` lists for EMF and WMF.
|
|
pub(super) const OPTIONS: &[FormatOption] = &[super::ARTBOARD, super::ARTBOARDS, super::RANGE, super::USE_ARTBOARDS];
|
|
|
|
/// Encode `doc` as `kind`: the chosen artboard (default the first), or with `use_artboards` one
|
|
/// picture per chosen artboard (default all).
|
|
pub(super) fn encode(doc: &Document, p: &Value, use_artboards: Option<bool>, kind: Kind) -> Result<Encoded> {
|
|
let f = super::format(kind.id()).ok_or_else(|| bad(C, "no metafile format"))?;
|
|
let pick: ArtboardPick = super::encode::options(f, p)?;
|
|
let n = doc.artboards.len();
|
|
let boards = match use_artboards {
|
|
Some(true) => pick.resolve(n).map_err(|e| bad(C, e))?.unwrap_or_else(|| (0..n).collect()),
|
|
_ => vec![pick.one(n).map_err(|e| bad(C, e))?],
|
|
};
|
|
// Raster effects (shadows, glows, blurs) as images, as PDF export does.
|
|
let flat = crate::flatten_raster_effects(doc);
|
|
let src = flat.as_ref().unwrap_or(doc);
|
|
let mut enc = Encoded::default();
|
|
for b in boards {
|
|
let region = doc.artboards.get(b).ok_or_else(|| bad(C, format!("no artboard {}", b + 1)))?.rect;
|
|
let out = vectorcraft_metafile::export(src, region, kind).map_err(|e| bad(C, e))?;
|
|
enc.files.push((Some(b), out.bytes));
|
|
for w in out.warnings {
|
|
if !enc.warnings.contains(&w) {
|
|
enc.warnings.push(w);
|
|
}
|
|
}
|
|
}
|
|
if enc.files.is_empty() {
|
|
return Err(bad(C, "the document has no artboard"));
|
|
}
|
|
Ok(enc)
|
|
}
|
|
|
|
/// A metafile's document and the import's warnings.
|
|
pub(super) fn import(bytes: &[u8]) -> Result<(Document, Vec<String>)> {
|
|
let i = vectorcraft_metafile::import(bytes).map_err(super::err)?;
|
|
Ok((i.document, i.warnings))
|
|
}
|