Merge pull request #879 from storytold/fix/831-acb-color-books
Fix #831: color books (.acb) open as swatch libraries
This commit is contained in:
commit
5b26fd4d01
|
|
@ -153,7 +153,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
|
|||
- **Image Trace Flat Logo mode:** a fourth mode beside Black and White, Grayscale and Color, for flat-colour logos. Each pixel is read as a blend of the logo's colours (found automatically or named), the blend is interpolated smoothly and every outline is placed where two colours are equally present, so edges are accurate to a fraction of a pixel. Outlines are then cleaned: corners the blur rounded are put back, near-straight runs and near-circular arcs become exact lines and arcs, the rest is fitted with as few Béziers as the tolerance allows, touching colours overlap by a hair so no gap opens, and an outline that strays from the raw contour is fitted plainly instead. Gradients and photos are refused with advice to use Color. *Measured* (a local experiment, not run in CI): a flat-colour logo's vector original was rendered down to a soft 199 × 100 px transparent PNG, traced, and the result compared with the original at 1600 px wide. The built-in colour presets (6 Colors, 16 Colors, High Fidelity Photo, Low Fidelity Photo) had a mean edge error of 2.5 to 3.4 px and region overlap of 0.88 to 0.91; Flat Logo had 0.78 px and 0.966 (0.48 px and 0.975 from a crisp source), in about 0.1 s. Limits: flat-colour art only; needle-thin sharp tips are shortened by about 1.7 source pixels because the blur erases them; it works at no more than 1024 px on the long side (larger images are averaged down first); only one logo family has been scored against a vector original.
|
||||
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type (choosing a perspective tool shows the grid; Hide Grid hides it with the tool still chosen), Gradient Mesh, Shape Builder (stroke-only open paths and lines cut the regions they cross and enclose regions of their own; filled open paths count as closed; erase mode while Alt/Option is held: a minus pointer, red highlights of what will go, a click or drag deletes the regions touched, the pieces of open lines and the edges (outline pieces between the points where paths meet, opening their paths there) it passes over, one undo step, `shapeBuilder.merge {erase}` and `shapeBuilder.regions` edges over MCP), Live Paint (every path an edge, open lines included), Image Trace (12 presets, colour layers traced in parallel; a trace past two million anchor points or an image over 64 megapixels is refused with advice rather than running out of memory, and a selection too large to outline shows its bounds (#525); Object › Image Trace › Make and Make and Expand trace at once with the Default preset, #543; with an image selected the Control bar and the Properties panel show Image Trace, whose arrow traces with any preset, Mask (`object.maskImage`: a clipping path on the image's outline, selected so its handles crop), Crop Image (a crop box on the image, starting on its part over the artboard, dragged by its handles or inside it, the outside dimmed, with X/Y/W/H, Apply and Cancel in the Control bar, Enter and Escape, #734; `object.cropImage {rect}` for agents, `{trim: true}` cuts an image to its pixels that aren't fully transparent, pixel for pixel) and Embed or Unembed in place of Fill and Stroke; with an Image Trace object selected, its Preset (choosing another traces again), View, the Image Trace panel and Expand, plus Release in Properties; the five View modes (Tracing Result, with Outlines, Outlines, Outlines with Source Image, Source Image) redraw a trace on screen without tracing again, are saved with it and `imageTrace.setView` sets them, while exports and Expand keep the result; the panel's Advanced section is a header that stays open or closed as it was left (part of #641); Color mode's Palette: Limited, Full Tone (as many colours as the tones need, Less … More), Automatic (flat art with its own colours, else Full Tone; a heuristic) and Document Library (the most used colours of the document's swatches or a swatch library, filled with the swatch colours as they are), part of #641; Advanced › Create: Fills and/or Strokes, where lines up to the Stroke width trace as stroked centre lines (distance transform, thinning, a skeleton graph with spurs pruned, curve fitting; one width per line; without Fills, wider areas become stroked outlines), part of #641), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror).
|
||||
- **Paint and appearance (M3):**
|
||||
- **Swatches:** names follow the colour model and are unique; Swatch Options (process/spot, Global, Gray/RGB/HSB/CMYK/Web, live preview); editing a global or spot swatch recolours every linked fill, stroke and text run in one undo step; multi-select and delete with confirmation; New Swatch and New Color Group dialogs (a group from the selected artwork's colours); swatches in colour groups convert, separate and count like any other. The Swatches panel selects ranges and whole groups, has a find field, and drags swatches to reorder, regroup or paint art; Add Used Colors, Select All Unused, Merge, Ungroup and Sort by Kind; a CMYK document keeps applied colours in CMYK. Swatch libraries: nine generated colour libraries and five gradient libraries in a library panel, saved as `.vcswatches`, `.gpl`, swatch exchange (`.ase`) or CSS, and loaded from `.vcswatches`, `.gpl` and `.ase` files (`.ase` both ways: RGB, CMYK, Lab and Gray; global, spot and process; groups); `swatch.library.copyToUser` copies a loaded library into the User Defined folder without replacing a file, and a file with the same extension and bytes as a User Defined library opens as that library. Tints of global and spot colours ("Name 40%") stay linked and separate as a percentage of their plate. Spot colours can be defined in Lab (Spot Colors options). A built-in [Registration] swatch prints on every plate.
|
||||
- **Swatches:** names follow the colour model and are unique; Swatch Options (process/spot, Global, Gray/RGB/HSB/CMYK/Web, live preview); editing a global or spot swatch recolours every linked fill, stroke and text run in one undo step; multi-select and delete with confirmation; New Swatch and New Color Group dialogs (a group from the selected artwork's colours); swatches in colour groups convert, separate and count like any other. The Swatches panel selects ranges and whole groups, has a find field, and drags swatches to reorder, regroup or paint art; Add Used Colors, Select All Unused, Merge, Ungroup and Sort by Kind; a CMYK document keeps applied colours in CMYK. Swatch libraries: nine generated colour libraries and five gradient libraries in a library panel, saved as `.vcswatches`, `.gpl`, swatch exchange (`.ase`) or CSS, and loaded from `.vcswatches`, `.gpl` and `.ase` files (`.ase` both ways: RGB, CMYK, Lab and Gray; global, spot and process; groups) and from color books (`.acb`, read only: the RGB, CMYK or Lab colors of a book such as a Pantone book a user owns, named with its prefix and suffix, spot unless it says process, #831); `swatch.library.copyToUser` copies a loaded library into the User Defined folder without replacing a file, and a file with the same extension and bytes as a User Defined library opens as that library. Tints of global and spot colours ("Name 40%") stay linked and separate as a percentage of their plate. Spot colours can be defined in Lab (Spot Colors options). A built-in [Registration] swatch prints on every plate.
|
||||
- **Colour:** a Color Picker (colour field, H/S/B/R/G/B channel slider, HSB/RGB/Lab/CMYK fields, hex, web-only snapping, out-of-gamut correction); the Color panel follows each colour's own model and Alt-click paints the other proxy; proxies look through groups and show "?" for mixed paint; Invert, Complement, Apply Last Color (`,`) and Apply Last Gradient (`.`) keep the colour model; every paint command feeds the recent colours. The Color Guide has 21 harmony rules, Steps and Variation options and Limit to Library. Adjust Color Balance (its Global mode shifts tints) and Saturate preview live; Edit Colors and Recolor also reach meshes, images and patterns. Recolor Artwork has colour reduction, five recolour methods, presets, Limit to Library, a colour-wheel Edit tab and Edit Color Group; a local Color Themes panel saves five-colour themes. Fill/Stroke chips open swatch and mixer popovers (the Properties panel has the Control bar's Fill and Stroke chips, Stroke link and weight spinner, with nothing selected too), panels have Illustrator's Window menu keys (Align Shift+F7, Transform Shift+F8, Pathfinder Cmd+Shift+F9, Layers F7, Character Cmd+T…; every panel can be given one in Edit › Keyboard Shortcuts), and Eyedropper Options pick up and apply chosen attributes (Alt and Shift+Alt, image pixels). An ICC CMYK profile converts straight into the RGB working space (not through sRGB, so a wider space such as Wide Gamut RGB keeps colours sRGB can't hold) with the colour settings' black-point compensation, on the canvas and in RGB exports alike. Fills and strokes can overprint (Attributes panel, Overprint Black options), and CMYK documents blend in inks on screen, in PDF and when flattening. Document Color Mode converts the art, symbols, pattern tiles and swatches through the colour settings: Gray stays Gray, RGB greys separate into four-colour greys and a rich black as Illustrator does, or go on the black plate only with `grays: "black"` (also for `document.open {colorMode}`).
|
||||
- **Gradients:** lossless, validated gradient params; gradients follow rotate, reflect, shear, non-uniform scale and distortions; the Gradient tool edits the fill or stroke of the active proxy, type included; an interactive on-canvas annotator (move, length/angle, rotate, add/move/delete/duplicate stops, midpoints, a stop popover, Delete and arrow keys). The Gradient panel has the Fill/Stroke proxy, a gradient-swatch dropdown with Save to Swatches, midpoint locations, Alt-drag to copy or swap stops, swatch drops on the ramp and a stop eyedropper; copied appearances and graphic styles place gradients on each target's own bounds; gradient handles snap and honour Constrain Angle. Gradient stops stay linked to global and spot swatches. Freeform gradients (points and lines) are edited on the canvas; radial gradients have an extent ellipse, an aspect handle and an off-centre focal point (SVG `fx`/`fy`, PDF two-point radials). A stroke's gradient runs within, along or across it. Object ▸ Expand turns gradients into N objects or a gradient mesh.
|
||||
- **Strokes:** one stroke-geometry module shared by the canvas, SVG, PDF and Outline Stroke; dotted lines (zero-length dashes draw dots or squares); arrowheads with hollow outlines, tip-on-end or extend-past-end alignment and one opacity for line and head. SVG and PDF export arrowheads, width profiles, brushes and aligned strokes exactly as the canvas draws them; Outline Stroke and the live Outline Stroke effect outline every stroke item as drawn (dashes, heads, profiles, alignment); dashes can be fitted to corners and path ends; width profiles carry through dashes, and their corners take the join. The Stroke panel edits type's character strokes, follows the stroke units (its weight presets are round values in that unit: `0.25 mm`, `0.0625 in`, `1 px`, #426; fields show four decimals in mm, cm and inches, three in points), shows mixed values blank and opens from the Control bar. A width profile library, thirty more arrowheads, and Width Point Edit with multi-select and discontinuous points. Visual bounds and clicks cover the whole painted stroke. Scale Strokes & Effects, Scale Corners and Use Preview Bounds work everywhere, and new art inherits stroke options, a graphic style or the whole last appearance.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
//! Swatch library files: the native `.vcswatches` JSON (colour models, global, spot, gradients
|
||||
//! and colour groups exactly), `.gpl` palettes (8-bit RGB; colour groups as `# Group:` comment
|
||||
//! headers), swatch exchange `.ase` files (binary: solid colors in their own model, global, spot
|
||||
//! and process colors, color groups) and CSS custom properties (written only).
|
||||
//! and process colors, color groups), color books `.acb` (read only: the books a user owns, such
|
||||
//! as a Pantone book) and CSS custom properties (written only).
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
|
|
@ -79,6 +80,8 @@ const ASE_MAX_ENTRIES: usize = 100_000;
|
|||
/// The most UTF-16 code units of a name that are kept.
|
||||
const ASE_MAX_NAME: usize = 1024;
|
||||
const ASE_CUT_SHORT: &str = "the swatch exchange file is cut short";
|
||||
const ACB_SIGNATURE: &[u8] = b"8BCB";
|
||||
const ACB_CUT_SHORT: &str = "the color book is cut short";
|
||||
|
||||
/// Write `lib` in `format` → the file's bytes. Pattern swatches (their tiles live in a document)
|
||||
/// and None are left out; `.gpl` keeps solid colours only, as 8-bit RGB, and `.ase` keeps solid
|
||||
|
|
@ -326,6 +329,9 @@ pub fn read_bytes(bytes: &[u8], name: &str) -> Result<SwatchLibrary, String> {
|
|||
if bytes.starts_with(ASE_SIGNATURE) {
|
||||
return read_ase(bytes, name);
|
||||
}
|
||||
if bytes.starts_with(ACB_SIGNATURE) {
|
||||
return read_acb(bytes, name).map_err(|e| if e == ASE_CUT_SHORT { ACB_CUT_SHORT.into() } else { e });
|
||||
}
|
||||
read(&String::from_utf8_lossy(bytes), name)
|
||||
}
|
||||
|
||||
|
|
@ -333,6 +339,7 @@ pub fn read_bytes(bytes: &[u8], name: &str) -> Result<SwatchLibrary, String> {
|
|||
/// [`read_bytes`] reads it, with the invalid bytes replaced (its first KiB is enough).
|
||||
pub fn sniff_bytes(bytes: &[u8]) -> bool {
|
||||
bytes.starts_with(ASE_SIGNATURE)
|
||||
|| bytes.starts_with(ACB_SIGNATURE)
|
||||
|| std::str::from_utf8(bytes).is_ok_and(sniff)
|
||||
|| sniff(&String::from_utf8_lossy(bytes.get(..1024).unwrap_or(bytes)))
|
||||
}
|
||||
|
|
@ -427,6 +434,67 @@ fn read_ase(bytes: &[u8], fallback: &str) -> Result<SwatchLibrary, String> {
|
|||
Ok(lib)
|
||||
}
|
||||
|
||||
/// `.acb` (a color book, read only), from public descriptions of the format: after the signature
|
||||
/// come, big-endian, a `u16` version (1) and book id, the title, prefix, suffix and description
|
||||
/// as strings, a `u16` color count, page size and page selector offset, a `u16` color space (0
|
||||
/// RGB, 2 CMYK, 7 Lab) and the colors: each a name, a six-character catalog code and its
|
||||
/// components as bytes. RGB runs from 0 to 255, CMYK ink is stored inverted (255 is no ink), Lab
|
||||
/// lightness is 0 to 255 for 0 to 100 and a and b are offset by 128. A string is a `u32` count of
|
||||
/// UTF-16 code units, then the units; a `$$$/key=Text` string means `Text`, and `^R`, `^C` stand
|
||||
/// for ® and ©. An optional `spflspot` or `spflproc` at the end says whether the colors are spot
|
||||
/// or process colors (a book without it is a spot color book).
|
||||
///
|
||||
/// The book's colors are named with its prefix and suffix ("PANTONE 185 C"); colors without a
|
||||
/// name (the padding of a book's pages) are left out. A version other than 1, a color space other
|
||||
/// than those three or more than [`ASE_MAX_ENTRIES`] colors is an error.
|
||||
fn read_acb(bytes: &[u8], fallback: &str) -> Result<SwatchLibrary, String> {
|
||||
let mut data = AseData(bytes);
|
||||
data.bytes(ACB_SIGNATURE.len())?;
|
||||
let version = data.u16()?;
|
||||
if version != 1 {
|
||||
return Err(format!("color book version {version} isn't supported (only 1)"));
|
||||
}
|
||||
let _book_id = data.u16()?;
|
||||
let [title, prefix, suffix, _description] = [data.acb_string()?, data.acb_string()?, data.acb_string()?, data.acb_string()?];
|
||||
let count = usize::from(data.u16()?);
|
||||
if count > ASE_MAX_ENTRIES {
|
||||
return Err(format!("the color book holds more than {ASE_MAX_ENTRIES} colors"));
|
||||
}
|
||||
let (_page_size, _page_offset) = (data.u16()?, data.u16()?);
|
||||
let space = data.u16()?;
|
||||
let mut colors = Vec::with_capacity(count);
|
||||
for _ in 0..count {
|
||||
let name = data.acb_string()?;
|
||||
let _code = data.bytes(6)?;
|
||||
let color = match space {
|
||||
0 => {
|
||||
let [r, g, b] = data.array::<3>()?;
|
||||
Color::rgb8(r, g, b)
|
||||
}
|
||||
2 => {
|
||||
let ink = |v: u8| f32::from(255 - v) / 255.0;
|
||||
let [c, m, y, k] = data.array::<4>()?;
|
||||
Color::cmyk(ink(c), ink(m), ink(y), ink(k))
|
||||
}
|
||||
7 => {
|
||||
let [l, a, b] = data.array::<3>()?;
|
||||
Color::lab(f32::from(l) * 100.0 / 255.0, f32::from(a) - 128.0, f32::from(b) - 128.0)
|
||||
}
|
||||
other => return Err(format!("color books in color space {other} aren't supported (only RGB, CMYK and Lab)")),
|
||||
};
|
||||
colors.push((name, color));
|
||||
}
|
||||
// Spot colors unless the book says process ones.
|
||||
let spot = data.0.windows(8).last() != Some(b"spflproc".as_slice());
|
||||
let mut lib = SwatchLibrary { name: if title.is_empty() { fallback.into() } else { title }, ..Default::default() };
|
||||
let mut names = UniqueNames::default();
|
||||
for (name, color) in colors.into_iter().filter(|(n, _)| !n.trim().is_empty()) {
|
||||
let name = names.unique(format!("{prefix}{name}{suffix}").trim().to_string());
|
||||
lib.swatches.push(Swatch { name, paint: Paint::solid(color), global: spot, spot });
|
||||
}
|
||||
Ok(lib)
|
||||
}
|
||||
|
||||
/// The swatch of an `.ase` color block, `None` for a model other than RGB, CMYK, Lab and Gray.
|
||||
/// Components are clamped to their ranges.
|
||||
fn ase_swatch(body: &mut AseData) -> Result<Option<Swatch>, String> {
|
||||
|
|
@ -503,6 +571,20 @@ impl<'a> AseData<'a> {
|
|||
let v = f32::from_be_bytes(self.array()?);
|
||||
Ok(if v.is_finite() { v } else { 0.0 })
|
||||
}
|
||||
/// A color book string ([`read_acb`]): a `u32` count of UTF-16 code units, then the units,
|
||||
/// with `$$$/key=Text` read as `Text` and `^R`, `^C` as ®, ©; at most [`ASE_MAX_NAME`] units
|
||||
/// are kept.
|
||||
fn acb_string(&mut self) -> Result<String, String> {
|
||||
let n = usize::try_from(self.u32()?).unwrap_or(usize::MAX);
|
||||
let (units, _) = self.bytes(n.checked_mul(2).ok_or(ASE_CUT_SHORT)?)?.as_chunks::<2>();
|
||||
let units: Vec<u16> = units.iter().take(ASE_MAX_NAME).map(|u| u16::from_be_bytes(*u)).take_while(|&u| u != 0).collect();
|
||||
let s = String::from_utf16_lossy(&units);
|
||||
let s = match s.strip_prefix("$$$/") {
|
||||
Some(key) => key.split_once('=').map_or("", |(_, text)| text).to_string(),
|
||||
None => s,
|
||||
};
|
||||
Ok(s.replace("^R", "®").replace("^C", "©"))
|
||||
}
|
||||
/// A name up to its terminating zero, trimmed. A block that ends before it has no name.
|
||||
fn name(&mut self) -> Result<String, String> {
|
||||
if self.0.is_empty() {
|
||||
|
|
@ -553,6 +635,68 @@ mod tests {
|
|||
use crate::{Gradient, GradientPaint, GradientStop};
|
||||
use proptest::prelude::*;
|
||||
|
||||
/// A color book's string: a `u32` count of UTF-16 code units, then the units.
|
||||
fn acb_str(out: &mut Vec<u8>, s: &str) {
|
||||
let units: Vec<u16> = s.encode_utf16().collect();
|
||||
out.extend((units.len() as u32).to_be_bytes());
|
||||
units.iter().for_each(|u| out.extend(u.to_be_bytes()));
|
||||
}
|
||||
|
||||
/// A color book in `space` (0 RGB, 2 CMYK, 7 Lab) with `colors` (name, components) and an
|
||||
/// optional spot/process marker.
|
||||
fn acb(space: u16, colors: &[(&str, &[u8])], marker: &[u8]) -> Vec<u8> {
|
||||
let mut b = b"8BCB".to_vec();
|
||||
b.extend(1u16.to_be_bytes());
|
||||
b.extend(3000u16.to_be_bytes());
|
||||
for s in ["$$$/colorbook/Test/title=Test Book^R", "TEST ", " C", "a book"] {
|
||||
acb_str(&mut b, s);
|
||||
}
|
||||
b.extend((colors.len() as u16).to_be_bytes());
|
||||
b.extend(7u16.to_be_bytes());
|
||||
b.extend(0u16.to_be_bytes());
|
||||
b.extend(space.to_be_bytes());
|
||||
for (name, comps) in colors {
|
||||
acb_str(&mut b, name);
|
||||
b.extend(b"TST001");
|
||||
b.extend(*comps);
|
||||
}
|
||||
b.extend(marker);
|
||||
b
|
||||
}
|
||||
|
||||
/// #831: color books (`.acb`) are read: their colors in RGB, CMYK or Lab, named with the book's
|
||||
/// prefix and suffix, spot unless the book says process; page padding is left out.
|
||||
#[test]
|
||||
fn color_books_are_read() {
|
||||
let lib = read_bytes(&acb(0, &[("185", &[228, 0, 43]), ("", &[0, 0, 0]), ("Red 032", &[239, 51, 64])], b"spflspot"), "fallback").unwrap();
|
||||
assert_eq!(lib.name, "Test Book®");
|
||||
let names: Vec<&str> = lib.swatches.iter().map(|s| s.name.as_str()).collect();
|
||||
assert_eq!(names, ["TEST 185 C", "TEST Red 032 C"]);
|
||||
assert!(lib.swatches.iter().all(|s| s.spot && s.global));
|
||||
assert_eq!(lib.swatches[0].paint.color(), Some(Color::rgb8(228, 0, 43)));
|
||||
// CMYK ink is stored inverted; a book marked process gives process colors.
|
||||
let lib = read_bytes(&acb(2, &[("Cyan", &[0, 255, 255, 255])], b"spflproc"), "x").unwrap();
|
||||
assert_eq!(lib.swatches[0].paint.color(), Some(Color::cmyk(1.0, 0.0, 0.0, 0.0)));
|
||||
assert!(!lib.swatches[0].spot && !lib.swatches[0].global);
|
||||
// Lab: lightness 0..255 for 0..100, a and b offset by 128; no marker: spot.
|
||||
let lib = read_bytes(&acb(7, &[("Grey", &[255, 128, 128])], b""), "x").unwrap();
|
||||
assert_eq!(lib.swatches[0].paint.color(), Some(Color::lab(100.0, 0.0, 0.0)));
|
||||
assert!(lib.swatches[0].spot);
|
||||
assert!(sniff_bytes(&acb(0, &[], b"")));
|
||||
// Unsupported versions and color spaces, and short files, are errors.
|
||||
let mut v2 = acb(0, &[], b"");
|
||||
v2[5] = 2;
|
||||
assert!(read_bytes(&v2, "x").is_err());
|
||||
assert!(read_bytes(&acb(1, &[("x", &[1, 2, 3])], b""), "x").is_err());
|
||||
let full = acb(0, &[("185", &[228, 0, 43])], b"");
|
||||
for cut in 4..full.len() {
|
||||
assert!(read_bytes(&full[..cut], "x").is_err(), "{cut} bytes");
|
||||
}
|
||||
let mut huge = b"8BCB\0\x01\0\x01".to_vec();
|
||||
huge.extend(u32::MAX.to_be_bytes());
|
||||
assert!(read_bytes(&huge, "x").is_err(), "a string longer than the file");
|
||||
}
|
||||
|
||||
fn sample() -> SwatchLibrary {
|
||||
let grad = Paint::Gradient(Box::new(GradientPaint::new(Gradient {
|
||||
kind: GradientKind::Linear,
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"Other Library…",
|
||||
["Window", "Swatch Libraries"],
|
||||
None,
|
||||
"{path? | data?: file text | dataBase64?, name?: file name (default: the path's)} load a .vcswatches, .gpl or .ase (swatch exchange) library, or the swatches of any document VectorCraft opens (see document.formats), for the library panel (Window → Swatch Libraries lists it until the app quits; a file of the user library folder, or a file with the same extension and bytes as one there, opens as that User Defined library) → {library: id, name, count}",
|
||||
"{path? | data?: file text | dataBase64?, name?: file name (default: the path's)} load a .vcswatches, .gpl, .ase (swatch exchange) or .acb (color book, read only) library, or the swatches of any document VectorCraft opens (see document.formats), for the library panel (Window → Swatch Libraries lists it until the app quits; a file of the user library folder, or a file with the same extension and bytes as one there, opens as that User Defined library) → {library: id, name, count}",
|
||||
always,
|
||||
load
|
||||
),
|
||||
|
|
@ -130,7 +130,7 @@ impl LibraryFile for SwatchLibrary {
|
|||
}
|
||||
|
||||
/// The extensions of library files [`palette_io::read_bytes`] reads.
|
||||
pub const LIBRARY_EXTS: &[&str] = &["vcswatches", "gpl", "ase"];
|
||||
pub const LIBRARY_EXTS: &[&str] = &["vcswatches", "gpl", "ase", "acb"];
|
||||
|
||||
/// A library file the save commands write ([`Libraries::write`]).
|
||||
pub(crate) enum FileData {
|
||||
|
|
|
|||
|
|
@ -819,9 +819,11 @@ groups; `.gpl` is 8-bit RGB; a swatch exchange `.ase` file keeps solid colors in
|
|||
as the color it shows and leaves gradients out; CSS writes custom properties); without `path`
|
||||
it returns `{data}`, or `{dataBase64}` for `.ase`, and `user: true` saves into the user library
|
||||
folder of the desktop app (listed as category `user`, User Defined). `swatch.library.load {path? | data? |
|
||||
dataBase64?, name?}` loads a `.vcswatches`, `.gpl` or swatch exchange (`.ase`) file, or another document's swatches,
|
||||
as a library to add from. From an `.ase` file it reads RGB, CMYK, Lab and Gray colors as global, spot or process
|
||||
swatches and keeps their color groups. A file in the user library folder, or a file with the
|
||||
dataBase64?, name?}` loads a `.vcswatches`, `.gpl`, swatch exchange (`.ase`) or color book (`.acb`) file, or another
|
||||
document's swatches, as a library to add from. From an `.ase` file it reads RGB, CMYK, Lab and Gray colors as global,
|
||||
spot or process swatches and keeps their color groups. A color book (such as a Pantone book you own) gives its RGB,
|
||||
CMYK or Lab colors named with the book's prefix and suffix ("PANTONE 185 C"), as spot colors unless the book marks
|
||||
them as process colors; `.acb` files are read, never written. A file in the user library folder, or a file with the
|
||||
same extension and bytes as one there, loads as that User Defined library (category `user`).
|
||||
`swatch.library.copyToUser {library}` copies a loaded library into the user library folder of the desktop
|
||||
app (a library file as it is; a document's swatches or a library loaded from `data` or `dataBase64`
|
||||
|
|
|
|||
Loading…
Reference in New Issue