mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-30 21:47:00 -06:00
@@ -50,6 +50,8 @@ const MAX_CHARS: usize = 32_768;
|
||||
const MAX_OBJECT_BYTES: usize = 64 * 1024 * 1024;
|
||||
/// Nesting depth of region nodes.
|
||||
const MAX_REGION_DEPTH: usize = 32;
|
||||
/// Region nodes and path points parsed together, including nodes in sibling branches.
|
||||
const MAX_REGION_WORK: usize = MAX_POINTS;
|
||||
|
||||
const TA_RIGHT: u32 = 2;
|
||||
const TA_CENTER: u32 = 6;
|
||||
@@ -1106,12 +1108,14 @@ fn path(r: Bytes) -> Option<Vec<Op>> {
|
||||
/// A region object as a convex area in world units (non-convex parts become their bounding
|
||||
/// rectangle).
|
||||
fn region(r: Bytes) -> Option<Obj> {
|
||||
region_node(r, 8, 0).map(|(a, _)| Obj::Region(a))
|
||||
let mut work = 0;
|
||||
region_node(r, 8, 0, &mut work).map(|(a, _)| Obj::Region(a))
|
||||
}
|
||||
|
||||
/// One region node at `off`: its area and its length in bytes.
|
||||
fn region_node(r: Bytes, off: usize, depth: usize) -> Option<(Area, usize)> {
|
||||
if depth > MAX_REGION_DEPTH {
|
||||
fn region_node(r: Bytes, off: usize, depth: usize, work: &mut usize) -> Option<(Area, usize)> {
|
||||
*work = work.checked_add(1)?;
|
||||
if depth > MAX_REGION_DEPTH || *work > MAX_REGION_WORK {
|
||||
return None;
|
||||
}
|
||||
match r.u32(off)? {
|
||||
@@ -1121,14 +1125,23 @@ fn region_node(r: Bytes, off: usize, depth: usize) -> Option<(Area, usize)> {
|
||||
}
|
||||
0x1000_0001 => {
|
||||
let size = r.u32(off + 4)? as usize;
|
||||
let area = path(r.tail(off + 8)?).map(|ops| area_of_ops(&ops)).unwrap_or_default();
|
||||
if size < 12 {
|
||||
return None;
|
||||
}
|
||||
let data = Bytes(r.slice(off + 8, size)?);
|
||||
let points = (data.u32(4)? as usize).min(MAX_POINTS);
|
||||
*work = work.checked_add(points)?;
|
||||
if *work > MAX_REGION_WORK {
|
||||
return None;
|
||||
}
|
||||
let area = area_of_ops(&path(data)?);
|
||||
Some((Some(area), 8usize.checked_add(size)?))
|
||||
}
|
||||
0x1000_0002 => Some((Some(Vec::new()), 4)),
|
||||
0x1000_0003 => Some((None, 4)),
|
||||
mode @ 0..=5 => {
|
||||
let (a, la) = region_node(r, off + 4, depth + 1)?;
|
||||
let (b, lb) = region_node(r, off.checked_add(4 + la)?, depth + 1)?;
|
||||
let (a, la) = region_node(r, off + 4, depth + 1, work)?;
|
||||
let (b, lb) = region_node(r, off.checked_add(4 + la)?, depth + 1, work)?;
|
||||
let out = match mode {
|
||||
0 => b,
|
||||
1 => area_intersect(a, b),
|
||||
|
||||
@@ -1875,6 +1875,34 @@ fn emfplus_region_clip_intersects_rectangles() {
|
||||
assert_eq!((p.width, p.height), (20.0, 20.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emfplus_region_path_cannot_read_into_its_sibling() {
|
||||
let rect = |r: [f32; 4]| cat(vec![h32(0x1000_0000), f32s(&r)]);
|
||||
// The path declares only its 12-byte header, but claims three points. The following
|
||||
// rectangle must not supply those points or be parsed twice as the next node.
|
||||
let path = cat(vec![h32(0x1000_0001), h32(12), h32(0), h32(3), h32(0)]);
|
||||
let region = cat(vec![h32(0xDBC0_1002), h32(2), h32(1), path, rect([10.0, 10.0, 20.0, 20.0])]);
|
||||
let fill = prec(0x400A, 0x8000, &cat(vec![h32(0xFF00_0000), h32(1), f32s(&[0.0, 0.0, 100.0, 100.0])]));
|
||||
let p = plus_pic(vec![prec(0x4008, 0x0400 | 6, ®ion), prec(0x4034, 6, &[]), fill]);
|
||||
assert_eq!(p.items.len(), 1);
|
||||
assert_eq!((p.width, p.height), (100.0, 100.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emfplus_region_path_keeps_its_shape() {
|
||||
let mut path_data = cat(vec![h32(0), h32(4), h32(0x4000)]);
|
||||
for (x, y) in [(10i16, 10i16), (30, 10), (30, 30), (10, 30)] {
|
||||
path_data.extend(x.to_le_bytes());
|
||||
path_data.extend(y.to_le_bytes());
|
||||
}
|
||||
path_data.extend([0, 1, 1, 0x81]);
|
||||
let path_node = cat(vec![h32(0x1000_0001), h32(path_data.len() as u32), path_data]);
|
||||
let region = cat(vec![h32(0xDBC0_1002), h32(1), path_node]);
|
||||
let fill = prec(0x400A, 0x8000, &cat(vec![h32(0xFF00_0000), h32(1), f32s(&[0.0, 0.0, 100.0, 100.0])]));
|
||||
let p = plus_pic(vec![prec(0x4008, 0x0400 | 6, ®ion), prec(0x4034, 6, &[]), fill]);
|
||||
assert_eq!((p.width, p.height), (20.0, 20.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn emfplus_pie_and_arc() {
|
||||
// A quarter pie from 0 to 90 degrees in a 0..100 circle: centre, then (100, 50) round to (50, 100).
|
||||
|
||||
Reference in New Issue
Block a user