diff --git a/crates/engine/src/cmd/fileio/eps.rs b/crates/engine/src/cmd/fileio/eps.rs index 2c8ae4a84..d2424bf0a 100644 --- a/crates/engine/src/cmd/fileio/eps.rs +++ b/crates/engine/src/cmd/fileio/eps.rs @@ -12,7 +12,7 @@ use vectorcraft_doc::Document; use vectorcraft_eps::{EpsOptions, Level, Overprint, Preview, Raster}; use vectorcraft_geom::{Point, Rect}; -use super::super::flatten::{FlattenOptions, expand_transparent_tile_images, flatten_document}; +use super::super::flatten::{FlattenOptions, expand_transparent_tile_images, flatten_expanded}; use super::super::{CommandSpec, EngineError, Result, Session, bad, bool_or, cmd, has_doc, str_param}; use super::{ArtboardPick, Encoded, FormatOption}; @@ -302,7 +302,7 @@ fn encode_document(doc: &Document, p: &Value, saving: bool) -> Result { let expanded = expand_transparent_tile_images(doc, &set.flatten)?; let source = expanded.as_ref().unwrap_or(doc); let transparent = source.layers.iter().any(|l| set.flatten.shows_transparency(source, l)); - let flat = flatten_document(source, &set.flatten)?; + let flat = flatten_expanded(source, &set.flatten)?; let drawn = flat.as_ref().unwrap_or(source); let mut enc = Encoded { warnings, joiner: Some("_"), ..Encoded::default() }; if transparent && (flat.is_some() || expanded.is_some()) { diff --git a/crates/engine/src/cmd/flatten.rs b/crates/engine/src/cmd/flatten.rs index 94e05a357..ccbe9c049 100644 --- a/crates/engine/src/cmd/flatten.rs +++ b/crates/engine/src/cmd/flatten.rs @@ -49,7 +49,7 @@ mod presets; mod report; pub(crate) use analysis::{outlined_text, see_through_images}; -pub(crate) use apply::{expand_transparent_tile_images, flatten_document}; +pub(crate) use apply::{expand_transparent_tile_images, flatten_document, flatten_expanded, has_transparent_tile_images}; pub use commands::specs; pub use options::FlattenOptions; pub use presets::{FlattenerPreset, PRESET_EXTS, PRESET_FORMAT}; diff --git a/crates/engine/src/cmd/flatten/apply.rs b/crates/engine/src/cmd/flatten/apply.rs index 0cd1ab159..d229f1bbb 100644 --- a/crates/engine/src/cmd/flatten/apply.rs +++ b/crates/engine/src/cmd/flatten/apply.rs @@ -292,16 +292,21 @@ pub(super) fn opaque(n: &mut Node) { } } +/// Whether `doc` has pattern fills whose image tiles need transparency flattening with `o` +/// ([`expand_transparent_tile_images`]). +pub(crate) fn has_transparent_tile_images(doc: &Document, o: &FlattenOptions) -> bool { + let keys = o.no_transparency.clone().unwrap_or_default(); + let colorize = |d: &Document, n: &Node| o.counts_colorize(d, n); + !doc.patterns.is_empty() && crate::cmd::expand::has_transparent_image_patterns(doc, &keys, &colorize) +} + /// Expand pattern fills whose image tiles need transparency flattening. pub(crate) fn expand_transparent_tile_images(doc: &Document, o: &FlattenOptions) -> Result> { - if doc.patterns.is_empty() { + if !has_transparent_tile_images(doc, o) { return Ok(None); } let keys = o.no_transparency.clone().unwrap_or_default(); let colorize = |d: &Document, n: &Node| o.counts_colorize(d, n); - if !crate::cmd::expand::has_transparent_image_patterns(doc, &keys, &colorize) { - return Ok(None); - } let mut d = doc.clone(); let layers: Vec = d.layers.iter().filter(|l| l.visible && !matches!(l.kind, NodeKind::Layer { template: true, .. })).map(|l| l.id).collect(); @@ -319,10 +324,15 @@ pub(crate) fn expand_transparent_tile_images(doc: &Document, o: &FlattenOptions) /// expanded when `o.no_transparency` counts them. `None` when nothing changes. pub(crate) fn flatten_document(doc: &Document, o: &FlattenOptions) -> Result> { let expanded = expand_transparent_tile_images(doc, o)?; - let doc = expanded.as_ref().unwrap_or(doc); + Ok(flatten_expanded(expanded.as_ref().unwrap_or(doc), o)?.or(expanded)) +} + +/// [`flatten_document`] for `doc` whose pattern tile images were expanded already +/// ([`expand_transparent_tile_images`]). +pub(crate) fn flatten_expanded(doc: &Document, o: &FlattenOptions) -> Result> { let outlining = o.text_to_outlines || o.strokes_to_outlines || !o.preserve_overprints; if !outlining && !doc.layers.iter().any(|l| o.shows_transparency(doc, l) || o.draws_see_through_image(l)) { - return Ok(expanded); + return Ok(None); } let mut roots = vec![]; for l in doc.layers.iter().filter(|l| l.visible && !matches!(l.kind, NodeKind::Layer { template: true, .. })) { @@ -331,7 +341,7 @@ pub(crate) fn flatten_document(doc: &Document, o: &FlattenOptions) -> Result Result { let flat = super::rasterfx::flatten_raster_effects(doc); let doc = flat.as_ref().unwrap_or(doc); let bitmap = set.advanced.print_as_bitmap && set.output.mode == vectorcraft_pdf::OutputMode::Composite; - let expanded = if postscript && !bitmap { - let options = super::printadvanced::flattener(PRINT, &set, &s.prefs.flattener_presets)?; - options.as_ref().map(|o| expand_transparent_tile_images(doc, o)).transpose()?.flatten() - } else { - None - }; - let doc = expanded.as_ref().unwrap_or(doc); + // The flattener expands pattern fills whose tile images need flattening, in the layers that print. + let tile_images = postscript + && !bitmap + && super::printadvanced::flattener(PRINT, &set, &s.prefs.flattener_presets)? + .is_some_and(|o| has_transparent_tile_images(&vectorcraft_pdf::printed_document(doc, &set), &o)); // Print as Bitmap and the flattener preset (Advanced) render and flatten it first. let prepared = super::printadvanced::prepare(PRINT, doc, &set, &s.prefs.flattener_presets, s.prefs.black_output == "rich")?; let path = str_param(p, "path"); @@ -210,7 +208,7 @@ fn print(s: &mut Session, p: &Value) -> Result { None => Level::default(), }; let mut warnings = vec![]; - if !bitmap && doc.layers.iter().any(|l| l.shows_transparency()) { + if !bitmap && (tile_images || doc.layers.iter().any(|l| l.shows_transparency())) { warnings.push("transparency is flattened into opaque art and images (PostScript has none): see flattenerPreset".to_string()); } if !set.color.profile.trim().is_empty() && set.output.mode == vectorcraft_pdf::OutputMode::Composite { diff --git a/crates/engine/src/tests/legacy_ai.rs b/crates/engine/src/tests/legacy_ai.rs index f038ab251..1b0f7ad3f 100644 --- a/crates/engine/src/tests/legacy_ai.rs +++ b/crates/engine/src/tests/legacy_ai.rs @@ -820,6 +820,15 @@ fn flattening_that_keeps_colorize_leaves_colorized_pattern_tile_images_alone() { assert!(crate::cmd::flatten::flatten_document(d, &o).unwrap().is_none(), "the pattern isn't expanded for a colorize paint alone"); } +/// EPS export expands pattern tile images once: flattening the expanded +/// document leaves the patterns the expansion kept, past its object limit, as they are. +#[test] +fn flattening_an_expanded_document_does_not_expand_patterns_again() { + let s = colorized_pattern_over_blue(); + let d = &s.doc().unwrap().doc; + assert!(crate::cmd::flatten::flatten_expanded(d, &crate::cmd::FlattenOptions::default()).unwrap().is_none()); +} + #[test] fn eps_flattens_colorized_image_in_a_see_through_pattern_tile_group_and_warns() { let mut s = colorized_pattern_over_blue();