diff --git a/crates/model/src/text.rs b/crates/model/src/text.rs index 362f43939..c83d8020f 100644 --- a/crates/model/src/text.rs +++ b/crates/model/src/text.rs @@ -434,6 +434,7 @@ pub struct RunProps { pub soft_edge: Option, /// Spelling/grammar errors are not flagged. pub no_proof: Option, + pub normalize_height: Option, } impl RunProps { @@ -467,7 +468,8 @@ impl RunProps { glow, reflection, soft_edge, - no_proof + no_proof, + normalize_height ); } } diff --git a/crates/pptx/src/read/dml.rs b/crates/pptx/src/read/dml.rs index a2ed6abe7..3b03e3bb3 100644 --- a/crates/pptx/src/read/dml.rs +++ b/crates/pptx/src/read/dml.rs @@ -548,6 +548,7 @@ pub fn rpr(imp: &mut Imp, part: &Part, r: &El) -> RunProps { spacing: r.f64("spc").map(|s| (s / 100.0).clamp(-4000.0, 4000.0)), kern: r.f64("kern").map(|k| (k / 100.0).clamp(0.0, 4000.0)), no_proof: r.bool("noProof"), + normalize_height: r.bool("normalizeH"), ..Default::default() }; for e in r.elements() { diff --git a/crates/pptx/src/write/dml.rs b/crates/pptx/src/write/dml.rs index 320810958..23579350e 100644 --- a/crates/pptx/src/write/dml.rs +++ b/crates/pptx/src/write/dml.rs @@ -649,7 +649,8 @@ pub fn rpr(w: &mut W, x: &mut Exp, o: &mut Out, tag: &str, r: &RunProps) { ) .o("spc", r.spacing.map(|s| (s.clamp(-4000.0, 4000.0) * 100.0).round() as i64)) .o("baseline", r.baseline.map(|b| pct(b.clamp(-10.0, 10.0)))) - .b("noProof", r.no_proof); + .b("noProof", r.no_proof) + .b("normalizeH", r.normalize_height); let has_fx = r.shadow.is_some() || r.inner_shadow.is_some() || r.glow.is_some() || r.reflection.is_some() || r.soft_edge.is_some(); let has_kids = r.outline.is_some() || r.fill.is_some() diff --git a/crates/pptx/tests/it/roundtrip.rs b/crates/pptx/tests/it/roundtrip.rs index f651fc5c6..65cc5aa7d 100644 --- a/crates/pptx/tests/it/roundtrip.rs +++ b/crates/pptx/tests/it/roundtrip.rs @@ -468,6 +468,39 @@ fn text_and_formatting_round_trip() { assert_eq!(run.text, "x=1"); } +#[test] +fn normalized_character_height_survives_two_pptx_round_trips() { + let mut p = Presentation::default(); + let mut slide = new_slide(&mut p, "Normalized height"); + let mut enabled = shape(&mut p, "Enabled", 10.0, 10.0, 200.0, 80.0); + let mut enabled_body = TextBody::from_text("enabled"); + enabled_body.paragraphs[0].runs[0].props.normalize_height = Some(true); + enabled_body.paragraphs[0].runs[0].props.size = Some(18.0); + enabled_body.paragraphs[0].runs[0].props.caps = Some(Caps::Small); + enabled_body.paragraphs[0].runs[0].props.spacing = Some(1.5); + enabled.text = Some(enabled_body); + let mut disabled = shape(&mut p, "Disabled", 10.0, 100.0, 200.0, 80.0); + let mut disabled_body = TextBody::from_text("disabled"); + disabled_body.paragraphs[0].runs[0].props.normalize_height = Some(false); + disabled.text = Some(disabled_body); + let mut absent = shape(&mut p, "Absent", 10.0, 190.0, 200.0, 80.0); + absent.text = Some(TextBody::from_text("absent")); + slide.shapes = vec![enabled, disabled, absent]; + p.slides.push(Arc::new(slide)); + + let twice = round(&round(&p)); + let slide = twice.slides.last().expect("added slide"); + let run = &by_name(slide, "Enabled").text.as_ref().expect("text").paragraphs[0].runs[0]; + assert_eq!(run.props.normalize_height, Some(true)); + assert_eq!(run.props.size, Some(18.0)); + assert_eq!(run.props.caps, Some(Caps::Small)); + assert_eq!(run.props.spacing, Some(1.5)); + let run = &by_name(slide, "Disabled").text.as_ref().expect("text").paragraphs[0].runs[0]; + assert_eq!(run.props.normalize_height, Some(false)); + let run = &by_name(slide, "Absent").text.as_ref().expect("text").paragraphs[0].runs[0]; + assert_eq!(run.props.normalize_height, None); +} + #[test] fn shapes_fills_lines_effects_round_trip() { let p = rich_deck();