mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
.ai import: objects that draw differently from the page come in as its art, and the layers stay
When the layers fail the comparison with the file's own page because some objects draw differently (an object in another color, or one that only one side draws), the import finds each top-level object whose bounds hold a patch of such pixels (the smallest one that holds it) and replaces it with the page's art in its bounds, in the same way as an unread object. It then compares the layers with the page again. When that comparison passes, the layers stay, and a warning names the layer or sublayer of each replaced object (`<Layer>` for one without a name) and how many were replaced on it. Like the warning for unread objects, this warning counts as a loss, so an export or Save As to the file's own path needs `acknowledgeLoss: true`. A replaced wrap object keeps its wrap options, and area type is wrapped again around the page's art. Placed files at their other placement are tried too when the first placement can't be kept. The file still comes in as its PDF part, with the same reason, when only edges or type differ, when a difference lies outside every top-level object, when more than half of the top-level objects (or more than 64) hold one, when such an object is or holds type or holds a wrap object inside a group, when the page's type of a text object that stays would be copied into a replacement, when the page's art can't replace an object for the reasons it can't replace an unread one, when matching the differences to the objects would take too much work, or when the second comparison still fails. Trusting the editing data keeps the objects as read.
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