Print color: US spelling in the new code and docs

This commit is contained in:
aiongg
2026-10-20 13:36:14 +00:00
parent 8de7bc8ed7
commit 99cb64a779
14 changed files with 46 additions and 46 deletions
+2 -2
View File
@@ -63,7 +63,7 @@ DesignCraft aims at full Adobe InDesign parity — and to be better: faster, ope
- Endnotes (`endnote.insert/edit/delete/list/options`, Type ▸ Insert Endnote): references number through the document, the endnote frame on a new last page lists them under a heading; IDML keeps the listed text only.
- Editorial notes (`note.new/edit/delete/convertToText/list`, Notes panel): anchored in text, flagged on screen, never printed or exported.
- File ▸ Print Booklet (`file.printBooklet`): saddle-stitch or 2-up consecutive printer spreads as PDF, padded with blanks, with a gap between pages.
- Edit ▸ Transparency Blend Space (`edit.transparencyBlendSpace`; RGB for web/mobile documents; IDML TransparencyPreference) — exported PDFs blend in that space: CMYK print exports write their transparency groups and page groups in DeviceCMYK (PDF/X-4 also carries the CMYK working space as its `GTS_PDFX` output intent); RGB exports give pages with transparency a DeviceRGB page group. On screen, spreads with transparency in a CMYK blend space are shown through the working CMYK profile (their colours keep to its gamut, as when composited in CMYK).
- Edit ▸ Transparency Blend Space (`edit.transparencyBlendSpace`; RGB for web/mobile documents; IDML TransparencyPreference) — exported PDFs blend in that space: CMYK print exports write their transparency groups and page groups in DeviceCMYK (PDF/X-4 also carries the CMYK working space as its `GTS_PDFX` output intent); RGB exports give pages with transparency a DeviceRGB page group. On screen, spreads with transparency in a CMYK blend space are shown through the working CMYK profile (their colors keep to its gamut, as when composited in CMYK).
- High Contrast interface theme (fifth Interface Color Theme: black chrome, white text, yellow selection).
- Place Excel workbooks (.xlsx): the first worksheet's used range becomes a table (shared/inline strings, numbers, booleans).
- Object Library (`library.new/open/add/place/remove/list/json/close`, Library panel): `.dclib` files of snippets that keep stories, styles, swatches and images.
@@ -349,7 +349,7 @@ list) · [roadmap.md](docs/roadmap.md) (milestones, plan) · [parity-checklist.m
Newest first.
- **2026-10-20** · Print color (craft-libs#190): CMYK print PDFs blend in DeviceCMYK through craft-pdf's print settings instead of byte rewrites, with the CMYK working space as the output intent in PDF/X-4 only; overprint (Overprint Fill, Stroke and Gap, overprinting text, 100% [Black]) is written as `/op` `/OP` `/OPM 1`; Knockout Group writes `/K true` instead of becoming an image; File › Print prints the PDF Export PDF writes. No readiness percentage change.
- **2026-10-20** · Spot gradients print on their plates. A gradient stop keeps the swatch it was given (Gradient panel drop, `swatch.create {gradient}`, `object.gradient`, IDML `StopColor`), following its renames and edits. PDF export shades one spot with its tints or [Paper] as a Separation, and a spot with process inks or a second spot as DeviceN; a process gradient to [Paper] prints in DeviceCMYK. Documents with plain-colour stops export as before. No readiness percentage change.
- **2026-10-20** · Spot gradients print on their plates. A gradient stop keeps the swatch it was given (Gradient panel drop, `swatch.create {gradient}`, `object.gradient`, IDML `StopColor`), following its renames and edits. PDF export shades one spot with its tints or [Paper] as a Separation, and a spot with process inks or a second spot as DeviceN; a process gradient to [Paper] prints in DeviceCMYK. Documents with plain-color stops export as before. No readiness percentage change.
- **2026-10-16** · Type › Reload Fonts (`font.reload`): fonts installed, edited or removed while DesignCraft runs are read again without a restart, and the open documents recompose with them (#328, #547). An edited font file is noticed by its length and modification time; bundled, added and document fonts stay as they are. InDesign does this by itself, so noticing changes automatically is still open (gap 6). No readiness percentage change.
- **2026-10-16** · Parents can be deleted from their Pages-panel context menu or `layout.parents.delete {id}`. Applied-page and based-on references are cleared, local override copies survive, and Undo restores the parent. Addresses the deletion portion of #956; no readiness percentage change.
- **2026-10-17** · File › Package writes the document's and the IDML's links relative to the package (`Links/logo.png`, `file:Links/logo.png`), so a package holds no path from the machine that made it; opening a document resolves them to the files beside it. Links › Copy Link(s) To still links by full path. (#340). No readiness percentage change.
+5 -5
View File
@@ -28,7 +28,7 @@ impl GradientKind {
pub struct GradientStop {
/// 0..=1 along the gradient.
pub offset: f32,
/// The display colour. For a stop with a `swatch`, the swatch's colour when it was last
/// The display color. For a stop with a `swatch`, the swatch's color when it was last
/// resolved ([`GradientStop::swatch`]).
pub color: Color,
#[serde(default = "one")]
@@ -36,14 +36,14 @@ pub struct GradientStop {
/// Midpoint to the next stop, 0.13..=0.87 (InDesign's diamond), default 0.5.
#[serde(default = "half")]
pub midpoint: f32,
/// The colour swatch the stop takes its ink from: a spot or process colour, a tint of one,
/// or `[Paper]`. Print export writes the stop in that swatch's inks; `None` is a plain colour.
/// The color swatch the stop takes its ink from: a spot or process color, a tint of one,
/// or `[Paper]`. Print export writes the stop in that swatch's inks; `None` is a plain color.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub swatch: Option<String>,
}
impl GradientStop {
/// A plain-colour stop, opaque, with the midpoint halfway to the next stop.
/// A plain-color stop, opaque, with the midpoint halfway to the next stop.
pub fn new(offset: f32, color: Color) -> Self {
Self { offset, color, opacity: 1.0, midpoint: 0.5, swatch: None }
}
@@ -246,7 +246,7 @@ mod tests {
}
#[test]
fn saved_stops_without_a_swatch_are_plain_colours() {
fn saved_stops_without_a_swatch_are_plain_colors() {
let old = r#"{"offset": 0.0, "color": {"model": "cmyk", "c": 0.0, "m": 0.3, "y": 0.9, "k": 0.1}, "opacity": 1.0, "midpoint": 0.5}"#;
let stop: GradientStop = serde_json::from_str(old).unwrap();
assert_eq!(stop, GradientStop::new(0.0, Color::cmyk(0.0, 0.3, 0.9, 0.1)));
+4 -4
View File
@@ -142,8 +142,8 @@ impl Names<'_> {
}
impl Document {
/// Give gradient stops that name a colour swatch that swatch's current colour, after a
/// swatch edit. Stops whose swatch is gone or isn't a colour keep the colour they have.
/// Give gradient stops that name a color swatch that swatch's current color, after a
/// swatch edit. Stops whose swatch is gone or isn't a color keep the color they have.
pub fn refresh_gradient_stops(&mut self) {
let mut fresh: Vec<(usize, usize, designcraft_color::Color)> = Vec::new();
for (wi, w) in self.swatches.iter().enumerate() {
@@ -169,8 +169,8 @@ impl Document {
}
}
/// Turn gradient stops that name swatch `name` into plain colours (the swatch is being
/// deleted); each keeps the colour it shows.
/// Turn gradient stops that name swatch `name` into plain colors (the swatch is being
/// deleted); each keeps the color it shows.
pub fn forget_gradient_stop_swatch(&mut self, name: &str) {
for w in &mut self.swatches {
let SwatchValue::Gradient { gradient } = &mut w.value else { continue };
+7 -7
View File
@@ -404,7 +404,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Gradient",
[],
None,
"{kind?: linear|radial, stops?: [{location: 0–100, color: \"#rrggbb\"|{c,m,y,k}(0..100)|[r,g,b](0..255)|{model: rgb|cmyk|gray, …}(0..1) | swatch: a colour swatch's name (as dropped on the Gradient panel: its colour on screen, its inks in print), opacity?: 0–100, midpoint?: 13–87}], reverse?: bool, angle?: degrees, from?: [x,y], to?: [x,y] (spread coords: the Gradient Swatch tool's drag), target?: fill|stroke, ids?} → {swatch, kind, stops: [{location, color, swatch?, opacity, midpoint}]} — edits the fill's (or the stroke's) gradient (an unnamed gradient unless it equals a gradient swatch)",
"{kind?: linear|radial, stops?: [{location: 0–100, color: \"#rrggbb\"|{c,m,y,k}(0..100)|[r,g,b](0..255)|{model: rgb|cmyk|gray, …}(0..1) | swatch: a color swatch's name (as dropped on the Gradient panel: its color on screen, its inks in print), opacity?: 0–100, midpoint?: 13–87}], reverse?: bool, angle?: degrees, from?: [x,y], to?: [x,y] (spread coords: the Gradient Swatch tool's drag), target?: fill|stroke, ids?} → {swatch, kind, stops: [{location, color, swatch?, opacity, midpoint}]} — edits the fill's (or the stroke's) gradient (an unnamed gradient unless it equals a gradient swatch)",
super::has_selection,
apply_gradient
),
@@ -1300,7 +1300,7 @@ fn create_gradient_swatch(s: &mut Session, p: &Value, g: &Value) -> Result<Value
let (color, swatch) = match (st.get("swatch").and_then(Value::as_str), st.get("color")) {
(Some(n), _) => match doc.swatch(n).map(|w| &w.value) {
Some(SwatchValue::Gradient { .. }) => return Err(bad(ID, format!("`{n}` is a gradient; a stop takes a colour"))),
Some(_) => (doc.resolve_color(n, 1.0).ok_or_else(|| bad(ID, format!("`{n}` has no colour")))?, Some(n.to_string())),
Some(_) => (doc.resolve_color(n, 1.0).ok_or_else(|| bad(ID, format!("`{n}` has no color")))?, Some(n.to_string())),
None => return Err(bad(ID, format!("no swatch named `{n}`"))),
},
(None, Some(c)) => (parse_color(c).ok_or_else(|| bad(ID, "a stop has a bad color"))?, None),
@@ -1687,15 +1687,15 @@ fn apply_gradient(s: &mut Session, p: &Value) -> Result<Value> {
let doc = &s.doc()?.doc;
let mut v = Vec::new();
for st in a {
// A swatch dropped on a stop gives it the swatch's colour (a tint's mixed in) and
// A swatch dropped on a stop gives it the swatch's color (a tint's mixed in) and
// its inks for print.
// A stop naming a swatch that's gone keeps the colour given with it.
// A stop naming a swatch that's gone keeps the color given with it.
let color = st.get("color").and_then(parse_color);
let (color, swatch) = match st.get("swatch").and_then(Value::as_str) {
Some(name) => {
match doc.resolve_color(name, 1.0).filter(|_| designcraft_color::swatch::resolve_gradient(&doc.swatches, name).is_none()) {
Some(c) => (c, Some(name.to_string())),
None => (color.ok_or_else(|| bad("object.gradient", format!("`{name}` isn't a colour swatch")))?, None),
None => (color.ok_or_else(|| bad("object.gradient", format!("`{name}` isn't a color swatch")))?, None),
}
}
None => (color.ok_or_else(|| bad("object.gradient", "a stop needs a color or a swatch"))?, None),
@@ -3476,11 +3476,11 @@ mod gradient_swatch_tests {
};
assert_eq!(stops(&s)[0].swatch.as_deref(), Some("Gold"));
assert_eq!(stops(&s)[1].swatch.as_deref(), Some("[Paper]"));
// Renamed and edited: the stop names the new name and shows the new colour.
// Renamed and edited: the stop names the new name and shows the new color.
s.execute("swatch.options", &json!({"name": "Gold", "to": "Gold 871", "color": {"c": 0, "m": 20, "y": 80, "k": 20}})).unwrap();
assert_eq!(stops(&s)[0].swatch.as_deref(), Some("Gold 871"));
assert_eq!(stops(&s)[0].color, designcraft_color::Color::cmyk(0.0, 0.2, 0.8, 0.2));
// Deleted: the stop keeps its colour as a plain one.
// Deleted: the stop keeps its color as a plain one.
s.execute("swatch.delete", &json!({"name": "Gold 871"})).unwrap();
assert_eq!(stops(&s)[0].swatch, None);
assert_eq!(stops(&s)[0].color, designcraft_color::Color::cmyk(0.0, 0.2, 0.8, 0.2));
+2 -2
View File
@@ -594,8 +594,8 @@ impl<'a> Ex<'a> {
.attr("Visible", "true")
.attr("SwatchCreatorID", "7937");
for (i, stop) in gradient.stops.iter().enumerate() {
// A stop on a swatch names it; a plain colour names an equal colour swatch or
// an unnamed colour.
// A stop on a swatch names it; a plain color names an equal color swatch or
// an unnamed color.
let named = stop
.swatch
.as_deref()
+1 -1
View File
@@ -979,7 +979,7 @@ impl<'r> Importer<'r> {
let r = s.get("StopColor");
let c = r.map(|r| self.color_for_ref(r, &order)).unwrap_or(Color::BLACK);
// A stop on a named swatch (a spot, a tint, [Paper]) keeps it for its inks;
// unnamed colours stay plain. The id becomes the swatch's name below, once
// unnamed colors stay plain. The id becomes the swatch's name below, once
// repeated names are numbered.
let swatch = r.filter(|r| self.swatch_names.get(*r).is_some_and(|n| n != swatch::NONE)).map(str::to_string);
let loc = s.num("Location").unwrap_or(0.0) / 100.0;
+1 -1
View File
@@ -708,7 +708,7 @@ fn imports_hand_written_fixture() {
assert_eq!(gradient.stops[1].color, Color::cmyk(0.1, 0.2, 0.3, 0.4));
assert!((gradient.stops[0].midpoint - 0.3).abs() < 1e-6);
assert_eq!(gradient.stops[0].swatch.as_deref(), Some("Brand"), "a stop on a swatch keeps it");
assert_eq!(gradient.stops[1].swatch, None, "an unnamed colour stays plain");
assert_eq!(gradient.stops[1].swatch, None, "an unnamed color stays plain");
// The parent rectangle used an unnamed colour → value-named swatch.
let mr = &d.parents[0].items[0];
assert_eq!(mr.fill.swatch, "C=10 M=20 Y=30 K=40");
+5 -5
View File
@@ -1152,10 +1152,10 @@ impl Exporter<'_> {
self.swatch_color(swatch, tint).map(Into::into)
}
/// A gradient's stops in device colours: CMYK, RGB or Gray, as one space.
/// A gradient's stops in device colors: CMYK, RGB or Gray, as one space.
fn device_gradient_stops(&mut self, g: &designcraft_color::Gradient, swatch: &str) -> Vec<Stop> {
let mut expanded = g.expanded_stops();
// Grey and CMYK stops blend as inks, so a grey stop beside a CMYK one is the K ink.
// Gray and CMYK stops blend as inks, so a gray stop beside a CMYK one is the K ink.
if !self.rgb_only && expanded.iter().any(|(_, c, _)| matches!(c, Color::Cmyk { .. })) {
for (_, c, _) in &mut expanded {
if let Color::Gray { k } = *c {
@@ -1163,7 +1163,7 @@ impl Exporter<'_> {
}
}
}
// A PDF shading needs one colour space. Midpoint expansion can introduce RGB even
// A PDF shading needs one color space. Midpoint expansion can introduce RGB even
// when every authored stop is CMYK/Gray; PDF/A can also change the output space.
let components = |c: &Color| match c {
Color::Cmyk { .. } if !self.rgb_only => 4,
@@ -1172,7 +1172,7 @@ impl Exporter<'_> {
};
let mixed = expanded.first().is_some_and(|(_, first, _)| expanded.iter().any(|(_, c, _)| components(c) != components(first)));
if mixed {
self.warn(format!("gradient '{swatch}': mixed color spaces converted to RGB (colour appearance and separations may change)"));
self.warn(format!("gradient '{swatch}': mixed color spaces converted to RGB (color appearance and separations may change)"));
}
let mut stops: Vec<Stop> = Vec::new();
let mut last = 0.0f32;
@@ -1203,7 +1203,7 @@ impl Exporter<'_> {
c.into()
};
let closed = it.path.is_closed();
// Overprint Stroke covers the stroke and its arrowheads, Overprint Gap the gap colour;
// Overprint Stroke covers the stroke and its arrowheads, Overprint Gap the gap color;
// each is pushed around what it paints (a filled outline or a stroked path).
let stroke_op = self.overprints(&st.swatch, st.tint, st.overprint);
let gap_op = self.overprints(&st.gap_swatch, st.gap_tint, st.gap_overprint);
+6 -6
View File
@@ -23,7 +23,7 @@ enum Ink {
},
}
/// Inks of a plain colour: CMYK as it is, grey as black. RGB has none.
/// Inks of a plain color: CMYK as it is, gray as black. RGB has none.
fn process_inks(c: Color) -> Option<[f32; 4]> {
let tint = |v: f32| if v.is_finite() { v.clamp(0.0, 1.0) } else { 0.0 };
match c {
@@ -46,7 +46,7 @@ impl Exporter<'_> {
let Some(mut n) = stop.swatch.as_deref() else { return process_inks(stop.color).map(Ink::Process) };
let mut t = 1.0f32;
for _ in 0..8 {
// A swatch that's gone leaves the stop's own colour.
// A swatch that's gone leaves the stop's own color.
let Some(sw) = self.doc.swatch(n) else { return process_inks(stop.color).map(Ink::Process) };
match &sw.value {
SwatchValue::Paper { .. } => return Some(Ink::Process([0.0; 4])),
@@ -72,10 +72,10 @@ impl Exporter<'_> {
None
}
/// The gradient's stops in its inks, or `None` to write it in device colours (no spot, or
/// The gradient's stops in its inks, or `None` to write it in device colors (no spot, or
/// inks that can't share one shading space, which is warned about).
pub(crate) fn ink_gradient_stops(&mut self, g: &Gradient, swatch: &str) -> Option<Vec<Stop>> {
// Plain-colour stops (all older documents) keep their device colours.
// Plain-color stops (all older documents) keep their device colors.
if g.stops.iter().all(|s| s.swatch.is_none()) {
return None;
}
@@ -184,10 +184,10 @@ impl Exporter<'_> {
(!stops.is_empty()).then_some(stops)
}
/// Falls back to device colours, warning when that loses a spot ink.
/// Falls back to device colors, warning when that loses a spot ink.
fn unshaded(&mut self, swatch: &str, why: &str, has_spot: bool) -> Option<Vec<Stop>> {
if has_spot {
self.warn(format!("gradient '{swatch}': spot inks printed as process colour ({why})"));
self.warn(format!("gradient '{swatch}': spot inks printed as process color ({why})"));
}
None
}
+2 -2
View File
@@ -14,8 +14,8 @@
//! as `/Separation /All`.
//! Gradient stops on swatches shade in their inks: one spot with tints of itself or `[Paper]`
//! as a `/Separation` shading, a spot with process inks or a second spot as `/DeviceN`
//! (process colorants first; not in PDF/A, which goes to process colour with a warning), and in
//! print a process gradient to `[Paper]` as DeviceCMYK. Plain-colour stops keep their own
//! (process colorants first; not in PDF/A, which goes to process color with a warning), and in
//! print a process gradient to `[Paper]` as DeviceCMYK. Plain-color stops keep their own
//! space; gradients mixing RGB with CMYK or Gray are converted to RGB with an export warning.
//! - **Overprint:** Overprint Fill, Stroke and Gap, overprinting text and 100% [Black] (when
//! Appearance of Black says so) as `/op` and `/OP` with `/OPM 1`; left out of PDF/A.
+1 -1
View File
@@ -203,7 +203,7 @@ fn overprint_attributes_are_written() {
}
/// 100% [Black] fills, strokes and text overprint when Appearance of Black says so; tints of
/// it and other colours don't.
/// it and other colors don't.
#[test]
fn black_overprints_with_the_preference() {
let black = designcraft_color::swatch::BLACK;
+7 -7
View File
@@ -17,7 +17,7 @@ fn spot(d: &mut Document, name: &str, color: Color) {
}
/// A document with spot inks Gold and Silver, a 40 % tint of Gold, and one box filled with a gradient on `stops`
/// (swatch name or plain colour, location).
/// (swatch name or plain color, location).
fn document(stops: &[(Result<&str, Color>, f32)], midpoint: f32) -> Document {
let mut d = Document::new(&NewDocument { width: 120.0, height: 120.0, facing_pages: false, ..Default::default() });
spot(&mut d, "Gold", Color::cmyk(0.0, 0.3, 0.9, 0.1));
@@ -48,7 +48,7 @@ fn endpoint(function: &Dict<'_>, first: bool) -> Vec<f32> {
function.get::<Array<'_>>(if first { b"C0" } else { b"C1" }).unwrap().iter::<f32>().collect()
}
/// Each shading on the page: its color space family, colorant names, and the colours at its
/// Each shading on the page: its color space family, colorant names, and the colors at its
/// two ends.
struct Shading {
family: String,
@@ -124,7 +124,7 @@ fn a_spot_to_cyan_gradient_is_a_device_n_shading() {
assert_eq!(s.family, "DeviceN");
assert_eq!(s.colorants, ["Cyan", "Gold"]);
assert!(close(&s.start, &[0.0, 1.0]) && close(&s.end, &[1.0, 0.0]), "{:?} → {:?}", s.start, s.end);
// A midpoint stays in the inks: the colours at the ends are unchanged and in DeviceN.
// A midpoint stays in the inks: the colors at the ends are unchanged and in DeviceN.
let (shadings, _) = export(&document(&[(Ok("Gold"), 0.0), (Ok(CYAN), 1.0)], 0.25), Standard::PdfX4);
assert_eq!(shadings[0].family, "DeviceN");
assert!(close(&shadings[0].start, &[0.0, 1.0]) && close(&shadings[0].end, &[1.0, 0.0]));
@@ -156,19 +156,19 @@ fn a_process_gradient_to_paper_prints_in_cmyk() {
assert!(close(&shadings[0].start, &[1.0, 0.0, 0.0, 0.0]) && close(&shadings[0].end, &[0.0, 0.0, 0.0, 0.0]));
}
/// PDF/A forbids DeviceN's PostScript tint transform: the gradient goes to process colour, with
/// PDF/A forbids DeviceN's PostScript tint transform: the gradient goes to process color, with
/// a warning, and the export still succeeds.
#[test]
fn pdf_a_writes_mixed_spot_gradients_in_process_colour() {
fn pdf_a_writes_mixed_spot_gradients_in_process_color() {
let (shadings, warnings) = export(&document(&[(Ok("Gold"), 0.0), (Ok(CYAN), 1.0)], 0.5), Standard::PdfA2b);
assert!(warnings.iter().any(|w| w.contains("'Fade'") && w.contains("spot inks printed as process")), "{warnings:?}");
assert!(shadings.iter().all(|s| s.family != "DeviceN"));
}
/// Stops with plain colours (every document saved before stops named swatches) export as they
/// Stops with plain colors (every document saved before stops named swatches) export as they
/// did: the spot's values in DeviceCMYK.
#[test]
fn plain_colour_stops_keep_their_device_space() {
fn plain_color_stops_keep_their_device_space() {
let gold = Color::cmyk(0.0, 0.3, 0.9, 0.1);
let (shadings, warnings) = export(&document(&[(Err(gold), 0.0), (Err(Color::cmyk(1.0, 0.0, 0.0, 0.0)), 1.0)], 0.5), Standard::PdfX4);
no_conversion(&warnings);
+1 -1
View File
@@ -853,7 +853,7 @@ pub fn gradient_panel(app: &mut DesignApp, ui: &mut egui::Ui) {
let mut rgb = [(r * 255.0) as u8, (gg * 255.0) as u8, (bb * 255.0) as u8];
if egui::color_picker::color_edit_button_srgb(ui, &mut rgb).changed() {
let mut stops = stops_json(&g);
// A picked colour replaces the stop's swatch.
// A picked color replaces the stop's swatch.
if let Some(o) = stops.get_mut(i).and_then(serde_json::Value::as_object_mut) {
o.remove("swatch");
}
+2 -2
View File
@@ -318,7 +318,7 @@ It is evidence of breadth: each item exists. It is not a claim of InDesign-level
- Endnotes (`endnote.insert/edit/delete/list/options`, Type ▸ Insert Endnote): references number through the document, the endnote frame on a new last page lists them under a heading; IDML keeps the listed text only.
- Editorial notes (`note.new/edit/delete/convertToText/list`, Notes panel): anchored in text, flagged on screen, never printed or exported.
- File ▸ Print Booklet (`file.printBooklet`): saddle-stitch or 2-up consecutive printer spreads as PDF, padded with blanks, with a gap between pages.
- Edit ▸ Transparency Blend Space (`edit.transparencyBlendSpace`; RGB for web/mobile documents; IDML TransparencyPreference) — exported PDFs blend in that space: CMYK print exports write their transparency groups and page groups in DeviceCMYK (PDF/X-4 also carries the CMYK working space as its `GTS_PDFX` output intent); RGB exports give pages with transparency a DeviceRGB page group. On screen, CMYK spreads with separable blend modes use two ink-channel passes through the working colour settings. Parent-page items, nested groups, effects and pasteboard share that path; 100% K in Color Dodge whitens coloured areas while paper stays outside the blend backdrop. Effect blend layers sit outside their clips and offscreen filters, preserving their backdrop in RGB too. The pass count is independent of blended-object count, and unchanged viewports reuse one cached raster (64 MiB cap). Spreads containing Hue, Saturation, Color or Luminosity still use the existing RGB blend followed by a CMYK proof approximation; full nonseparable CMYK blending remains open.
- Edit ▸ Transparency Blend Space (`edit.transparencyBlendSpace`; RGB for web/mobile documents; IDML TransparencyPreference) — exported PDFs blend in that space: CMYK print exports write their transparency groups and page groups in DeviceCMYK (PDF/X-4 also carries the CMYK working space as its `GTS_PDFX` output intent); RGB exports give pages with transparency a DeviceRGB page group. On screen, CMYK spreads with separable blend modes use two ink-channel passes through the working color settings. Parent-page items, nested groups, effects and pasteboard share that path; 100% K in Color Dodge whitens colored areas while paper stays outside the blend backdrop. Effect blend layers sit outside their clips and offscreen filters, preserving their backdrop in RGB too. The pass count is independent of blended-object count, and unchanged viewports reuse one cached raster (64 MiB cap). Spreads containing Hue, Saturation, Color or Luminosity still use the existing RGB blend followed by a CMYK proof approximation; full nonseparable CMYK blending remains open.
- High Contrast interface theme (fifth Interface Color Theme: black chrome, white text, yellow selection).
- Place Excel workbooks (.xlsx): the first worksheet's used range becomes a table (shared/inline strings, numbers, booleans).
- Object Library (`library.new/open/add/place/remove/list/json/close`, Library panel): `.dclib` files of snippets that keep stories, styles, swatches and images.
@@ -436,7 +436,7 @@ It is evidence of breadth: each item exists. It is not a claim of InDesign-level
- View › Display Performance: Fast (grey boxes, no effects, ⌥⇧⌘Z), Typical (72 ppi proxies, ⌥⌘Z), High Quality (⌥⌘H, our default — the renderer is fast enough).
- OpenType menu (Character panel): discretionary ligatures, fractions, ordinal, swash, titling, contextual alternates, slashed zero, the four figure styles and stylistic sets 1–20; `type.openType`; mapped to IDML's OTF attributes.
- Add Anchor Point (=), Delete Anchor Point (-) and Convert Direction Point (⇧C) tools (click toggles smooth/corner, drag pulls out handles), with `path.addAnchor`, `path.deleteAnchor`, `path.convertAnchor`.
- Gradients: the Gradient Swatch tool (G) drags a gradient's start and end across objects (Shift: 45°); the Gradient panel (InDesign's layout: thumbnail, Type, Location, Angle, Reverse) sets type, angle, reverse and edits stops and midpoints on a ramp (drag, add, drag off to remove, location; drop a swatch from the Swatches panel on a stop or the ramp; stops keep their CMYK, and a stop on a swatch keeps it, so spot stops shade as Separation or DeviceN in PDF), and follows the Fill/Stroke proxy. Edited gradients are unnamed swatches until the Swatches panel's New Swatch names one "New Gradient Swatch" (`swatch.new`, which also duplicates a named swatch); `swatch.create {gradient}` builds one from colour swatches; `object.gradient` (`target: stroke` for a stroke's gradient, drawn across the path's box on screen and in PDF); the vector round-trips through IDML (`GradientFill…`/`GradientStroke…`). A radial gradient stretches with the object's box as InDesign's does (an ellipse reaching the sides of a non-square frame, on screen and in PDF). Text takes a gradient swatch as its fill or stroke, laid across its frame's box as in InDesign (screen and PDF).
- Gradients: the Gradient Swatch tool (G) drags a gradient's start and end across objects (Shift: 45°); the Gradient panel (InDesign's layout: thumbnail, Type, Location, Angle, Reverse) sets type, angle, reverse and edits stops and midpoints on a ramp (drag, add, drag off to remove, location; drop a swatch from the Swatches panel on a stop or the ramp; stops keep their CMYK, and a stop on a swatch keeps it, so spot stops shade as Separation or DeviceN in PDF), and follows the Fill/Stroke proxy. Edited gradients are unnamed swatches until the Swatches panel's New Swatch names one "New Gradient Swatch" (`swatch.new`, which also duplicates a named swatch); `swatch.create {gradient}` builds one from color swatches; `object.gradient` (`target: stroke` for a stroke's gradient, drawn across the path's box on screen and in PDF); the vector round-trips through IDML (`GradientFill…`/`GradientStroke…`). A radial gradient stretches with the object's box as InDesign's does (an ellipse reaching the sides of a non-square frame, on screen and in PDF). Text takes a gradient swatch as its fill or stroke, laid across its frame's box as in InDesign (screen and PDF).
- New Document (⌘N) as in InDesign: Recent, Saved, Print, Web and Mobile tabs of page cards (double-click creates) beside the preset's details: document name, size with arrows, units, orientation, pages, start page #, facing pages, primary text frame, columns, margins, bleed and slug per edge (linked or not); Save Document Preset (`file.savePreset`, `file.deletePreset`; saved presets are kept with the preferences, listed by `file.presets` and made by `file.new {preset: name}`); `file.new` takes `intent`, `units`, `startPage` and per-edge `bleed`/`slug`. Preview shows a document with the window's settings in the workspace while it is open; Create keeps it, Close removes it.
- Document Setup (⌥⌘P), laid out like InDesign's: intent, number of pages, start page # (an even start begins with a left page), facing pages, binding, Page Size (presets keeping the orientation, width and height with arrows, orientation), margins for every page and parent page, bleed and slug per edge (each group linked or not), and Adjust Layout (objects follow the new page size and margins), all in one undo step; measures are read in the document's units, and a value that can't be read keeps the dialog open; `layout.documentSetup {}` reports the setup, margins included.
- Stroke panel: cap, join, miter limit, alignment, type, Start/End arrowheads (12 kinds, drawn on screen and in PDF; the path is shortened under pointed heads) and gap colour under dashes and dots; with text selected (or text frames selected with Formatting Affects Text on) it shows the text's stroke weight, colour and Stroke Behind Fill.