Integration: an explicit SameParaStyleSpacing Ignore survives a native save (#473)

same_para_style_spacing uses deserialize_explicit_value, so Some(None) round-trips through
serde. Test: SameParaStyleSpacing as a Properties element on the based-on Normal style closes
the gap between consecutive headings; SetIgnore keeps the usual spacing.
This commit is contained in:
aiongg
2026-10-12 17:18:26 +00:00
parent dad91f0c74
commit bfaebb76ee
2 changed files with 54 additions and 1 deletions
+10 -1
View File
@@ -1123,7 +1123,7 @@ attr_set! {
space_before: f64 = 0.0,
space_after: f64 = 0.0,
/// Space between consecutive paragraphs of the same style; replaces space after/before there (None = ignore).
same_para_style_spacing: Option<f64> = None,
same_para_style_spacing: Option<f64> = None => "deserialize_explicit_value",
/// Drop cap: the paragraph's first `drop_cap_chars` characters (code points, never
/// splitting a base from its combining marks) set `drop_cap_lines` lines tall, in the
/// character style `drop_cap_style` ("" or `[None]`: the text's own formatting).
@@ -1625,6 +1625,15 @@ mod tests {
assert_eq!(p.word_space_max, 1.33);
}
#[test]
fn same_para_style_spacing_ignore_survives_serde() {
for v in [None, Some(None), Some(Some(0.0))] {
let a = ParaAttrs { same_para_style_spacing: v, ..Default::default() };
let back: ParaAttrs = serde_json::from_str(&serde_json::to_string(&a).unwrap()).unwrap();
assert_eq!(back.same_para_style_spacing, v);
}
}
#[test]
fn numbering_styles() {
assert_eq!(NumberStyle::UpperRoman.format(14), "XIV");
+44
View File
@@ -3246,3 +3246,47 @@ fn same_para_style_spacing_imports_and_round_trips() {
// The explicit Ignore survives the round trip too.
assert_eq!(b.paras[1].para.same_para_style_spacing, Some(None));
}
/// SameParaStyleSpacing set as a `Properties` element on the based-on style (`Normal`) reaches
/// the headings: consecutive headings sit one leading apart. SetIgnore keeps the usual spacing.
#[test]
fn same_para_style_spacing_from_a_based_on_style_element_closes_the_gap() {
let styles = r#"<?xml version="1.0" encoding="UTF-8"?>
<idPkg:Styles xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging" DOMVersion="16.0">
<RootCharacterStyleGroup Self="rc">
<CharacterStyle Self="CharacterStyle/$ID/[No character style]" Name="$ID/[No character style]"/>
</RootCharacterStyleGroup>
<RootParagraphStyleGroup Self="rp">
<ParagraphStyle Self="ParagraphStyle/$ID/[No paragraph style]" Name="$ID/[No paragraph style]" PointSize="12">
<Properties><AppliedFont type="string">Source Serif 4</AppliedFont><Leading type="unit">13</Leading></Properties>
</ParagraphStyle>
<ParagraphStyle Self="ParagraphStyle/$ID/NormalParagraphStyle" Name="$ID/NormalParagraphStyle">
<Properties><BasedOn type="string">$ID/[No paragraph style]</BasedOn><SameParaStyleSpacing type="unit">0</SameParaStyleSpacing></Properties>
</ParagraphStyle>
<ParagraphStyle Self="ParagraphStyle/Heading" Name="Heading" SpaceBefore="18" SpaceAfter="4.5">
<Properties><BasedOn type="object">ParagraphStyle/$ID/NormalParagraphStyle</BasedOn></Properties>
</ParagraphStyle>
<ParagraphStyle Self="ParagraphStyle/Loose" Name="Loose" SpaceBefore="18" SpaceAfter="4.5">
<Properties><BasedOn type="object">ParagraphStyle/$ID/NormalParagraphStyle</BasedOn><SameParaStyleSpacing type="enumeration">SetIgnore</SameParaStyleSpacing></Properties>
</ParagraphStyle>
</RootParagraphStyleGroup>
</idPkg:Styles>"#;
let para = |style: &str, text: &str| {
format!(
r#"<ParagraphStyleRange AppliedParagraphStyle="ParagraphStyle/{style}"><CharacterStyleRange AppliedCharacterStyle="CharacterStyle/$ID/[No character style]"><Content>{text}</Content><Br/></CharacterStyleRange></ParagraphStyleRange>"#
)
};
let story = format!(
r#"<idPkg:Story xmlns:idPkg="http://ns.adobe.com/AdobeInDesign/idml/1.0/packaging"><Story Self="s1">{}{}{}{}</Story></idPkg:Story>"#,
para("Heading", "One"),
para("Heading", "Two"),
para("Loose", "Three"),
para("Loose", "Four")
);
let d = import_idml(&fixture_with_table_styles(styles, &story)).unwrap();
let sid = d.stories.iter().find(|(_, s)| s.text.contains("One")).map(|(id, _)| *id).unwrap();
let cs = designcraft_compose::compose_story(&d, sid, &Default::default());
let baseline = |para: usize| cs.frames[0].lines.iter().find(|l| l.para == para).unwrap().baseline;
assert!((baseline(1) - baseline(0) - 13.0).abs() < 1e-6, "{} {}", baseline(0), baseline(1));
assert!((baseline(3) - baseline(2) - 13.0 - 4.5 - 18.0).abs() < 1e-6, "{} {}", baseline(2), baseline(3));
}