diff --git a/Cargo.lock b/Cargo.lock index 590867691..002202bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,10 +1242,10 @@ dependencies = [ [[package]] name = "craft-idml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2#1e5fdfdd2b5294d9781fa2257c3d6287af3cd2a2" +source = "git+https://github.com/storytold/craft-libs?rev=598a1e5b9#598a1e5b92e03b19115bd3012cbd4dce98267e13" dependencies = [ - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", - "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", + "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", "thiserror 2.0.21", ] @@ -1647,7 +1647,7 @@ dependencies = [ [[package]] name = "craft-xml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2#1e5fdfdd2b5294d9781fa2257c3d6287af3cd2a2" +source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" dependencies = [ "quick-xml 0.41.0", "thiserror 2.0.21", @@ -1656,7 +1656,7 @@ dependencies = [ [[package]] name = "craft-xml" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" +source = "git+https://github.com/storytold/craft-libs?rev=598a1e5b9#598a1e5b92e03b19115bd3012cbd4dce98267e13" dependencies = [ "quick-xml 0.41.0", "thiserror 2.0.21", @@ -1730,7 +1730,7 @@ dependencies = [ [[package]] name = "craft-zip" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2#1e5fdfdd2b5294d9781fa2257c3d6287af3cd2a2" +source = "git+https://github.com/storytold/craft-libs?rev=598a1e5b9#598a1e5b92e03b19115bd3012cbd4dce98267e13" dependencies = [ "flate2", "thiserror 2.0.21", @@ -2021,7 +2021,7 @@ dependencies = [ "craft-prepress", "craft-print", "craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)", - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)", "designcraft-color", "designcraft-compose", @@ -2103,9 +2103,9 @@ name = "designcraft-idml" version = "0.20.1" dependencies = [ "craft-file-formats", - "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", "craft-svg 0.1.0 (git+https://github.com/storytold/craft-libs?rev=cc23ffd4d26f3074808e2187d332564817624f8f)", - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", "designcraft-color", "designcraft-compose", "designcraft-doc", @@ -2222,7 +2222,7 @@ version = "0.20.1" dependencies = [ "craft-docx", "craft-file-formats", - "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1e5fdfdd2)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=598a1e5b9)", "craft-rtf", "craft-wordmodel", "craft-xlsx", diff --git a/Cargo.toml b/Cargo.toml index a3e4f474e..33971d0d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,10 +16,10 @@ craft-pdf = { git = "https://github.com/storytold/craft-libs", rev = "4052591363 craft-designcraft-format = { git = "https://github.com/storytold/craft-libs", rev = "19692b6190465671e41967c75d0cd4c439d0c84c", version = "=0.1.0" } craft-eps = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } craft-epub = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } -craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "1e5fdfdd2", version = "=0.1.0" } +craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "598a1e5b9", version = "=0.1.0" } craft-indd = { git = "https://github.com/storytold/craft-libs", rev = "47bf5650b0d503b97bfb5e841746c4957897d11f", version = "=0.1.0" } craft-svg = { git = "https://github.com/storytold/craft-libs", rev = "cc23ffd4d26f3074808e2187d332564817624f8f", version = "=0.1.0", features = ["tree-render", "tree-text"] } -craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "1e5fdfdd2", version = "=0.1.0" } +craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "598a1e5b9", version = "=0.1.0" } craft-xml-dtd = { package = "craft-xml", git = "https://github.com/storytold/craft-libs", rev = "f23b0f33f0d00b157476533a43cbbcfd901aa1f1", version = "=0.1.0" } craft-rtf = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } craft-xlsx = { git = "https://github.com/storytold/craft-libs", rev = "ba24fe2435adb3c81ad6057faf36365fbba736cf", version = "=0.1.0" } diff --git a/crates/idml/src/import/item.rs b/crates/idml/src/import/item.rs index 7dedbc056..f3100fff0 100644 --- a/crates/idml/src/import/item.rs +++ b/crates/idml/src/import/item.rs @@ -97,15 +97,14 @@ impl<'r> Importer<'r> { } // Transparency. if let Some(t) = self.with_style(e, "TransparencySetting") { - if let Some(bs) = t.find("BlendingSetting") { - if let Some(o) = bs.num("Opacity") { - it.opacity = (o / 100.0).clamp(0.0, 1.0) as f32; - } - if let Some(m) = bs.get("BlendMode") { - it.blend = names::blend_in(m); - } + let settings = craft_idml::model::TransparencySetting::from_el(&crate::xml::to_craft(&t)); + if let Some(o) = settings.opacity { + it.opacity = (o / 100.0).clamp(0.0, 1.0) as f32; } - self.effects_in(&t, &mut it.effects); + if let Some(m) = settings.blend_mode.as_deref() { + it.blend = names::blend_in(m); + } + self.effects_from_model(&settings, &mut it.effects); } // Text wrap. if let Some(w) = self.with_style(e, "TextWrapPreference") { diff --git a/crates/idml/src/import/object_style.rs b/crates/idml/src/import/object_style.rs index 343e67211..f8246e5e2 100644 --- a/crates/idml/src/import/object_style.rs +++ b/crates/idml/src/import/object_style.rs @@ -96,47 +96,44 @@ impl<'r> Importer<'r> { /// Reads the drop shadow, feather, gradient feather and directional feather of a /// `TransparencySetting` into `fx`. pub(super) fn effects_in(&mut self, t: &El, fx: &mut Effects) { - if let Some(ds) = t.find("DropShadowSetting") - && ds.get("Mode") == Some("Drop") - { - let x = ds.num("XOffset").unwrap_or(4.95); - let y = ds.num("YOffset").unwrap_or(4.95); + let settings = craft_idml::model::TransparencySetting::from_el(&crate::xml::to_craft(t)); + self.effects_from_model(&settings, fx); + } + + pub(super) fn effects_from_model(&mut self, settings: &craft_idml::model::TransparencySetting, fx: &mut Effects) { + if let Some(ds) = settings.drop_shadow.as_ref().filter(|ds| ds.mode.as_deref() == Some("Drop")) { + let x = ds.x_offset.unwrap_or(4.95); + let y = ds.y_offset.unwrap_or(4.95); let def = DropShadow::default(); fx.drop_shadow = DropShadow { on: true, - color: ds.get("EffectColor").map(|c| self.swatch_ref(c)).unwrap_or(def.color), - opacity: ds.num("Opacity").map(|o| (o / 100.0) as f32).unwrap_or(def.opacity), + color: ds.color.as_deref().map(|c| self.swatch_ref(c)).unwrap_or(def.color), + opacity: ds.opacity.map(|o| (o / 100.0) as f32).unwrap_or(def.opacity), angle: names_angle(x, y), distance: x.hypot(y), - size: ds.num("Size").unwrap_or(def.size), - spread: ds.num("Spread").unwrap_or(def.spread), + size: ds.size.unwrap_or(def.size), + spread: ds.spread.unwrap_or(def.spread), global_light: false, // Without the attribute (earlier DesignCraft exports) the shadow stays Normal, as it // rendered then; a new shadow's default is Multiply. - mode: ds.get("BlendMode").map_or(designcraft_color::BlendMode::Normal, names::blend_in), + mode: ds.blend_mode.as_deref().map_or(designcraft_color::BlendMode::Normal, names::blend_in), }; } - if let Some(f) = t.find("FeatherSetting") - && f.get("Mode").is_some_and(|m| m != "None") - { - fx.feather = f.num("Width").unwrap_or(9.0); - fx.feather_options.choke = finite(f.num("ChokeAmount")).unwrap_or(0.0).clamp(0.0, 100.0); - fx.feather_options.corners = match f.get("CornerType") { + if let Some(f) = settings.feather.as_ref().filter(|f| f.mode.as_deref().is_some_and(|m| m != "None")) { + fx.feather = f.width.unwrap_or(9.0); + fx.feather_options.choke = finite(f.choke_amount).unwrap_or(0.0).clamp(0.0, 100.0); + fx.feather_options.corners = match f.corner_type.as_deref() { Some("Sharp") => designcraft_doc::FeatherCorners::Sharp, Some("Rounded") => designcraft_doc::FeatherCorners::Rounded, _ => designcraft_doc::FeatherCorners::Diffused, }; } - if let Some(g) = t.find("GradientFeatherSetting") - && g.boolean("Applied") == Some(true) - { + if let Some(g) = settings.gradient_feather.as_ref().filter(|g| g.applied) { fx.gradient_feather = gradient_feather_in(g); } - if let Some(df) = t.find("DirectionalFeatherSetting") - && df.boolean("Applied") == Some(true) - { - let w = |k: &str| finite(df.num(k)).unwrap_or(0.0).max(0.0); - fx.directional_feather = DirectionalFeather { on: true, widths: [w("TopWidth"), w("LeftWidth"), w("BottomWidth"), w("RightWidth")] }; + if let Some(df) = settings.directional_feather.as_ref().filter(|df| df.applied) { + let w = |value| finite(value).unwrap_or(0.0).max(0.0); + fx.directional_feather = DirectionalFeather { on: true, widths: df.widths.map(w) }; } } @@ -286,33 +283,26 @@ fn overlay(base: &mut El, top: &El, depth: usize) { /// A `GradientFeatherSetting`. The model holds two opacity stops at the ends of the gradient: the /// first and last `OpacityGradientStop` give them; with a linear vector, their `Location`s move its /// ends. -fn gradient_feather_in(g: &El) -> GradientFeather { +fn gradient_feather_in(g: &craft_idml::model::GradientFeatherValues) -> GradientFeather { let def = GradientFeather::default(); - let angle = finite(g.num("Angle")).unwrap_or(0.0); - let mut stops: Vec<(f64, f64)> = g - .find_all("OpacityGradientStop") - .filter_map(|s| Some((finite(s.num("Location"))?.clamp(0.0, 100.0) / 100.0, finite(s.num("Opacity"))?.clamp(0.0, 100.0) / 100.0))) - .collect(); + let angle = finite(g.angle).unwrap_or(0.0); + let mut stops: Vec<(f64, f64)> = + g.stops.iter().filter_map(|s| Some((finite(s.location)?.clamp(0.0, 100.0) / 100.0, finite(s.opacity)?.clamp(0.0, 100.0) / 100.0))).collect(); stops.sort_by(|a, b| a.0.total_cmp(&b.0)); let (first, last) = match (stops.first(), stops.last()) { (Some(&a), Some(&b)) => (a, b), _ => ((0.0, def.start as f64), (1.0, def.end as f64)), }; - let start: Option<[f64; 2]> = g - .prop("GradientStart") - .and_then(|s| s.split_whitespace().map(|v| v.parse().ok().filter(|v: &f64| v.is_finite())).collect::>>()) - .and_then(|v| v.try_into().ok()); - let radial = g.get("Type") == Some("Radial"); - let vector = match (start, finite(g.num("Length"))) { + let vector = match (g.start, finite(g.length)) { (Some([x, y]), Some(len)) if len > 0.0 => { let a = angle.to_radians(); let (dx, dy) = (len * a.cos(), -len * a.sin()); - let (l0, l1) = if !radial && last.0 > first.0 { (first.0, last.0) } else { (0.0, 1.0) }; + let (l0, l1) = if !g.radial && last.0 > first.0 { (first.0, last.0) } else { (0.0, 1.0) }; Some([x + dx * l0, y + dy * l0, x + dx * l1, y + dy * l1]) } _ => None, }; - GradientFeather { on: true, radial, angle, start: first.1 as f32, end: last.1 as f32, vector } + GradientFeather { on: true, radial: g.radial, angle, start: first.1 as f32, end: last.1 as f32, vector } } fn names_angle(x: f64, y: f64) -> f64 {