From 59db0b67a7efc3f2459515238e97d291c57d9eaa Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 13 Nov 2026 09:43:29 -0500 Subject: [PATCH] Use shared IDML transparency writer --- Cargo.lock | 20 ++++---- Cargo.toml | 4 +- crates/idml/src/export/item.rs | 90 +++++++++++++++++++--------------- 3 files changed, 63 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a363e54f..4d2315343 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=5ca720669#5ca720669312d6c8c944efb866d3ab90e7807327" +source = "git+https://github.com/storytold/craft-libs?rev=1ad5f35bf#1ad5f35bf0b33f5846fd8aec782f0ba5a9e3b596" dependencies = [ - "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=5ca720669)", - "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=5ca720669)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", + "craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", "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=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" +source = "git+https://github.com/storytold/craft-libs?rev=1ad5f35bf#1ad5f35bf0b33f5846fd8aec782f0ba5a9e3b596" 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=5ca720669#5ca720669312d6c8c944efb866d3ab90e7807327" +source = "git+https://github.com/storytold/craft-libs?rev=40525913633942f84b06d4837a8d90534a55d63f#40525913633942f84b06d4837a8d90534a55d63f" dependencies = [ "quick-xml 0.41.0", "thiserror 2.0.21", @@ -1720,7 +1720,7 @@ dependencies = [ [[package]] name = "craft-zip" version = "0.1.0" -source = "git+https://github.com/storytold/craft-libs?rev=5ca720669#5ca720669312d6c8c944efb866d3ab90e7807327" +source = "git+https://github.com/storytold/craft-libs?rev=1ad5f35bf#1ad5f35bf0b33f5846fd8aec782f0ba5a9e3b596" 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=5ca720669)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", "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=5ca720669)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", "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=5ca720669)", + "craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", "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=5ca720669)", + "craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=1ad5f35bf)", "craft-rtf", "craft-wordmodel", "craft-xlsx", diff --git a/Cargo.toml b/Cargo.toml index a778f0567..54cc3d2f0 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 = "93d0998383aa150fa50a2ffbf49ef37786e37eca", 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 = "5ca720669", version = "=0.1.0" } +craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "1ad5f35bf", 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 = "5ca720669", version = "=0.1.0" } +craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "1ad5f35bf", 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/export/item.rs b/crates/idml/src/export/item.rs index e298564ce..5c7dafec3 100644 --- a/crates/idml/src/export/item.rs +++ b/crates/idml/src/export/item.rs @@ -156,46 +156,58 @@ impl<'a> Ex<'a> { let ds = &it.effects.drop_shadow; let (gf, df) = (&it.effects.gradient_feather, &it.effects.directional_feather); if it.opacity < 1.0 || it.blend != designcraft_color::BlendMode::Normal || ds.on || it.effects.feather > 0.0 || gf.on || df.on || styled { - let mut t = El::new("TransparencySetting"); - t.push(El::new("BlendingSetting").attr("BlendMode", names::blend_out(it.blend)).attr("Opacity", pct(it.opacity as f64))); - for c in self.effect_els(&it.effects, styled) { - t.push(c); - } - if df.on { - let [top, left, bottom, right] = df.widths; - t.push( - El::new("DirectionalFeatherSetting") - .attr("Applied", "true") - .attr("LeftWidth", num(left)) - .attr("RightWidth", num(right)) - .attr("TopWidth", num(top)) - .attr("BottomWidth", num(bottom)), - ); - } - if gf.on { - let mut g = El::new("GradientFeatherSetting").attr("Applied", "true").attr("Type", if gf.radial { "Radial" } else { "Linear" }); - if let Some([x0, y0, x1, y1]) = gf.vector { - // Gradient Feather tool vector: start, length and angle in the item's space. - g.set("Angle", num((-(y1 - y0)).atan2(x1 - x0).to_degrees())); - g.set("Length", num((x1 - x0).hypot(y1 - y0))); - g.set("GradientStart", format!("{} {}", num(x0), num(y0))); - } else { - g.set("Angle", num(gf.angle)); - } - let id = uid(it.id.0); - for (i, (opacity, location)) in [(gf.start, 0.0), (gf.end, 100.0)].into_iter().enumerate() { - let mut s = El::new("OpacityGradientStop") - .attr("Self", format!("{id}TransparencySetting1GradientFeatherSetting1OpacityGradientStop{i}")) - .attr("Opacity", pct(opacity as f64)) - .attr("Location", num(location)); - if i > 0 { - s.set("Midpoint", "50"); + let gradient_feather = if gf.on { + let (angle, length, start) = gf.vector.map_or((Some(gf.angle), None, None), |[x0, y0, x1, y1]| { + (Some((-(y1 - y0)).atan2(x1 - x0).to_degrees()), Some((x1 - x0).hypot(y1 - y0)), Some([x0, y0])) + }); + Some(craft_idml::model::GradientFeatherValues { + applied: true, + radial: gf.radial, + angle, + length, + start, + stops: vec![ + craft_idml::model::OpacityStopValues { location: Some(0.0), opacity: Some(f64::from(gf.start) * 100.0) }, + craft_idml::model::OpacityStopValues { location: Some(100.0), opacity: Some(f64::from(gf.end) * 100.0) }, + ], + }) + } else { + None + }; + let directional_feather = df.on.then(|| craft_idml::model::DirectionalFeatherValues { applied: true, widths: df.widths.map(Some) }); + let transparency = craft_idml::model::TransparencySetting { + blend_mode: Some(names::blend_out(it.blend).into()), + opacity: Some(f64::from(it.opacity) * 100.0), + drop_shadow: (ds.on || styled).then(|| { + let a = self.d.light_angle(ds.angle, ds.global_light).to_radians(); + craft_idml::model::DropShadowValues { + mode: Some(if ds.on { "Drop" } else { "None" }.into()), + x_offset: Some(-ds.distance * a.cos()), + y_offset: Some(ds.distance * a.sin()), + color: Some(self.sw(&ds.color)), + opacity: Some(f64::from(ds.opacity) * 100.0), + size: Some(ds.size), + spread: Some(ds.spread), + blend_mode: Some(names::blend_out(ds.mode).into()), } - g.push(s); - } - t.push(g); - } - el.push(t); + }), + feather: (it.effects.feather > 0.0 || styled).then(|| { + let corners = match it.effects.feather_options.corners { + designcraft_doc::FeatherCorners::Sharp => "Sharp", + designcraft_doc::FeatherCorners::Rounded => "Rounded", + designcraft_doc::FeatherCorners::Diffused => "Diffusion", + }; + craft_idml::model::FeatherValues { + mode: Some(if it.effects.feather > 0.0 { "Standard" } else { "None" }.into()), + width: Some(it.effects.feather), + choke_amount: Some(it.effects.feather_options.choke), + corner_type: Some(corners.into()), + } + }), + gradient_feather, + directional_feather, + }; + el.push(crate::xml::from_craft(transparency.to_el(&uid(it.id.0)))); } }