mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 18:54:57 +00:00
Merge pull request #5486 from dotCooCoo/fix/ai-page-mismatch-keeps-layers-2
.ai import: objects that draw differently from the page come in as its art, and the layers stay
This commit is contained in:
@@ -127,6 +127,45 @@ fn an_ai_unread_group_keeps_its_layers_and_live_neighbor() {
|
||||
assert!(result["warnings"].to_string().contains("`ZZ` (1)"), "{result}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_differing_object_keeps_the_layers_with_a_note() {
|
||||
let square = |cmyk: &str, x: f64| format!("{cmyk} k\n{}f\n", ai::rect(x, 40.0, x + 16.0, 56.0));
|
||||
let front = square("0 0 0 1", 90.0) + &square("0 0 1 0", 160.0);
|
||||
let data = ai::editing_data(200.0, 100.0, &(ai::layer("Back", &square("0 0 1 0", 20.0)) + &ai::layer("Front", &front)));
|
||||
let page = "0 0 1 0 k 20 40 16 16 re f 1 0 0 0 k 90 40 16 16 re f 0 0 1 0 k 160 40 16 16 re f";
|
||||
let mut session = Session::new();
|
||||
let result = open(&mut session, "differs.ai", &ai::ai(&compressed(data.as_bytes()), page), json!({})).unwrap();
|
||||
let doc = &session.doc().unwrap().doc;
|
||||
assert_eq!(names(&doc.layers), ["Back", "Front"], "{result}");
|
||||
let front = doc.layers[1].children().unwrap();
|
||||
assert!(matches!(front[0].kind, NodeKind::Group { .. }) && matches!(front[1].kind, NodeKind::Path { .. }), "{result}");
|
||||
let replacement = &front[0].children().unwrap()[0];
|
||||
assert!(
|
||||
matches!(replacement.appearance.items.first(), Some(vectorcraft_doc::AppearanceItem::Fill(fill))
|
||||
if fill.paint == vectorcraft_color::Paint::solid(vectorcraft_color::Color::cmyk(1.0, 0.0, 0.0, 0.0))),
|
||||
"{result}"
|
||||
);
|
||||
assert!(
|
||||
result["warnings"].to_string().contains("objects that drew differently from the file's own page came in as its art: Front (1)"),
|
||||
"{result}"
|
||||
);
|
||||
// An export to the file's own path is refused unless it is given `acknowledgeLoss`.
|
||||
let d = std::env::temp_dir().join(format!("vc-aidiffers-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&d).unwrap();
|
||||
let path = d.join("differs.ai");
|
||||
let file = ai::ai(&compressed(data.as_bytes()), page);
|
||||
std::fs::write(&path, &file).unwrap();
|
||||
let path = path.to_string_lossy().to_string();
|
||||
let mut s = Session::new();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
let e = s.execute("document.export", &json!({"path": path})).unwrap_err().to_string();
|
||||
assert!(e.contains("left things out") && e.contains("objects that drew differently"), "{e}");
|
||||
assert_eq!(std::fs::read(&path).unwrap(), file);
|
||||
s.execute("document.export", &json!({"path": path, "acknowledgeLoss": true})).unwrap();
|
||||
assert_ne!(std::fs::read(&path).unwrap(), file);
|
||||
std::fs::remove_dir_all(d).ok();
|
||||
}
|
||||
|
||||
/// A palette of three swatches and a color group `Inks` holding a fourth.
|
||||
const INKS_PALETTE: &str = "%AI5_BeginPalette\n0 0 Pb\n0 0 0 0 1 1 1 Xa\n(White)\nPc\n0.04 0.3 0 0 81.96 21 -8 (Pink ink) 0 2 Xx\n(Pink ink)\nPc\n\
|
||||
0.06 0.98 1 0 (Red ink) 0 x\n(Red ink)\nPc\n1 (Inks) 1 Pg\n0.02 0.1 1 0 (Yellow ink) 0 x\n(Yellow ink)\nPc\nPB\n%AI5_EndPalette\n";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::compare::{MAX_DIFFERENCE, PageView, compare, difference};
|
||||
use super::compare::{MAX_DIFFERENCE, MAX_SOLID_DIFFERENCE, PageView, compare, difference};
|
||||
use super::plan::{made_only, make_slots, plan, readable_all};
|
||||
use super::slots::{
|
||||
ART_LEFT_OUT, Content, MAX_PAIRS, MAX_SLOTS, MAX_TEXT, OUTLINE_REACH, SlotInfo, TEXT_LEFT_OUT, TYPE_LEFT_OUT, collect_slots, extent, few,
|
||||
fill_slots, letters, text_objects, words,
|
||||
ART_LEFT_OUT, Content, DREW_DIFFERENTLY, MAX_PAIRS, MAX_SLOTS, MAX_TEXT, OUTLINE_REACH, SlotInfo, TEXT_LEFT_OUT, TYPE_LEFT_OUT, collect_slots,
|
||||
extent, few, fill_slots, letters, text_objects, words,
|
||||
};
|
||||
use super::{Affine, Arc, BTreeSet, Document, Node, NodeId, NodeKind, PathData, Rect, Scaling, Story, Structure, Texts, Unit, Vec2, ai};
|
||||
|
||||
@@ -341,10 +341,22 @@ pub(super) fn build_from(
|
||||
doc = other;
|
||||
notes.extend(unseen_note(unseen).into_iter().chain(note));
|
||||
}
|
||||
_ => {
|
||||
let more = drawn_or_trusted(&frame, &mut view, &mut doc, &envelopes_drawn, ¬es, unseen, trust, why, reflow)?;
|
||||
notes.extend(more);
|
||||
}
|
||||
other => match drawn_or_trusted(&frame, &mut view, &mut doc, &envelopes_drawn, ¬es, unseen, trust, why, reflow) {
|
||||
Ok(more) => notes.extend(more),
|
||||
// The objects that draw differently from the page come in as its art, from
|
||||
// the placement that draws closer to it.
|
||||
Err(why) => {
|
||||
let other = other.map(|(_, other, unseen)| (other, unseen));
|
||||
let placements = closer_first(&frame, &mut view, (doc, unseen), other, reflow);
|
||||
let Some((kept, unseen, kept_notes)) = placements.into_iter().find_map(|(base, unseen)| {
|
||||
keep_differing(&base, &frame, &mut view, &threaded_frames, &shown, reflow).map(|(kept, n)| (kept, unseen, n))
|
||||
}) else {
|
||||
return Err(why);
|
||||
};
|
||||
doc = kept;
|
||||
notes.extend(unseen_note(unseen).into_iter().chain(kept_notes));
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -766,6 +778,135 @@ fn replace_unread(
|
||||
Ok(counts.into_iter().map(|(kind, count)| format!("{kind} ({count})")).collect())
|
||||
}
|
||||
|
||||
/// Most objects of the layers that come in as the page's art where they draw differently from it.
|
||||
const MAX_DIFFERING: usize = 64;
|
||||
|
||||
/// Most pairs of a patch where the layers and the page differ and a top-level object that may hold
|
||||
/// it, weighed to find the objects that come in as the page's art.
|
||||
const MAX_PATCH_PAIRS: usize = 4_000_000;
|
||||
|
||||
/// The layers as read and at the placed files' other placement (each with its count of text
|
||||
/// objects the page doesn't draw), the one that draws closer to the page first. `reflow` as in
|
||||
/// [`PageView::difference`].
|
||||
fn closer_first(
|
||||
frame: &Frame<'_>,
|
||||
view: &mut PageView,
|
||||
doc: (Document, usize),
|
||||
other: Option<(Document, usize)>,
|
||||
reflow: bool,
|
||||
) -> Vec<(Document, usize)> {
|
||||
match other {
|
||||
Some(other) if view.difference(&other.0, frame.rect, reflow).any < view.difference(&doc.0, frame.rect, reflow).any => vec![other, doc],
|
||||
Some(other) => vec![doc, other],
|
||||
None => vec![doc],
|
||||
}
|
||||
}
|
||||
|
||||
/// `doc`, whose layers draw differently from the page (an object in another color, or one that only
|
||||
/// one of them draws), with each of its top-level objects that holds such a difference replaced by the
|
||||
/// page's art in its bounds, when that makes the two look alike → the document and its notes: the
|
||||
/// comparison's, and how many objects were replaced on each layer (by name, in the layers' order).
|
||||
/// `None` when only edges or type differ, a difference lies outside every object, more than half
|
||||
/// of the objects (or more than [`MAX_DIFFERING`]) hold one, one of them holds type or a wrap
|
||||
/// object, the page's art can't replace one of them ([`replace_unread`]), the page's type of a text
|
||||
/// object that stays is copied into a replacement, or the two still differ. `shown` is the page's
|
||||
/// type of each text object that shows. `reflow` as in [`PageView::difference`].
|
||||
fn keep_differing(
|
||||
doc: &Document,
|
||||
frame: &Frame<'_>,
|
||||
view: &mut PageView,
|
||||
threaded_frames: &BTreeSet<NodeId>,
|
||||
shown: &[Vec<Arc<Node>>],
|
||||
reflow: bool,
|
||||
) -> Option<(Document, Vec<String>)> {
|
||||
/// The top-level objects that show and print (the outermost objects under a layer or
|
||||
/// sublayer), with the name of that layer or sublayer as the Layers panel shows it and their
|
||||
/// bounds.
|
||||
fn top_level(nodes: &[Arc<Node>], layer: &str, out: &mut Vec<(String, NodeId, Rect)>) {
|
||||
for n in nodes.iter().filter(|n| n.visible) {
|
||||
match &n.kind {
|
||||
NodeKind::Layer { printable: false, .. } => {}
|
||||
NodeKind::Layer { .. } => {
|
||||
let name = Some(n.display_name()).filter(|name| !name.is_empty()).unwrap_or_else(|| format!("<{}>", n.kind_label()));
|
||||
top_level(n.children().map_or(&[], Vec::as_slice), &name, out);
|
||||
}
|
||||
NodeKind::Path { guide: true, .. } => {}
|
||||
_ => {
|
||||
if let Some(b) = n.visual_bounds().filter(|b| b.area().is_finite() && b.area() > 0.0) {
|
||||
out.push((layer.to_string(), n.id, b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Where only spread edges or type differ, or only the art around the page, no object is
|
||||
// replaced.
|
||||
let (diff, regions) = view.solid_regions(doc, frame.rect, reflow)?;
|
||||
if diff.solid <= MAX_SOLID_DIFFERENCE {
|
||||
return None;
|
||||
}
|
||||
let mut objects = vec![];
|
||||
top_level(&doc.layers, "", &mut objects);
|
||||
objects.retain(|(_, _, b)| !b.intersect(frame.rect).is_zero_area());
|
||||
if regions.len().saturating_mul(objects.len()) > MAX_PATCH_PAIRS {
|
||||
return None;
|
||||
}
|
||||
// Each patch is the smallest object's that holds it.
|
||||
let most = MAX_DIFFERING.min(objects.len() / 2);
|
||||
let mut targets = BTreeSet::new();
|
||||
for region in regions {
|
||||
let (_, id, _) = objects.iter().filter(|(_, _, b)| inside(region, *b)).min_by(|a, b| a.2.area().total_cmp(&b.2.area()))?;
|
||||
targets.insert(*id);
|
||||
if targets.len() > most {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
if targets.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// A replaced wrap object keeps its wrap options, and the area type is wrapped around the page's
|
||||
// art that replaces it. An object inside a replaced group can't keep them, so such a group isn't
|
||||
// replaced. Neither is an object that holds type: the page's art that replaces it would hold
|
||||
// type the page draws for other text objects too.
|
||||
let mut wraps = vec![];
|
||||
for id in &targets {
|
||||
let node = doc.node(*id)?;
|
||||
let mut held = matches!(node.kind, NodeKind::Text(_));
|
||||
for child in node.children().into_iter().flatten() {
|
||||
child.walk(&mut |n| held |= n.extras().wrap.is_some() || matches!(n.kind, NodeKind::Text(_)));
|
||||
}
|
||||
if held {
|
||||
return None;
|
||||
}
|
||||
if let Some(wrap) = node.extras().wrap {
|
||||
wraps.push((*id, wrap));
|
||||
}
|
||||
}
|
||||
let mut kept = doc.clone();
|
||||
let replace: Vec<(NodeId, Vec<String>)> = targets.iter().map(|id| (*id, vec![])).collect();
|
||||
let mut copied = BTreeSet::new();
|
||||
replace_unread(&mut kept, frame, &replace, &[], threaded_frames, &mut copied).ok()?;
|
||||
// A text object that stays draws its page type, so a copy of that type would draw it twice.
|
||||
if shown.iter().flatten().any(|n| copied.contains(&n.id)) {
|
||||
return None;
|
||||
}
|
||||
for (id, wrap) in wraps {
|
||||
kept.node_mut(id)?.extras_mut().wrap = Some(wrap);
|
||||
}
|
||||
resolve_text_wrap(&mut kept).ok()?;
|
||||
let note = compare(frame, view, &kept, reflow).ok()?;
|
||||
let mut layers: Vec<(String, usize)> = vec![];
|
||||
for (layer, _, _) in objects.iter().filter(|(_, id, _)| targets.contains(id)) {
|
||||
match layers.iter_mut().find(|(name, _)| name == layer) {
|
||||
Some((_, count)) => *count += 1,
|
||||
None => layers.push((layer.clone(), 1)),
|
||||
}
|
||||
}
|
||||
let layers: Vec<String> = layers.into_iter().map(|(layer, count)| format!("{layer} ({count})")).collect();
|
||||
Some((kept, note.into_iter().chain([format!("{DREW_DIFFERENTLY}: {}", layers.join(", "))]).collect()))
|
||||
}
|
||||
|
||||
/// Copy page drawing IDs, including mask art outside the ordinary child tree.
|
||||
fn reid_page_art(doc: &mut Document, source: &Node) -> Node {
|
||||
let mut copy = doc.reid(source);
|
||||
|
||||
@@ -182,6 +182,12 @@ impl PageView {
|
||||
let printed = as_printed(b);
|
||||
let b = printed.as_ref().unwrap_or(b);
|
||||
let y = comparison_render(&mut self.layers, b, rb, self.scale, self.samples);
|
||||
let areas = self.counted_type(b, rb, reflow);
|
||||
compared(&self.page, &y, &areas, &self.restyled)
|
||||
}
|
||||
|
||||
/// Where [`Self::difference`] counts `b`'s and the page's pixels as type, in the page's pixels.
|
||||
fn counted_type(&self, b: &Document, rb: Rect, reflow: bool) -> Vec<Rect> {
|
||||
let mut areas = vec![];
|
||||
type_areas(&b.layers, rb, self.scale, &mut areas);
|
||||
if reflow {
|
||||
@@ -194,7 +200,7 @@ impl PageView {
|
||||
} else {
|
||||
areas.extend(&self.page_type);
|
||||
}
|
||||
compared(&self.page, &y, &areas, &self.restyled)
|
||||
areas
|
||||
}
|
||||
|
||||
/// The share (0 to 1) of the pixels where `b` on `rb` draws another colour than the page
|
||||
@@ -210,6 +216,52 @@ impl PageView {
|
||||
let differing = self.page.pixels.as_chunks::<4>().0.iter().zip(y.pixels.as_chunks::<4>().0).filter(|(p, q)| differ_in_colour(p, q)).count();
|
||||
differing as f64 / (f64::from(y.width) * f64::from(y.height)).max(1.0)
|
||||
}
|
||||
|
||||
/// How `b` on `rb` draws differently from the page ([`Self::difference`]), and the boxes (in
|
||||
/// `b`'s terms) around each patch of the pixels counted in its `solid` share (they and all
|
||||
/// their neighbors differ, away from type), neighbors touching corner to corner counting as one patch. `None` when the two
|
||||
/// drawings aren't the same size.
|
||||
pub(super) fn solid_regions(&mut self, b: &Document, rb: Rect, reflow: bool) -> Option<(Difference, Vec<Rect>)> {
|
||||
let printed = as_printed(b);
|
||||
let b = printed.as_ref().unwrap_or(b);
|
||||
let y = comparison_render(&mut self.layers, b, rb, self.scale, self.samples);
|
||||
let areas = self.counted_type(b, rb, reflow);
|
||||
let (diff, mut solid) = compared_with_solid(&self.page, &y, &areas, &self.restyled);
|
||||
let (w, h) = (y.width as usize, y.height as usize);
|
||||
if solid.len() != w * h {
|
||||
return None;
|
||||
}
|
||||
let mut regions = vec![];
|
||||
let mut stack = vec![];
|
||||
for start in 0..solid.len() {
|
||||
if !solid.get(start).copied().unwrap_or(false) {
|
||||
continue;
|
||||
}
|
||||
// The patch's pixels are cleared as they are taken, so each is taken once.
|
||||
if let Some(s) = solid.get_mut(start) {
|
||||
*s = false;
|
||||
}
|
||||
stack.push(start);
|
||||
let (mut i0, mut j0, mut i1, mut j1) = (start % w, start / w, start % w, start / w);
|
||||
while let Some(k) = stack.pop() {
|
||||
let (i, j) = (k % w, k / w);
|
||||
(i0, j0, i1, j1) = (i0.min(i), j0.min(j), i1.max(i), j1.max(j));
|
||||
for n in j.saturating_sub(1)..=(j + 1).min(h - 1) {
|
||||
for m in i.saturating_sub(1)..=(i + 1).min(w - 1) {
|
||||
if let Some(s) = solid.get_mut(n * w + m).filter(|s| **s) {
|
||||
*s = false;
|
||||
stack.push(n * w + m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let s = self.scale;
|
||||
let at = |i: usize, j: usize| (rb.x0 + i as f64 / s, rb.y0 + j as f64 / s);
|
||||
let ((x0, y0), (x1, y1)) = (at(i0, j0), at(i1 + 1, j1 + 1));
|
||||
regions.push(Rect::new(x0, y0, x1, y1));
|
||||
}
|
||||
Some((diff, regions))
|
||||
}
|
||||
}
|
||||
|
||||
/// How `a` on `ra` and `b` on `rb` (rectangles of the same size) draw differently ([`compared`]),
|
||||
@@ -233,8 +285,14 @@ pub(super) fn difference(a: &Document, ra: Rect, b: &Document, rb: Rect) -> Diff
|
||||
/// at all ([`differ`]): the page of an RGB document is written in the colours' CMYK equivalents,
|
||||
/// and what the comparison looks for is art that is missing or out of place.
|
||||
pub(super) fn compared(x: &vectorcraft_render::Rendered, y: &vectorcraft_render::Rendered, areas: &[Rect], skip: &[Rect]) -> Difference {
|
||||
compared_with_solid(x, y, areas, skip).0
|
||||
}
|
||||
|
||||
/// [`compared`], and which of the pixels (row by row) are counted in its `solid` share: none when
|
||||
/// the drawings can't be compared.
|
||||
fn compared_with_solid(x: &vectorcraft_render::Rendered, y: &vectorcraft_render::Rendered, areas: &[Rect], skip: &[Rect]) -> (Difference, Vec<bool>) {
|
||||
if x.width == 0 || x.height == 0 || x.width != y.width || x.height != y.height {
|
||||
return Difference { any: 1.0, art: 1.0, solid: 1.0 };
|
||||
return (Difference { any: 1.0, art: 1.0, solid: 1.0 }, vec![]);
|
||||
}
|
||||
let (w, h) = (x.width as usize, x.height as usize);
|
||||
// Pixels in `skip` count as alike.
|
||||
@@ -245,12 +303,22 @@ pub(super) fn compared(x: &vectorcraft_render::Rendered, y: &vectorcraft_render:
|
||||
let typed = in_areas(areas, w, h);
|
||||
let art = differs.iter().zip(&typed).filter(|(d, t)| **d && !**t).count();
|
||||
// A pixel on the region's edge has neighbours outside it, which don't count as differing.
|
||||
let solid = (1..h.saturating_sub(1))
|
||||
.flat_map(|j| (1..w.saturating_sub(1)).map(move |i| (i, j)))
|
||||
.filter(|&(i, j)| !typed.get(j * w + i).copied().unwrap_or(true) && (j - 1..=j + 1).all(|n| (i - 1..=i + 1).all(|m| at(m, n))))
|
||||
.count();
|
||||
let mut solid = vec![false; w * h];
|
||||
for (i, j) in (1..h.saturating_sub(1)).flat_map(|j| (1..w.saturating_sub(1)).map(move |i| (i, j))) {
|
||||
if !typed.get(j * w + i).copied().unwrap_or(true)
|
||||
&& (j - 1..=j + 1).all(|n| (i - 1..=i + 1).all(|m| at(m, n)))
|
||||
&& let Some(s) = solid.get_mut(j * w + i)
|
||||
{
|
||||
*s = true;
|
||||
}
|
||||
}
|
||||
let area = (w as f64 * h as f64).max(1.0);
|
||||
Difference { any: differs.iter().filter(|d| **d).count() as f64 / area, art: art as f64 / area, solid: solid as f64 / area }
|
||||
let shares = Difference {
|
||||
any: differs.iter().filter(|d| **d).count() as f64 / area,
|
||||
art: art as f64 / area,
|
||||
solid: solid.iter().filter(|s| **s).count() as f64 / area,
|
||||
};
|
||||
(shares, solid)
|
||||
}
|
||||
|
||||
/// Where the type that shows in `nodes` is, in pixels of `region` drawn at `scale` (with a margin
|
||||
|
||||
@@ -18,6 +18,10 @@ pub(super) const PAGE_TYPE_UNCHECKED: &str =
|
||||
|
||||
pub(super) const LAYERS_UNREAD: &str = "the file's layers weren't read";
|
||||
|
||||
/// Objects of the layers replaced with the page's art where they drew differently from it,
|
||||
/// followed by the layers they are on.
|
||||
pub(super) const DREW_DIFFERENTLY: &str = "objects that drew differently from the file's own page came in as its art";
|
||||
|
||||
/// Does this import note say that the file has something the document doesn't (hidden text, art or
|
||||
/// layers that could not be read, or the page's type that no text object takes or may not take)?
|
||||
/// Writing the document over the file would lose it for good.
|
||||
@@ -27,7 +31,9 @@ pub fn is_loss(note: &str) -> bool {
|
||||
|| note.ends_with("they came in as the page's art")
|
||||
|| note == PAGE_TYPE_LEFT_OUT
|
||||
|| note == PAGE_TYPE_UNCHECKED
|
||||
|| [ART_LEFT_OUT, LAYERS_UNREAD, crate::import::graphics::TOO_MUCH, crate::import::graphics::FAR_AWAY].iter().any(|n| note.starts_with(n))
|
||||
|| [ART_LEFT_OUT, LAYERS_UNREAD, DREW_DIFFERENTLY, crate::import::graphics::TOO_MUCH, crate::import::graphics::FAR_AWAY]
|
||||
.iter()
|
||||
.any(|n| note.starts_with(n))
|
||||
}
|
||||
|
||||
/// Most text objects in the layers, and most pieces of type in one place on the page, that are matched
|
||||
|
||||
@@ -824,6 +824,215 @@ fn top_level_placed_file_without_page_art_falls_back() {
|
||||
assert!(notes.iter().any(|n| n.contains("page art wasn't found")), "{notes:?}");
|
||||
}
|
||||
|
||||
const BLACK: &str = "0 0 0 1";
|
||||
const YELLOW: &str = "0 0 1 0";
|
||||
const PAGE_CYAN: [f32; 4] = [1.0, 0.0, 0.0, 0.0];
|
||||
const PAGE_YELLOW: [f32; 4] = [0.0, 0.0, 1.0, 0.0];
|
||||
|
||||
/// A square of 16 points at (`x`, 10) in the format's operators, painted in CMYK `cmyk`.
|
||||
fn square_16(x: u32, cmyk: &str) -> String {
|
||||
format!("{cmyk} k\n{x} 10 m\n{} 10 L\n{} 26 L\n{x} 26 L\nf", x + 16, x + 16)
|
||||
}
|
||||
|
||||
/// A 100 × 100 page with a square of 16 points at each (`x`, `y`, CMYK) of `squares`: at `y` 74
|
||||
/// where [`square_16`] puts the layers' squares.
|
||||
fn page_16(squares: &[(f64, f64, [f32; 4])]) -> Document {
|
||||
let mut d = Document::new(100.0, 100.0);
|
||||
let layer = d.layers[0].id;
|
||||
for (i, (x, y, [c, m, ye, k])) in squares.iter().enumerate() {
|
||||
let path = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(*x, *y, x + 16.0, y + 16.0));
|
||||
let paint = vectorcraft_color::Paint::solid(vectorcraft_color::Color::cmyk(*c, *m, *ye, *k));
|
||||
let item = vectorcraft_doc::AppearanceItem::Fill(vectorcraft_doc::FillLayer::new(paint));
|
||||
let n = Node::path(d.alloc_id(), path, vectorcraft_doc::Appearance { items: vec![item], ..Default::default() });
|
||||
d.insert(Some(layer), i, n).unwrap();
|
||||
}
|
||||
d
|
||||
}
|
||||
|
||||
/// Three squares on two layers, the middle one black in the layers and cyan on the page.
|
||||
fn middle_differs() -> (String, Document) {
|
||||
let front = format!("{}\n{}", square_16(40, BLACK), square_16(76, YELLOW));
|
||||
let art = native(&(layer("Back", true, &square_16(4, YELLOW)) + &layer("Front", true, &front)));
|
||||
(art, page_16(&[(4.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_CYAN), (76.0, 74.0, PAGE_YELLOW)]))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_object_that_draws_differently_comes_in_as_the_page_art() {
|
||||
let (art, page) = middle_differs();
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Back", "Front"]);
|
||||
assert_eq!(kinds(&doc.layers[0]), ["path"]);
|
||||
assert_eq!(kinds(&doc.layers[1]), ["group[1]", "path"]);
|
||||
replacement_is_cyan(&doc, 1, 0);
|
||||
let note = "objects that drew differently from the file's own page came in as its art: Front (1)";
|
||||
assert!(notes.iter().any(|n| n == note && crate::is_loss(n)), "{notes:?}");
|
||||
assert!(!notes.iter().any(|n| n.contains("weren't read")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_differing_objects_on_different_layers_are_both_replaced() {
|
||||
let back = format!("{}\n{}", square_16(4, BLACK), square_16(28, YELLOW));
|
||||
let front = format!("{}\n{}", square_16(52, YELLOW), square_16(76, BLACK));
|
||||
let art = native(&(layer("Back", true, &back) + &layer("Front", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_CYAN), (28.0, 74.0, PAGE_YELLOW), (52.0, 74.0, PAGE_YELLOW), (76.0, 74.0, PAGE_CYAN)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Back", "Front"]);
|
||||
assert_eq!(kinds(&doc.layers[0]), ["group[1]", "path"]);
|
||||
assert_eq!(kinds(&doc.layers[1]), ["path", "group[1]"]);
|
||||
replacement_is_cyan(&doc, 0, 0);
|
||||
replacement_is_cyan(&doc, 1, 1);
|
||||
let note = "objects that drew differently from the file's own page came in as its art: Back (1), Front (1)";
|
||||
assert!(notes.iter().any(|n| n == note), "{notes:?}");
|
||||
}
|
||||
|
||||
/// The text wrap options of a 2-point offset, for the object before them.
|
||||
const WRAP_OPTIONS: &str = "%_/ArtDictionary :\n%_0 /Bool (AI11TextWrapInvert) ,\n%_2 /Real (AI11TextWrapOffset) ,\n%_;\n%_\n";
|
||||
|
||||
#[test]
|
||||
fn a_differing_wrap_object_keeps_its_wrap_options() {
|
||||
let front = format!("{}\n{WRAP_OPTIONS}{}", square_16(40, BLACK), square_16(76, YELLOW));
|
||||
let art = native(&(layer("Back", true, &square_16(4, YELLOW)) + &layer("Front", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_CYAN), (76.0, 74.0, PAGE_YELLOW)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(read, "{notes:?}");
|
||||
assert_eq!(kinds(&doc.layers[1]), ["group[1]", "path"]);
|
||||
replacement_is_cyan(&doc, 1, 0);
|
||||
let group = &doc.layers[1].children().unwrap()[0];
|
||||
assert_eq!(group.extras().wrap, Some(vectorcraft_doc::TextWrap { offset: 2.0, invert: false }));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_differing_group_holding_a_wrap_object_still_falls_back() {
|
||||
let group = os1_group(&format!("{}\n{WRAP_OPTIONS}", square_16(40, BLACK)));
|
||||
let front = format!("{group}\n{}", square_16(76, YELLOW));
|
||||
let art = native(&(layer("Back", true, &square_16(4, YELLOW)) + &layer("Front", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_CYAN), (76.0, 74.0, PAGE_YELLOW)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(!notes.iter().any(|n| n.starts_with("objects that drew")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_page_that_differs_everywhere_still_comes_in_as_its_pdf_part() {
|
||||
let front = format!("{}\n{}", square_16(40, BLACK), square_16(76, BLACK));
|
||||
let art = native(&(layer("Back", true, &square_16(4, BLACK)) + &layer("Front", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_CYAN), (40.0, 74.0, PAGE_CYAN), (76.0, 74.0, PAGE_CYAN)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(notes.iter().any(|n| n.contains("weren't read") && n.contains("differs from the page's")), "{notes:?}");
|
||||
assert!(!notes.iter().any(|n| n.starts_with("objects that drew")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_page_object_outside_every_layer_object_still_falls_back() {
|
||||
let front = format!("{}\n{}", square_16(40, YELLOW), square_16(76, YELLOW));
|
||||
let art = native(&(layer("Back", true, &square_16(4, YELLOW)) + &layer("Front", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_YELLOW), (76.0, 74.0, PAGE_YELLOW), (40.0, 20.0, PAGE_CYAN)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(notes.iter().any(|n| n.contains("differs from the page's")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_differing_object_with_live_art_inside_falls_back() {
|
||||
// The group reaches over the square between its own two.
|
||||
let group = os1_group(&format!("{}\n{}", square_16(4, BLACK), square_16(40, YELLOW)));
|
||||
let front = format!("{group}\n{}\n{}", square_16(22, YELLOW), square_16(76, YELLOW));
|
||||
let art = native(&layer("Front", true, &front));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_CYAN), (22.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_YELLOW), (76.0, 74.0, PAGE_YELLOW)]);
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(notes.iter().any(|n| n.contains("differs from the page's")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trusted_editing_data_keeps_a_differing_object() {
|
||||
let (art, page) = middle_differs();
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, true, 1.0);
|
||||
assert!(read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Back", "Front"]);
|
||||
assert_eq!(kinds(&doc.layers[1]), ["path", "path"]);
|
||||
assert!(notes.iter().any(|n| n.ends_with("they come from its editing data all the same")), "{notes:?}");
|
||||
assert!(!notes.iter().any(|n| n.starts_with("objects that drew")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_differing_object_on_an_unnamed_layer_is_counted_under_its_panel_name() {
|
||||
let front = format!("{}\n{}", square_16(40, BLACK), square_16(76, YELLOW));
|
||||
let art = native(&(layer("Back", true, &square_16(4, YELLOW)) + &layer("", true, &front)));
|
||||
let page = page_16(&[(4.0, 74.0, PAGE_YELLOW), (40.0, 74.0, PAGE_CYAN), (76.0, 74.0, PAGE_YELLOW)]);
|
||||
let (_, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(read, "{notes:?}");
|
||||
let note = "objects that drew differently from the file's own page came in as its art: <Layer> (1)";
|
||||
assert!(notes.iter().any(|n| n == note), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn at_most_64_differing_objects_come_in_as_the_page_art() {
|
||||
// 130 squares of 4 points, the first `black` of them black in the layers and cyan on the page.
|
||||
let grid = |black: usize| {
|
||||
let at = |i: usize| (4 + 7 * (i % 13) as u32, 4 + 7 * (i / 13) as u32);
|
||||
let art: String = (0..130)
|
||||
.map(|i| {
|
||||
let (x, y) = at(i);
|
||||
if i < black { format!("{BLACK} k\n{}", square_path(x, y)) } else { square(x, y) }
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
let mut page = Document::new(100.0, 100.0);
|
||||
let layer_id = page.layers[0].id;
|
||||
for i in 0..130 {
|
||||
let (x, y) = at(i);
|
||||
let (x, y) = (f64::from(x), 96.0 - f64::from(y));
|
||||
let path = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(x, y, x + 4.0, y + 4.0));
|
||||
let color =
|
||||
if i < black { vectorcraft_color::Color::cmyk(1.0, 0.0, 0.0, 0.0) } else { vectorcraft_color::Color::cmyk(0.0, 0.0, 1.0, 0.0) };
|
||||
let item = vectorcraft_doc::AppearanceItem::Fill(vectorcraft_doc::FillLayer::new(vectorcraft_color::Paint::solid(color)));
|
||||
let n = Node::path(page.alloc_id(), path, vectorcraft_doc::Appearance { items: vec![item], ..Default::default() });
|
||||
page.insert(Some(layer_id), i, n).unwrap();
|
||||
}
|
||||
crate::layered_ai(&private(&native(&layer("Grid", true, &art))), page, vec![], false, false, 1.0)
|
||||
};
|
||||
let (_, notes, read) = grid(64);
|
||||
assert!(read, "{notes:?}");
|
||||
let note = "objects that drew differently from the file's own page came in as its art: Grid (64)";
|
||||
assert!(notes.iter().any(|n| n == note), "{notes:?}");
|
||||
let (doc, notes, read) = grid(65);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(!notes.iter().any(|n| n.starts_with("objects that drew")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_differing_object_under_the_type_of_a_text_object_that_stays_falls_back() {
|
||||
// A black rectangle under area type whose frame reaches past it; the page draws the rectangle
|
||||
// cyan, with the type on it.
|
||||
let big = format!("{BLACK} k\n10 2 m\n60 2 L\n60 40 L\n10 40 L\nf");
|
||||
let doc = document_of(&[area_frame(34.0, 26.0, 95.0, 38.0)], &[story("Hi\\r", &[0], "/0 0", "/1 8.0")]);
|
||||
let art = native_with_text(&(layer("Art", true, &format!("{big}\n{}", frame_object(0, 0))) + &layer("Top", true, &square_16(76, YELLOW))), &doc);
|
||||
let mut page = page_16(&[(76.0, 74.0, PAGE_YELLOW)]);
|
||||
let layer_id = page.layers[0].id;
|
||||
let path = vectorcraft_geom::shapes::rectangle(vectorcraft_geom::Rect::new(10.0, 60.0, 60.0, 98.0));
|
||||
let paint = vectorcraft_color::Paint::solid(vectorcraft_color::Color::cmyk(1.0, 0.0, 0.0, 0.0));
|
||||
let item = vectorcraft_doc::AppearanceItem::Fill(vectorcraft_doc::FillLayer::new(paint));
|
||||
let square = Node::path(page.alloc_id(), path, vectorcraft_doc::Appearance { items: vec![item], ..Default::default() });
|
||||
page.insert(Some(layer_id), 0, square).unwrap();
|
||||
let style = vectorcraft_doc::CharStyle { size: 8.0, ..Default::default() };
|
||||
let text = vectorcraft_doc::TextObject::point(vectorcraft_geom::Point::new(34.0, 69.0), "Hi", style);
|
||||
let text = Node::new(page.alloc_id(), NodeKind::Text(Box::new(text)));
|
||||
page.insert(Some(layer_id), 1, text).unwrap();
|
||||
let (doc, notes, read) = crate::layered_ai(&private(&art), page, vec![], false, false, 1.0);
|
||||
assert!(!read, "{notes:?}");
|
||||
assert_eq!(names(&doc), ["Layer 1"]);
|
||||
assert!(!notes.iter().any(|n| n.starts_with("objects that drew")), "{notes:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unread_object_on_second_artboard_takes_second_page_art() {
|
||||
let boards = "%_/Document :\n%_/Dictionary :\n%_/Array :\n\
|
||||
|
||||
@@ -6026,6 +6026,7 @@ breakText Glyphs Глифи
|
||||
Gap 5 Разстояние 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: те са внесени като изображението от страницата
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} обектите, изобразени различно от собствената страница на файла, са внесени като изображението от страницата: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Трябва да изберете страничен шаблон.
|
||||
@msg invalid pattern tile choice невалиден избор на плочка за шаблон
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6060,6 +6060,7 @@ shape Open Otvoren
|
||||
@msg the paths have nothing to merge there putanje tu nemaju šta spojiti
|
||||
@msg the path is locked or hidden putanja je zaključana ili skrivena
|
||||
@msg {_1}: they came in as the page's art {_1}: uvezeni su kao ilustracija stranice
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objekti koji su se iscrtali drugačije od vlastite stranice datoteke uvezeni su kao ilustracija stranice: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Morate odabrati bočni uzorak.
|
||||
@msg invalid pattern tile choice nevažeći izbor pločice uzorka
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6158,6 +6158,7 @@ breakText Glyphs Glifs
|
||||
Gap 5 Separació 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: han entrat com a art de la pàgina
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} els objectes que es dibuixaven diferent de la pàgina pròpia del fitxer han entrat com a art de la pàgina: {_1}
|
||||
|
||||
@msg the JPEG 2000 file can't be read no es pot llegir el fitxer JPEG 2000
|
||||
@msg the JPEG 2000 file has an unsupported color space or JPX content el fitxer JPEG 2000 té un espai de color o contingut JPX no compatible
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6174,6 +6174,7 @@ breakText Glyphs Glyfy
|
||||
Gap 5 Mezera 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: byly načteny jako kresba ze stránky
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objekty, které se vykreslily jinak než vlastní stránka souboru, byly načteny jako kresba ze stránky: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Musíte zvolit boční vzorek.
|
||||
@msg invalid pattern tile choice neplatná volba dlaždice vzorku
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6042,6 +6042,7 @@ breakText Glyphs Glyffer
|
||||
Gap 5 Mellemrum 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: de blev importeret som sidens grafik
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objekter, der blev tegnet anderledes end filens egen side, blev importeret som sidens grafik: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Du skal vælge et sidemønster.
|
||||
@msg invalid pattern tile choice ugyldigt valg af mønsterflise
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5760,6 +5760,7 @@ breakText Glyphs Glyphen
|
||||
Gap 5 Lücke 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: sie wurden als Seitengrafik importiert
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} Objekte, die anders als die eigene Seite der Datei gezeichnet wurden, wurden als Seitengrafik importiert: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Sie müssen ein Seitenmuster auswählen.
|
||||
@msg invalid pattern tile choice ungültige Auswahl einer Musterkachel
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6126,6 +6126,7 @@ breakText Glyphs Γλύφοι
|
||||
Gap 5 Διάκενο 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: εισήχθησαν ως γραφικά της σελίδας
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} τα αντικείμενα που σχεδιάστηκαν διαφορετικά από την ίδια τη σελίδα του αρχείου εισήχθησαν ως γραφικά της σελίδας: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Πρέπει να επιλέξετε πλευρικό μοτίβο.
|
||||
@msg invalid pattern tile choice μη έγκυρη επιλογή πλακιδίου μοτίβου
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 163 and column 5.
|
@@ -6116,6 +6116,7 @@ breakText Glyphs Glifos
|
||||
Gap 5 Hueco 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: se importaron como el arte de la página
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} los objetos que se dibujaban de forma distinta a la página del propio archivo se importaron como el arte de la página: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Debe elegir un motivo lateral.
|
||||
@msg invalid pattern tile choice selección de mosaico de motivo no válida
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6204,6 +6204,7 @@ breakText Glyphs Merkit
|
||||
Gap 5 Väli 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: ne tuotiin sivun piirroksena
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objektit, jotka piirtyivät eri tavoin kuin tiedoston oma sivu, tuotiin sivun piirroksena: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Valitse sivukuvio.
|
||||
@msg invalid pattern tile choice virheellinen kuviolaatan valinta
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6169,6 +6169,7 @@ breakText Glyphs Glyphes
|
||||
Gap 5 Écart 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1} : ils ont été importés comme le dessin de la page
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} les objets dessinés différemment de la page du fichier lui-même ont été importés comme le dessin de la page : {_1}
|
||||
|
||||
@msg You must choose a side pattern. Vous devez choisir un motif latéral.
|
||||
@msg invalid pattern tile choice choix de tuile de motif non valide
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6158,6 +6158,7 @@ breakText Glyphs Glifák
|
||||
Gap 5 Hézag 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: az oldal rajzaként lettek importálva
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} a fájl saját oldalától eltérően rajzolt objektumok az oldal rajzaként lettek importálva: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Válasszon oldalsó mintát.
|
||||
@msg invalid pattern tile choice érvénytelen mintacsempe-választás
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6042,6 +6042,7 @@ breakText Glyphs Glif
|
||||
Gap 5 Jarak 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: diimpor sebagai gambar halaman
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objek yang tergambar berbeda dari halaman file itu sendiri diimpor sebagai gambar halaman: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Anda harus memilih pola sisi.
|
||||
@msg invalid pattern tile choice pilihan ubin pola tidak valid
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2214 and column 5.
|
@@ -5794,6 +5794,7 @@ breakText Glyphs Glifi
|
||||
Gap 5 Spazio 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: sono stati importati come elementi grafici della pagina
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} gli oggetti disegnati in modo diverso dalla pagina del file stesso sono stati importati come elementi grafici della pagina: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Devi scegliere un motivo laterale.
|
||||
@msg invalid pattern tile choice scelta della tessera motivo non valida
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5859,6 +5859,7 @@ breakText Glyphs 字形
|
||||
Gap 5 間隔 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: ページのアートとして読み込みました
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} ファイル自身のページと描画が異なるオブジェクトをページのアートとして読み込みました: {_1}
|
||||
|
||||
@msg You must choose a side pattern. 側面パターンを選択してください。
|
||||
@msg invalid pattern tile choice パターンタイルの選択が無効です
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6162,6 +6162,7 @@ breakText Glyphs გლიფები
|
||||
Gap 5 შუალედი 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: იმპორტირებულია გვერდის გამოსახულების სახით
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} ობიექტები, რომლებიც ფაილის საკუთარი გვერდისგან განსხვავებულად იხატებოდა, იმპორტირებულია გვერდის გამოსახულების სახით: {_1}
|
||||
|
||||
@msg You must choose a side pattern. აირჩიეთ გვერდითი ნიმუში.
|
||||
@msg invalid pattern tile choice ნიმუშის ფილა არასწორად არის არჩეული
|
||||
|
||||
|
Can't render this file because it is too large.
|
@@ -5755,6 +5755,7 @@ breakText Glyphs 글리프
|
||||
Gap 5 간격 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: 페이지의 아트로 가져왔습니다
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} 파일 자체 페이지와 다르게 그려진 오브젝트를 페이지의 아트로 가져왔습니다: {_1}
|
||||
|
||||
@msg You must choose a side pattern. 측면 패턴을 선택해야 합니다.
|
||||
@msg invalid pattern tile choice 잘못된 패턴 타일 선택
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6086,6 +6086,7 @@ breakText Glyphs Glyphs
|
||||
Gap 5 Tussenruimte 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: ze zijn geïmporteerd als pagina-afbeelding
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objecten die anders werden getekend dan de eigen pagina van het bestand, zijn geïmporteerd als pagina-afbeelding: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Kies een zijpatroon.
|
||||
@msg invalid pattern tile choice ongeldige keuze voor patroontegel
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6029,6 +6029,7 @@ breakText Glyphs Glify
|
||||
Gap 5 Odstęp 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: zaimportowano je jako grafikę strony
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} obiekty rysowane inaczej niż własna strona pliku zaimportowano jako grafikę strony: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Musisz wybrać wzór boczny.
|
||||
@msg invalid pattern tile choice nieprawidłowy wybór kafelka wzoru
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6092,6 +6092,7 @@ breakText Glyphs Glifos
|
||||
Gap 5 Lacuna 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: foram importados como arte da página
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} os objetos desenhados de forma diferente da própria página do arquivo foram importados como arte da página: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Você deve escolher um padrão lateral.
|
||||
@msg invalid pattern tile choice escolha inválida de ladrilho de padrão
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -6151,6 +6151,7 @@ breakText Glyphs Глифы
|
||||
Gap 5 Зазор 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: импортированы как графика страницы
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} объекты, которые отрисовывались иначе, чем собственная страница файла, импортированы как графика страницы: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Выберите боковой узор.
|
||||
@msg invalid pattern tile choice недопустимый выбор плитки узора
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -6226,6 +6226,7 @@ breakText Glyphs Glyfer
|
||||
Gap 5 Mellanrum 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: de importerades som sidans bild
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} objekt som ritades annorlunda än filens egen sida importerades som sidans bild: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Du måste välja ett sidomönster.
|
||||
@msg invalid pattern tile choice ogiltigt val av mönsterplatta
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -6131,6 +6131,7 @@ breakText Glyphs Glifler
|
||||
Gap 5 Boşluk 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: sayfanın çizimi olarak içe aktarıldılar
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} dosyanın kendi sayfasından farklı çizilen nesneler sayfanın çizimi olarak içe aktarıldı: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Bir yan desen seçmelisiniz.
|
||||
@msg invalid pattern tile choice geçersiz desen döşemesi seçimi
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 5 and column 25.
|
@@ -5774,6 +5774,7 @@ breakText Glyphs Гліфи
|
||||
Gap 5 Проміжок 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: їх імпортовано як графіку сторінки
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} об’єкти, що малювалися інакше, ніж власна сторінка файлу, імпортовано як графіку сторінки: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Виберіть бічний візерунок.
|
||||
@msg invalid pattern tile choice недійсний вибір плитки візерунка
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 177 and column 5.
|
@@ -5775,6 +5775,7 @@ breakText Glyphs Glyph
|
||||
Gap 5 Khoảng hở 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}: đã được nhập dưới dạng hình vẽ của trang
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} các đối tượng được vẽ khác với trang riêng của tệp đã được nhập dưới dạng hình vẽ của trang: {_1}
|
||||
|
||||
@msg You must choose a side pattern. Bạn phải chọn mẫu bên.
|
||||
@msg invalid pattern tile choice lựa chọn ô mẫu không hợp lệ
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 199 and column 5.
|
@@ -6015,6 +6015,7 @@ breakText Glyphs 字形
|
||||
Gap 5 间隔 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}:已作为页面图稿导入
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} 与文件自身页面绘制不同的对象已作为页面图稿导入:{_1}
|
||||
|
||||
@msg You must choose a side pattern. 必须选择侧边图案。
|
||||
@msg invalid pattern tile choice 图案拼块选项无效
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -5812,6 +5812,7 @@ breakText Glyphs 字符
|
||||
Gap 5 間隙 5
|
||||
|
||||
@msg {_1}: they came in as the page's art {_1}:已作為頁面圖稿匯入
|
||||
@msg objects that drew differently from the file's own page came in as its art: {_1} 與檔案本身頁面繪製不同的物件已作為頁面圖稿匯入:{_1}
|
||||
|
||||
@msg You must choose a side pattern. 必須選擇側邊圖樣。
|
||||
@msg invalid pattern tile choice 圖樣拼塊選項無效
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user