Write paragraph box formatting through shared IDML model

This commit is contained in:
Dylan M. Taylor committed 2026-11-13 07:49:05 -05:00
1 parent f4551ac6f3
commit d91fa12aa7
3 files changed
+63 -106

No files matched your search

Generated
+23 -23
View File
@@ -1242,20 +1242,20 @@ dependencies = [
[[package]]
name = "craft-idml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558#eed3c28cf016723d004d6f2c79c61424c15d4558"
source = "git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf#16de952845b5b959e462012fc4298d18cd9a91cf"
dependencies = [
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf)",
"thiserror 2.0.21",
]
[[package]]
name = "craft-idml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641#fbc0fd1db11429350fb48a769c5cde4ace26e641"
source = "git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558#eed3c28cf016723d004d6f2c79c61424c15d4558"
dependencies = [
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558)",
"craft-zip 0.1.0 (git+https://github.com/storytold/craft-libs?rev=eed3c28cf016723d004d6f2c79c61424c15d4558)",
"thiserror 2.0.21",
]
@@ -1644,6 +1644,15 @@ dependencies = [
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf#16de952845b5b959e462012fc4298d18cd9a91cf"
dependencies = [
"quick-xml 0.41.0",
"thiserror 2.0.21",
]
[[package]]
name = "craft-xml"
version = "0.1.0"
@@ -1709,12 +1718,13 @@ dependencies = [
]
[[package]]
name = "craft-xml"
name = "craft-zip"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641#fbc0fd1db11429350fb48a769c5cde4ace26e641"
source = "git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf#16de952845b5b959e462012fc4298d18cd9a91cf"
dependencies = [
"quick-xml 0.41.0",
"flate2",
"thiserror 2.0.21",
"zip 9.1.1",
]
[[package]]
@@ -1767,16 +1777,6 @@ dependencies = [
"zip 9.1.1",
]
[[package]]
name = "craft-zip"
version = "0.1.0"
source = "git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641#fbc0fd1db11429350fb48a769c5cde4ace26e641"
dependencies = [
"flate2",
"thiserror 2.0.21",
"zip 9.1.1",
]
[[package]]
name = "crc32fast"
version = "1.5.3"
@@ -2021,8 +2021,8 @@ 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=16de952845b5b959e462012fc4298d18cd9a91cf)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=f23b0f33f0d00b157476533a43cbbcfd901aa1f1)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"designcraft-color",
"designcraft-compose",
"designcraft-doc",
@@ -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=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf)",
"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=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"craft-xml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf)",
"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=fbc0fd1db11429350fb48a769c5cde4ace26e641)",
"craft-idml 0.1.0 (git+https://github.com/storytold/craft-libs?rev=16de952845b5b959e462012fc4298d18cd9a91cf)",
"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 = "fbc0fd1db11429350fb48a769c5cde4ace26e641", version = "=0.1.0" }
craft-idml = { git = "https://github.com/storytold/craft-libs", rev = "16de952845b5b959e462012fc4298d18cd9a91cf", 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 = "fbc0fd1db11429350fb48a769c5cde4ace26e641", version = "=0.1.0" }
craft-xml = { git = "https://github.com/storytold/craft-libs", rev = "16de952845b5b959e462012fc4298d18cd9a91cf", 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" }
+38 -81
View File
@@ -22,13 +22,6 @@ macro_rules! b {
};
}
/// The Overprint switches of a paragraph's shading, border and border gap.
fn para_box_overprints(el: &mut El, a: &ParaAttrs) {
b!(a, el, shading_overprint, "ParagraphShadingOverprint");
b!(a, el, border_overprint, "ParagraphBorderOverprint");
b!(a, el, border_gap_overprint, "ParagraphBorderGapOverprint");
}
impl<'a> Ex<'a> {
pub(super) fn char_attrs(&mut self, el: &mut El, props: &mut Vec<El>, a: &CharAttrs) {
if let Some(v) = a.leading_aki {
@@ -482,82 +475,46 @@ impl<'a> Ex<'a> {
props.extend(list.to_els().into_iter().map(crate::xml::from_craft));
}
fn para_box_corners(el: &mut El, a: &ParaAttrs) {
const CORNERS: [&str; 4] = ["TopLeft", "TopRight", "BottomRight", "BottomLeft"];
for (prefix, corners) in [("ParagraphBorder", a.border_corners), ("ParagraphShading", a.shading_corners)] {
if let Some(cs) = corners {
for (c, k) in cs.iter().zip(CORNERS) {
el.set(&format!("{prefix}{k}CornerOption"), names::corner_out(c.shape));
el.set(&format!("{prefix}{k}CornerRadius"), num(c.size));
}
}
}
}
fn para_box_attrs(&self, el: &mut El, props: &mut Vec<El>, a: &ParaAttrs) {
b!(a, el, shading_on, "ParagraphShadingOn");
if let Some(c) = &a.shading_color {
props.push(p("ParagraphShadingColor", "object", self.sw(c)));
let corners = |values: Option<[designcraft_geom::corners::Corner; 4]>| {
values.map(|corners| corners.map(|corner| (names::corner_out(corner.shape).to_string(), corner.size)))
};
let values = craft_idml::model::ParagraphBoxValues {
shading_on: a.shading_on,
shading_color: a.shading_color.as_ref().map(|color| self.sw(color)),
shading_tint: a.shading_tint.map(|tint| f64::from(tint) * 100.0),
shading_offsets: a.shading_offsets,
shading_overprint: a.shading_overprint,
border_on: a.border_on,
border_color: a.border_color.as_ref().map(|color| self.sw(color)),
border_tint: a.border_tint.map(|tint| f64::from(tint) * 100.0),
border_weights: a.border_weights,
border_offsets: a.border_offsets,
border_type: a.border_type.as_ref().map(names::stroke_type_out),
border_cap: a.border_cap.map(|cap| names::cap_out(cap).to_string()),
border_join: a.border_join.map(|join| names::join_out(join).to_string()),
border_corners: corners(a.border_corners),
border_width: a.border_width.map(|width| names::box_width_out(width).to_string()),
border_top: a.border_top.map(|origin| names::box_top_out(origin).to_string()),
border_bottom: a.border_bottom.map(|origin| names::box_bottom_out(origin).to_string()),
border_display_if_splits: a.border_display_if_splits,
border_merge: a.border_merge,
border_gap_color: a.border_gap_color.as_ref().map(|color| self.sw(color)),
border_gap_tint: a.border_gap_tint.map(|tint| f64::from(tint) * 100.0),
border_overprint: a.border_overprint,
border_gap_overprint: a.border_gap_overprint,
shading_width: a.shading_width.map(|width| names::box_width_out(width).to_string()),
shading_top: a.shading_top.map(|origin| names::box_top_out(origin).to_string()),
shading_bottom: a.shading_bottom.map(|origin| names::box_bottom_out(origin).to_string()),
shading_clip: a.shading_clip,
shading_nonprinting: a.shading_nonprinting,
shading_corners: corners(a.shading_corners),
};
let (attributes, properties) = values.to_parts();
for (key, value) in attributes {
el.set(&key, value);
}
n!(a, el, shading_tint, "ParagraphShadingTint", pct);
para_box_overprints(el, a);
const SIDES: [&str; 4] = ["Top", "Left", "Bottom", "Right"];
if let Some(o) = a.shading_offsets {
for (i, side) in SIDES.iter().enumerate() {
el.set(&format!("ParagraphShading{side}Offset"), num(o[i]));
}
}
b!(a, el, border_on, "ParagraphBorderOn");
if let Some(c) = &a.border_color {
props.push(p("ParagraphBorderColor", "object", self.sw(c)));
}
n!(a, el, border_tint, "ParagraphBorderTint", pct);
if let Some(w) = a.border_weights {
for (i, side) in SIDES.iter().enumerate() {
el.set(&format!("ParagraphBorder{side}LineWeight"), num(w[i]));
}
}
if let Some(o) = a.border_offsets {
for (i, side) in SIDES.iter().enumerate() {
el.set(&format!("ParagraphBorder{side}Offset"), num(o[i]));
}
}
if let Some(t) = &a.border_type {
props.push(p("ParagraphBorderType", "object", names::stroke_type_out(t)));
}
if let Some(c) = a.border_cap {
el.set("ParagraphBorderStrokeEndCap", names::cap_out(c));
}
if let Some(j) = a.border_join {
el.set("ParagraphBorderStrokeEndJoin", names::join_out(j));
}
Self::para_box_corners(el, a);
if let Some(w) = a.border_width {
el.set("ParagraphBorderWidth", names::box_width_out(w));
}
if let Some(t) = a.border_top {
el.set("ParagraphBorderTopOrigin", names::box_top_out(t));
}
if let Some(b) = a.border_bottom {
el.set("ParagraphBorderBottomOrigin", names::box_bottom_out(b));
}
b!(a, el, border_display_if_splits, "ParagraphBorderDisplayIfSplits");
b!(a, el, border_merge, "MergeConsecutiveParaBorders");
if let Some(c) = &a.border_gap_color {
props.push(p("ParagraphBorderGapColor", "object", self.sw(c)));
}
n!(a, el, border_gap_tint, "ParagraphBorderGapTint", pct);
if let Some(w) = a.shading_width {
el.set("ParagraphShadingWidth", names::box_width_out(w));
}
if let Some(t) = a.shading_top {
el.set("ParagraphShadingTopOrigin", names::box_top_out(t));
}
if let Some(b) = a.shading_bottom {
el.set("ParagraphShadingBottomOrigin", names::box_bottom_out(b));
}
b!(a, el, shading_clip, "ParagraphShadingClipToFrame");
b!(a, el, shading_nonprinting, "ParagraphShadingSuppressPrinting");
props.extend(properties.into_iter().map(crate::xml::from_craft));
}
/// Write paragraph attributes `a`, set over the resolved paragraph style `parent` (`None` for the