mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-09 03:49:34 -06:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb1bd1036 | ||
|
|
95471c9c58 |
@@ -415,11 +415,6 @@ impl CaptureScanner {
|
||||
}
|
||||
|
||||
fn read_parts(src: &(impl ByteSource + ?Sized), parts: &[(u64, u32)]) -> Result<Vec<u8>> {
|
||||
// Ranges come from a scan of some source; check them against this one before sizing a buffer.
|
||||
let len = src.len();
|
||||
if parts.iter().any(|&(o, n)| o.checked_add(u64::from(n)).is_none_or(|end| end > len)) {
|
||||
return Err(Error::Invalid("packet range lies outside the source".into()));
|
||||
}
|
||||
let mut v = Vec::with_capacity(parts.iter().map(|p| p.1 as usize).sum());
|
||||
for &(o, n) in parts {
|
||||
let a = v.len();
|
||||
|
||||
@@ -274,13 +274,3 @@ fn a_budget_too_small_to_search_is_spent() {
|
||||
let f = opus_file(6, 312, 0);
|
||||
assert_eq!(scanner.find(&f, 0, f.len() as u64).unwrap(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_packet_checks_its_ranges_against_the_source_before_allocating() {
|
||||
let f = opus_file(10, 312, 500);
|
||||
let o = open(&f).unwrap();
|
||||
let s = o.stream_of(Codec::Opus).unwrap();
|
||||
// The last packet lies past the end of a source cut short: an error, not a read of missing bytes.
|
||||
let short = &f[..f.len() / 2];
|
||||
assert!(matches!(o.read_packet(&short, s, 9), Err(Error::Invalid(_))));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ use crate::xml::{A, El, W};
|
||||
|
||||
const DEG: f64 = 60_000.0;
|
||||
|
||||
/// Distinct path coordinate spaces whose guides are evaluated for one custom geometry; paths in
|
||||
/// further spaces are left out.
|
||||
const MAX_SPACES: usize = 16;
|
||||
|
||||
/// Guide variables for one shape.
|
||||
pub struct Guides {
|
||||
vars: HashMap<String, f64>,
|
||||
@@ -151,14 +155,26 @@ fn pt(g: &Guides, e: &El) -> (f64, f64) {
|
||||
pub fn read_cust_geom(cg: &El, w_emu: f64, h_emu: f64) -> Vec<CustomPath> {
|
||||
let mut out = vec![];
|
||||
let Some(pl) = cg.child("pathLst") else { return out };
|
||||
let mut spaces: HashMap<(u64, u64), Guides> = HashMap::new();
|
||||
// The cap on cached spaces saves guide evaluation: a geometry without guides has none to save.
|
||||
let guided = ["avLst", "gdLst"].iter().any(|n| cg.child(n).is_some_and(|l| l.elements().next().is_some()));
|
||||
for p in pl.children_named("path").take(1000) {
|
||||
let pw = p.f64("w").unwrap_or(0.0);
|
||||
let ph = p.f64("h").unwrap_or(0.0);
|
||||
// Guides are evaluated in the path's own space when it has one.
|
||||
let (gw, gh) = if pw > 0.0 || ph > 0.0 { (pw, ph) } else { (w_emu, h_emu) };
|
||||
let mut g = Guides::new(gw, gh);
|
||||
g.eval_list(cg.child("avLst"));
|
||||
g.eval_list(cg.child("gdLst"));
|
||||
// The guide lists are evaluated once per coordinate space, not once per path.
|
||||
let key = (gw.to_bits(), gh.to_bits());
|
||||
if !spaces.contains_key(&key) {
|
||||
if guided && spaces.len() >= MAX_SPACES {
|
||||
continue;
|
||||
}
|
||||
let mut g = Guides::new(gw, gh);
|
||||
g.eval_list(cg.child("avLst"));
|
||||
g.eval_list(cg.child("gdLst"));
|
||||
spaces.insert(key, g);
|
||||
}
|
||||
let Some(g) = spaces.get(&key) else { continue };
|
||||
let mut d = String::new();
|
||||
let mut cur = (0.0, 0.0);
|
||||
let mut start = (0.0, 0.0);
|
||||
@@ -166,7 +182,7 @@ pub fn read_cust_geom(cg: &El, w_emu: f64, h_emu: f64) -> Vec<CustomPath> {
|
||||
match c.local() {
|
||||
"moveTo" => {
|
||||
if let Some(q) = c.child("pt") {
|
||||
cur = pt(&g, q);
|
||||
cur = pt(g, q);
|
||||
start = cur;
|
||||
d.push_str("M ");
|
||||
num(&mut d, cur.0);
|
||||
@@ -177,7 +193,7 @@ pub fn read_cust_geom(cg: &El, w_emu: f64, h_emu: f64) -> Vec<CustomPath> {
|
||||
}
|
||||
"lnTo" => {
|
||||
if let Some(q) = c.child("pt") {
|
||||
cur = pt(&g, q);
|
||||
cur = pt(g, q);
|
||||
d.push_str("L ");
|
||||
num(&mut d, cur.0);
|
||||
d.push(' ');
|
||||
@@ -186,7 +202,7 @@ pub fn read_cust_geom(cg: &El, w_emu: f64, h_emu: f64) -> Vec<CustomPath> {
|
||||
}
|
||||
}
|
||||
"cubicBezTo" => {
|
||||
let pts: Vec<(f64, f64)> = c.children_named("pt").map(|q| pt(&g, q)).collect();
|
||||
let pts: Vec<(f64, f64)> = c.children_named("pt").map(|q| pt(g, q)).collect();
|
||||
if let [a, b, e] = pts.as_slice() {
|
||||
d.push_str("C ");
|
||||
for (x, y) in [a, b, e] {
|
||||
@@ -199,7 +215,7 @@ pub fn read_cust_geom(cg: &El, w_emu: f64, h_emu: f64) -> Vec<CustomPath> {
|
||||
}
|
||||
}
|
||||
"quadBezTo" => {
|
||||
let pts: Vec<(f64, f64)> = c.children_named("pt").map(|q| pt(&g, q)).collect();
|
||||
let pts: Vec<(f64, f64)> = c.children_named("pt").map(|q| pt(g, q)).collect();
|
||||
if let [a, e] = pts.as_slice() {
|
||||
d.push_str("Q ");
|
||||
for (x, y) in [a, e] {
|
||||
@@ -470,6 +486,29 @@ mod tests {
|
||||
assert_eq!(paths[1].d, "M 1 1 Q 2 2 3 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn paths_share_guides_and_distinct_spaces_are_capped() {
|
||||
let path = |w: usize| format!(r#"<a:path w="{w}" h="10"><a:moveTo><a:pt x="mid" y="1"/></a:moveTo></a:path>"#);
|
||||
let paths: String = (1..=40).map(|i| path(10 * i)).collect();
|
||||
let x = format!(
|
||||
r#"<a:custGeom xmlns:a="x"><a:gdLst><a:gd name="mid" fmla="*/ w 1 2"/></a:gdLst><a:pathLst>{paths}{}</a:pathLst></a:custGeom>"#,
|
||||
path(10)
|
||||
);
|
||||
let d = parse(x.as_bytes()).unwrap();
|
||||
let out = read_cust_geom(&d.root, 1.0, 1.0);
|
||||
assert_eq!(out.len(), MAX_SPACES + 1, "16 spaces plus the repeat of the first");
|
||||
assert!(out[0].d.starts_with("M 5 1"), "{}", out[0].d);
|
||||
assert!(out[MAX_SPACES].d.starts_with("M 5 1"), "{}", out[MAX_SPACES].d);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guide_free_geometries_are_not_capped_by_space() {
|
||||
let paths: String = (1..=40).map(|i| format!(r#"<a:path w="{}" h="10"><a:moveTo><a:pt x="1" y="1"/></a:moveTo></a:path>"#, 10 * i)).collect();
|
||||
let x = format!(r#"<a:custGeom xmlns:a="x"><a:avLst/><a:gdLst/><a:pathLst>{paths}</a:pathLst></a:custGeom>"#);
|
||||
let d = parse(x.as_bytes()).unwrap();
|
||||
assert_eq!(read_cust_geom(&d.root, 1.0, 1.0).len(), 40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_then_read_path() {
|
||||
let p = CustomPath { w: 100.0, h: 50.0, d: "M 0 0 L 100 0 C 100 10 90 50 50 50 Q 0 50 0 0 Z".into(), fill: FillMode::Norm, stroke: true };
|
||||
|
||||
+1
-7
@@ -18,13 +18,7 @@ fn walk(dir: &Path, c: &mut Counts) {
|
||||
let p = e.path();
|
||||
let name = e.file_name();
|
||||
let name = name.to_string_lossy();
|
||||
// `file_type` does not follow links, so a directory link is not walked (and aliases are
|
||||
// not counted twice).
|
||||
let Ok(kind) = e.file_type() else { continue };
|
||||
if kind.is_symlink() {
|
||||
continue;
|
||||
}
|
||||
if kind.is_dir() {
|
||||
if p.is_dir() {
|
||||
// Nested fuzz workspaces and build output are not part of the crate.
|
||||
if name != "target" && name != "fuzz" && !name.starts_with('.') {
|
||||
walk(&p, c);
|
||||
|
||||
Reference in New Issue
Block a user