OLE2FRAME: MOVE rewrites the corners in the OLE data only when they are the frame's corners (review on #2587)

The data layout is only known when its corners match the frame's groups; any other
header is left byte-identical while the tags still move. Test: move_leaves_ole_data_that_is_not_the_frames_corners_alone.
This commit is contained in:
SBaksa
2026-11-01 23:32:58 +11:00
parent 0c486e337a
commit 7fdda542d3
2 changed files with 34 additions and 2 deletions
+16 -2
View File
@@ -51,7 +51,7 @@ const OLE_CORNERS: usize = 12;
/// Move the four corners at the start of the OLE data (the 310 groups' hex, in order) by `m`,
/// keeping z and every tag's length. Data that is not valid hex or is too short is left alone.
fn ole_data_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3) {
fn ole_data_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3, frame: [f64; 4]) {
let idx: Vec<usize> = outside_app(tags).into_iter().filter(|&i| tags.get(i).is_some_and(|t| t.code == 310)).collect();
let mut bytes: Vec<u8> = Vec::new();
let mut lens: Vec<usize> = Vec::new();
@@ -71,6 +71,19 @@ fn ole_data_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3) {
if bytes.len() < end {
return;
}
// Only data whose corners are this frame's corners (UL, UR, LR, LL) has the layout above; any
// other header is not ours to rewrite.
let [x1, y1, x2, y2] = frame;
let want = [(x1, y1), (x2, y1), (x2, y2), (x1, y2)];
let near = |a: f64, b: f64| (a - b).abs() <= 1e-9 * a.abs().max(b.abs()).max(1.0);
for (c, (wx, wy)) in want.iter().enumerate() {
let at = OLE_HEADER + c * 24;
let rd = |o: usize| bytes.get(o..o + 8).and_then(|s| <[u8; 8]>::try_from(s).ok()).map(f64::from_le_bytes);
let (Some(x), Some(y)) = (rd(at), rd(at + 8)) else { return };
if !(near(x, *wx) && near(y, *wy)) {
return;
}
}
let mut out = bytes.clone();
for c in 0..4 {
let at = OLE_HEADER + c * 24;
@@ -103,6 +116,7 @@ fn ole_data_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3) {
/// left alone.
pub fn ole_frame_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3) {
let Some([(ix1, x1), (iy1, y1), (ix2, x2), (iy2, y2)]) = ole_corner_groups(tags) else { return };
let frame = [x1, y1, x2, y2];
let (a, b) = (m.apply(Vec2::new(x1, y1)), m.apply(Vec2::new(x2, y2)));
if !(a.x.is_finite() && a.y.is_finite() && b.x.is_finite() && b.y.is_finite()) {
return;
@@ -112,7 +126,7 @@ pub fn ole_frame_transform(tags: &mut [crate::RawTag], m: &cadcraft_geom::Mat3)
t.value = v.to_string();
}
}
ole_data_transform(tags, m);
ole_data_transform(tags, m, frame);
}
/// Whether `m` can move an OLE2FRAME: only a translation (move, copy, stretch of the whole frame).
@@ -124,3 +124,21 @@ fn move_leaves_malformed_or_short_ole_data_alone() {
assert_eq!(corners(&s)[0].1, "40", "the corner groups still move");
}
}
/// OLE data whose bytes are not this frame's corners (an unknown header, so the layout does not
/// apply) is not rewritten by MOVE; the frame's own corner groups still move (review on #2587).
#[test]
fn move_leaves_ole_data_that_is_not_the_frames_corners_alone() {
let mut blob = vec![9u8, 9];
for (x, y) in [(1.0f64, 2.0f64), (3.0, 2.0), (3.0, 4.0), (1.0, 4.0)] {
for v in [x, y, 7.0] {
blob.extend_from_slice(&v.to_le_bytes());
}
}
let hex: String = blob.iter().map(|b| format!("{b:02X}")).collect();
let (mut s, h) = data_session(&[40, 100], Some(&hex));
let before = data_tags(&s);
s.execute("move", &json!({ "handles": [h], "delta": [10, 5] })).unwrap();
assert_eq!(data_tags(&s), before);
assert_eq!(corners(&s)[0].1, "40", "the corner groups still move");
}