mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
Gradient: Perceptual PDF help counts stops as written, CMYK documents included
The method help of paint.editGradient and docs/mcp.md said a Perceptual gradient with spot stops becomes an image in PDF unless every other stop is paper white, CMYK or gray. PDF export checks each stop's color as it is written: a CMYK document, or an export that converts colors to a CMYK destination profile, writes RGB and Lab stops in CMYK, so the gradient becomes a DeviceN shading. Both texts now say so, and a test covers the CMYK document and CMYK destination cases.
This commit is contained in:
@@ -23,7 +23,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Gradient",
|
||||
["Window", "Gradient"],
|
||||
None,
|
||||
"{stroke?: bool (default: the targeted item's kind, else the active proxy), kind?: linear|radial|freeform (freeform places points on each object, coloured along the stops), mode?: points|lines (freeform: how the Gradient tool adds points), method?: classic|perceptual (how the colours between the stops mix: in display RGB, or perceptually (OKLab) so the steps look even; SVG exports draw a Perceptual gradient as an image, and PDF exports do too unless it is linear or radial, has spot color stops, every other stop is paper white, CMYK or gray, it is not a dithered fill and its curve does not have too many stops: then PDF writes tints of one spot ink as a Separation shading in that ink, and spot inks mixed with other spot, CMYK or gray inks as a DeviceN shading; freeform gradients mix their own way), stops?: [{offset 0..1, color? (needed without swatch; #rrggbbaa and #rgba are refused), opacity? 0..1 (or 0..100), midpoint? 0.13..0.87, swatch?: colour swatch name (a global or spot colour or tint swatch links the stop, so swatch edits recolour it and a spot stop prints on its plate; a process colour just gives its colour), tint?: 0..100 (% of the linked swatch; default 100, or a tint swatch's own)}] (at least 2; a freeform gradient's points are recoloured along them), keepModel?: bool (as paint.setFill; a stop color the gradient already has keeps its model), angle?: deg, aspect?: %, reverse?: bool, dither?: bool (fills only: a linear or radial gradient fill is drawn with fine noise that hides banding, and as pixels in SVG and PDF exports), item?: fill/stroke item index|null (omitted: the Appearance panel's active item when it is of the edited kind), ids?|id?, strokeMode?: within|along|across (strokes only: the gradient lies on the page and shows through the stroke, runs from the start of each subpath to its end, or runs from the stroke's left edge to its right all along it; with nothing selected, for the next object drawn; type characters' own strokes always paint within), target?: \"object\"|\"contents\"} edit the gradient in place (keeps its placement); solid/none paints become the default gradient",
|
||||
"{stroke?: bool (default: the targeted item's kind, else the active proxy), kind?: linear|radial|freeform (freeform places points on each object, coloured along the stops), mode?: points|lines (freeform: how the Gradient tool adds points), method?: classic|perceptual (how the colours between the stops mix: in display RGB, or perceptually (OKLab) so the steps look even; SVG exports draw a Perceptual gradient as an image, and PDF exports do too unless it is linear or radial, has spot color stops, every other stop is paper white or written in CMYK or gray (a CMYK document, or an export that converts colors to a CMYK destination profile, writes RGB and Lab stops in CMYK), it is not a dithered fill and its curve does not have too many stops: then PDF writes tints of one spot ink as a Separation shading in that ink, and spot inks mixed with other spot, CMYK or gray inks as a DeviceN shading; freeform gradients mix their own way), stops?: [{offset 0..1, color? (needed without swatch; #rrggbbaa and #rgba are refused), opacity? 0..1 (or 0..100), midpoint? 0.13..0.87, swatch?: colour swatch name (a global or spot colour or tint swatch links the stop, so swatch edits recolour it and a spot stop prints on its plate; a process colour just gives its colour), tint?: 0..100 (% of the linked swatch; default 100, or a tint swatch's own)}] (at least 2; a freeform gradient's points are recoloured along them), keepModel?: bool (as paint.setFill; a stop color the gradient already has keeps its model), angle?: deg, aspect?: %, reverse?: bool, dither?: bool (fills only: a linear or radial gradient fill is drawn with fine noise that hides banding, and as pixels in SVG and PDF exports), item?: fill/stroke item index|null (omitted: the Appearance panel's active item when it is of the edited kind), ids?|id?, strokeMode?: within|along|across (strokes only: the gradient lies on the page and shows through the stroke, runs from the start of each subpath to its end, or runs from the stroke's left edge to its right all along it; with nothing selected, for the next object drawn; type characters' own strokes always paint within), target?: \"object\"|\"contents\"} edit the gradient in place (keeps its placement); solid/none paints become the default gradient",
|
||||
has_doc,
|
||||
edit_gradient
|
||||
),
|
||||
|
||||
@@ -290,6 +290,33 @@ fn perceptual_spot_gradients_mixed_with_other_inks_export_as_device_n_shadings()
|
||||
assert!(!pdf.contains("/Subtype/Image") && pdf.contains("/ShadingType 3") && pdf.contains("/DeviceN[/Cyan/Ink]/DeviceCMYK"), "radial");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn perceptual_spot_gradients_with_rgb_stops_in_a_cmyk_document_export_as_device_n_shadings() {
|
||||
let (mut s, id) = setup();
|
||||
run(&mut s, "paint.editGradient", json!({"stops": [{"offset": 0, "swatch": "Ink"}, {"offset": 1, "color": "#00ff00"}], "method": "perceptual"}));
|
||||
assert!(gradient(&s, id).gradient.stops.iter().any(|st| matches!(st.color, Color::Rgb { .. })), "an RGB stop");
|
||||
let (pdf, _) = pdf_of(&s);
|
||||
assert!(pdf.contains("/Subtype/Image"), "an image in an RGB document");
|
||||
// A CMYK document, or a conversion to a CMYK destination, writes the RGB stop in CMYK: a
|
||||
// DeviceN shading, not an image.
|
||||
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("/Subtype/Image"), "{what}: no image");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spot_gradients_drawn_as_images_warn() {
|
||||
let (mut s, _) = setup();
|
||||
|
||||
+7
-5
@@ -925,9 +925,10 @@ Method (the Gradient panel's menu): `classic` (the default) mixes the colours be
|
||||
`perceptual` mixes a linear or radial gradient's colours in OKLab so the steps look even. SVG exports write a
|
||||
Perceptual gradient as an image at the Document Raster Effects resolution (a dithered Perceptual fill with its noise).
|
||||
PDF exports write it the same way, except for a linear or radial gradient with spot color stops whose other stops
|
||||
are all paper white, CMYK or gray, that is not a dithered fill and whose curve does not have too many stops. PDF
|
||||
writes tints of one spot ink as a Separation shading in that ink, and spot inks mixed with other spot, CMYK or gray
|
||||
inks as a DeviceN shading.
|
||||
are all paper white or written in CMYK or gray, that is not a dithered fill and whose curve does not have too many
|
||||
stops. A CMYK document, or an export that converts colors to a CMYK destination profile, writes RGB and Lab stops in
|
||||
CMYK. PDF writes tints of one spot ink as a Separation shading in that ink, and spot inks mixed with other spot, CMYK
|
||||
or gray inks as a DeviceN shading.
|
||||
`paint.setFill`/`paint.setStroke` take it in `gradient` too:
|
||||
|
||||
```json
|
||||
@@ -2786,8 +2787,9 @@ give it `swatch` (and `tint` %, default 100 or a tint swatch's own) instead of `
|
||||
{stops}` or a `gradient` paint's stops. Editing or deleting the swatch then recolours or unlinks the stop, in art and
|
||||
in gradient swatches. A spot stop separates on its plate. A linear or radial gradient whose stops are all tints of one spot ink (or
|
||||
paper white, 0 %) exports to PDF as a Separation shading, and one that mixes spot inks with other spot, CMYK or gray
|
||||
inks exports as a DeviceN shading. A gradient that mixes a spot ink with RGB or Lab colors is written in process
|
||||
colors, with a warning. Freeform gradients and dithered fills export to PDF as images in process colors, and Method
|
||||
inks exports as a DeviceN shading. A gradient that mixes a spot ink with colors written in RGB or Lab is written in
|
||||
process colors, with a warning; a CMYK document, or an export that converts colors to a CMYK destination profile,
|
||||
writes RGB and Lab stops in CMYK. Freeform gradients and dithered fills export to PDF as images in process colors, and Method
|
||||
above covers Perceptual gradients.
|
||||
|
||||
```json
|
||||
|
||||
Reference in New Issue
Block a user