.ai import: version 7 linked images open as linked images with a placeholder

A legacy .ai in the version 7 form names a linked image's file with
`(path) modified XG` and draws the image with `XF`, which takes the
operands `XI` takes and has no samples after it (Adobe Illustrator File
Format Specification 7.0, sections 8.2 and 8.3). The reader ignored both
operators, so the image was dropped without a warning.

- `XF` after `XG` becomes a linked image of that file, its pixels placed
  as an `XI` image of the same operands is placed. The file keeps no
  pixels of it, so a placeholder stands in until the file is relinked in
  the Links panel, with a note in the open result. No pixels are read,
  so the image is not held to the pixel limit of `XI`'s samples.
- An `XF` without a file name before it, or with operands `XI` couldn't
  read (no matrix, too few numbers, no pixels, or samples of other than
  1 or 8 bits), is left out with the existing "an image VectorCraft
  can't read was left out" warning. An `XG` applies only to the image
  after it in the same raster object; `() 1 XG` before an embedded `XI`
  keeps that image embedded.
- A PostScript page draws nothing for `XF`, so there the placeholder's
  area is left out of the comparison of the layers with the page, and
  the file keeps its layers. A PDF part draws the image's pixels, so a
  file with one is compared with it there as before, and its layers are
  not used without the PDF part.
- Linked images of different files get image keys of their own even
  when they show the same pixels (the placeholder, or two identical
  placed-file previews), so updating or exporting one file's image
  reads that file only.
- `vectorcraft_doc::links::missing_placeholder` draws the placeholder: a
  light tile, crossed out, as SVG import shows for a missing file.

Tests: reader tests for the placement, the image keys and the cases left
out, an end-to-end test of a legacy file with a linked image, a test of
a file with a PDF part, and a fuzz case for damaged files with `XG` and
`XF`. The new note has a row in each catalog that translates the
placed-file note.
This commit is contained in:
dotCooCoo
2026-11-05 17:02:57 +07:00
committed by bflatastic
parent 5ca8ef49e2
commit c5269bd096
39 changed files with 365 additions and 11 deletions
+24
View File
@@ -166,6 +166,24 @@ impl ImageBlob {
}
}
/// The side of [`missing_placeholder`]'s image, in pixels.
pub const MISSING_SIDE: u32 = 64;
/// What a linked image shows when an importer has no pixels for it (its file is missing or isn't
/// in the data): a light tile, crossed out, [`MISSING_SIDE`] pixels a side, as a PNG. `None` when
/// it can't be encoded.
pub fn missing_placeholder() -> Option<Vec<u8>> {
let last = MISSING_SIDE - 1;
let img = image::RgbaImage::from_fn(MISSING_SIDE, MISSING_SIDE, |x, y| {
let edge = x == 0 || y == 0 || x == last || y == last;
let cross = x.abs_diff(y) <= 1 || (x + y).abs_diff(last) <= 1;
image::Rgba(if edge || cross { [200, 60, 60, 255] } else { [236, 236, 236, 255] })
});
let mut png = Vec::new();
img.write_to(&mut Cursor::new(&mut png), image::ImageFormat::Png).ok()?;
Some(png)
}
/// `bytes` decoded and shrunk to fit [`PROXY_SIZE`] as a PNG; `None` when small enough already or
/// undecodable.
fn proxy_png(bytes: &[u8]) -> Option<Vec<u8>> {
@@ -445,6 +463,12 @@ mod tests {
.unwrap()
}
#[test]
fn the_placeholder_is_a_png() {
let png = missing_placeholder().unwrap();
assert_eq!(image::load_from_memory(&png).unwrap().width(), MISSING_SIDE);
}
#[test]
fn a_path_string_from_older_files_reads_as_a_link() {
let old: ImageObject = serde_json::from_str(r#"{"key":"k","width":2,"height":1,"xf":[1,0,0,1,0,0],"link":"C:\\art\\photo.png"}"#).unwrap();
+19
View File
@@ -3675,6 +3675,19 @@ proptest! {
let text = mutate_text(&fixture, cut, &edits);
survive("mutated PostScript-form .ai with placed files", || vectorcraft_engine::cmd::fileio::load("x.ai", text.as_bytes()).ok().map(|l| l.doc))?;
}
/// The same file with linked images in the version 7 form (`XG`, then `XF` with no samples),
/// one with an embedded image between its `XG` and `XF`, damaged.
#[test]
fn ps_form_ai_with_linked_images_never_panics(cut in 0usize..1_400, edits in prop::collection::vec((0usize..1_400, prop::sample::select(vec!['0', '9', '-', '.', ' ', '\n', '(', ')', '[', ']', '%', 'X', 'F', 'G'])), 0..12)) {
let linked = "%AI5_File:\n%AI5_BeginRaster\n(a.tif) 0 XG\n[ 2 0 0 3 10 80 ] 0 0 10 20 10 20 8 3 0 0 0 0 XF\n%AI5_EndRaster\n\
%AI5_File:\n%AI5_BeginRaster\n(b.tif) 1 XG\n[ 1 0 0 1 0 100 ] 0 0 1 1 1 1 8 3 0 0 0 0 XI\n%FF0000\n%AI5_EndRaster\n\
[ 0 1 -1 0 50 50 ] 0 0 4 4 4 4 8 3 0 0 0 0 XF\n";
let fixture = NAMED_PROLOG_AI.replacen("0 Ap\nu\n", &format!("0 Ap\n{linked}u\n"), 1);
prop_assert!(fixture.contains("(a.tif)"));
let text = mutate_text(&fixture, cut, &edits);
survive("mutated PostScript-form .ai with linked images", || vectorcraft_engine::cmd::fileio::load("x.ai", text.as_bytes()).ok().map(|l| l.doc))?;
}
}
/// A `.ai` in the legacy format without layers (Illustrator 3) whose prolog is included: its
@@ -3878,6 +3891,12 @@ fn arb_ai_token() -> impl Strategy<Value = String> {
"[ 1 0 0 1 0 0 ] 0 0 2 2 2 2 8 3 1 0 1 0",
"[ 1 0 0 1 0 0 ] 0 0 3 2 3 2 1 4 1 0 1 0",
"[ 1 0 0 1 0 0 ] 0 0 99999 99999 99999 99999 1 1 0 0 0 0",
// A linked image in the version 7 form (§8.2, §8.3): `(path) modified XG`, then `XF`.
"XG",
"(a.tif) 0 XG",
"XF",
"[ 1 0 0 1 0 0 ] 0 0 2 2 2 2 8 3 0 0 0 0 XF",
"[ 1e308 0 0 1e308 0 0 ] 0 0 1e6 1e6 1e6 1e6 8 3 0 0 0 0 XF",
"[",
"]",
"/Name",
+16 -1
View File
@@ -35,6 +35,11 @@
//! and the samples after `XI`. 8-bit CMYK samples keep their ink amounts in a CMYK TIFF, and
//! an alpha channel that hides part of such an image becomes its opacity mask; other samples
//! become an RGBA PNG.
//! - Linked images in the version 7 form (§8.2, §8.3): `(path) modified XG`, then `XF` with the
//! operands `XI` takes and no samples. Each is a linked image of that file showing a placeholder
//! over the image's place until its file is found. An `XF` without a file name before it, or
//! with operands `XI` couldn't read, is left out with a warning. An `XI` after `XG` (newer files
//! write `() 1 XG` before their embedded images) stays an embedded image.
//! - Placed (linked) files, as written in files users sent: `%AI5_BeginPlace`, `[matrix] llx lly urx
//! ury … (path)` and the operator `` ` ``, the file's preview between `%AI26_BeginPlacedObjectPreview`
//! and its end (`w h rowBytes 32 n `, then rows of premultiplied ARGB, the top one first), `~`. The
@@ -124,7 +129,7 @@ pub(crate) use document::{slot_frame, slot_of};
pub use types::IMAGE_TOO_LARGE;
use types::{BLEND_BUDGET, FLIP, Frame, GState, Graph, Kind, PatternHead, PendingEnvelope, Place, Raster};
#[cfg(test)]
pub use types::{GRAPH_PLAIN, GRAPH_UNENDED, NON_NATIVE_ART, NON_NATIVE_ART_UNREAD, VERSIONED_UNENDED};
pub use types::{GRAPH_PLAIN, GRAPH_UNENDED, LINKED_PLACEHOLDERS, NON_NATIVE_ART, NON_NATIVE_ART_UNREAD, VERSIONED_UNENDED};
pub(super) use types::{blend_mode, blend_name};
#[path = "legacy_text.rs"]
@@ -155,6 +160,10 @@ pub struct Structure {
/// Where the drawn looks lay that objects read with their art style took the place of (the
/// document's coordinates): the page draws Illustrator's rendering of those styles there.
pub restyled: Vec<Rect>,
/// Where linked images whose pixels the data doesn't keep (`XF`) show a placeholder (the
/// document's coordinates). A PostScript page doesn't draw them there; a PDF part draws the
/// image's pixels.
pub placeholders: Vec<Rect>,
/// Placed files whose pixels the file puts at another point too: their image's id and the
/// move, in its pixels, that places them there (the corner of the box they are written with,
/// in place of the canvas point of art space's origin).
@@ -259,6 +268,8 @@ struct Reader<'a> {
/// a global process colour, so these make swatches only for names no colour operator defines.
stop_spots: Vec<Named>,
raster: Option<Raster>,
/// The file `XG` names for the image after it (`XF`), until an image is read.
image_link: Option<String>,
/// The placed file being read (`%AI5_BeginPlace` … `~`).
place: Option<Place>,
/// The last object read is the last child of the innermost frame, and nothing started since.
@@ -343,6 +354,8 @@ struct Reader<'a> {
palette: super::palette::Palette,
/// See [`Structure::restyled`].
restyled: Vec<Rect>,
/// See [`Structure::placeholders`].
placeholders: Vec<Rect>,
/// See [`Structure::placed_elsewhere`].
placed_elsewhere: Vec<(NodeId, Affine)>,
unread_elsewhere: Vec<(NodeId, Rect)>,
@@ -443,6 +456,7 @@ impl<'a> Reader<'a> {
spots: Vec::new(),
stop_spots: Vec::new(),
raster: None,
image_link: None,
place: None,
foreign_data: None,
after_object: false,
@@ -487,6 +501,7 @@ impl<'a> Reader<'a> {
style_links: vec![],
palette: super::palette::Palette::default(),
restyled: vec![],
placeholders: vec![],
placed_elsewhere: vec![],
unread_elsewhere: vec![],
mesh: None,
+90 -4
View File
@@ -1,13 +1,14 @@
use super::containers::close;
use super::document::TEXT_SLOT;
use super::types::{
FLIP, Frame, IMAGE_TOO_LARGE, Kind, LINKED_PREVIEWS, MAX_PIXELS, MAX_PLACED_ELSEWHERE, MAX_POINTS, MAX_SIDE, NON_NATIVE_ART,
FLIP, Frame, IMAGE_TOO_LARGE, Kind, LINKED_PLACEHOLDERS, LINKED_PREVIEWS, MAX_PIXELS, MAX_PLACED_ELSEWHERE, MAX_POINTS, MAX_SIDE, NON_NATIVE_ART,
NON_NATIVE_ART_UNREAD, Place,
};
use super::{
Affine, AnchorKind, Appearance, AppearanceItem, Arc, BezPath, CANVAS_CENTRE, FillLayer, ImageBlob, ImageObject, Inks, Instance, LinkInfo, Node,
NodeId, NodeKind, Obj, OpacityMask, Paint, PathData, PathEl, Point, Reader, Rect, StrokeLayer, V, ps,
};
use vectorcraft_doc::links::missing_placeholder;
impl<'a> Reader<'a> {
/// A text object (`/AI11Text`): an empty group standing for it, named after its story.
@@ -383,14 +384,46 @@ impl<'a> Reader<'a> {
self.image_from(vals, data)
}
/// The matrix and the numbers of an image's operands (`[a b c d tx ty] llx lly urx ury w h bits
/// ImageType AlphaChannelCount reserved bin-ascii ImageMask`, as `XI` and `XF` take them); `None`
/// without a matrix or with fewer numbers than up to the encoding (bin-ascii).
fn image_operands(vals: &[V]) -> Option<(Vec<f64>, Vec<f64>)> {
let m = vals.iter().find_map(|v| if let V::Arr(a) = v { Some(Self::nums(a)) } else { None })?;
let n = Self::nums(vals);
(n.len() >= 11).then_some((m, n))
}
/// `XG` (`w`, which names the file of the image after it) or `XF` (that image), with their
/// operands `vals`. Before the art, the setup's procedures run: there they do nothing.
pub(super) fn image_link_op(&mut self, w: &str, vals: Vec<V>, hidden: bool) -> Result<(), String> {
if self.frames.is_empty() {
return Ok(());
}
if w == "XG" {
self.image_link = Self::image_link(&vals);
return Ok(());
}
self.linked_image(vals, hidden)
}
/// The file `XG`'s operands (`(path) modified`) name; `None` for operands of another shape or
/// an empty path.
fn image_link(vals: &[V]) -> Option<String> {
match vals {
[.., V::Str(path), V::Num(_)] if !path.is_empty() => Some(super::obj::text(path)),
_ => None,
}
}
/// An image of operands `vals` and samples `data`.
pub(super) fn image_from(&mut self, vals: Vec<V>, data: &[u8]) -> Result<(), String> {
// A file `XG` named before this image isn't the file of a later `XF`.
self.image_link = None;
let space = self.raster.as_ref().and_then(|r| r.space.clone()).unwrap_or_default();
let Some(m) = vals.iter().find_map(|v| if let V::Arr(a) = v { Some(Self::nums(a)) } else { None }) else {
let Some((m, n)) = Self::image_operands(&vals) else {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
};
let n = Self::nums(&vals);
let [_, _, _, _, w, h, bits, kind, alpha, _, binary, ..] = n.as_slice() else {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
@@ -476,6 +509,52 @@ impl<'a> Reader<'a> {
self.add_raster(ImageBlob::new("image/png", png), None, &m, (w as u32, h as u32))
}
/// `XF` (§8.2): an image whose pixels are in the file the `XG` before it names becomes a linked
/// image of that file, of its `w` × `h` pixels placed as the matrix maps them, as `XI` places an
/// image. The data keeps no pixels or preview of it, so a placeholder is spread over those pixels
/// until its file is found. One without that file name, or whose operands `XI` couldn't read (no
/// matrix, too few numbers, no pixels, or samples of other than 1 or 8 bits), is left out with a
/// warning. No pixels are read, so its size isn't held to the limit on `XI`'s samples.
fn linked_image(&mut self, vals: Vec<V>, hidden: bool) -> Result<(), String> {
let path = self.image_link.take();
let operands = Self::image_operands(&vals);
let (Some(path), Some((m, n))) = (path, operands) else {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
};
let (&[a, b, c, d, tx, ty], &[_, _, _, _, w, h, bits, ..]) = (m.as_slice(), n.as_slice()) else {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
};
if !(1.0..=MAX_SIDE).contains(&w) || !(1.0..=MAX_SIDE).contains(&h) || ![1.0, 8.0].contains(&bits) {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
}
let (w, h) = (w as u32, h as u32);
let xf = self.space() * Affine::new([a, b, c, d, tx, ty]) * FLIP;
if !xf.as_coeffs().iter().all(|v| v.is_finite()) || xf.determinant().abs() < 1e-12 {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
}
let Some(png) = missing_placeholder() else {
self.warn("an image VectorCraft can't read was left out");
return Ok(());
};
// The placeholder is all the document has of the file: what a missing link shows.
let bytes = Arc::new(png);
let blob = ImageBlob { mime: "image/png".into(), bytes: bytes.clone(), proxy: Some(bytes) };
let key = linked_key(&blob, &path);
self.doc.images.entry(key.clone()).or_insert(blob);
self.warn(LINKED_PLACEHOLDERS);
// A PostScript page doesn't draw the placeholder: there its pixels are left out of the
// comparisons with the page.
if !hidden && !self.gs.hidden && self.shown() {
self.placeholders.push(xf.transform_rect_bbox(Rect::new(0.0, 0.0, f64::from(w), f64::from(h))));
}
let im = ImageObject { key, width: w, height: h, xf, link: Some(LinkInfo::new(path)), placement: Default::default(), pixels: None };
self.add_image(im, hidden)
}
/// Add the image `blob` (`w` × `h` pixels), which the matrix `m` maps into art space, with
/// `alpha` (its alpha channel, one byte a pixel) as its opacity mask when it hides part of the
/// image.
@@ -597,7 +676,7 @@ impl<'a> Reader<'a> {
// The preview is all the document has of the file: what a missing link shows.
let bytes = Arc::new(png);
let blob = ImageBlob { mime: "image/png".into(), bytes: bytes.clone(), proxy: Some(bytes) };
let key = blob.content_key();
let key = linked_key(&blob, &path);
self.doc.images.entry(key.clone()).or_insert(blob);
self.warn(LINKED_PREVIEWS);
if let Some(shift) = elsewhere
@@ -723,6 +802,13 @@ pub(super) fn relative_path(folder: &str, name: &str) -> Option<String> {
Some(format!("{}/{name}", folder.trim_end_matches('/')))
}
/// The image key of a linked image of the file at `path` that shows `blob` (its preview or a
/// placeholder): the blob's own key and the path's. Images of other files that show the same pixels
/// have keys of their own, and reading one file's pixels in changes that file's images only.
fn linked_key(blob: &ImageBlob, path: &str) -> String {
format!("{}-{}", blob.content_key(), vectorcraft_doc::links::hash_bytes(path.as_bytes()))
}
pub(super) fn sane(r: Rect) -> bool {
[r.x0, r.y0, r.x1, r.y1].iter().all(|v| v.is_finite() && v.abs() <= MAX_SIDE)
}
@@ -160,8 +160,12 @@ impl<'a> Reader<'a> {
return Ok(());
}
if line.starts_with("AI5_BeginRaster") {
// An `XG` outside a raster object names no image in it.
self.image_link = None;
self.raster = Some(Raster::default());
} else if line.starts_with("AI5_EndRaster") {
// A file `XG` named in this raster object is no later one's.
self.image_link = None;
if let Some(Raster { operands: Some(vals), hex, .. }) = self.raster.take() {
return self.image_from(vals, &hex);
}
@@ -894,6 +894,7 @@ impl<'a> Reader<'a> {
unread_elsewhere: self.unread_elsewhere,
slot_names: self.slot_names,
restyled: self.restyled,
placeholders: self.placeholders,
placed_elsewhere: self.placed_elsewhere,
envelopes_drawn: self.envelopes_drawn,
styles_read,
+3
View File
@@ -371,6 +371,9 @@ impl<'a> Reader<'a> {
r.operands = Some(vals);
}
}
// A linked image (§8.2, §8.3): `(path) modified XG` names the file of the `XF` image after
// it, which has `XI`'s operands and no samples.
"XG" | "XF" => self.image_link_op(w, vals, hidden)?,
// A plugin group ends; a blend's record follows on `%` lines.
"XP" => {
let name = vals.iter().find_map(V::text).unwrap_or_default();
+5 -2
View File
@@ -269,6 +269,11 @@ pub(super) const MAX_PLACED_FOLDER: usize = 4096;
pub(super) const LINKED_PREVIEWS: &str =
"linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel";
/// What the open result says of linked images the data keeps no pixels of (`XF`): they show a
/// placeholder.
pub const LINKED_PLACEHOLDERS: &str =
"linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel";
/// Operators that change nothing the reader keeps.
pub(super) const IGNORED: &[&str] = &[
"Ae",
@@ -276,12 +281,10 @@ pub(super) const IGNORED: &[&str] = &[
"As",
"Xd",
"Xr",
"XG",
// `/DeviceGray XC` after a linked image's file name (`XG`): its colour space.
"XC",
"Xh",
"XH",
"XF",
"Bc",
"XP",
"Np",
+103
View File
@@ -2112,6 +2112,90 @@ fn a_rotated_image_is_placed_as_its_matrix_maps_it() {
}
}
/// A raster object in the version 7 form (§8): `%AI5_BeginRaster`, `head` (a link's `XG`, or
/// nothing), the operands `[matrix] 0 0 w h w h 8 3 0 0 0 0`, the operator `op` and `tail` (an
/// `XI`'s samples on `%` lines), `%AI5_EndRaster`.
fn raster7(head: &str, matrix: &str, (w, h): (u32, u32), op: &str, tail: &str) -> String {
format!("%AI5_File:\n%AI5_BeginRaster\n{head}{matrix} 0 0 {w} {h} {w} {h} 8 3 0 0 0 0 {op}\n{tail}%AI5_EndRaster\n")
}
/// A linked image in the version 7 form (§8.2, §8.3), `(path) modified XG` and then `XF` with no
/// samples, is a linked image of that file. The data keeps no pixels of it, so it shows a
/// placeholder over the place the matrix maps the image's pixels to, as an embedded image (`XI`)
/// of the same operands is placed.
#[test]
fn a_version_7_linked_image_shows_a_placeholder_where_the_image_is() {
let (s, c) = 30f64.to_radians().sin_cos();
for m in [[2.0, 0.0, 0.0, 3.0, 100.0, 400.0], [3.0 * c, 3.0 * s, -2.0 * s, 2.0 * c, 40.0, 60.0]] {
let matrix = format!("[ {} ]", m.map(|v| v.to_string()).join(" "));
let linked = raster7("(Macintosh HD:Images:photo.tif) 0 XG\n", &matrix, (10, 20), "XF", "");
let embedded = raster7("", &matrix, (10, 20), "XI", &"%FF0000\n".repeat(200));
let r = read(stream7(&layer7("L", &format!("{linked}{embedded}"))).as_bytes()).unwrap();
let a = art(&r.doc);
assert_eq!(a.len(), 2, "{:?}", r.warnings);
let (NodeKind::Image(link), NodeKind::Image(emb)) = (&a[0].kind, &a[1].kind) else { panic!("{:?}", a) };
assert_eq!(link.link.as_ref().map(|l| l.path.as_str()), Some("Macintosh HD:Images:photo.tif"));
assert_eq!(emb.link, None, "an image whose samples follow stays embedded");
assert!(r.doc.images[&link.key].is_proxy(), "the placeholder is what a missing link shows");
// The file's pixels, placed as the embedded image's are: relinking keeps that size.
assert_eq!((link.width, link.height, link.xf), (emb.width, emb.height, emb.xf));
assert_eq!((link.width, link.height), (10, 20));
assert_eq!(r.warnings.iter().filter(|w| *w == read::LINKED_PLACEHOLDERS).count(), 1, "{:?}", r.warnings);
assert!(!r.warnings.iter().any(|w| w.contains("left out")), "{:?}", r.warnings);
}
// Images of other files keep images of their own, though each shows the same placeholder; two
// of one file share theirs.
let m = "[ 1 0 0 1 0 100 ]";
let three = ["(a.tif) 0 XG\n", "(b.tif) 0 XG\n", "(a.tif) 0 XG\n"].map(|xg| raster7(xg, m, (2, 2), "XF", "")).concat();
let r = read(stream7(&layer7("L", &three)).as_bytes()).unwrap();
let keys: Vec<_> = art(&r.doc).iter().filter_map(|n| if let NodeKind::Image(im) = &n.kind { Some(im.key.clone()) } else { None }).collect();
assert!(matches!(&keys[..], [a, b, c] if a != b && a == c), "{keys:?}");
assert_eq!(r.doc.images.len(), 2);
}
/// `XG` names the file of the `XF` after it only: an `XF` without a file name before it, after an
/// `XG` with operands of another shape or an empty path, or after an image that took the name,
/// is left out with a warning, as is one whose operands `XI` couldn't read. Of two `XG`s, the
/// second names the file.
#[test]
fn a_version_7_linked_image_without_its_file_name_is_left_out_with_a_warning() {
let m = "[ 1 0 0 1 0 100 ]";
let embedded = raster7("() 1 XG\n", m, (1, 1), "XI", "%FF0000\n");
for (what, src) in [
("no XG", raster7("", m, (2, 2), "XF", "")),
("a name, not a string", raster7("/photo 0 XG\n", m, (2, 2), "XF", "")),
("no modified flag", raster7("(a.tif) XG\n", m, (2, 2), "XF", "")),
("an empty path", raster7("() 0 XG\n", m, (2, 2), "XF", "")),
("taken by an embedded image", format!("{}{}", raster7("(a.tif) 0 XG\n", m, (1, 1), "XI", "%FF0000\n"), raster7("", m, (2, 2), "XF", ""))),
("named in an earlier raster object", format!("%AI5_BeginRaster\n(a.tif) 0 XG\n%AI5_EndRaster\n{}", raster7("", m, (2, 2), "XF", ""))),
("named outside a raster object", format!("(a.tif) 0 XG\n{}", raster7("", m, (2, 2), "XF", ""))),
("too few operands", raster7("(a.tif) 0 XG\n", m, (2, 2), "XF", "").replace(" 8 3 0 0 0 0 XF", " 8 3 0 XF")),
("no matrix", raster7("(a.tif) 0 XG\n", "", (2, 2), "XF", "")),
("a matrix with no area", raster7("(a.tif) 0 XG\n", "[ 0 0 0 0 0 0 ]", (2, 2), "XF", "")),
("no pixels", raster7("(a.tif) 0 XG\n", m, (0, 2), "XF", "")),
("a side past the largest", raster7("(a.tif) 0 XG\n", m, (2_000_000, 2), "XF", "")),
("samples of 7 bits", raster7("(a.tif) 0 XG\n", m, (2, 2), "XF", "").replace(" 8 3 0 0 0 0 XF", " 7 3 0 0 0 0 XF")),
] {
let body = format!("{embedded}{src}");
let r = read(stream7(&layer7("L", &body)).as_bytes()).unwrap();
let a = art(&r.doc);
assert_eq!(a.len(), body.matches(" XI\n").count(), "{what}: {:?}", r.warnings);
assert!(a.iter().all(|n| matches!(&n.kind, NodeKind::Image(im) if im.link.is_none())), "{what}: the embedded images only");
let left_out = r.warnings.iter().filter(|w| *w == "an image VectorCraft can't read was left out").count();
assert_eq!(left_out, 1, "{what}: {:?}", r.warnings);
assert!(!r.warnings.iter().any(|w| w == read::LINKED_PLACEHOLDERS), "{what}: {:?}", r.warnings);
}
// More pixels than an embedded image may have: no pixels are read, so the linked image comes in.
let r = read(stream7(&layer7("L", &raster7("(a.tif) 0 XG\n", m, (100_000, 100_000), "XF", ""))).as_bytes()).unwrap();
let a = art(&r.doc);
assert!(matches!(&a[..], [n] if matches!(&n.kind, NodeKind::Image(im) if (im.width, im.height) == (100_000, 100_000))), "{a:?}");
assert!(!r.warnings.iter().any(|w| w == read::IMAGE_TOO_LARGE), "{:?}", r.warnings);
let twice = raster7("(a.tif) 0 XG\n(b.tif) 0 XG\n", m, (2, 2), "XF", "");
let r = read(stream7(&layer7("L", &twice)).as_bytes()).unwrap();
let a = art(&r.doc);
assert!(matches!(&a[..], [n] if matches!(&n.kind, NodeKind::Image(im) if im.link.as_ref().is_some_and(|l| l.path == "b.tif"))), "{a:?}");
}
/// CMYK images keep their ink amounts, as the file's PDF part keeps them, and an alpha channel
/// becomes their opacity mask: a grayscale image over the image.
#[test]
@@ -2482,6 +2566,25 @@ fn xml_ids_stay_within_their_byte_budget() {
assert_eq!(bytes, super::read::MAX_XML_ID_BYTES);
}
/// Placed files of other paths keep images of their own though their previews are alike; two of
/// one path share theirs.
#[test]
fn placed_files_of_other_paths_keep_images_of_their_own() {
let preview = [&b"2 1 12 32 17 "[..], &[255, 255, 0, 0, 128, 0, 64, 0, 9, 9, 9, 9]].concat();
let object = |path: &str| {
let data = placed(true, "[5 0 0 -5 0 200]", path, Some(&preview), "");
let find = |s: &[u8]| data.windows(s.len()).position(|w| w == s).unwrap();
data[find(b"%AI5_BeginPlace")..find(b"%AI5_EndPlace\r") + 14].to_vec()
};
let mut data = placed(true, "[5 0 0 -5 0 200]", "a.png", Some(&preview), "");
let end = data.windows(14).position(|w| w == b"%AI5_EndPlace\r").unwrap() + 14;
data.splice(end..end, [object("b.png"), object("a.png")].concat());
let r = read(&data).unwrap();
let keys: Vec<_> = art(&r.doc).iter().filter_map(|n| if let NodeKind::Image(im) = &n.kind { Some(im.key.clone()) } else { None }).collect();
assert!(matches!(&keys[..], [a, b, c] if a != b && a == c), "{keys:?}");
assert_eq!(r.doc.images.len(), 2);
}
#[test]
fn placed_files_are_linked_images_showing_their_preview() {
// A 2×1 preview (premultiplied ARGB, a padded row) of a file of 4×2 pixels.
+30 -3
View File
@@ -94,7 +94,7 @@ pub(super) type Built = Result<(Document, Vec<String>), String>;
/// `structure` (the editing copy `data` read) with nothing compared with the pages, when the file's
/// text document makes the type of every text object (at least one shows, and none is type that a
/// look draws), no object is unread or has another placement or drawn art that the page would
/// choose, and its artboards are the pages, one for one and of their size, in points or in the pages'
/// choose, no linked image shows a placeholder, and its artboards are the pages, one for one and of their size, in points or in the pages'
/// units ([`page_scale_alone`]). The document is drawn at that scale. Else `Err(structure)`, for
/// [`build_from`] with the page.
pub(super) fn build_without_page(structure: Structure, data: &[u8], pages: &[(f64, f64)], unit: f64) -> Result<Built, Box<Structure>> {
@@ -116,9 +116,11 @@ pub(super) fn build_without_page(structure: Structure, data: &[u8], pages: &[(f6
/// Is nothing in `structure` waiting on the page: no object unread (the page's art takes its
/// place), no placed file with another placement, no live envelope with the drawn art it replaced
/// (the comparison with the page chooses between them)?
/// (the comparison with the page chooses between them), no linked image showing a placeholder
/// where the page has its pixels?
fn nothing_waits_on_page(structure: &Structure) -> bool {
structure.unread.is_empty()
structure.placeholders.is_empty()
&& structure.unread.is_empty()
&& structure.unread_elsewhere.is_empty()
&& structure.placed_elsewhere.is_empty()
&& structure.envelopes_drawn.is_empty()
@@ -168,6 +170,16 @@ fn page_scale_alone(structure: &Structure, pages: &[(f64, f64)], unit: f64) -> O
[1.0, unit].into_iter().find(|&k| k.is_finite() && k >= 1.0 && fits(k))
}
/// The areas left out of the comparisons with `page`: the `restyled` looks', and on a PostScript
/// page, which draws nothing for them, the linked images' `placeholders`. A PDF part draws those
/// images' pixels, which the placeholders don't match.
fn left_out_of_comparisons(page: Page, mut restyled: Vec<Rect>, placeholders: Vec<Rect>) -> Vec<Rect> {
if matches!(page, Page::Art) {
restyled.extend(placeholders);
}
restyled
}
/// [`build`] from `structure`, the editing copy `data` already read.
pub(super) fn build_from(
structure: Structure,
@@ -189,6 +201,7 @@ pub(super) fn build_from(
unread_elsewhere,
slot_names,
restyled,
placeholders,
placed_elsewhere,
envelopes_drawn,
styles_read,
@@ -198,6 +211,7 @@ pub(super) fn build_from(
bindings_skipped,
warnings,
} = structure;
let restyled = left_out_of_comparisons(page, restyled, placeholders);
// Editing data in the units of the page's user space ([`in_units`]), or at its declared large
// canvas scale ([`page_scale`]), is compared with the page scaled down to those units, and the
// document read from it is scaled up by `unit`.
@@ -1600,6 +1614,19 @@ mod candidate_tests {
assert_eq!(more_pages(Some(&tiny_boards), &tiny, 1).len() + 1, MAX_PAGES_COMPARED);
}
/// A linked image showing a placeholder (`XF`) waits on the page, whose PDF part draws its
/// pixels: the layers aren't used without it.
#[test]
fn a_linked_image_placeholder_waits_on_the_page() {
let raster = "%AI5_File:\n%AI5_BeginRaster\n(photo.tif) 0 XG\n[ 1 0 0 1 0 4 ] 0 0 4 4 4 4 8 3 0 0 0 0 XF\n%AI5_EndRaster\n";
let data = format!(
"%!PS-Adobe-3.0 \n%%BoundingBox: 0 0 100 100\n%AI3_Cropmarks: 0 0 100 100\n%AI5_BeginLayer\n1 1 1 1 0 0 1 0 79 128 255 0 50 0 Lb\n(L) Ln\n{raster}LB\n%AI5_EndLayer--\n%%Trailer\n"
);
let structure = ai::read(data.as_bytes(), false).unwrap();
assert_eq!(structure.placeholders.len(), 1);
assert!(!nothing_waits_on_page(&structure));
}
/// The pages a box touches with a margin round it include those it only comes near.
#[test]
fn the_pages_within_reach_of_a_box_count_as_touched() {
@@ -350,6 +350,35 @@ fn a_legacy_file_with_a_placed_eps_comes_in_through_its_layers() {
assert!(r.warnings.iter().any(|w| w.contains("placed files")), "{:?}", r.warnings);
}
/// A legacy file with a linked image (version 7 specification §8.2, §8.3: `(path) modified XG`, then
/// `XF` with no samples) comes in through its layers, the image as a linked image of that file
/// showing a placeholder where the matrix puts the image.
#[test]
fn a_legacy_file_with_a_linked_image_comes_in_through_its_layers() {
let prolog = "/Lb {10 {pop} repeat} def /Ln {pop} def /LB {} def /A {pop} def /k {setcmykcolor} def\n\
/XG {pop pop} def /XF {13 {pop} repeat} def";
let raster = "%AI5_File:\n%AI5_BeginRaster\n(photo.tif) 0 XG\n[2 0 0 3 20 80] 0 0 10 20 10 20 8 3 0 0 0 0 XF\n%AI5_EndRaster\n";
let body = format!("%AI5_BeginLayer\n1 1 1 1 0 0 0 79 128 255 Lb\n(Back) Ln\n0 A\n0 0 0 1 k\n{}\n{raster}LB\n%AI5_EndLayer--\n", tri(10));
let file = String::from_utf8(ai(AI8, "", &body)).unwrap().replace("%%EndProlog", &format!("{prolog}\n%%EndProlog"));
let r = import(file.as_bytes()).unwrap();
assert!(!r.preview);
let names: Vec<_> = r.document.layers.iter().map(|l| l.name.clone().unwrap_or_default()).collect();
assert_eq!(names, ["Back"], "{:?}", r.warnings);
let art = r.document.layers[0].children().unwrap();
assert_eq!(art.len(), 2, "{:?}", r.warnings);
let NodeKind::Image(im) = &art[1].kind else { panic!("{:?}", art[1].kind) };
assert_eq!(im.link.as_ref().map(|l| l.path.as_str()), Some("photo.tif"));
// The image's 10 × 20 pixels from (20, 80) in art space, 2 and 3 points each: 20 20 40 80 on
// the page, y down.
assert_eq!((im.width, im.height), (10, 20));
let (top_left, bottom_right) = (im.xf * Point::ZERO, im.xf * Point::new(10.0, 20.0));
assert!(
(top_left - Point::new(20.0, 20.0)).hypot() < 0.01 && (bottom_right - Point::new(40.0, 80.0)).hypot() < 0.01,
"{top_left:?} {bottom_right:?}"
);
assert!(r.warnings.iter().any(|w| w.contains("show a placeholder")), "{:?}", r.warnings);
}
/// A file in the legacy format without layers (Illustrator 3) whose prolog is included: its prolog,
/// a setup, and `art`.
fn layerless(art: &str) -> Vec<u8> {
+12
View File
@@ -419,6 +419,18 @@ fn group_with_only_placed_file_without_preview_uses_page_art() {
assert!(notes.iter().any(|n| n.contains("placed files (1)")), "{notes:?}");
}
/// A linked image whose pixels the editing data doesn't keep (`XF`) shows a placeholder where the
/// PDF part draws the image: the layers are compared with the PDF part there, and the file comes in
/// as its PDF part when they differ.
#[test]
fn a_linked_image_without_pixels_is_compared_with_the_pdf_part() {
let linked = "%AI5_File:\n%AI5_BeginRaster\n(photo.tif) 0 XG\n[ 10 0 0 10 50 54 ] 0 0 4 4 4 4 8 3 0 0 0 0 XF\n%AI5_EndRaster";
let art = native(&(layer("Live", true, &square(10, 10)) + &layer("Linked", true, linked)));
let (doc, notes, read) = crate::layered_ai(&private(&art), pdf_two_squares(), vec![], false, false, 1.0);
assert!(!read, "{notes:?}");
assert_eq!(names(&doc), ["Layer 1"]);
}
#[test]
fn top_level_pattern_uses_page_art_beside_live_path() {
let body = format!("(Absent) 0 0 1 1 0 0 0 0 0 [1 0 0 1 0 0] p\n{}\n{}", square_path(50, 10), square(70, 10));
+1
View File
@@ -4524,6 +4524,7 @@ axis Both И двете
@msg an image VectorCraft can't read was left out изображение, което VectorCraft не може да разчете, беше пропуснато
@msg an image whose data is cut short was left out изображение с непълни данни беше пропуснато
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel свързаните картини показват записания във файла предварителен преглед с ниска резолюция, докато файловете им бъдат намерени: презакачете ги в панела „Връзки“
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel свързаните картини без предварителен преглед във файла показват заместител, докато файловете им бъдат намерени: презакачете ги в панела „Връзки“
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текстовите рамки за област в редове, твърде къси за шрифта, в който се показват, получиха по-високи рамки ({_1}): текстът им иначе нямаше да се види
@msg its editing data doesn't say where its art is данните му за редактиране не посочват къде се намира неговата графика
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) този файл е записан без съвместимост с PDF и данните му за редактиране не могат да бъдат прочетени ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4923,6 +4923,7 @@ imageTrace Outline Obris
@msg an image VectorCraft can't read was left out izostavljena je slika koju VectorCraft ne može pročitati
@msg an image whose data is cut short was left out izostavljena je slika čiji su podaci skraćeni
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel povezane slike prikazuju pregled niske rezolucije sačuvan u datoteci dok se njihove datoteke ne pronađu: ponovo ih povežite u panelu Veze
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel povezane slike bez pregleda u datoteci prikazuju čuvar mjesta dok se njihove datoteke ne pronađu: ponovo ih povežite u panelu Veze
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise tekst u području u okvirima prekratkim za red fonta u kojem se prikazuje dobio je više okvire ({_1}): inače se tekst ne bi prikazao
@msg its editing data doesn't say where its art is njeni podaci za uređivanje ne govore gdje joj je crtež
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ova datoteka je sačuvana bez PDF kompatibilnosti, a njeni podaci za uređivanje se ne mogu pročitati ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -5181,6 +5181,7 @@ digits Malayalam Malaiàlam
@msg library panel `{given}` isn't missing its library al panell de biblioteca `{given}` no li falta la biblioteca
@msg library panel `{given}` isn't open el panell de biblioteca `{given}` no està obert
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel les imatges enllaçades mostren la previsualització de baixa resolució desada al fitxer fins que es trobin els seus fitxers: torneu-les a enllaçar al panell Enllaços
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel les imatges enllaçades sense previsualització al fitxer mostren un marcador de posició fins que es trobin els seus fitxers: torneu-les a enllaçar al panell Enllaços
@msg Live Paint Merge: select a Live Paint group and the paths to add Fusió de pintura interactiva: seleccioneu un grup de pintura interactiva i els traçats que voleu afegir
@msg Live Paint takes paths and compound paths: convert type, images and other objects to paths first La pintura interactiva accepta traçats i traçats compostos: convertiu abans el text, les imatges i altres objectes a traçats
@msg Live Paint: select paths or compound paths Pintura interactiva: seleccioneu traçats o traçats compostos
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4981,6 +4981,7 @@ imageTrace Outline Obrys
@msg graphs come in as plain art, without their data or settings grafy se načtou jako obyčejná grafika, bez svých dat a nastavení
@msg an image whose data is cut short was left out obrázek s useknutými daty byl vynechán
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel propojené obrázky se zobrazují jako náhled v nízkém rozlišení uložený v souboru, dokud se nenajdou jejich soubory: znovu je propojte v panelu Propojení
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel propojené obrázky bez náhledu v souboru se zobrazují jako zástupný symbol, dokud se nenajdou jejich soubory: znovu je propojte v panelu Propojení
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise text v oblasti v rámcích příliš nízkých pro řádek písma, ve kterém se zde zobrazuje, dostal vyšší rámce ({_1}): jinak by nebyl vidět
@msg its editing data doesn't say where its art is z jeho dat pro úpravy není zřejmé, kde je kresba
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) tento soubor byl uložen bez kompatibility s PDF a jeho data pro úpravy nelze přečíst ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4611,6 +4611,7 @@ imageTrace Outline Kontur
@msg an image VectorCraft can't read was left out et billede, som VectorCraft ikke kan læse, blev udeladt
@msg an image whose data is cut short was left out et billede, hvis data er afkortet, blev udeladt
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel linkede billeder viser den gemte forhåndsvisning i lav opløsning, indtil deres filer findes: link dem igen i Links-panelet
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel linkede billeder uden forhåndsvisning i filen viser en pladsholder, indtil deres filer findes: link dem igen i Links-panelet
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise rammetekst i rammer, der er for lave til en tekstlinje i den skrifttype, den viser i, fik højere rammer ({_1}): teksten ville ellers ikke blive vist
@msg its editing data doesn't say where its art is dens redigeringsdata siger ikke, hvor dens grafik er
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) denne fil blev gemt uden PDF-kompatibilitet, og dens redigeringsdata kan ikke læses ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4591,6 +4591,7 @@ theme Appearance Erscheinungsbild
@msg graphs come in as plain art, without their data or settings Diagramme werden als einfache Grafik übernommen, ohne ihre Daten und Einstellungen
@msg an image whose data is cut short was left out ein Bild mit abgeschnittenen Daten wurde ausgelassen
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel verknüpfte Bilder zeigen die in der Datei gespeicherte Vorschau in niedriger Auflösung, bis ihre Dateien gefunden werden: Verknüpfen Sie sie im Bedienfeld „Verknüpfungen“ neu
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel verknüpfte Bilder ohne Vorschau in der Datei zeigen einen Platzhalter, bis ihre Dateien gefunden werden: Verknüpfen Sie sie im Bedienfeld „Verknüpfungen“ neu
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise Flächentext in Rahmen, die für eine Zeile der Schrift, in der er hier erscheint, zu niedrig sind, hat höhere Rahmen bekommen ({_1}): Sonst wäre der Text nicht zu sehen
@msg its editing data doesn't say where its art is seine Bearbeitungsdaten geben nicht an, wo seine Grafik liegt
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) diese Datei wurde ohne PDF-Kompatibilität gespeichert, und ihre Bearbeitungsdaten können nicht gelesen werden ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4624,6 +4624,7 @@ digits Malayalam Μαλαγιάλαμ
@msg graphs come in as plain art, without their data or settings τα γραφήματα εισάγονται ως απλή γραφική, χωρίς τα δεδομένα και τις ρυθμίσεις τους
@msg an image whose data is cut short was left out παραλείφθηκε εικόνα με περικομμένα δεδομένα
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel οι συνδεδεμένες εικόνες εμφανίζουν την προεπισκόπηση χαμηλής ανάλυσης που έχει αποθηκευτεί, μέχρι να βρεθούν τα αρχεία τους: επανασυνδέστε τις στον πίνακα Σύνδεσμοι
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel οι συνδεδεμένες εικόνες χωρίς προεπισκόπηση στο αρχείο εμφανίζουν ένα σύμβολο κράτησης θέσης, μέχρι να βρεθούν τα αρχεία τους: επανασυνδέστε τις στον πίνακα Σύνδεσμοι
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise οι περιοχές κειμένου με πλαίσια πολύ μικρά για μία γραμμή της γραμματοσειράς τους απέκτησαν μεγαλύτερο ύψος ({_1}), διαφορετικά το κείμενο δεν θα εμφανιζόταν
@msg its editing data doesn't say where its art is τα δεδομένα επεξεργασίας του δεν προσδιορίζουν τη θέση του σχεδίου
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) το αρχείο αποθηκεύτηκε χωρίς συμβατότητα PDF και τα δεδομένα επεξεργασίας του δεν μπορούν να διαβαστούν ({why})
Can't render this file because it contains an unexpected character in line 163 and column 5.
+1
View File
@@ -4675,6 +4675,7 @@ axis Both Ambos
@msg graphs come in as plain art, without their data or settings los gráficos se importan como ilustración normal, sin sus datos ni sus ajustes
@msg an image whose data is cut short was left out se omitió una imagen con datos incompletos
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel las imágenes vinculadas muestran la vista previa de baja resolución guardada en el archivo hasta que se encuentren sus archivos: vuelva a vincularlas en el panel Vínculos
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel las imágenes vinculadas sin vista previa en el archivo muestran un marcador de posición hasta que se encuentren sus archivos: vuelva a vincularlas en el panel Vínculos
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise el texto de área en marcos demasiado bajos para una línea de la fuente en que se muestra recibió marcos más altos ({_1}): de otro modo no se vería
@msg its editing data doesn't say where its art is sus datos de edición no dicen dónde está su ilustración
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) este archivo se guardó sin compatibilidad con PDF y sus datos de edición no se pueden leer ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4883,6 +4883,7 @@ imageTrace Outline Ääriviiva
@msg graphs come in as plain art, without their data or settings kaaviot tuodaan tavallisena grafiikkana ilman niiden tietoja ja asetuksia
@msg an image whose data is cut short was left out kuva, jonka data on katkennut, jätettiin pois
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel linkitetyt kuvat näyttävät tiedostoon tallennetun matalan tarkkuuden esikatselun, kunnes niiden tiedostot löytyvät: linkitä ne uudelleen Linkit-paneelissa
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel linkitetyt kuvat, joilla ei ole esikatselua tiedostossa, näkyvät paikkamerkkinä, kunnes niiden tiedostot löytyvät: linkitä ne uudelleen Linkit-paneelissa
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise aluetekstin kehykset, jotka olivat liian matalia yhdelle riville niiden fontilla, korotettiin ({_1}): muuten niiden teksti ei näkyisi
@msg its editing data doesn't say where its art is sen muokkaustiedoista ei selviä kuvituksen sijainti
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) tämä tiedosto tallennettiin ilman PDF-yhteensopivuutta, eikä sen muokkaustietoja voi lukea ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4663,6 +4663,7 @@ axis Both Les deux
@msg graphs come in as plain art, without their data or settings les graphes sont importés comme illustration ordinaire, sans leurs données ni leurs réglages
@msg an image whose data is cut short was left out une image aux données tronquées a été ignorée
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel les images liées affichent l’aperçu basse résolution enregistré dans le fichier tant que leurs fichiers ne sont pas trouvés : liez-les à nouveau dans le panneau Liens
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel les images liées sans aperçu dans le fichier affichent un espace réservé tant que leurs fichiers ne sont pas trouvés : liez-les à nouveau dans le panneau Liens
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise le texte captif dans des cadres trop bas pour une ligne de la police dans laquelle il s’affiche a reçu des cadres plus hauts ({_1}) : il ne s’afficherait pas autrement
@msg its editing data doesn't say where its art is ses données d’édition ne disent pas où se trouve son illustration
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ce fichier a été enregistré sans compatibilité PDF et ses données d’édition sont illisibles ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4689,6 +4689,7 @@ imageTrace Outline Körvonal
@msg graphs come in as plain art, without their data or settings a diagramok egyszerű grafikaként kerülnek be, adataik és beállításaik nélkül
@msg an image whose data is cut short was left out kimaradt egy csonka adatú kép
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel a csatolt képek a fájlban mentett kis felbontású előnézetet mutatják, amíg a fájljaik elő nem kerülnek: csatold őket újra a Csatolások panelen
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel a fájlban előnézet nélküli csatolt képek helyőrzőt mutatnak, amíg a fájljaik elő nem kerülnek: csatold őket újra a Csatolások panelen
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise a túl alacsony keretben lévő területszöveg, amelybe a megjelenítő betűtípus egy sora sem fér el, magasabb keretet kapott ({_1}): különben nem látszana
@msg an object can't be read egy objektum nem olvasható
@msg an object in perspective would leave the edited grid egy perspektivikus objektum kikerülne a szerkesztett rácsból
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4611,6 +4611,7 @@ imageTrace Outline Kontur
@msg an image VectorCraft can't read was left out gambar yang tidak dapat dibaca VectorCraft tidak disertakan
@msg an image whose data is cut short was left out gambar yang datanya terpotong tidak disertakan
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel gambar tertaut menampilkan pratinjau resolusi rendah yang tersimpan dalam berkas sampai berkasnya ditemukan: tautkan ulang di panel Tautan
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel gambar tertaut tanpa pratinjau dalam berkas menampilkan penanda tempat sampai berkasnya ditemukan: tautkan ulang di panel Tautan
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise teks area dalam bingkai yang terlalu pendek untuk satu baris font yang ditampilkannya mendapat bingkai yang lebih tinggi ({_1}): tanpa itu teksnya tidak akan tampil
@msg its editing data doesn't say where its art is data pengeditannya tidak menyatakan di mana karya seninya berada
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) berkas ini disimpan tanpa kompatibilitas PDF, dan data pengeditannya tidak dapat dibaca ({why})
Can't render this file because it contains an unexpected character in line 2235 and column 5.
+1
View File
@@ -4717,6 +4717,7 @@ axis Both Entrambi
@msg graphs come in as plain art, without their data or settings i grafici vengono importati come grafica semplice, senza i loro dati e le loro impostazioni
@msg an image whose data is cut short was left out un'immagine con dati incompleti è stata omessa
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel le immagini collegate mostrano l'anteprima a bassa risoluzione salvata nel file finché i loro file non vengono trovati: ricollegale nel pannello Collegamenti
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel le immagini collegate senza anteprima nel file mostrano un segnaposto finché i loro file non vengono trovati: ricollegale nel pannello Collegamenti
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise il testo in area in cornici troppo basse per una riga del font in cui viene mostrato ha ricevuto cornici più alte ({_1}): altrimenti non sarebbe visibile
@msg its editing data doesn't say where its art is i suoi dati di modifica non dicono dove si trova la grafica
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) questo file è stato salvato senza compatibilità PDF e i suoi dati di modifica non si possono leggere ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4404,6 +4404,7 @@ swatch Bright Violet ビビッドバイオレット
@msg graphs come in as plain art, without their data or settings グラフは通常のアートとして読み込まれ、データと設定は保持されません
@msg an image whose data is cut short was left out データが途中で切れている画像は除外されました
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel リンク画像は、元のファイルが見つかるまで、ファイルに保存された低解像度のプレビューで表示されます:リンクパネルで再リンクしてください
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel ファイルにプレビューのないリンク画像は、元のファイルが見つかるまでプレースホルダーで表示されます:リンクパネルで再リンクしてください
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 表示に使われるフォントの 1 行に対して低すぎるフレーム内のエリア内文字は、フレームを高くしました ({_1}): そうしないと文字が表示されません
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it グラデーション「{name}」はファイル内で定義されていません: 代わりに単色を使用しています
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft がまだ読み込めないブラシ {_1} 個はブラシパネルに追加されませんでした
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4779,6 +4779,7 @@ font-technology Variable ცვლადი
Even თანაბრად
Stacked დასტად
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel დაკავშირებული გამოსახულებები ფაილში შენახულ დაბალი გარჩევადობის მინიატურას აჩვენებს, სანამ მათი ფაილები არ მოიძებნება: დაუკავშირეთ ისინი ხელახლა ბმულების პანელში
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel დაკავშირებული გამოსახულებები, რომელთა მინიატურაც ფაილში არ არის, ადგილის შემნახველს აჩვენებს, სანამ მათი ფაილები არ მოიძებნება: დაუკავშირეთ ისინი ხელახლა ბმულების პანელში
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise არის ტექსტს ჩარჩოებში, რომლებიც მისი შრიფტის ერთი სტრიქონისთვის ზედმეტად დაბალი იყო, ჩარჩოები გაეზარდა ({_1}): სხვაგვარად ტექსტი არ გამოჩნდებოდა
@msg the art on them differs from the page's in {_1}% of it: they come from its editing data all the same მათზე არსებული ნახატი გვერდისგან {_1}%-ით განსხვავდება: მიუხედავად ამისა, ისინი რედაქტირების მონაცემებიდან აიღება
@msg every numbered name for {name} is taken in the document's folder დოკუმენტის საქაღალდეში {name}-ის ყველა დანომრილი სახელი დაკავებულია
Can't render this file because it is too large.
+1
View File
@@ -4331,6 +4331,7 @@ theme Appearance 외관
@msg graphs come in as plain art, without their data or settings 그래프는 데이터와 설정 없이 일반 아트로 가져왔습니다
@msg an image whose data is cut short was left out 데이터가 잘린 이미지는 제외했습니다
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel 연결된 그림은 해당 파일을 찾을 때까지 파일에 저장된 저해상도 미리 보기로 표시됩니다: 링크 패널에서 다시 연결하세요
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel 파일에 미리 보기가 없는 연결된 그림은 해당 파일을 찾을 때까지 자리 표시자로 표시됩니다: 링크 패널에서 다시 연결하세요
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 표시되는 글꼴의 한 줄보다 낮은 프레임의 영역 문자는 프레임을 더 높였습니다({_1}). 그렇지 않으면 문자가 표시되지 않습니다
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it 그레이디언트 “{name}”이(가) 파일에 정의되어 있지 않아 단색으로 대체했습니다
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft에서 아직 읽을 수 없는 브러시는 브러시 패널에서 제외되었습니다({_1})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4623,6 +4623,7 @@ imageTrace Outline Contour
@msg an image VectorCraft can't read was left out een afbeelding die VectorCraft niet kan lezen, is weggelaten
@msg an image whose data is cut short was left out een afbeelding waarvan de gegevens zijn afgekapt, is weggelaten
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel gekoppelde afbeeldingen tonen het in het bestand opgeslagen voorbeeld met lage resolutie totdat hun bestanden zijn gevonden: koppel ze opnieuw in het paneel Koppelingen
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel gekoppelde afbeeldingen zonder voorbeeld in het bestand tonen een tijdelijke aanduiding totdat hun bestanden zijn gevonden: koppel ze opnieuw in het paneel Koppelingen
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise vlaktekst in kaders die te kort zijn voor één regel van het lettertype waarin de tekst wordt weergegeven, heeft hogere kaders gekregen ({_1}): anders zou de tekst niet zichtbaar zijn
@msg its editing data doesn't say where its art is zijn bewerkingsgegevens zeggen niet waar de illustratie zich bevindt
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) dit bestand is opgeslagen zonder PDF-compatibiliteit en de bewerkingsgegevens kunnen niet worden gelezen ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4807,6 +4807,7 @@ imageTrace Outline Kontur
@msg graphs come in as plain art, without their data or settings wykresy są wczytywane jako zwykła grafika, bez ich danych i ustawień
@msg an image whose data is cut short was left out pominięto obraz z uciętymi danymi
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel połączone obrazy pokazują zapisany w pliku podgląd niskiej rozdzielczości, dopóki ich pliki nie zostaną znalezione: połącz je ponownie w panelu Łącza
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel połączone obrazy bez podglądu w pliku pokazują symbol zastępczy, dopóki ich pliki nie zostaną znalezione: połącz je ponownie w panelu Łącza
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise tekst akapitowy w ramkach niższych niż wiersz wyświetlanej czcionki otrzymał wyższe ramki ({_1}): inaczej tekst nie byłby widoczny
@msg its editing data doesn't say where its art is jego dane edycyjne nie określają położenia kompozycji
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) ten plik zapisano bez zgodności z PDF, a jego danych edycyjnych nie można odczytać ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4901,6 +4901,7 @@ axis Both Ambos
@msg object {id} is not a selected top-level object o objeto {id} não é um objeto selecionado de nível superior
@msg {path}: not a file {path}: não é um arquivo
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel as imagens vinculadas mostram a visualização de baixa resolução salva no arquivo até que seus arquivos sejam encontrados: revincule-as no painel Links
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel as imagens vinculadas sem visualização no arquivo mostram um espaço reservado até que seus arquivos sejam encontrados: revincule-as no painel Links
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise o texto de área em molduras baixas demais para uma linha da fonte em que aparece recebeu molduras mais altas ({_1}): senão o texto não apareceria
@msg every numbered name for {name} is taken in the document's folder todos os nomes numerados para {name} já existem na pasta do documento
@msg No linked files to look for Nenhum arquivo vinculado para procurar
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1
View File
@@ -4712,6 +4712,7 @@ axis Both Оба
@msg graphs come in as plain art, without their data or settings диаграммы импортируются как обычная графика, без их данных и настроек
@msg an image whose data is cut short was left out изображение с обрезанными данными пропущено
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel связанные изображения показываются как сохранённый в файле просмотр низкого разрешения, пока их файлы не найдены: восстановите связи на панели «Связи»
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel связанные изображения без просмотра в файле показываются как заполнитель, пока их файлы не найдены: восстановите связи на панели «Связи»
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текст в области в рамках, слишком низких для строки шрифта, которым он отображается, получил более высокие рамки ({_1}): иначе он не был бы виден
@msg its editing data doesn't say where its art is в его данных редактирования не указано, где находится графика
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) этот файл сохранён без совместимости с PDF, и его данные редактирования не удаётся прочитать ({why})
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4574,6 +4574,7 @@ axis Both Båda
@msg graphs come in as plain art, without their data or settings diagram läses in som vanlig grafik, utan sina data och inställningar
@msg an image whose data is cut short was left out en bild med avkortade data utelämnades
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel länkade bilder visar förhandsgranskningen med låg upplösning som sparats i filen tills deras filer hittas: länka om dem i panelen Länkar
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel länkade bilder utan förhandsgranskning i filen visar en platshållare tills deras filer hittas: länka om dem i panelen Länkar
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise områdestext i ramar som var för låga för en rad i det typsnitt den visas med fick högre ramar ({_1}): annars skulle texten inte synas
@msg its editing data doesn't say where its art is dess redigeringsdata anger inte var dess grafik finns
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) den här filen sparades utan PDF-kompatibilitet och dess redigeringsdata kan inte läsas ({why})
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -4411,6 +4411,7 @@ axis Both Her İkisi
@msg graphs come in as plain art, without their data or settings grafikler, verileri ve ayarları olmadan düz çizim olarak içe aktarılır
@msg an image whose data is cut short was left out verileri kısa kesilen bir resim dışarıda bırakıldı
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel bağlantılı resimler, dosyaları bulunana kadar dosyaya kaydedilen düşük çözünürlüklü önizlemeyi gösterir: bunları Bağlantılar panelinde yeniden bağlayın
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel dosyada önizlemesi olmayan bağlantılı resimler, dosyaları bulunana kadar bir yer tutucu gösterir: bunları Bağlantılar panelinde yeniden bağlayın
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise çerçevelerdeki alan türü, daha uzun çerçevelerde gösterdiği yazı tipinin bir satırı için çok kısa ({_1}): bunların türü aksi halde gösterilmez
@msg its editing data doesn't say where its art is düzenleme verileri sanatın nerede olduğunu söylemiyor
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) bu dosya PDF uyumluluğu olmadan kaydedildi ve düzenleme verileri okunamıyor ({why})
Can't render this file because it contains an unexpected character in line 5 and column 25.
+1
View File
@@ -4701,6 +4701,7 @@ axis Both Обидві
@msg graphs come in as plain art, without their data or settings діаграми імпортуються як звичайна графіка, без їхніх даних і налаштувань
@msg an image whose data is cut short was left out зображення з обрізаними даними пропущено
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel пов’язані зображення показуються як збережений у файлі перегляд низької роздільності, доки їхні файли не знайдено: відновіть зв’язки на панелі «Зв’язки»
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel пов’язані зображення без перегляду у файлі показуються як заповнювач, доки їхні файли не знайдено: відновіть зв’язки на панелі «Зв’язки»
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise текст в області в рамках, занизьких для рядка шрифту, яким він показується, отримав вищі рамки ({_1}): інакше його не було б видно
@msg its editing data doesn't say where its art is у даних редагування не зазначено, де знаходиться графіка
@msg this file was saved without PDF compatibility, and its editing data can't be read ({why}) цей файл збережений без сумісності з PDF, і його дані редагування не вдається прочитати ({why})
Can't render this file because it contains an unexpected character in line 177 and column 5.
+1
View File
@@ -4535,6 +4535,7 @@ theme Appearance Giao diện
@msg line {line}: value for `{var}` must be true or false dòng {line}: giá trị của `{var}` phải là true hoặc false
@msg line {line}: value for `{var}` is longer than {MAX_GRAPH_CSV} bytes dòng {line}: giá trị của `{var}` dài hơn {MAX_GRAPH_CSV} byte
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel ảnh liên kết hiển thị bản xem trước độ phân giải thấp đã lưu trong tệp cho đến khi tìm thấy tệp gốc: liên kết lại trong bảng Liên kết
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel ảnh liên kết không có bản xem trước trong tệp hiển thị ô giữ chỗ cho đến khi tìm thấy tệp gốc: liên kết lại trong bảng Liên kết
@msg Live Paint Merge: select a Live Paint group and the paths to add Hợp nhất tô trực tiếp: chọn nhóm tô trực tiếp và các đường dẫn để thêm
@msg Live Paint takes paths and compound paths: convert type, images and other objects to paths first Tô trực tiếp nhận đường dẫn và đường dẫn phức: hãy chuyển văn bản, hình ảnh và các đối tượng khác thành đường dẫn trước
@msg Live Paint: select paths or compound paths Tô trực tiếp: chọn đường dẫn hoặc đường dẫn phức
Can't render this file because it contains an unexpected character in line 199 and column 5.
+1
View File
@@ -3464,6 +3464,7 @@ digits Malayalam 马拉雅拉姆数字
Update Changed Links 更新已更改的链接
@plural {n} linked image changed on disk. Show the new version?|{n} linked images changed on disk. Show the new versions? 有 {n} 个链接图像已在磁盘上更改。要显示新版本吗?
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel 链接的图片在找到其文件之前显示文件中存储的低分辨率预览:请在“链接”面板中重新链接
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel 文件中没有预览的链接图片在找到其文件之前显示占位符:请在“链接”面板中重新链接
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 框架高度不足以容纳所显示字体一行的区域文字已加高框架 ({_1}):否则文字不会显示
Update Modified Links 更新已修改的链接
UPPERCASE 大写
Can't render this file because it contains an unexpected character in line 2 and column 73.
+1
View File
@@ -4494,6 +4494,7 @@ digits Malayalam 馬來亞拉姆數字
Update Changed Links 更新已變更的連結
@plural {n} linked image changed on disk. Show the new version?|{n} linked images changed on disk. Show the new versions? 有 {n} 個連結影像已在磁碟上變更。要顯示新版本嗎?
@msg linked pictures show the low-resolution preview saved in the file until their files are found: relink them in the Links panel 連結的圖片在找到其檔案之前顯示檔案中儲存的低解析度預覽:請在「連結」面板中重新連結
@msg linked pictures with no preview in the file show a placeholder until their files are found: relink them in the Links panel 檔案中沒有預覽的連結圖片在找到其檔案之前顯示預留位置:請在「連結」面板中重新連結
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 框架高度不足以容納所顯示字體一行的區域文字已加高框架 ({_1}):否則文字不會顯示
Update Modified Links 更新已修改的連結
UPPERCASE 全部大寫
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3 -1
View File
@@ -120,7 +120,9 @@ private editing data. VectorCraft:
without the prolog opens since #1027, its legacy type (`To` … `TO`) since #868, with its paragraphs' letter
and word spacing (`TC`, `TW`), hanging punctuation (`Tq`) and hyphenation (`Th`). Its patterns in
the version 7 form (a list of `@` and `&` layers, `_ &` for the tile's background) open as pattern swatches,
with a fill's offset, scale, rotation, reflection and shear applied before its matrix.
with a fill's offset, scale, rotation, reflection and shear applied before its matrix. Its linked images in
the version 7 form (`(path) modified XG`, then `XF` with no samples) come in as linked images of their files,
showing a placeholder where the image goes until the file is relinked in the Links panel.
- Estimate: **~65%** for real exchange with Illustrator users (open: ~75%; save: PDF-compatible, or legacy
version 7 with layers and groups; not yet tested by users who hand files back).
Closing the open-side issues: 10–20 h with users' files.