mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Merge pull request #4395 from dotCooCoo/fix/issue-3235
Part of #3235: PDF import keeps the spot swatch of a DeviceN color that mixes inks
This commit is contained in:
@@ -301,6 +301,9 @@ impl<'p> Colors<'p> {
|
||||
let process = cmyk.iter().any(|v| *v > 0.0);
|
||||
let (c, t) = match (ink, first_ink) {
|
||||
(Some(_), _) if several || process => {
|
||||
// The color can't link to one swatch, but each ink it paints with is still a
|
||||
// spot swatch.
|
||||
self.mark_inks(Space::Inks(space), comps);
|
||||
self.mixed = true;
|
||||
return None;
|
||||
}
|
||||
@@ -320,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 {
|
||||
|
||||
@@ -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<f32> = linear_keep(&pts).iter().filter_map(|&k| pts.get(k)).map(|p| p.0).collect();
|
||||
let mut stop_at = |t: f32, offset: f32| -> GradientStop {
|
||||
|
||||
@@ -164,6 +164,48 @@ fn device_n_inks_become_spot_swatches_and_process_plates_cmyk() {
|
||||
assert_eq!(d.swatches.iter().filter(|s| s.spot).count(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_spot_ink_mixed_with_a_process_plate_keeps_its_swatch() {
|
||||
// Gold and Cyan → CMYK (cyan, 0, gold, 0): a fill, then an axial shading of Gold 0 to 1 over
|
||||
// Cyan at 0.3.
|
||||
let space = format!("[/DeviceN [/Gold /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 17 >>\nstream\n{ exch 0 exch 0 }\nendstream";
|
||||
let shading = format!(
|
||||
"<< /ShadingType 2 /ColorSpace {space} /Coords [0 0 50 0] /Function << /FunctionType 2 /Domain [0 1] /C0 [0 0.3] /C1 [1 0.3] /N 1 >> /Extend [true true] >>"
|
||||
);
|
||||
let fill = page_with(&format!("/CS0 {space}"), &[("CS0", "0.5 0.3")]);
|
||||
let sh = PdfPage { resources: format!("/Shading << /Sh0 {shading} >>"), ..PdfPage::new(100.0, 50.0, "q 0 0 50 50 re W n /Sh0 sh Q") };
|
||||
for (what, page) in [("fill", fill), ("shading", sh)] {
|
||||
let r = import_with_report(&pdf_with(&[page], &[func], None), &ImportOptions::default()).unwrap();
|
||||
let d = &r.document;
|
||||
assert!(r.warnings.iter().any(|w| w.contains("several inks")), "{what}: {:?}", r.warnings);
|
||||
assert!(links(d).iter().all(Option::is_none), "{what}: the art stays RGB");
|
||||
let gold = d.swatch("Gold").unwrap_or_else(|| panic!("{what}: no Gold swatch"));
|
||||
assert!(gold.spot && gold.global, "{what}: Gold is a spot swatch");
|
||||
assert!(near(gold.paint.color().unwrap(), Color::cmyk(0.0, 0.0, 1.0, 0.0)), "{what}: {gold:?}");
|
||||
assert_eq!(d.swatches.iter().filter(|s| s.spot).count(), 1, "{what}: Cyan is a process plate");
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
||||
Reference in New Issue
Block a user