diff --git a/crates/pdf/src/import_color.rs b/crates/pdf/src/import_color.rs index ff63f9493..2362634ed 100644 --- a/crates/pdf/src/import_color.rs +++ b/crates/pdf/src/import_color.rs @@ -303,14 +303,7 @@ impl<'p> Colors<'p> { (Some(_), _) if several || process => { // The color can't link to one swatch, but each ink it paints with is still a // spot swatch. - for (c, t) in colorants.iter().zip(comps) { - if let Colorant::Ink(k) = *c - && round3(t.clamp(0.0, 1.0)) > 0.0 - && let Some(ink) = self.inks.get_mut(k) - { - ink.used = true; - } - } + self.mark_inks(Space::Inks(space), comps); self.mixed = true; return None; } @@ -330,6 +323,24 @@ impl<'p> Colors<'p> { } } + /// Mark each ink that components `comps` of `space` paint with (a tint above 0) as used, so it + /// becomes a spot swatch; nothing for a space with a colorant that can't be read. + pub fn mark_inks(&mut self, space: Space, comps: &[f32]) { + let Space::Inks(i) = space else { return }; + let Some(s) = self.spaces.get(i) else { return }; + if s.colorants.iter().any(|c| matches!(c, Colorant::Unknown)) { + return; + } + for (c, t) in s.colorants.iter().zip(comps) { + if let Colorant::Ink(k) = *c + && round3(t.clamp(0.0, 1.0)) > 0.0 + && let Some(ink) = self.inks.get_mut(k) + { + ink.used = true; + } + } + } + /// Find the file's Separation and DeviceN colour spaces (once, when a colour first needs them). fn scan(&mut self) { if self.scanned { diff --git a/crates/pdf/src/import_shading.rs b/crates/pdf/src/import_shading.rs index ed169d724..1d2bc9cca 100644 --- a/crates/pdf/src/import_shading.rs +++ b/crates/pdf/src/import_shading.rs @@ -45,6 +45,10 @@ pub(crate) fn shading_gradient(sp: &ShadingPattern, colors: &mut Colors<'_>, ink for i in 0..=N { let t = i as f32 / N as f32; pts.push((t, sample(t)?.to_rgb())); + // An ink that paints only at samples dropped below is still a spot swatch. + if let Some(v) = at(t) { + colors.mark_inks(space, &v); + } } let ts: Vec = linear_keep(&pts).iter().filter_map(|&k| pts.get(k)).map(|p| p.0).collect(); let mut stop_at = |t: f32, offset: f32| -> GradientStop { diff --git a/crates/pdf/src/tests/import_color.rs b/crates/pdf/src/tests/import_color.rs index e2a7283df..e26697858 100644 --- a/crates/pdf/src/tests/import_color.rs +++ b/crates/pdf/src/tests/import_color.rs @@ -187,6 +187,25 @@ fn a_spot_ink_mixed_with_a_process_plate_keeps_its_swatch() { } } +#[test] +fn a_spot_ink_only_between_a_shadings_stops_keeps_its_swatch() { + // Varnish and Cyan → CMYK (cyan, 0, 0, 0): Varnish draws nothing, so the shading's RGB is one + // colour and only its ends (Varnish 0) become stops; Varnish rises to 1 at the middle. + let space = format!("[/DeviceN [/Varnish /Cyan] /DeviceCMYK {} 0 R]", first_extra(1)); + let func = "<< /FunctionType 4 /Domain [0 1 0 1] /Range [0 1 0 1 0 1 0 1] /Length 18 >>\nstream\n{ exch pop 0 0 0 }\nendstream"; + let half = |a: u8, b: u8| format!("<< /FunctionType 2 /Domain [0 1] /C0 [{a} 0.3] /C1 [{b} 0.3] /N 1 >>"); + let shading = format!( + "<< /ShadingType 2 /ColorSpace {space} /Coords [0 0 50 0] /Function << /FunctionType 3 /Domain [0 1] /Functions [{} {}] /Bounds [0.5] /Encode [0 1 0 1] >> /Extend [true true] >>", + half(0, 1), + half(1, 0) + ); + let page = PdfPage { resources: format!("/Shading << /Sh0 {shading} >>"), ..PdfPage::new(100.0, 50.0, "q 0 0 50 50 re W n /Sh0 sh Q") }; + let r = import_with_report(&pdf_with(&[page], &[func], None), &ImportOptions::default()).unwrap(); + let d = &r.document; + let varnish = d.swatch("Varnish").unwrap_or_else(|| panic!("no Varnish swatch: {:?}", d.swatches)); + assert!(varnish.spot && varnish.global, "{varnish:?}"); +} + /// The spaces `/CS1` and `/CS2` of two inks (`[/{kind} {a} …]`, `[/{kind} {b} …]`) with the same /// alternate and tint transform, blue at 100 %: they look the same to the interpreter. fn blues(kind: &str, a: &str, b: &str) -> String {