From ab274489215e1d88e483d2bfe346c506c9e56eb9 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 10 Nov 2026 15:34:06 -0800 Subject: [PATCH] Docs: spot gradients with RGB or Lab stops keep the ink in a CMYK document Two PDF export passages in docs/target-app-parity.md said a gradient that mixes a spot color with RGB or Lab stops is always written without the spot ink, or as an image when Perceptual. A CMYK document, or an export that converts colors to a CMYK destination profile, writes those stops in CMYK first, so the gradient is a DeviceN shading that keeps the ink; an RGB destination profile keeps them in RGB. The passages now say so, as docs/mcp.md and the paint.editGradient params text already do. A new test checks the Classic case in an RGB document, a CMYK document, with a CMYK destination and with an RGB destination. --- crates/engine/src/tests/linked_stops.rs | 35 +++++++++++++++++++++++++ docs/target-app-parity.md | 4 +-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/crates/engine/src/tests/linked_stops.rs b/crates/engine/src/tests/linked_stops.rs index 951a9ec31..7f70d24ff 100644 --- a/crates/engine/src/tests/linked_stops.rs +++ b/crates/engine/src/tests/linked_stops.rs @@ -349,6 +349,41 @@ fn perceptual_spot_gradients_with_rgb_stops_in_a_cmyk_document_export_as_device_ assert!(r.warnings.iter().any(|w| w.contains("spot")), "RGB destination: {:?}", r.warnings); } +#[test] +fn classic_spot_gradients_with_rgb_stops_in_a_cmyk_document_export_as_device_n_shadings() { + let (mut s, _) = setup(); + run(&mut s, "paint.editGradient", json!({"stops": [{"offset": 0, "swatch": "Ink"}, {"offset": 1, "color": "#00ff00"}]})); + let (pdf, warnings) = pdf_of(&s); + assert!(!pdf.contains("/DeviceN") && warnings.iter().any(|w| w.contains("spot")), "an RGB document drops the ink: {warnings:?}"); + // A CMYK document, or a conversion to a CMYK destination, writes the RGB stop in CMYK: a + // DeviceN shading that keeps the ink. + let mut cmyk = doc(&s).clone(); + cmyk.color_mode = vectorcraft_doc::ColorMode::Cmyk; + let plain = vectorcraft_pdf::PdfOptions { created: Some(0), ..vectorcraft_pdf::PdfOptions::uncompressed() }; + let mut converted = plain.clone(); + converted.settings.output = vectorcraft_pdf::OutputSettings { + conversion: vectorcraft_pdf::ColorConversion::Destination, + destination: vectorcraft_pdf::DOCUMENT_CMYK.into(), + ..Default::default() + }; + for (what, d, opts) in [("CMYK document", &cmyk, &plain), ("CMYK destination", doc(&s), &converted)] { + let r = vectorcraft_pdf::export_with_report(d, opts).unwrap(); + let pdf = String::from_utf8_lossy(&r.bytes); + assert!(pdf.contains("/ShadingType 2") && pdf.contains("/Ink]/DeviceCMYK"), "{what}: a DeviceN shading with Ink"); + assert!(!r.warnings.iter().any(|w| w.contains("spot")), "{what}: {:?}", r.warnings); + } + // A conversion to an RGB destination keeps the RGB stop in RGB, in a CMYK document too. + let mut to_rgb = plain.clone(); + to_rgb.settings.output = vectorcraft_pdf::OutputSettings { + conversion: vectorcraft_pdf::ColorConversion::Destination, + destination: vectorcraft_color::cms::SRGB.into(), + ..Default::default() + }; + let r = vectorcraft_pdf::export_with_report(&cmyk, &to_rgb).unwrap(); + assert!(!String::from_utf8_lossy(&r.bytes).contains("/DeviceN"), "RGB destination: no DeviceN shading"); + assert!(r.warnings.iter().any(|w| w.contains("spot")), "RGB destination: {:?}", r.warnings); +} + #[test] fn spot_gradients_drawn_as_images_warn() { let (mut s, _) = setup(); diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6bf45e9c8..7a74d9ea8 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1346,7 +1346,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur Invisible text in a PDF (rendering mode 3, which carries a scanned page's recognized text over the scan) opens as type with no fill or stroke, a run of its own beside visible text on the same line; text that can't come in as type (opened as outlines, Type 3 fonts) is left out with a warning. PDF export, and printing, write characters with no fill or stroke as real text in rendering mode 3, so they draw nothing but stay selectable and searchable, also in PDF/X-1a and PDF/A-2b (#2675, `pdf/src/tests/import_fidelity.rs`, `pdf/src/tests/realtext.rs`). A PDF, or a `.ai` read through its PDF part, whose spot inks have the same values but different names opens with a spot swatch for each ink. The solid fills and strokes of paths and type, and the stops of gradients on paths, link to the swatch of the ink they are painted in, the points of a gradient mesh in one of these inks keep the ink's color, and PDF export writes each of these inks as its own separation (`pdf/src/tests/import_color.rs`). Paint in these inks inside pattern cells, soft masks and type 3 glyphs, and gradients on type in these inks, still import as RGB. Type in one of these inks can also import as RGB when type in another Separation or DeviceN ink is shown with no path drawn between them. The stops of an axial or radial shading in a DeviceN space that mixes one spot ink with process plates (a spot ink over a constant cyan, for example) link to the ink's swatch at its tint and keep the process plates, and PDF and EPS (Level 3) export write those stops in a DeviceN space of the ink and those plates, so the spot plate is not lost (#3235, `import_color::a_device_n_gradient_of_a_spot_ink_over_a_process_plate_keeps_both_inks`). Solid fills that mix a spot ink with process plates, and stops that mix several spot inks, still import as RGB, with the spot inks kept as swatches. A `.ai` or EPS opened from its editing data keeps both swatches when a pattern's name, or a named color's, is also another swatch's: a pattern's swatch named as a default swatch, a named color or a palette color becomes "Name 2", and a named color named as a gradient or pattern swatch of the palette becomes "Name 2", with the fills, strokes and gradient stops linked to it following it (`ai::tests::a_pattern_swatch_named_as_another_swatch_is_numbered`, `ai::tests::a_color_named_as_a_palette_gradient_or_pattern_is_numbered`). - PDF export writes a linear or radial gradient that mixes a spot color with process colors, or two spot colors, as a DeviceN shading of the spot inks and the process inks its stops use, so each spot ink prints on its own plate; an off-center midpoint is written as a stop with the inks halfway between its two stops. A gradient that mixes a spot color with RGB or Lab colors is still written without the spot ink, with a warning. A Perceptual gradient of spot inks is written as a Separation or DeviceN shading of the stops on its curve; a dithered gradient fill, and a Perceptual gradient with RGB or Lab stops other than white or more than 4,096 stops on its curve, are written as images in process colors, with a warning. A Lab spot color in such a shading is named by the same Separation space, with the Lab alternate, as its flat fills (`engine/src/tests/linked_stops.rs`, `engine/src/tests/labspots.rs`). + PDF export writes a linear or radial gradient that mixes a spot color with process colors, or two spot colors, as a DeviceN shading of the spot inks and the process inks its stops use, so each spot ink prints on its own plate; an off-center midpoint is written as a stop with the inks halfway between its two stops. A gradient that mixes a spot color with RGB or Lab colors is still written without the spot ink, with a warning. A CMYK document, or an export that converts colors to a CMYK destination profile, writes RGB and Lab stops in CMYK first (unless the export converts colors to an RGB destination profile), so there such a gradient, Classic or Perceptual, is written as a DeviceN shading that keeps the spot ink. A Perceptual gradient of spot inks is written as a Separation or DeviceN shading of the stops on its curve; a dithered gradient fill, and a Perceptual gradient with RGB or Lab stops other than white or more than 4,096 stops on its curve, are written as images in process colors, with a warning. A Lab spot color in such a shading is named by the same Separation space, with the Lab alternate, as its flat fills (`engine/src/tests/linked_stops.rs`, `engine/src/tests/labspots.rs`). PDF export writes a spot color named All or None (in a Separation space, the names of every plate and of no plate: [ISO 32000-1:2008](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf), 8.6.6.4) as the ink All Spot or None Spot (All Spot 2 and on when a spot swatch already has that name) in its fills, strokes, gradients, colorized images and Lab alternates; Registration still prints on every plate (`pdf/src/tests/output.rs`). - `.vectorcraft` (lossless JSON, compressed, atomic saves, save down to v1), Save As with a format chooser, Save a Copy, Revert, templates, Data Recovery after a crash (browser tabs hold their copies with Web Locks too, so a background tab's copies are never taken for a crash's, and a tab repairs copies another removed when it resumes: #367), background save and export. The desktop app writes its warnings and errors (graphics adapter, a lost graphics device, panics the guard recovered from) to standard error and to `logs/vectorcraft.log` next to the preferences, the two previous runs kept and each file capped at 16 MiB, so a launch without a terminal still leaves something to attach to a bug report (#465). Native documents with a lot of path data (traced or generated art, in layers, symbols or patterns) are saved as compact JSON, about a fifth of the size; small documents stay pretty-printed (#1492). Saving to a network drive on macOS falls back to a plain fsync when the share refuses a full flush (#1625, part of #1620; not confirmed on a real share), and a save still succeeds when a folder refuses the temporary file, the rename over the document or copying permissions: the content is written elsewhere first and copied in, and a failed copy keeps the temporary file and names it (#1649, part of #1620). A document opened by a relative path finds its relatively linked images, and `vectorcraft-cli convert` lists what reading the input left out as `importWarnings` (#1606, fixes #1604). Preferences › File Handling › Data Recovery › Folder has a Choose… button on the desktop that picks the folder Data Recovery keeps its copies in ([Illustrator reference, Set Data Recovery preferences](https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf)); a picked folder that Data Recovery cannot create its folders in or list is refused with a status message, and the field keeps its value. OK applies the same check to a folder typed into the field (VectorCraft's own rule): when it fails, the dialog stays open, the status bar says why and no preference changes; an empty field (the default folder) and the folder already saved are not checked. Preferences › Plug-ins & Scratch Disks is where additional plug-ins folders are set ([Illustrator preferences help](https://helpx.adobe.com/illustrator/desktop/get-started/preferences-and-settings/set-app-preferences.html)); its Additional Plug-ins Folder has a Choose… button on the desktop as well, VectorCraft's own, matching Data Recovery's, and VectorCraft's own rule refuses a picked or typed folder whose contents can't be listed. Additional Fonts Folder and Templates Folder stay typed only. @@ -1692,7 +1692,7 @@ applies to fills). Tests: `color::gradient::tests` (perceptual midpoint, expansi `pdf::tests::dithered_perceptual_gradients_export_as_images_with_noise_on_the_fill` and the Gradient panel's `the_method_menu_makes_the_gradient_perceptual`. Scores and hours unchanged. -PDF export writes a Perceptual linear or radial gradient whose stops are tints of one spot color (and paper white) as a Separation shading in that ink instead of an image, so the ink keeps its plate ([swatches](https://helpx.adobe.com/illustrator/using/using-creating-swatches.html); [separations](https://helpx.adobe.com/illustrator/using/printing-color-separations.html)). Its tints follow the curve's stops, each the tint with the OKLab lightness of the color there (VectorCraft's own rule). A Perceptual gradient that mixes spot colors with each other or with CMYK or gray stops is written as a DeviceN shading of its inks: at each stop on the curve, the inks of the two stops on either side are mixed at the weight where the two colors mixed in display RGB have the OKLab lightness of the color there, or at the stop's mix position between the two colors when that mix's lightness turns back on the way from one color to the other, as from red to blue (VectorCraft's own rule). Dithered fills, freeform gradients, Perceptual gradients that mix a spot color with RGB or Lab stops, and Perceptual gradients whose curve has more than 4,096 stops once expanded (VectorCraft's own limit) are still images, and the export warns that their spot colors are written in process colors. Tests: `color::gradient::tests::ink_tints_follow_the_perceptual_curve`, `ink_mixes_follow_the_perceptual_curve` and `ink_mixes_use_the_mix_position_when_the_lightness_turns_back`, `engine::tests::linked_stops::perceptual_spot_gradients_export_as_separation_shadings`, `perceptual_spot_gradients_mixed_with_other_inks_export_as_device_n_shadings` and `spot_gradients_drawn_as_images_warn`. +PDF export writes a Perceptual linear or radial gradient whose stops are tints of one spot color (and paper white) as a Separation shading in that ink instead of an image, so the ink keeps its plate ([swatches](https://helpx.adobe.com/illustrator/using/using-creating-swatches.html); [separations](https://helpx.adobe.com/illustrator/using/printing-color-separations.html)). Its tints follow the curve's stops, each the tint with the OKLab lightness of the color there (VectorCraft's own rule). A Perceptual gradient that mixes spot colors with each other or with CMYK or gray stops is written as a DeviceN shading of its inks: at each stop on the curve, the inks of the two stops on either side are mixed at the weight where the two colors mixed in display RGB have the OKLab lightness of the color there, or at the stop's mix position between the two colors when that mix's lightness turns back on the way from one color to the other, as from red to blue (VectorCraft's own rule). Dithered fills, freeform gradients, Perceptual gradients that mix a spot color with RGB or Lab stops, and Perceptual gradients whose curve has more than 4,096 stops once expanded (VectorCraft's own limit) are still images, and the export warns that their spot colors are written in process colors. In a CMYK document, or an export that converts colors to a CMYK destination profile, RGB and Lab stops are written in CMYK, so a Perceptual gradient that mixes a spot color with them is a DeviceN shading; an export that converts colors to an RGB destination profile keeps them in RGB. Tests: `color::gradient::tests::ink_tints_follow_the_perceptual_curve`, `ink_mixes_follow_the_perceptual_curve` and `ink_mixes_use_the_mix_position_when_the_lightness_turns_back`, `engine::tests::linked_stops::perceptual_spot_gradients_export_as_separation_shadings`, `perceptual_spot_gradients_mixed_with_other_inks_export_as_device_n_shadings`, `perceptual_spot_gradients_with_rgb_stops_in_a_cmyk_document_export_as_device_n_shadings` and `spot_gradients_drawn_as_images_warn`. In these Separation and DeviceN shadings, a white stop linked to a swatch that is not a spot color (a global White swatch, for example) is paper white, 0% of the ink, as an unlinked white stop is, so a gradient of one spot ink and such a stop stays a Separation shading; a stop linked to a white spot color stays a stop of that ink (VectorCraft's own rule). Test: `engine::tests::linked_stops::white_stops_linked_to_process_swatches_are_paper_in_spot_gradients`.