Map the WMF window onto the placeable box (#2673)

This commit is contained in:
MarekSpodBiedry
2026-10-18 01:11:38 +07:00
committed by bflatastic
parent b5164825d6
commit f999688b41
2 changed files with 39 additions and 1 deletions
+20 -1
View File
@@ -99,6 +99,13 @@ fn window(bytes: &[u8], mut at: usize) -> Option<Rect> {
Some(Rect::new(o.x, o.y, o.x + e.x, o.y + e.y).abs())
}
/// Logical → document for a placeable file whose window (`org`, `ext`) is mapped onto its box `b`;
/// a negative extent flips that axis, as in GDI.
fn window_map(b: Rect, k: f64, org: Point, ext: Point) -> Affine {
let ratio = |box_len: f64, e: f64| if e.abs() > 1e-12 { box_len / e } else { 1.0 };
Affine::scale_non_uniform(k * ratio(b.width(), ext.x), k * ratio(b.height(), ext.y)) * Affine::translate(-org.to_vec2())
}
pub(super) fn play(bytes: &[u8]) -> Result<Imported, String> {
let (start, placeable) = header(bytes).ok_or("the WMF header is damaged")?;
let (frame, inch) = match placeable {
@@ -109,6 +116,9 @@ pub(super) fn play(bytes: &[u8]) -> Result<Imported, String> {
let origin = frame.map_or(Point::ZERO, |f| f.origin());
let artboard = frame.map_or(Rect::new(0.0, 0.0, 1.0, 1.0), |f| Rect::new(0.0, 0.0, f.width() * k, f.height() * k));
let mut p = Player::new(Kind::Wmf, Affine::scale(k) * Affine::translate(-origin.to_vec2()), 25.4 / inch, artboard);
// With a placeable header, the window records map the window onto the box.
let placed = placeable.map(|(b, _)| b).filter(|b| b.width() > 0.0 && b.height() > 0.0);
let mut win = (placed.map_or(Point::ZERO, |b| b.origin()), placed.map_or(Point::ZERO, |b| Point::new(b.width(), b.height())));
let mut at = start;
let mut ended = false;
while let Some(head) = bytes.get(at..at + 6) {
@@ -122,7 +132,16 @@ pub(super) fn play(bytes: &[u8]) -> Result<Imported, String> {
if !matches!(function, 0x0213 | 0x0214) {
p.flush();
}
if record(&mut p, function, rec.get(6..).unwrap_or_default()).is_none() {
if let (Some(b), 0x020B | 0x020C) = (placed, function) {
if let Some(v) = point_yx(&mut Reader::new(rec.get(6..).unwrap_or_default())) {
if function == 0x020B {
win.0 = v;
} else {
win.1 = v;
}
p.dev_to_doc = window_map(b, k, win.0, win.1);
}
} else if record(&mut p, function, rec.get(6..).unwrap_or_default()).is_none() {
p.skip();
}
at += rec.len();
+19
View File
@@ -289,6 +289,25 @@ fn hand_built_wmf_plays_like_gdi() {
assert_eq!(fill_rgb(&p[1]), Some([0, 255, 0]), "the new brush took the freed slot");
}
#[test]
fn wmf_window_is_mapped_onto_the_placeable_box() {
// The box is 1000 × 500 units at 1440 an inch: 50 × 25 pt.
let want = Rect::new(12.5, 6.25, 37.5, 18.75);
// Window 0,0 – 4000 × 2000: the rectangle 1000,500 – 3000,1500 is the middle half.
let ext = vec![(0x020C, le16(&[2000, 4000])), (0x041B, le16(&[1500, 3000, 500, 1000]))];
// Window 10000,20000 – 1000 × 500 (the box): the rectangle 10250,20125 – 10750,20375.
let org = vec![(0x020B, le16(&[20000, 10000])), (0x020C, le16(&[500, 1000])), (0x041B, le16(&[20375, 10750, 20125, 10250]))];
// Window 0,2000 – 4000 × -2000 (y up): the same rectangle, flipped onto the same place.
let flip = vec![(0x020B, le16(&[2000, 0])), (0x020C, le16(&[-2000, 4000])), (0x041B, le16(&[1500, 3000, 500, 1000]))];
for records in [ext, org, flip] {
let back = import(&hand_wmf(1000, 500, &records)).unwrap();
assert!(near(back.document.artboards[0].rect, Rect::new(0.0, 0.0, 50.0, 25.0), 1e-9));
let p = paths(&back.document);
assert_eq!(p.len(), 1);
assert!(near(p[0].geometric_bounds().unwrap(), want, 1e-6), "{:?}", p[0].geometric_bounds());
}
}
#[test]
fn damaged_files_fail_or_import_partly() {
let good = emf(&sample()).bytes;