mirror of
https://github.com/storytold/designcraft.git
synced 2026-10-24 10:16:59 -06:00
Merge pull request #1675 from Silvertree2010/fix/1589-audit-dc-08-non-finite-idml-numb
Keep non-finite IDML numbers out of imported documents
This commit is contained in:
+21
-10
@@ -387,13 +387,10 @@ fn mojikumi_row(e: &El) -> Result<designcraft_doc::cjk::MojikumiAki> {
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
/// The numbers in a whitespace-separated list; empty when any number is NaN or infinite (JSON
|
||||
/// can't store them, and dropping just those would shift the rest).
|
||||
fn nums(s: &str) -> Vec<f64> {
|
||||
s.split_whitespace().filter_map(|v| v.parse().ok()).collect()
|
||||
}
|
||||
|
||||
/// Like [`nums`], but empty when any number is NaN or infinite.
|
||||
fn finite_nums(s: &str) -> Vec<f64> {
|
||||
let v = nums(s);
|
||||
let v: Vec<f64> = s.split_whitespace().filter_map(|v| v.parse().ok()).collect();
|
||||
if v.iter().all(|x| x.is_finite()) { v } else { Vec::new() }
|
||||
}
|
||||
|
||||
@@ -895,6 +892,15 @@ impl<'r> Importer<'r> {
|
||||
_ if v.len() >= 4 => Color::cmyk((v[0] / 100.0) as f32, (v[1] / 100.0) as f32, (v[2] / 100.0) as f32, (v[3] / 100.0) as f32),
|
||||
_ => return None,
|
||||
};
|
||||
// Huge values (1e300) overflow `f32` to infinity.
|
||||
let parts = match c {
|
||||
Color::Rgb { r, g, b } => [r, g, b, 0.0],
|
||||
Color::Cmyk { c, m, y, k } => [c, m, y, k],
|
||||
Color::Gray { k } => [k, 0.0, 0.0, 0.0],
|
||||
};
|
||||
if !parts.iter().all(|v| v.is_finite()) {
|
||||
return None;
|
||||
}
|
||||
let ty = if e.get("Model") == Some("Spot") { ColorType::Spot } else { ColorType::Process };
|
||||
Some((c, ty))
|
||||
}
|
||||
@@ -947,14 +953,14 @@ impl<'r> Importer<'r> {
|
||||
let (Some(id), Some(name)) = (e.get("Self"), e.get("Name")) else { continue };
|
||||
let kind = match e.local() {
|
||||
"StripedStrokeStyle" => designcraft_doc::StrokeType::Stripes {
|
||||
bands: finite_nums(e.get("StripeArray").unwrap_or(""))
|
||||
bands: nums(e.get("StripeArray").unwrap_or(""))
|
||||
.as_chunks::<2>()
|
||||
.0
|
||||
.iter()
|
||||
.map(|c| (c[0] / 100.0, (c[1] - c[0]) / 100.0))
|
||||
.collect(),
|
||||
},
|
||||
"DashedStrokeStyle" => designcraft_doc::StrokeType::Dashed { pattern: finite_nums(e.get("DashArray").unwrap_or("")) },
|
||||
"DashedStrokeStyle" => designcraft_doc::StrokeType::Dashed { pattern: nums(e.get("DashArray").unwrap_or("")) },
|
||||
_ => designcraft_doc::StrokeType::Dotted,
|
||||
};
|
||||
let _ = id;
|
||||
@@ -2856,7 +2862,7 @@ impl<'r> Importer<'r> {
|
||||
{
|
||||
it.fill.gradient_angle = Some(a);
|
||||
}
|
||||
let start: Option<Vec<f64>> = e.prop("GradientFillStart").map(|s| finite_nums(&s));
|
||||
let start: Option<Vec<f64>> = e.prop("GradientFillStart").map(|s| nums(&s));
|
||||
if let (Some([x, y]), Some(len)) = (start.as_deref().and_then(|v| <[f64; 2]>::try_from(v).ok()), e.num("GradientFillLength"))
|
||||
&& len > 0.0
|
||||
{
|
||||
@@ -2874,7 +2880,7 @@ impl<'r> Importer<'r> {
|
||||
{
|
||||
it.stroke.gradient_angle = Some(a);
|
||||
}
|
||||
let start: Option<Vec<f64>> = e.prop("GradientStrokeStart").map(|s| finite_nums(&s));
|
||||
let start: Option<Vec<f64>> = e.prop("GradientStrokeStart").map(|s| nums(&s));
|
||||
if let (Some([x, y]), Some(len)) = (start.as_deref().and_then(|v| <[f64; 2]>::try_from(v).ok()), e.num("GradientStrokeLength"))
|
||||
&& len > 0.0
|
||||
{
|
||||
@@ -3702,6 +3708,11 @@ fn path_capped(pg: &El, max: usize) -> PathData {
|
||||
let closed = g.get("PathOpen") != Some("true");
|
||||
let mut anchors = Vec::new();
|
||||
if let Some(arr) = g.find("PathPointArray") {
|
||||
// A NaN or infinite point drops its subpath (JSON can't store it).
|
||||
let non_finite = |s: Option<&str>| s.is_some_and(|s| s.split_whitespace().any(|v| v.parse::<f64>().is_ok_and(|x| !x.is_finite())));
|
||||
if arr.find_all("PathPointType").take(left).any(|p| ["Anchor", "LeftDirection", "RightDirection"].iter().any(|k| non_finite(p.get(k)))) {
|
||||
continue;
|
||||
}
|
||||
for p in arr.find_all("PathPointType").take(left) {
|
||||
let Some(a) = parse_point(p.get("Anchor")) else { continue };
|
||||
let hin = parse_point(p.get("LeftDirection")).unwrap_or(a);
|
||||
|
||||
@@ -4402,3 +4402,39 @@ fn exports_graphic_in_table_cell() {
|
||||
assert!(cell.contains("<Image "), "{story}");
|
||||
assert!(cell.contains("ClipContentToCell=\"true\""), "{story}");
|
||||
}
|
||||
|
||||
/// Non-finite numbers in colour values, page bounds and item transforms (NaN, infinities and
|
||||
/// values like 1e300 that overflow `f32`) never reach the model: JSON would write them as null.
|
||||
#[test]
|
||||
fn non_finite_number_lists_never_reach_the_model() {
|
||||
let graphic = GRAPHIC.replace(r#"ColorValue="255 0 0""#, r#"ColorValue="NaN 1e300 inf""#);
|
||||
let spread = SPREAD
|
||||
.replace(r#"Self="p2" AppliedMaster="m1" GeometricBounds="0 0 700 500""#, r#"Self="p2" AppliedMaster="m1" GeometricBounds="0 0 NaN 500""#)
|
||||
.replace(r#"ItemTransform="1 0 0 1 50 -300""#, r#"ItemTransform="1 0 0 1 inf -300""#);
|
||||
let bytes = zip_files(&[
|
||||
("designmap.xml", DESIGNMAP),
|
||||
("Resources/Graphic.xml", &graphic),
|
||||
("Resources/Styles.xml", STYLES),
|
||||
("Resources/Preferences.xml", PREFS),
|
||||
("MasterSpreads/MasterSpread_m1.xml", MASTER),
|
||||
("Spreads/Spread_sp1.xml", &spread),
|
||||
("Stories/Story_s1.xml", STORY),
|
||||
]);
|
||||
let doc = import_idml(&bytes).unwrap();
|
||||
for s in &doc.swatches {
|
||||
if let SwatchValue::Color { color, .. } | SwatchValue::Paper { color } = &s.value {
|
||||
let parts: Vec<f32> = match *color {
|
||||
Color::Rgb { r, g, b } => vec![r, g, b],
|
||||
Color::Cmyk { c, m, y, k } => vec![c, m, y, k],
|
||||
Color::Gray { k } => vec![k],
|
||||
};
|
||||
assert!(parts.iter().all(|v| v.is_finite()), "swatch {} is not finite: {color:?}", s.name);
|
||||
}
|
||||
}
|
||||
for sp in doc.spreads.iter().chain(&doc.parents) {
|
||||
for p in &sp.pages {
|
||||
assert!(p.width.is_finite() && p.height.is_finite() && p.x.is_finite(), "page {p:?}");
|
||||
}
|
||||
}
|
||||
assert_eq!(doc.non_finite_item(), None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user