Use shared IDML transparency settings model

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 06:51:28 -05:00
1 parent ee2d488089
commit b8f5fcc2e2
4 files changed
+47 -58

No files matched your search

Generated
+10 -10
View File
@@ -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",
+2 -2
View File
@@ -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" }
+7 -8
View File
@@ -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") {
+28 -38
View File
@@ -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::<Option<Vec<f64>>>())
.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 {