Fix #4937: artboards can have a background fill, included in exports and print

This commit is contained in:
bflatastic
2026-11-01 04:13:57 +07:00
parent aab25476bb
commit 9c17301c46
42 changed files with 309 additions and 18 deletions
+33 -2
View File
@@ -35,8 +35,8 @@ pub struct Artboard {
pub video_ruler_pixel_aspect_ratio: f64,
/// Background color (Properties › Artboards › Fill, Artboard Options). The canvas draws the
/// artboard's paper in it, multiplied with the Simulate Colored Paper color while that is on;
/// without one the paper is white, or the Simulate Colored Paper color. Exports and print leave
/// it out.
/// without one the paper is white, or the Simulate Colored Paper color. Exports and print draw
/// it behind the art ([`super::Document::with_artboard_fills`]).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<vectorcraft_color::Color>,
}
@@ -77,6 +77,37 @@ impl Artboard {
}
}
impl super::Document {
/// A copy for output with the background colors ([`Artboard::color`]) of the artboards at
/// `indices` drawn behind all the art: each a rectangle of its artboard filled with its color
/// (in the color's own model), on a layer of their own (Artboard Fill) below the
/// others. `None` when none of them has a color.
pub fn with_artboard_fills(&self, indices: impl IntoIterator<Item = usize>) -> Option<Self> {
let fills: Vec<(Rect, vectorcraft_color::Color)> = indices
.into_iter()
.filter_map(|i| {
let a = self.artboards.get(i)?;
Some((a.rect, a.color?))
})
.collect();
if fills.is_empty() {
return None;
}
let mut d = self.clone();
let mut layer = Node::layer(d.alloc_id(), "Artboard Fill", super::LayerColor::Preset(0));
let paths: Vec<Arc<Node>> = fills
.into_iter()
.map(|(r, c)| {
let look = super::Appearance::basic(vectorcraft_color::Paint::solid(c), vectorcraft_color::Paint::None, 0.0);
Arc::new(Node::path(d.alloc_id(), vectorcraft_geom::shapes::rectangle(r), look))
})
.collect();
*layer.children_mut()? = paths;
d.layers.insert(0, Arc::new(layer));
Some(d)
}
}
/// A document's locked artboards ([`Document::locked_artboards`]).
pub struct LockedArtboards<'a> {
pub(crate) rects: Vec<Rect>,
+23
View File
@@ -55,6 +55,29 @@ fn doc_with_rects() -> (Document, NodeId, NodeId) {
(d, a, b)
}
/// Output draws the background colors of the artboards asked for, and only theirs, as rectangles
/// of those artboards on a layer below the art; without a color there is nothing to draw.
#[test]
fn artboard_fills_go_below_the_art() {
let (mut d, a, _) = doc_with_rects();
assert!(d.with_artboard_fills([0]).is_none(), "no color, no fill");
let second = Rect::new(700.0, 0.0, 800.0, 100.0);
d.artboards.push(Artboard { id: 2, rect: second, ..d.artboards[0].clone() });
let blue = vectorcraft_color::Color::cmyk(1.0, 0.5, 0.0, 0.0);
d.artboards[1].color = Some(blue);
assert!(d.with_artboard_fills([0]).is_none(), "only the artboards asked for");
let out = d.with_artboard_fills([0, 1]).unwrap();
assert_eq!(out.layers.len(), d.layers.len() + 1);
assert_eq!(out.layers[1..], d.layers[..], "the art stays as it was, above the fills");
let fills = out.layers[0].children().unwrap();
assert_eq!(out.layers[0].display_name(), "Artboard Fill");
assert_eq!(fills.len(), 1);
assert_eq!(fills[0].geometric_bounds(), Some(second));
assert_eq!(fills[0].appearance.fill_paint(), vectorcraft_color::Paint::solid(blue), "in the color's own model");
assert!(out.node(a).is_some() && out.node(fills[0].id).is_some() && fills[0].id != a);
assert_eq!(d.artboards, out.artboards);
}
#[test]
fn new_document_has_layer_and_artboard() {
let d = Document::new(612.0, 792.0);
+3
View File
@@ -57,6 +57,7 @@ pub use dxf::{UNSUPPORTED, Unsupported, unsupported};
pub use encode::{ARTBOARD_PARAMS, ArtboardPick, Encoded, encode, encode_all, encode_with_warnings};
pub(crate) use encode::{anti_alias, background, with_single_artboard};
pub use export::export_source;
pub(crate) use export::with_artboard_fills;
pub(crate) use export::{export_crop, isolated};
pub(crate) use load::check_not_lossy_overwrite;
use load::err;
@@ -132,6 +133,8 @@ mod tests;
#[cfg(test)]
mod tests_affinity;
#[cfg(test)]
mod tests_artboard_fill;
#[cfg(test)]
mod tests_pdf;
#[cfg(test)]
mod tests_pdfcolor;
+3 -3
View File
@@ -36,7 +36,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Export Document",
[],
None,
"{path?, format?: svg|svgz|pdf|ai|png|jpg|webp|gif|png8|txt|dxf|eps|emf|wmf|tiff|bmp|tga|psd|vectorcraft|template (default: from the path's extension, else png; png8 writes an indexed .png; ait, a .ait template, is written by file.saveAsTemplate; an exporter plug-in's format is plugin.<plug-in id>, which an extension only it lists picks too), params?: {…} (an exporter plug-in's parameters; missing ones take their defaults, see plugin.info), selectedOnly?: false (the selected objects alone, in their layers), artboard?: 0, artboards?: [i…], rect?: [x, y, width, height] (raster formats: that area of the page in points, y down, instead of an artboard; not with artboard, artboards, range or useArtboards), range?: \"1-3, 5\" | \"all\" (1-based; PDF and .ai write one page per artboard, default all; SVG writes one file per artboard, {stem}-{artboard}.svg; raster formats write one artboard), useArtboards?: true (raster: one file per chosen artboard, default all, {stem}-{artboard}.{ext}; pdf/ai: every page) | false (pdf/ai/raster: the bounds of the visible art; SVG has it as an SVG option), ppi?: 72 (raster formats: pixels per inch, stored in the file; wins over scale), scale?: 1 (pixels per point), background?: transparent|white|black|\"#rrggbb\" (jpg: white when transparent), antiAlias?: none|art (default)|type (text snapped to pixels), interlaced?: false (png, Adam7), embedIcc?: true (png: an sRGB chunk marks the colours as sRGB), quality?: 90 (jpg: 0–100), colorModel?: rgb|cmyk|gray (jpg), method?: baseline|optimized|progressive, scans?: 3 (3–5, progressive), embedIcc?: true, imageMap?: none|client|server (an HTML or NCSA map of the objects with a URL, written as <stem>.html / <stem>.map), colors?: 256 (gif/png8: 2–256), reduction?: perceptual|selective (default)|adaptive|web|blackWhite|gray, dither?: none|diffusion (default)|pattern|noise, ditherAmount?: 100, transparency?: true, matte?: white|\"#rrggbb\"|none, interlaced?, lossless?: true (webp: lossy WebP isn't available yet: written lossless, with a warning), encoding?: utf8|utf16 (txt: the stories in stacking order, back to front, a thread once; utf16 with a byte order mark), lineEndings?: lf|crlf, selectionOnly?: false, svg?: {styling, outlineText, images, objectIds, decimals, minify, responsive, useArtboards, preserveEditing, metadata, fewerTspans, hiddenLayers, encoding, profile, embedFonts} (see document.formats), …the SVG options given flat instead, …the PDF options of document.exportPdf, …the DXF options of document.exportDxf (useArtboards: one drawing per artboard), …the EPS options of document.exportEps (useArtboards: one file per artboard, {stem}_{artboard}.eps, after a central {stem}.eps of the whole document when there are two or more and art shows; else the visible art), …emf and wmf write one picture of the artboard (useArtboards: true one file per chosen artboard, false the bounds of the visible art; EMF keeps curves, clipping, transparent images and gradients as images clipped to their shape; WMF flattens curves into polygons behind a placeable header; what a format leaves out comes back in warnings), colorModel?: rgb|cmyk|gray (tiff: rgb keeps transparency as an alpha channel), lzw?: true, byteOrder?: little|big, embedIcc?: true, colorModel?: rgb|gray (bmp), depth?: 24 (bmp: 1 black and white, 4|8 a palette by reduction and dither, 16, 24, 32 with alpha), fileFormat?: windows|os2 (1, 4, 8 or 24 bits), rle?: false (4 and 8 bits, windows), flipRows?: false (top-down rows), depth?: 24 (tga: 16 one-bit alpha, 24, 32 with alpha), colorModel?: rgb|cmyk|gray (psd), layers?: true (each top-level layer a pixel layer with its opacity and blend mode; a background colour is a Background layer; false: one flat image, on white), maxEditability?: false (layers and sublayers become groups, each object a layer named as in the Layers panel, text by its text; clipping, masked or knockout layers stay one layer), hiddenLayers?: false (hidden layers and objects written hidden instead of left out), embedIcc?: true (at most 30000 pixels a side; formats without alpha are flattened on white)} → {path, format, bytes, warnings, files?: [path…] (several), linked?: [path…] (linked images, image maps)}; no path → {dataBase64, format, bytes, warnings, files?: [{name, dataBase64}], linked?: [{name, dataBase64}]}. Never changes the document's path",
"{path?, format?: svg|svgz|pdf|ai|png|jpg|webp|gif|png8|txt|dxf|eps|emf|wmf|tiff|bmp|tga|psd|vectorcraft|template (default: from the path's extension, else png; png8 writes an indexed .png; ait, a .ait template, is written by file.saveAsTemplate; an exporter plug-in's format is plugin.<plug-in id>, which an extension only it lists picks too), params?: {…} (an exporter plug-in's parameters; missing ones take their defaults, see plugin.info), selectedOnly?: false (the selected objects alone, in their layers), includeArtboardFill?: true (raster, PDF, SVG and EPS draw each exported artboard's background color behind its art, a transparent background too; false leaves it out), artboard?: 0, artboards?: [i…], rect?: [x, y, width, height] (raster formats: that area of the page in points, y down, instead of an artboard; not with artboard, artboards, range or useArtboards), range?: \"1-3, 5\" | \"all\" (1-based; PDF and .ai write one page per artboard, default all; SVG writes one file per artboard, {stem}-{artboard}.svg; raster formats write one artboard), useArtboards?: true (raster: one file per chosen artboard, default all, {stem}-{artboard}.{ext}; pdf/ai: every page) | false (pdf/ai/raster: the bounds of the visible art; SVG has it as an SVG option), ppi?: 72 (raster formats: pixels per inch, stored in the file; wins over scale), scale?: 1 (pixels per point), background?: transparent|white|black|\"#rrggbb\" (jpg: white when transparent), antiAlias?: none|art (default)|type (text snapped to pixels), interlaced?: false (png, Adam7), embedIcc?: true (png: an sRGB chunk marks the colours as sRGB), quality?: 90 (jpg: 0–100), colorModel?: rgb|cmyk|gray (jpg), method?: baseline|optimized|progressive, scans?: 3 (3–5, progressive), embedIcc?: true, imageMap?: none|client|server (an HTML or NCSA map of the objects with a URL, written as <stem>.html / <stem>.map), colors?: 256 (gif/png8: 2–256), reduction?: perceptual|selective (default)|adaptive|web|blackWhite|gray, dither?: none|diffusion (default)|pattern|noise, ditherAmount?: 100, transparency?: true, matte?: white|\"#rrggbb\"|none, interlaced?, lossless?: true (webp: lossy WebP isn't available yet: written lossless, with a warning), encoding?: utf8|utf16 (txt: the stories in stacking order, back to front, a thread once; utf16 with a byte order mark), lineEndings?: lf|crlf, selectionOnly?: false, svg?: {styling, outlineText, images, objectIds, decimals, minify, responsive, useArtboards, preserveEditing, metadata, fewerTspans, hiddenLayers, encoding, profile, embedFonts} (see document.formats), …the SVG options given flat instead, …the PDF options of document.exportPdf, …the DXF options of document.exportDxf (useArtboards: one drawing per artboard), …the EPS options of document.exportEps (useArtboards: one file per artboard, {stem}_{artboard}.eps, after a central {stem}.eps of the whole document when there are two or more and art shows; else the visible art), …emf and wmf write one picture of the artboard (useArtboards: true one file per chosen artboard, false the bounds of the visible art; EMF keeps curves, clipping, transparent images and gradients as images clipped to their shape; WMF flattens curves into polygons behind a placeable header; what a format leaves out comes back in warnings), colorModel?: rgb|cmyk|gray (tiff: rgb keeps transparency as an alpha channel), lzw?: true, byteOrder?: little|big, embedIcc?: true, colorModel?: rgb|gray (bmp), depth?: 24 (bmp: 1 black and white, 4|8 a palette by reduction and dither, 16, 24, 32 with alpha), fileFormat?: windows|os2 (1, 4, 8 or 24 bits), rle?: false (4 and 8 bits, windows), flipRows?: false (top-down rows), depth?: 24 (tga: 16 one-bit alpha, 24, 32 with alpha), colorModel?: rgb|cmyk|gray (psd), layers?: true (each top-level layer a pixel layer with its opacity and blend mode; a background colour is a Background layer; false: one flat image, on white), maxEditability?: false (layers and sublayers become groups, each object a layer named as in the Layers panel, text by its text; clipping, masked or knockout layers stay one layer), hiddenLayers?: false (hidden layers and objects written hidden instead of left out), embedIcc?: true (at most 30000 pixels a side; formats without alpha are flattened on white)} → {path, format, bytes, warnings, files?: [path…] (several), linked?: [path…] (linked images, image maps)}; no path → {dataBase64, format, bytes, warnings, files?: [{name, dataBase64}], linked?: [{name, dataBase64}]}. Never changes the document's path",
has_doc,
export::export
),
@@ -54,7 +54,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Export for Screens",
["File", "Export"],
None,
"{folder?, zip?: false (one .zip of every file, each deflated or stored, whichever is smaller: no folder → {name, dataBase64, bytes, files: [name…]}; with a folder it is written there → {path, bytes, files}), artboards?: [index…] | range?: \"1-3\" (default all), assets?: [asset id…] (instead of artboards: each asset's art alone, cropped to its bounds and named after the asset; see assets.list), layers?: [layer id…] (instead of artboards: each layer's art alone, sublayers included, named after the layer), selection?: one|each (instead of artboards: the selected art as one file, or a file per selected object, named after it), fullDocument?: false (one file per format instead: a PDF of every artboard, other formats the bounds of all visible art, named after the document), includeBleed?: false (artboards grown by the document's bleed), pdfs?: multiple|single (Export PDFs As: with several artboards, single writes them as the pages of one PDF per PDF row, named after the document), exportPreset?: (a preset of document.exportPresets.list: its settings, under these params), subfolders?: false (each row's files in a sub-folder: its folder, else its size for raster (1x, 2x, 100w…) or its format (SVG, PDF)), folders?: none|rows (as subfolders)|format (a folder per extension: png, svg, pdf…)|item (a folder per artboard, asset, layer or object)|itemFormat (item/extension) (default: rows with subfolders or a preset, else none), name?: (a file name pattern instead of the item's name, with <filename> for the document, <name> for the artboard, asset, layer or object, <index> from 1, #### for the counter zero-padded to the run of #, <format>, <scale>, <suffix> (else the row's suffix ends the name), YYYYMMDD, YYYY-MM-DD, YYYY, <date>, <time>, <YYYY>, <MM>, <DD>, <hh>, <mm> and <ss>; a trailing .ext is dropped, as in `COMPANY-Logo-<filename>-####-YYYYMMDD`; different files it names alike get -2, -3…), counterStart?: 1, counterPer?: item (every file of an item has its number)|file, date?: YYYY-MM-DD (the date the pattern uses; default today, UTC), preset?: mobile (PNG 1x, 2x, 3x) | density (PNG 0.75x–4x in ldpi…xxxhdpi sub-folders) instead of formats (turns subfolders on), formats?: [{format: png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf|eps|ai|vectorcraft, scale?: 1 | \"2x\" | \"100w\" | \"100h\" | \"72ppi\", width?: px | height?: px | ppi? (raster only; width, then height, then ppi win over scale), suffix?: (raster default: @2x, @100w, @100h, none at 1x; vector formats drop size suffixes), folder?: (its sub-folder), quality?: (jpg 0–100), …the format's options (antiAlias, background, preset (pdf)…)}], settings?: {png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf|eps|ai|vectorcraft: {…options for every row of that format}} (rows' own options win), prefix?, antiAlias?: none|art|type (raster rows without their own), openLocation?: (remembered; the app shows the files after exporting)} one file per artboard and format (a PDF holds its artboard alone; artboards with the same name, in any case, get -2, -3…; an unnamed one is Artboard-N) → {files: [path…]}; no folder → {files: [{name, dataBase64}]} (names include sub-folders: 2x/Icon@2x.png); every reply has warnings: [the encoders' warnings for the files, each once: an image that couldn't be decoded, type written in the fallback font…]. eps, ai and vectorcraft rows write a document of the item alone (its one artboard). The params (but zip and assets) are remembered in the document (document.exportSettings; not an undo step)",
"{folder?, zip?: false (one .zip of every file, each deflated or stored, whichever is smaller: no folder → {name, dataBase64, bytes, files: [name…]}; with a folder it is written there → {path, bytes, files}), artboards?: [index…] | range?: \"1-3\" (default all), assets?: [asset id…] (instead of artboards: each asset's art alone, cropped to its bounds and named after the asset; see assets.list), layers?: [layer id…] (instead of artboards: each layer's art alone, sublayers included, named after the layer), selection?: one|each (instead of artboards: the selected art as one file, or a file per selected object, named after it), fullDocument?: false (one file per format instead: a PDF of every artboard, other formats the bounds of all visible art, named after the document), includeBleed?: false (artboards grown by the document's bleed), includeArtboardFill?: true (Include Artboard Fill: each artboard's background color behind its art; false leaves it out), pdfs?: multiple|single (Export PDFs As: with several artboards, single writes them as the pages of one PDF per PDF row, named after the document), exportPreset?: (a preset of document.exportPresets.list: its settings, under these params), subfolders?: false (each row's files in a sub-folder: its folder, else its size for raster (1x, 2x, 100w…) or its format (SVG, PDF)), folders?: none|rows (as subfolders)|format (a folder per extension: png, svg, pdf…)|item (a folder per artboard, asset, layer or object)|itemFormat (item/extension) (default: rows with subfolders or a preset, else none), name?: (a file name pattern instead of the item's name, with <filename> for the document, <name> for the artboard, asset, layer or object, <index> from 1, #### for the counter zero-padded to the run of #, <format>, <scale>, <suffix> (else the row's suffix ends the name), YYYYMMDD, YYYY-MM-DD, YYYY, <date>, <time>, <YYYY>, <MM>, <DD>, <hh>, <mm> and <ss>; a trailing .ext is dropped, as in `COMPANY-Logo-<filename>-####-YYYYMMDD`; different files it names alike get -2, -3…), counterStart?: 1, counterPer?: item (every file of an item has its number)|file, date?: YYYY-MM-DD (the date the pattern uses; default today, UTC), preset?: mobile (PNG 1x, 2x, 3x) | density (PNG 0.75x–4x in ldpi…xxxhdpi sub-folders) instead of formats (turns subfolders on), formats?: [{format: png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf|eps|ai|vectorcraft, scale?: 1 | \"2x\" | \"100w\" | \"100h\" | \"72ppi\", width?: px | height?: px | ppi? (raster only; width, then height, then ppi win over scale), suffix?: (raster default: @2x, @100w, @100h, none at 1x; vector formats drop size suffixes), folder?: (its sub-folder), quality?: (jpg 0–100), …the format's options (antiAlias, background, preset (pdf)…)}], settings?: {png|png8|jpg|webp|gif|tiff|bmp|tga|psd|svg|svgz|pdf|eps|ai|vectorcraft: {…options for every row of that format}} (rows' own options win), prefix?, antiAlias?: none|art|type (raster rows without their own), openLocation?: (remembered; the app shows the files after exporting)} one file per artboard and format (a PDF holds its artboard alone; artboards with the same name, in any case, get -2, -3…; an unnamed one is Artboard-N) → {files: [path…]}; no folder → {files: [{name, dataBase64}]} (names include sub-folders: 2x/Icon@2x.png); every reply has warnings: [the encoders' warnings for the files, each once: an image that couldn't be decoded, type written in the fallback font…]. eps, ai and vectorcraft rows write a document of the item alone (its one artboard). The params (but zip and assets) are remembered in the document (document.exportSettings; not an undo step)",
has_doc,
screens::export_for_screens
),
@@ -72,7 +72,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Save Export Preset",
[],
None,
"{name, settings: {formats, settings, preset, name, prefix, counterStart, counterPer, folders, subfolders, includeBleed, antiAlias, folder, fullDocument} (as document.exportForScreens; other keys are left out)} save (or replace) a batch export preset, kept with the preferences; built-in names are refused → {name, created, settings}",
"{name, settings: {formats, settings, preset, name, prefix, counterStart, counterPer, folders, subfolders, includeBleed, includeArtboardFill, antiAlias, folder, fullDocument} (as document.exportForScreens; other keys are left out)} save (or replace) a batch export preset, kept with the preferences; built-in names are refused → {name, created, settings}",
always,
screens::presets_save
),
+3
View File
@@ -565,6 +565,9 @@ fn encode_files(doc: &Document, f: &Format, p: &Value) -> Result<Encoded> {
(RasterFormat::Jpeg, Some(m)) => MapKind::from_id(m).ok_or_else(|| bad(C, format!("imageMap `{m}`: none, client or server")))?,
_ => MapKind::None,
};
// The artboards' background colors, behind the art (a transparent background too).
let filled = super::export::with_artboard_fills(doc, p, chosen.iter().copied()).map_err(|e| bad(C, e))?;
let doc = &*filled;
let mut enc = Encoded::default();
let mut renderer = vectorcraft_render::Renderer::new();
let total = chosen.len() as u64;
+3
View File
@@ -300,6 +300,9 @@ fn encode_document(doc: &Document, p: &Value, saving: bool) -> Result<Encoded> {
if pages.is_empty() {
return Err(bad(C, "the document has no artboard"));
}
// The artboards' background colors, behind the art (the editing data keeps them as colors).
let filled = super::export::with_artboard_fills(doc, p, pages.iter().filter_map(|(b, ..)| *b)).map_err(|e| bad(C, e))?;
let doc = &*filled;
let regions: Vec<Rect> = pages.iter().map(|(_, region, _)| *region).collect();
let expanded = expand_transparent_tile_images(doc, &set.flatten)?;
let source = expanded.as_ref().unwrap_or(doc);
+15
View File
@@ -146,6 +146,21 @@ pub(super) fn art_over(doc: &Document, rect: vectorcraft_geom::Rect) -> Document
d
}
/// `doc` with the background colors of `artboards` drawn behind its art
/// ([`Document::with_artboard_fills`]), what exports and print draw, unless `p` turns
/// `includeArtboardFill` off (Export for Screens › Include Artboard Fill; on by default).
pub(crate) fn with_artboard_fills<'a>(
doc: &'a Document,
p: &Value,
artboards: impl IntoIterator<Item = usize>,
) -> std::result::Result<Cow<'a, Document>, String> {
let include = match p.get("includeArtboardFill") {
None | Some(Value::Null) => true,
Some(v) => v.as_bool().ok_or("`includeArtboardFill` must be true or false")?,
};
Ok(include.then(|| doc.with_artboard_fills(artboards)).flatten().map_or(Cow::Borrowed(doc), Cow::Owned))
}
/// `p` without its artboard choice, also inside its SVG options (for documents made of one
/// synthetic artboard, and for callers that pick the artboard themselves).
pub(super) fn without_artboards(p: &Value) -> Value {
+4
View File
@@ -284,6 +284,10 @@ fn overflow_warning(cmd: &str, doc: &Document, p: &Value) -> Result<Option<Strin
/// native document with its save options, whose pages may be left blank).
pub(super) fn encode_carrying(cmd: &str, doc: &Document, p: &Value, native: impl FnOnce() -> Result<Vec<u8>>) -> Result<(Vec<u8>, Vec<String>)> {
let mut opts = options(cmd, doc, p)?;
// The pages' background colors, behind the art (the editing data keeps them as colors).
let pages = opts.artboards.clone().unwrap_or_else(|| (0..doc.artboards.len()).collect());
let filled = super::export::with_artboard_fills(doc, p, pages).map_err(|e| bad(cmd, e))?;
let doc = &*filled;
if opts.settings.thumbnails {
opts.thumbnails = thumbnails(doc, &opts).map_err(|e| pdf_error(cmd, e))?;
}
+12 -3
View File
@@ -21,7 +21,7 @@ use super::encode::single_artboard;
use super::export::{export_crop, isolated, selected_artwork, without_artboards};
use super::naming::{self, NameParts};
use super::{
ARTBOARD_PARAMS, ArtboardPick, Format, artboard_file_names, create_dir, distinct_names, encode_with_warnings, unique_file_names,
ARTBOARD_PARAMS, ArtboardPick, Format, artboard_file_names, create_dir, distinct_names, encode_with_warnings, merge, unique_file_names,
with_black_output, writable, write_file,
};
@@ -289,6 +289,8 @@ struct ScreenParams {
zip: bool,
full_document: bool,
include_bleed: bool,
/// Include Artboard Fill: the artboards' background colors behind the art (default on).
include_artboard_fill: Option<bool>,
subfolders: bool,
preset: Option<String>,
prefix: Option<String>,
@@ -579,7 +581,7 @@ pub struct ScreenExportPreset {
/// What a preset keeps: the formats, their settings, the naming and the folders; not which art
/// (it changes with the document) nor where to (unless given).
pub const PRESET_KEYS: [&str; 13] = [
pub const PRESET_KEYS: [&str; 14] = [
"formats",
"settings",
"preset",
@@ -590,6 +592,7 @@ pub const PRESET_KEYS: [&str; 13] = [
"folders",
"subfolders",
"includeBleed",
"includeArtboardFill",
"antiAlias",
"folder",
"fullDocument",
@@ -708,7 +711,9 @@ pub(crate) fn export(s: &mut Session, p: &Value) -> Result<Value> {
let crop = rows.iter().any(|r| r.format.id != "pdf");
let items = items(&doc, &o, selection, &selected, &naming.filename, crop, true)?;
// Appearance of Black › Printing / Exporting for RGB raster rows.
let rows = rows.into_iter().map(|r| Row { options: with_black_output(s, &r.options), ..r }).collect();
// Include Artboard Fill off: the encoders leave the background colors out.
let fill = if o.include_artboard_fill == Some(false) { json!({ "includeArtboardFill": false }) } else { json!({}) };
let rows = rows.into_iter().map(|r| Row { options: merge(with_black_output(s, &r.options), fill.clone()), ..r }).collect();
let export = ScreenExport { cmd: C, o, rows, naming, items };
match &mut s.screens_hand_off {
Some(slot) => {
@@ -830,6 +835,10 @@ fn encode_and_write(export: ScreenExport, writer: &mut dyn FnMut(&str, &[u8]) ->
.entry(b)
.or_insert_with(|| {
let mut d = single_artboard(&item.doc, item.region, &item.name);
// It keeps its background color.
if let (Some(a), Some(from)) = (d.artboards.first_mut(), item.doc.artboards.get(b)) {
a.color = from.color;
}
super::save::keep_art_on(&mut d, item.region);
Arc::new(d)
})
+3 -1
View File
@@ -169,7 +169,9 @@ pub(super) fn encode(doc: &Document, p: &Value, compressed: bool) -> Result<Enco
// Draw linked files as vectors when available, or their previews with a warning. Keep
// that preparation out of the native attachment so missing links remain relinkable.
let (full, warnings) = crate::cmd::place::document::full_documents(doc);
let doc = &*full;
// The artboards' background colors, behind the art (the editing data keeps them as colors).
let filled = super::export::with_artboard_fills(&full, p, boards.iter().flatten().copied())?;
let doc = &*filled;
// A chosen artboard's file holds the art over it; otherwise the file holds all of it.
let regions: Option<Vec<vectorcraft_geom::Rect>> =
chosen.then(|| boards.iter().flatten().filter_map(|b| doc.artboards.get(*b)).map(|a| a.rect).collect());
@@ -0,0 +1,134 @@
//! Artboard background colors in output (#4937): raster, PDF, SVG and EPS exports, Export for
//! Screens and print draw each artboard's color behind its art, inside the artboard;
//! `includeArtboardFill: false` (Include Artboard Fill) leaves it out; the editing data keeps it as
//! the artboard's color, not as art.
use serde_json::{Value, json};
use super::*;
const RED: [u8; 4] = [255, 0, 0, 255];
/// Two 60×40 pt artboards, a 20 pt square (white fill, black stroke) on the first, which is red.
fn session() -> Session {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 60, "height": 40, "artboards": 2})).unwrap();
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 20, "height": 20})).unwrap();
s.execute("artboard.setProps", &json!({"index": 0, "color": "#ff0000"})).unwrap();
s
}
fn b64(v: &Value) -> Vec<u8> {
vectorcraft_format::base64_decode(v["dataBase64"].as_str().expect("dataBase64")).unwrap()
}
fn with(mut p: Value, more: &Value) -> Value {
if let (Some(o), Some(m)) = (p.as_object_mut(), more.as_object()) {
o.extend(m.iter().map(|(k, v)| (k.clone(), v.clone())));
}
p
}
/// The pixels of the PNG `document.export` writes of the active document with `p`, on transparency.
fn png(s: &mut Session, p: Value) -> image::RgbaImage {
let v = s.execute("document.export", &with(json!({"format": "png", "background": "transparent"}), &p)).unwrap();
image::load_from_memory(&b64(&v)).unwrap().to_rgba8()
}
/// The file `document.export` writes with `p`.
fn file(s: &mut Session, p: Value) -> Vec<u8> {
b64(&s.execute("document.export", &p).unwrap())
}
/// The file `bytes` named `name`, opened in a new session.
fn opened(name: &str, bytes: &[u8]) -> Session {
let mut s = Session::new();
s.execute("document.open", &json!({"name": name, "dataBase64": vectorcraft_format::base64_encode(bytes)})).unwrap();
s
}
/// What the file `bytes` named `name` shows near its top-left corner, opened and drawn.
fn corner(name: &str, bytes: &[u8]) -> [u8; 4] {
png(&mut opened(name, bytes), json!({"artboard": 0})).get_pixel(2, 2).0
}
#[test]
fn raster_exports_paint_the_color_behind_the_art() {
let mut s = session();
let on = png(&mut s, json!({"artboard": 0}));
assert_eq!(on.get_pixel(2, 2).0, RED, "a transparent background still shows the color");
assert_eq!(on.get_pixel(20, 20).0, [255, 255, 255, 255], "the art over it");
assert_eq!(png(&mut s, json!({"artboard": 0, "includeArtboardFill": false})).get_pixel(2, 2)[3], 0, "left out");
assert_eq!(png(&mut s, json!({"artboard": 1})).get_pixel(2, 2)[3], 0, "an artboard without a color");
let jpg = file(&mut s, json!({"format": "jpg", "artboard": 0, "quality": 100}));
let px = image::load_from_memory(&jpg).unwrap().to_rgb8().get_pixel(2, 2).0;
assert!(px[0] > 240 && px[1] < 16 && px[2] < 16, "JPEG too: {px:?}");
for bad in [json!("no"), json!(1)] {
assert!(s.execute("document.export", &json!({"format": "png", "includeArtboardFill": bad})).is_err(), "{bad}");
}
assert_eq!(s.doc().unwrap().doc.layers.len(), 1, "the document gains no layer");
}
#[test]
fn the_color_fills_its_artboard_alone_behind_the_art() {
let s = session();
let doc = &s.doc().unwrap().doc;
let board = doc.artboards[0].rect;
let filled = doc.with_artboard_fills([0, 1]).unwrap();
let img = vectorcraft_render::Renderer::new().render_region(&filled, board.inflate(10.0, 10.0), 1.0, false);
let rgba = img.to_straight();
let px = |x: usize, y: usize| rgba[(y * img.width as usize + x) * 4..][..4].to_vec();
assert_eq!(px(5, 5)[3], 0, "outside the artboard");
assert_eq!(px(15, 15), RED, "inside it");
assert_eq!(px(30, 30), [255, 255, 255, 255], "the art over it");
}
#[test]
fn pdf_and_svg_show_the_color_and_their_editing_data_keeps_it_as_the_artboards() {
let mut s = session();
for (format, name) in [("pdf", "a.pdf"), ("svg", "a.svg")] {
let p = json!({"format": format, "artboard": 0, "preserveEditing": false});
assert_eq!(corner(name, &file(&mut s, p.clone())), RED, "{format}");
assert_eq!(corner(name, &file(&mut s, with(p, &json!({"includeArtboardFill": false}))))[3], 0, "{format}");
}
let reopened = opened("a.pdf", &file(&mut s, json!({"format": "pdf", "preserveEditing": true})));
let (back, doc) = (&reopened.doc().unwrap().doc, &s.doc().unwrap().doc);
assert_eq!(back.artboards[0].color, doc.artboards[0].color);
assert_eq!(back.layers.len(), doc.layers.len(), "no art made of it");
}
#[test]
fn eps_and_postscript_print_draw_the_color() {
let mut s = session();
// The only red the document has, the first artboard's color: RGB, or CMYK PostScript's.
let red = |t: &str| t.contains("1 0 0 rg\n") || t.contains("0 0.9725 1 0 k\n");
let eps = |s: &mut Session, more: Value| {
String::from_utf8_lossy(&file(s, with(json!({"format": "eps", "useArtboards": true, "artboard": 0}), &more))).into_owned()
};
assert!(red(&eps(&mut s, json!({}))), "EPS");
assert!(!red(&eps(&mut s, json!({"includeArtboardFill": false}))), "EPS without it");
let ps = |s: &mut Session, more: Value| {
String::from_utf8_lossy(&b64(&s.execute("file.print", &with(json!({"format": "postscript"}), &more)).unwrap())).into_owned()
};
assert!(red(&ps(&mut s, json!({}))), "print");
assert!(!red(&ps(&mut s, json!({"includeArtboardFill": false}))), "print without it");
}
#[test]
fn export_for_screens_includes_the_fill_unless_unchecked() {
let mut s = session();
let screens = |s: &mut Session, more: Value| {
let p = with(json!({"artboards": [0], "formats": [{"format": "png"}], "settings": {"png": {"background": "transparent"}}}), &more);
s.execute("document.exportForScreens", &p).unwrap()
};
let corner = |r: &Value| image::load_from_memory(&b64(&r["files"][0])).unwrap().to_rgba8().get_pixel(2, 2).0;
assert_eq!(corner(&screens(&mut s, json!({}))), RED, "on by default");
assert_eq!(corner(&screens(&mut s, json!({"includeArtboardFill": false})))[3], 0, "unchecked");
assert_eq!(s.execute("document.exportSettings", &json!({})).unwrap()["settings"]["includeArtboardFill"], false, "remembered");
assert!(s.execute("document.exportForScreens", &json!({"includeArtboardFill": "no", "formats": [{"format": "png"}]})).is_err());
// A document row's artboard keeps its color, as a color.
let r = screens(&mut s, json!({"formats": [{"format": "vectorcraft"}]}));
let back = vectorcraft_format::load(&b64(&r["files"][0])).unwrap();
assert_eq!(back.artboards[0].color, s.doc().unwrap().doc.artboards[0].color);
assert_eq!(back.layers.len(), 1, "not as art");
}
+4 -2
View File
@@ -41,7 +41,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Print",
[],
Some("Cmd+P"),
"{preset?: name (as print.setup's: its settings instead of the document's; the saved settings stay), settings?: {…settings of print.setup} (over the document's or the preset's), path?, format?: pdf (default: a print-ready PDF) | postscript (the default for a .ps path: a PostScript file with DSC comments, %%Pages, a page per sheet with its paper size, marks, separations with their ink and halftone screen, the fixed flatness, negatives), level?: 3|2 (the PostScript language level), flattenerPreset?: \"medium\" (PostScript: the preset transparency is flattened with — high, low or a saved one, see flattener.presets.list), acknowledgeLoss?: bool (with path: write over the file the document was read from although reading it left things out), forPrinter?: bool (the PDF goes to a print queue: a page whose paper is turned to landscape is written upright, the art turned a quarter anticlockwise, as a printer driver turns a landscape job; macOS prints a landscape page as it is, cut off on upright paper; paper wide in Portrait and transverse pages stay as they are)} print the document, one page per sheet of paper (per tile, per ink, per copy) → {pages, format, path, bytes, warnings}; no path → {dataBase64, bytes, pages, format, warnings}. Raster effects print at the document's raster effects resolution; a PDF leaves halftone screens and flatness to the output device",
"{preset?: name (as print.setup's: its settings instead of the document's; the saved settings stay), settings?: {…settings of print.setup} (over the document's or the preset's), includeArtboardFill?: true (each artboard's background color prints behind its art; false leaves it out), path?, format?: pdf (default: a print-ready PDF) | postscript (the default for a .ps path: a PostScript file with DSC comments, %%Pages, a page per sheet with its paper size, marks, separations with their ink and halftone screen, the fixed flatness, negatives), level?: 3|2 (the PostScript language level), flattenerPreset?: \"medium\" (PostScript: the preset transparency is flattened with — high, low or a saved one, see flattener.presets.list), acknowledgeLoss?: bool (with path: write over the file the document was read from although reading it left things out), forPrinter?: bool (the PDF goes to a print queue: a page whose paper is turned to landscape is written upright, the art turned a quarter anticlockwise, as a printer driver turns a landscape job; macOS prints a landscape page as it is, cut off on upright paper; paper wide in Portrait and transverse pages stay as they are)} print the document, one page per sheet of paper (per tile, per ink, per copy) → {pages, format, path, bytes, warnings}; no path → {dataBase64, bytes, pages, format, warnings}. Raster effects print at the document's raster effects resolution; a PDF leaves halftone screens and flatness to the output device",
has_doc,
print
),
@@ -176,7 +176,9 @@ fn print(s: &mut Session, p: &Value) -> Result<Value> {
}
// Placed documents print their files' art.
let (full, _) = super::place::document::full_documents(doc);
let doc = &*full;
// The artboards' background colors print behind the art.
let filled = super::fileio::with_artboard_fills(&full, p, 0..full.artboards.len()).map_err(|e| bad(PRINT, e))?;
let doc = &*filled;
// Raster effects print as images at the document's raster effects resolution.
let flat = super::rasterfx::flatten_raster_effects(doc);
let doc = flat.as_ref().unwrap_or(doc);
+1 -1
View File
@@ -42,7 +42,7 @@ const HOST_COMMANDS: &[(&str, &str, &str)] = &[
(
"file.exportForScreens",
"Export for Screens…",
"{folder?, zip?, artboards? | range? | fullDocument? | assets?, includeBleed?, subfolders?, preset?, formats?, settings?, prefix?} = document.exportForScreens (no folder → the files, or one zip, as dataBase64)",
"{folder?, zip?, artboards? | range? | fullDocument? | assets?, includeBleed?, includeArtboardFill?, subfolders?, preset?, formats?, settings?, prefix?} = document.exportForScreens (no folder → the files, or one zip, as dataBase64)",
),
("tool.select", "Select Tool", "{tool} e.g. selection, directSelection, pen, rectangle, ellipse, polygon, star, lineSegment"),
(
@@ -1,6 +1,6 @@
//! File → Export → Export for Screens, laid out as Illustrator's: on the left the Artboards and
//! Assets tabs (thumbnails with checkboxes, in a grid or a list), Clear Selection and the prefix;
//! on the right Select (All, a range or Full Document, Include Bleed), the destination (folder
//! on the right Select (All, a range or Full Document, Include Bleed, Include Artboard Fill), the destination (folder
//! picker, Open Location after Export, Create Sub-folders by scale or by format; the web downloads
//! instead), Export PDFs As (one file of every artboard or a file each), and the format rows (scale
//! as `2x`, `100w`, `100h` or `72ppi`, suffix, format) with the iOS and Android rows, the format
@@ -16,6 +16,7 @@
//!
//! Fields (`ui.dialog.set`): `tab` (artboards|assets), `select` (all|range|full), `range`,
//! `boards` ([bool] per artboard), `assets` ([asset id] checked on the Assets tab), `includeBleed`,
//! `includeArtboardFill`,
//! `folder`, `openLocation`, `subfolders`, `subfolderBy` (scale|format), `pdfs` (single|multiple),
//! `prefix`, `preset` (""|mobile|density: where the rows came from), `formats` (rows of
//! `document.exportForScreens`, `scale` as text), `settings` ({png|png8|jpg|webp|gif|tiff|svg|pdf:
@@ -164,6 +165,7 @@ pub fn open(app: &mut VectorcraftApp) {
"boards": boards,
"assets": assets,
"includeBleed": flag("includeBleed", false),
"includeArtboardFill": flag("includeArtboardFill", true),
"folder": folder.unwrap_or_default(),
"openLocation": flag("openLocation", true),
"subfolders": subfolders,
@@ -324,6 +326,7 @@ fn selection_params(app: &VectorcraftApp, d: &Dialog) -> Result<(Map<String, Val
let pdf_rows = rows.iter().filter(|r| r["format"].as_str().and_then(fileio::format).is_some_and(|f| f.id == "pdf")).count();
let files = if single { (rows.len() - pdf_rows) * pieces + pdf_rows } else { rows.len() * pieces };
p.insert("includeBleed".into(), json!(d.bool("includeBleed")));
p.insert("includeArtboardFill".into(), json!(d.bool("includeArtboardFill")));
let folders = match (d.bool("subfolders"), d.str("subfolderBy").as_str()) {
(false, _) => "none",
(true, "format") => "format",
@@ -448,7 +451,8 @@ fn boards(app: &VectorcraftApp, d: &Dialog) -> Vec<bool> {
boards
}
/// Select: All or a Range (which checks the artboards it names), Include Bleed and Full Document.
/// Select: All or a Range (which checks the artboards it names), Include Bleed, Include Artboard
/// Fill and Full Document.
fn select_options(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) {
let n = app.session.active().map_or(0, |st| st.doc.artboards.len());
let select = d.str("select");
@@ -470,7 +474,10 @@ fn select_options(app: &VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog) {
}
});
});
form::check(ui, d, "includeBleed", tl!("Include Bleed"));
ui.horizontal(|ui| {
form::check(ui, d, "includeBleed", tl!("Include Bleed"));
form::check(ui, d, "includeArtboardFill", tl!("Include Artboard Fill"));
});
if widgets::radio(ui, tl!("Full Document"), d.str("select") == "full", true) {
set_select(d, "full");
}
@@ -740,8 +747,10 @@ pub(super) fn apply_preset(d: &mut Dialog, preset: &Value) {
if let Some(prefix) = st["prefix"].as_str() {
d.fields.insert("prefix".into(), json!(prefix));
}
if let Some(b) = st["includeBleed"].as_bool() {
d.fields.insert("includeBleed".into(), json!(b));
for key in ["includeBleed", "includeArtboardFill"] {
if let Some(b) = st[key].as_bool() {
d.fields.insert(key.into(), json!(b));
}
}
d.fields.insert("exportPreset".into(), json!(preset["name"]));
}
+2 -1
View File
@@ -545,7 +545,8 @@ fn art_texture(app: &mut VectorcraftApp, ctx: &egui::Context, layers: PrintLayer
{
return Some(tex);
}
let mut doc = (*st.doc).clone();
// The artboards' background colors print behind the art.
let mut doc = st.doc.with_artboard_fills(0..st.doc.artboards.len()).unwrap_or_else(|| (*st.doc).clone());
vectorcraft_pdf::keep_layers(&mut doc.layers, layers);
let tex = widgets::region_texture(ctx, &mut app.canvas.renderer, "print-preview-art", &doc, area, px);
ctx.data_mut(|m| m.insert_temp(key, (stamp, tex.clone())));
@@ -65,6 +65,7 @@ fn opens_on_defaults_and_draws_every_view() {
assert_eq!(field(&app, "folder"), json!(fileio::export_folder().unwrap_or_default()), "the Desktop, else home");
assert_eq!((field(&app, "openLocation"), field(&app, "subfolders"), field(&app, "preset")), (json!(true), json!(true), json!("")));
assert_eq!((field(&app, "subfolderBy"), field(&app, "pdfs"), field(&app, "view")), (json!("scale"), json!("multiple"), json!("grid")));
assert_eq!((field(&app, "includeBleed"), field(&app, "includeArtboardFill")), (json!(false), json!(true)));
let settings = field(&app, "settings");
for f in ["png", "png8", "jpg", "webp", "gif", "tiff", "svg", "pdf"] {
assert!(settings[f].is_object(), "{f} has settings");
@@ -450,6 +451,30 @@ fn the_web_downloads_one_zip_or_the_file() {
assert_eq!(image::load_from_memory(&log[0].1).unwrap().to_rgba8().get_pixel(1, 1).0, [0, 0, 0, 255], "the PNG settings' background");
}
/// Include Artboard Fill: checked, the file shows the artboard's background color behind the art
/// (on transparency too); unchecked, it doesn't, and the dialog reopens unchecked.
#[test]
fn include_artboard_fill_reaches_the_files_and_is_remembered() {
let (mut app, log) = app(1, true);
app.run("artboard.setProps", json!({"index": 0, "color": "#ff0000"})).unwrap();
for include in [true, false] {
log.borrow_mut().clear();
open(&mut app);
assert_eq!(field(&app, "includeArtboardFill"), json!(true), "on until unchecked");
set(&mut app, "includeArtboardFill", json!(include));
set(&mut app, "settings", json!({"png": {"background": "transparent"}}));
frame(&mut app);
confirm(&mut app).unwrap();
let px = image::load_from_memory(&log.borrow()[0].1).unwrap().to_rgba8().get_pixel(58, 38).0;
assert_eq!(px, if include { [255, 0, 0, 255] } else { [0, 0, 0, 0] }, "include {include}");
if include {
continue;
}
open(&mut app);
assert_eq!(field(&app, "includeArtboardFill"), json!(false), "remembered");
}
}
#[test]
fn the_status_bar_says_when_an_image_could_not_be_decoded() {
const NOTE: &str = "with 1 note(s): an image couldn't be decoded and is missing from the picture";
@@ -784,6 +809,7 @@ fn the_dialog_is_laid_out_as_illustrators() {
"Select:",
"Full Document",
"Include Bleed",
"Include Artboard Fill",
"Export to:",
"Open Location after Export",
"Create Sub-folders",
+1
View File
@@ -1524,6 +1524,7 @@ font-technology Variable Променливи
In Tangent Handles Входящи управляващи точки
In the selected objects В избраните обекти
Inches Инчове
Include Artboard Fill Включване на запълването на работната област
Include Blacks with CMY: Включване на черни към CMY:
Include Bleed Включване на изтичане
Include Cmy Blacks Включване на CMYK черни
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2221,6 +2221,7 @@ digits Malayalam Malajalam
Compatible Gradient and Gradient Mesh Printing Kompatibilno štampanje gradijenata i gradijentnih mreža
Embed Fonts (for other applications) Ugradi fontove (za druge aplikacije)
EPS Options EPS opcije
Include Artboard Fill Uključi ispunu radne ploče
Include CMYK PostScript in RGB Files Uključi CMYK PostScript u RGB datoteke
Include Document Thumbnails Uključi sličice dokumenta
Include Linked Files Uključi povezane datoteke
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1944,6 +1944,7 @@ digits Malayalam Malaiàlam
In the selected objects Als objectes seleccionats
In VectorCraft A VectorCraft
Inches Polzades
Include Artboard Fill Inclou el farciment del tauler
Include all profiles Inclou tots els perfils
Include All Subdirectories Inclou tots els subdirectoris
Include Blacks with CMY: Inclou negres amb CMY:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2249,6 +2249,7 @@ digits Malayalam Malajálamské
Compatible Gradient and Gradient Mesh Printing Kompatibilní tisk přechodů a přechodových sítí
Embed Fonts (for other applications) Vložit písma (pro jiné aplikace)
EPS Options Možnosti EPS
Include Artboard Fill Zahrnout výplň pracovní plochy
Include CMYK PostScript in RGB Files Zahrnout CMYK PostScript do souborů RGB
Include Document Thumbnails Zahrnout miniatury dokumentu
Include Linked Files Zahrnout propojené soubory
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2052,6 +2052,7 @@ digits Malayalam Malayalam
Compatible Gradient and Gradient Mesh Printing Kompatibel udskrivning af gradienter og gradientnet
Embed Fonts (for other applications) Indlejr skrifttyper (til andre programmer)
EPS Options EPS-indstillinger
Include Artboard Fill Medtag kunstpladens udfyldning
Include CMYK PostScript in RGB Files Medtag CMYK-PostScript i RGB-filer
Include Document Thumbnails Medtag dokumentminiaturer
Include Linked Files Medtag linkede filer
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1562,6 +1562,7 @@ file-filter Templates Vorlagen
In Tangent Handles Eingehende Tangentengriffe
In the selected objects In den ausgewählten Objekten
Inches Zoll
Include Artboard Fill Zeichenflächenfüllung einschließen
Include Blacks with CMY: Schwarz mit CMY einschließen:
Include Bleed Anschnitt einschließen
Include Cmy Blacks CMY-Schwarz einschließen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1487,6 +1487,7 @@ font-technology Variable Μεταβλητές
In Tangent Handles Λαβές εφαπτομένης εισόδου
In the selected objects Στα επιλεγμένα αντικείμενα
Inches Ίντσες
Include Artboard Fill Συμπερίληψη γεμίσματος πίνακα σχεδίασης
Include Blacks with CMY: Συμπερίληψη μαύρων με CMY:
Include Bleed Συμπερίληψη ξακρίσματος
Include Cmy Blacks Συμπερίληψη μαύρων CMY
Can't render this file because it contains an unexpected character in line 164 and column 5.
+1
View File
@@ -1562,6 +1562,7 @@ font-technology Variable Variables
In Tangent Handles Manejadores de tangente de entrada
In the selected objects En los objetos seleccionados
Inches Pulgadas
Include Artboard Fill Incluir relleno de la mesa de trabajo
Include Blacks with CMY: Incluir negros con CMY:
Include Bleed Incluir sangrado
Include Cmy Blacks Incluir negros CMY
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2204,6 +2204,7 @@ digits Malayalam Malajalam
Compatible Gradient and Gradient Mesh Printing Yhteensopiva liukuvärin ja liukuväriverkon tulostus
Embed Fonts (for other applications) Upota fontit (muita sovelluksia varten)
EPS Options EPS-asetukset
Include Artboard Fill Sisällytä piirtoalustan täyttö
Include CMYK PostScript in RGB Files Sisällytä CMYK PostScript RGB-tiedostoihin
Include Document Thumbnails Sisällytä asiakirjan pikkukuvat
Include Linked Files Sisällytä linkitetyt tiedostot
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1562,6 +1562,7 @@ font-technology Variable Variables
In Tangent Handles Poignées de tangente entrantes
In the selected objects Dans les objets sélectionnés
Inches Pouces
Include Artboard Fill Inclure le fond du plan de travail
Include Blacks with CMY: Inclure les noirs avec CMJ :
Include Bleed Inclure le fond perdu
Include Cmy Blacks Inclure les noirs CMJ
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1961,6 +1961,7 @@ file-filter Templates Sablonok
In Tangent Handles Bejövő érintőfogantyúk
In the selected objects A kijelölt objektumokban
Inches Hüvelyk
Include Artboard Fill Rajztábla kitöltésével együtt
Include all profiles Összes profil beágyazása
Include Blacks with CMY: CMY-alapú feketék is:
Include Bleed Kifutóval együtt
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2052,6 +2052,7 @@ digits Malayalam Malayalam
Compatible Gradient and Gradient Mesh Printing Pencetakan gradien dan mesh gradien yang kompatibel
Embed Fonts (for other applications) Sematkan font (untuk aplikasi lain)
EPS Options Opsi EPS
Include Artboard Fill Sertakan isian artboard
Include CMYK PostScript in RGB Files Sertakan PostScript CMYK dalam file RGB
Include Document Thumbnails Sertakan miniatur dokumen
Include Linked Files Sertakan file tertaut
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
2052
2053
2054
2055
2056
2057
2058
+1
View File
@@ -1586,6 +1586,7 @@ file-filter Templates Modelli
In Tangent Handles Maniglie tangente in entrata
In the selected objects Negli oggetti selezionati
Inches Pollici
Include Artboard Fill Includi riempimento tavola da disegno
Include Blacks with CMY: Includi neri con CMY:
Include Bleed Includi abbondanza
Include Cmy Blacks Includi neri CMY
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1856,6 +1856,7 @@ digits Malayalam マラヤーラム数字
In Tangent Handles 入る側のハンドル
In the selected objects 選択したオブジェクト内
Inches インチ
Include Artboard Fill アートボードの塗りを含める
Include Bleed 裁ち落としを含める
Include CMYK PostScript in RGB Files RGB ファイルに CMYK PostScript を含める
Include Document Thumbnails ドキュメントのサムネールを含める
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1952,6 +1952,7 @@ digits Malayalam მალაიალამური
Compatible Gradient and Gradient Mesh Printing თავსებადი გრადიენტისა და გრადიენტული ბადის ბეჭდვა
Embed Fonts (for other applications) შრიფტების ჩაშენება (სხვა აპლიკაციებისთვის)
EPS Options EPS-ის პარამეტრები
Include Artboard Fill სამუშაო დაფის შევსების ჩართვა
Include CMYK PostScript in RGB Files CMYK PostScript-ის ჩართვა RGB ფაილებში
Include Document Thumbnails დოკუმენტის ესკიზების ჩართვა
Include Linked Files დაკავშირებული ფაილების ჩართვა
Can't render this file because it is too large.
+1
View File
@@ -1884,6 +1884,7 @@ digits Malayalam 말라얄람어
In Tangent Handles 안쪽 접선 핸들
In the selected objects 선택한 개체 안에서
Inches 인치
Include Artboard Fill 아트보드 칠 포함
Include Bleed 블리드 포함
Include CMYK PostScript in RGB Files RGB 파일에 CMYK PostScript 포함
Include Document Thumbnails 문서 미리 보기 이미지 포함
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2066,6 +2066,7 @@ digits Malayalam Malayalam
Compatible Gradient and Gradient Mesh Printing Compatibel afdrukken van verlopen en verlooprasters
Embed Fonts (for other applications) Lettertypen insluiten (voor andere programma's)
EPS Options EPS-opties
Include Artboard Fill Tekenbordvulling opnemen
Include CMYK PostScript in RGB Files CMYK PostScript opnemen in RGB-bestanden
Include Document Thumbnails Documentminiaturen opnemen
Include Linked Files Gekoppelde bestanden opnemen
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -2165,6 +2165,7 @@ digits Malayalam Malajalam
Compatible Gradient and Gradient Mesh Printing Zgodne drukowanie gradientów i siatek gradientowych
Embed Fonts (for other applications) Osadź czcionki (dla innych aplikacji)
EPS Options Opcje EPS
Include Artboard Fill Uwzględnij wypełnienie obszaru kompozycji
Include CMYK PostScript in RGB Files Dołącz CMYK PostScript w plikach RGB
Include Document Thumbnails Dołącz miniatury dokumentu
Include Linked Files Dołącz połączone pliki
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1347,6 +1347,7 @@
In Tangent Handles Alças de Tangente de Entrada
In the selected objects Nos objetos selecionados
Inches Polegadas
Include Artboard Fill Incluir Preenchimento da Prancheta
Include Bleed Incluir Sangria
Include CMYK PostScript in RGB Files Incluir PostScript CMYK em Arquivos RGB
Include Document Thumbnails Incluir Miniaturas do Documento
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -1584,6 +1584,7 @@ file-filter Templates Шаблоны
In Tangent Handles Маркеры входящей касательной
In the selected objects В выделенных объектах
Inches Дюймы
Include Artboard Fill Включать заливку монтажной области
Include Blacks with CMY: Включать чёрные с CMY:
Include Bleed Включать вылет
Include Cmy Blacks Включать чёрные CMY
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1526,6 +1526,7 @@ font-technology Variable Variabla
In Tangent Handles Inkommande tangenthandtag
In the selected objects I de markerade objekten
Inches Tum
Include Artboard Fill Inkludera ritytans fyllning
Include Blacks with CMY: Inkludera svart med CMY:
Include Bleed Inkludera utfall
Include Cmy Blacks Inkludera CMY-svarta
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -1412,6 +1412,7 @@ font-technology Variable Değişken
In Tangent Handles Teğet Tutamaçlarda
In the selected objects Seçilen nesnelerde
Inches İnç
Include Artboard Fill Çalışma Yüzeyi Dolgusunu Dahil Et
Include Blacks with CMY: CMY ile Siyahları Dahil Et:
Include Bleed Taşmayı Dahil Et
Include Cmy Blacks Cmy Black'leri dahil et
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -1580,6 +1580,7 @@ file-filter Templates Шаблони
In Tangent Handles Маркери вхідної дотичної
In the selected objects У виділених об’єктах
Inches Дюйми
Include Artboard Fill Включати заливку монтажної області
Include Blacks with CMY: Включати чорні з CMY:
Include Bleed Включати виліт
Include Cmy Blacks Включати чорні CMY
Can't render this file because it contains an unexpected character in line 178 and column 5.
+1
View File
@@ -1842,6 +1842,7 @@ digits Hindi Hindi
In Tangent Handles Tay nắm tiếp tuyến vào
In the selected objects Trong các đối tượng đã chọn
Inches Inch
Include Artboard Fill Bao gồm màu tô bảng vẽ
Include Blacks with CMY: Bao gồm đen với CMY:
Include Bleed Bao gồm vùng chảy tràn
Include Cmy Blacks Bao gồm đen CMY
Can't render this file because it contains an unexpected character in line 200 and column 5.
+1
View File
@@ -1513,6 +1513,7 @@ file-filter Templates 模板
In Tangent Handles 内侧切线控制手柄
In the selected objects 在所选对象中
Inches 英寸
Include Artboard Fill 包含画板填色
Include Blacks with CMY: 包含 CMY 的黑色:
Include Bleed 包含出血
Include Cmy Blacks 包含 CMY 黑色
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -1551,6 +1551,7 @@ file-filter Templates 範本
In Tangent Handles 內側切線控制把手
In the selected objects 在選取的物件中
Inches 英吋
Include Artboard Fill 包含工作區域填色
Include Blacks with CMY: 包含 CMY 的黑色:
Include Bleed 包含出血
Include Cmy Blacks 包含 CMY 黑色
Can't render this file because it contains an unexpected character in line 6 and column 47.