diff --git a/crates/color/src/ase.rs b/crates/color/src/ase.rs index ecae906f..c20c249e 100644 --- a/crates/color/src/ase.rs +++ b/crates/color/src/ase.rs @@ -69,7 +69,7 @@ pub fn read(bytes: &[u8]) -> Result, AseError> { r.take(4)?; // version let blocks = r.u32()?; let (mut out, mut colors) = (Vec::new(), 0usize); - for _ in 0..blocks { + 'blocks: for _ in 0..blocks { let kind = r.u16()?; let len = r.u32()? as usize; let body = r.take(len)?; @@ -84,6 +84,19 @@ pub fn read(bytes: &[u8]) -> Result, AseError> { let mut b = R { b: body, i: 0 }; let name = b.name()?; let model = b.take(4)?.to_vec(); + let n = match &model[..] { + b"CMYK" => 4, + b"RGB " | b"LAB " => 3, + b"Gray" => 1, + _ => continue, + }; + // A NaN or infinite component cannot be stored in a document: leave that swatch out. + let mut peek = R { b: body, i: b.i }; + for _ in 0..n { + if !peek.f32()?.is_finite() { + continue 'blocks; + } + } let color = match &model[..] { b"CMYK" => Color::Cmyk { c: b.f32()?, m: b.f32()?, y: b.f32()?, k: b.f32()? }, b"RGB " => Color::Rgb { r: b.f32()?, g: b.f32()?, b: b.f32()? }, @@ -91,6 +104,9 @@ pub fn read(bytes: &[u8]) -> Result, AseError> { b"LAB " => { let (l, a, bb) = (b.f32()?, b.f32()?, b.f32()?); let [r, g, bl] = cms::lab::lab_to_srgb(cms::Lab { l: l * 100.0, a, b: bb }); + if ![r, g, bl].iter().all(|v| v.is_finite()) { + continue; + } Color::Rgb { r: r.clamp(0.0, 1.0), g: g.clamp(0.0, 1.0), b: bl.clamp(0.0, 1.0) } } _ => continue, @@ -214,4 +230,43 @@ mod tests { assert_eq!(read(&write(&sw)).err(), Some(AseError::TooManyColors)); assert_eq!(read(&write(&sw[1..])).map(|w| w.len()), Ok(MAX_COLORS), "up to the limit"); } + + #[test] + fn non_finite_components_are_skipped() { + let sw = |name: &str, color: Color| Swatch { + name: name.into(), + value: SwatchValue::Color { color, color_type: ColorType::Process }, + locked: false, + named: true, + hidden: false, + }; + let mut list = vec![sw("Ok", Color::Rgb { r: 0.1, g: 0.2, b: 0.3 })]; + for bad in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] { + list.push(sw("Rgb", Color::Rgb { r: bad, g: 0.0, b: 0.0 })); + list.push(sw("Cmyk", Color::Cmyk { c: 0.0, m: 0.0, y: 0.0, k: bad })); + list.push(sw("Gray", Color::Gray { k: bad })); + } + let mut bytes = write(&list); + // Append Lab blocks with a non-finite component. + let mut extra = 0u32; + for bad in [f32::NAN, f32::INFINITY] { + let mut c = Vec::new(); + c.extend(2u16.to_be_bytes()); + c.extend([0, b'L', 0, 0]); + c.extend(b"LAB "); + for v in [bad, 0.0, 0.0] { + c.extend(v.to_bits().to_be_bytes()); + } + c.extend(2u16.to_be_bytes()); + bytes.extend(COLOR.to_be_bytes()); + bytes.extend((c.len() as u32).to_be_bytes()); + bytes.extend(&c); + extra += 1; + } + let count = u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]) + extra; + bytes[8..12].copy_from_slice(&count.to_be_bytes()); + let back = read(&bytes).unwrap(); + let names: Vec<&str> = back.iter().map(|s| s.name.as_str()).collect(); + assert_eq!(names, ["Ok"], "non-finite swatches must be left out"); + } }