diff --git a/crates/eps/src/ai_editing.rs b/crates/eps/src/ai_editing.rs index 7ca953703..bdcefb56b 100644 --- a/crates/eps/src/ai_editing.rs +++ b/crates/eps/src/ai_editing.rs @@ -366,7 +366,9 @@ fn artboards(s: &mut String, doc: &Document, to_art: Affine, origin: (f64, f64)) s.push_str("/RealPoint\n%_ (RulerOrigin) ,\n%_"); let par = ab.video_ruler_pixel_aspect_ratio; ps::push_num(s, if par.is_finite() && par > 0.0 { par } else { 1.0 }); - let _ = writeln!(s, " /Real (PAR) ,\n%_0 /Int (DisplayMark) ,\n%_{} /Bool (IsArtboardSelected) ,", u8::from(i == 0)); + // Display marks: 1 the center mark, 2 cross hairs, 4 video safe areas. + let mark = u8::from(ab.show_center_mark) | u8::from(ab.show_cross_hairs) << 1 | u8::from(ab.show_video_safe_areas) << 2; + let _ = writeln!(s, " /Real (PAR) ,\n%_{mark} /Int (DisplayMark) ,\n%_{} /Bool (IsArtboardSelected) ,", u8::from(i == 0)); let _ = writeln!(s, "%_{} /UnicodeString (Name) ,\n%_; ,", ps::bytes(ab.name.as_bytes())); } s.push_str("%_; (ArtboardArray) ,\n"); diff --git a/crates/eps/src/ai_editing/tests.rs b/crates/eps/src/ai_editing/tests.rs index cb96fa9ff..5a355fe3d 100644 --- a/crates/eps/src/ai_editing/tests.rs +++ b/crates/eps/src/ai_editing/tests.rs @@ -949,6 +949,42 @@ fn a_group_fill_in_a_pattern_tile_is_written() { assert!(all.iter().any(|p| p.appearance.items.iter().any(|i| paint(i.paint()) == red)), "the tiles keep their group's fill"); } +/// A pattern fill that would place more tiles than a fill places is written as the shape filled +/// gray, as a pattern that isn't found is, instead of as nothing. +#[test] +fn a_pattern_fill_of_too_many_tiles_is_written_gray() { + let mut doc = Document::new(1200.0, 1200.0); + let layer = doc.layers[0].id; + let dot = Node::path(doc.alloc_id(), shapes::rectangle(Rect::new(0.0, 0.0, 0.5, 0.5)), Appearance::basic(solid(Color::BLACK), Paint::None, 0.0)); + let tiny = vectorcraft_doc::PatternDef { tile: Rect::new(0.0, 0.0, 1.0, 1.0), ..vectorcraft_doc::PatternDef::new("Tiny", vec![Arc::new(dot)]) }; + doc.patterns.push(tiny); + let filled = Node::path( + doc.alloc_id(), + shapes::rectangle(Rect::new(100.0, 100.0, 1100.0, 1100.0)), + Appearance::basic(Paint::Pattern { pattern: "Tiny".into(), xf: Affine::IDENTITY }, Paint::None, 0.0), + ); + doc.insert(Some(layer), usize::MAX, filled).unwrap(); + let back = read_back(&doc); + let mut all = vec![]; + paths(back.layers[0].children().unwrap(), &mut all); + let gray = paint(&solid(Color::gray(0.5))); + assert!(all.iter().any(|p| p.appearance.items.iter().any(|i| paint(i.paint()) == gray)), "{}", all.len()); + let out = export_ai_editing(&doc, &opts()).unwrap(); + assert!(out.warnings.iter().any(|w| w == writer::PATTERN_TILES), "{:?}", out.warnings); +} + +/// An artboard's center mark, cross hairs and video safe areas are written as the bits of its +/// `DisplayMark` and read back. +#[test] +fn artboard_display_marks_are_written() { + let mut doc = Document::new(200.0, 100.0); + (doc.artboards[0].show_center_mark, doc.artboards[0].show_video_safe_areas) = (true, true); + assert!(program(&doc).contains("%_5 /Int (DisplayMark) ,\n")); + let back = read_back(&doc); + let ab = &back.artboards[0]; + assert!(ab.show_center_mark && !ab.show_cross_hairs && ab.show_video_safe_areas); +} + #[test] fn effects_on_a_fill_are_reported_left_out() { let mut doc = Document::new(200.0, 100.0); diff --git a/crates/eps/src/ai_editing/writer.rs b/crates/eps/src/ai_editing/writer.rs index 325203188..e0afd0366 100644 --- a/crates/eps/src/ai_editing/writer.rs +++ b/crates/eps/src/ai_editing/writer.rs @@ -27,6 +27,7 @@ const MAX_AVERAGED_PIXELS: u64 = 1 << 24; pub(super) const TYPE: &str = "type is written as its outlines in the editing data for other apps"; pub(super) const GRADIENTS: &str = "gradients are written as their average color in the editing data for other apps"; const PATTERNS: &str = "pattern fills are written as their tiles cut to the shape in the editing data for other apps"; +pub(super) const PATTERN_TILES: &str = "pattern fills with more tiles than a fill places are written filled gray in the editing data for other apps"; pub(super) const CLIPPING: &str = "clipping groups and clipping layers are written as their art cut to the clipping path in the editing data for other apps"; pub(super) const IMAGES: &str = "images are written as their outlines filled with their average color in the editing data for other apps"; @@ -457,6 +458,13 @@ impl<'a> Writer<'a> { return self.path_object(path, rule, Some(&flat), None); }; let tiles = def.instances_in(*xf, path.to_bezpath().bounding_box()); + // More tiles than a fill places come back as none: the shape is filled gray, as a + // pattern that isn't found is. + if tiles.is_empty() && !def.art.is_empty() { + self.warn(PATTERN_TILES); + let flat = FillLayer { paint: Paint::solid(Color::gray(0.5)), ..f.clone() }; + return self.path_object(path, rule, Some(&flat), None); + } let outer = self.clip.clone(); let region = self.cut(path, rule); self.clip = Some(region);