AF.5: cap dash patterns at 64 lengths

A line style's `Patn` was copied twice (filtered, then scaled) whatever its
length, before any budget saw it. Patterns longer than 64 lengths now import
as solid lines, with a warning.

Addresses CodeRabbit's third review on #3548.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
dwetscher
2026-10-22 18:44:39 +07:00
committed by bflatastic
co-authored by Claude Opus 5.5
parent b3ccdf01a7
commit 2d7004f972
2 changed files with 20 additions and 0 deletions
+7
View File
@@ -126,6 +126,9 @@ pub struct Stroke {
/// Gradient stops per gradient.
const MAX_STOPS: usize = 1024;
/// Dash and gap lengths per dash pattern (a pattern has a few; strokes and text runs copy theirs).
const MAX_DASHES: usize = 64;
fn floats(v: &Value) -> Option<Vec<f64>> {
match v {
Value::Floats(f) => Some(f.clone()),
@@ -319,6 +322,10 @@ pub(crate) fn stroke(r: &mut Reader, pen: ObjId, ldsc: ObjId, local: Affine, beh
_ => Align::Center,
};
let dash = match s.field(lsty, b"Patn") {
Some(Value::Array(a)) if a.len() > MAX_DASHES => {
r.warn("dash patterns of more than 64 lengths (imported as solid lines)");
None
}
Some(Value::Array(a)) if data.get(10) == Some(&2) || !a.is_empty() => {
let mut v: Vec<f64> = a.iter().filter_map(|x| if let Value::Float(f) = x { Some(*f) } else { None }).collect();
while v.len() >= 2 && v.iter().rev().take(2).all(|x| *x == 0.0) {
+13
View File
@@ -391,6 +391,19 @@ fn master_pages_of_embedded_documents_are_left_out() {
named(&loaded.doc, "Page box");
}
/// A dash pattern has a few lengths; a crafted one of thousands would be copied with every stroke.
#[test]
fn dash_patterns_of_more_than_64_lengths_import_as_solid_lines() {
use vectorcraft_affinity::model::Kind;
for (lengths, dashed) in [(64, true), (65, false)] {
let dash: Vec<f64> = (0..lengths).map(|i| 1.0 + f64::from(i)).collect();
let d = vectorcraft_affinity::read(&canvas_with(vec![stroked(10, 2, &dash)], None), vectorcraft_affinity::Limits::default()).unwrap();
let Kind::Shape { strokes, .. } = &d.spreads[0].nodes[0].kind else { panic!("{:?}", d.spreads[0].nodes[0].kind) };
assert_eq!(strokes[0].dash.as_ref().map(|(d, _)| d.len()), dashed.then_some(64));
assert_eq!(d.warnings.iter().any(|w| w.contains("dash patterns of more than 64")), !dashed, "{:?}", d.warnings);
}
}
#[test]
fn master_pages_show_on_the_pages_that_use_them() {
let loaded = open("master.afpub", &with_master(1));