Merge pull request #2062 from cloutier/fix/2060-large-adjustment-guides

PPTX: preserve large connector adjustment guides
This commit is contained in:
FredApps authored and GitHub committed 2026-11-13 10:41:19 +01:00
commit ef9ac9ac7a
2 files changed
+59 -1

No files matched your search

+20 -1
View File
@@ -106,7 +106,9 @@ pub fn geometry(w: &mut W, g: &Geom) {
let single = !adj1 && if defaults.is_empty() { vals.len() == 1 } else { defaults.len() == 1 };
for (i, v) in vals.iter().enumerate() {
let n = if single && i == 0 { "adj".to_string() } else { format!("adj{}", i + 1) };
let v = if v.is_finite() { v.round().clamp(-2.0e9, 2.0e9) as i64 } else { 0 };
// A near-zero-width connector can need a large ratio for an ordinary-size bend.
// Keep guide literals bounded to the signed integer serialization domain.
let v = if v.is_finite() { v.round().clamp(i64::MIN as f64, i64::MAX as f64) as i64 } else { 0 };
w.empty("a:gd", A::new().a("name", n).a("fmla", format!("val {v}")));
}
for (n, v) in extra_guides(name) {
@@ -1032,6 +1034,23 @@ pub fn tree_header(w: &mut W) {
mod tests {
use super::*;
#[test]
fn guide_literals_remain_finite_bounded_integers() {
for (guide, expected) in [
(f64::NAN, 0),
(f64::INFINITY, 0),
(f64::NEG_INFINITY, 0),
(f64::MAX, i64::MAX),
(f64::MIN, i64::MIN),
(12500.6, 12501),
(-12500.6, -12501),
] {
let mut w = W::frag();
geometry(&mut w, &Geom::Preset { name: "bentConnector3".into(), adj: vec![guide] });
assert!(w.s.contains(&format!(r#"fmla="val {expected}""#)), "{}", w.s);
}
}
/// PowerPoint asks to repair a `star5` whose guide list lacks `hf`/`vf` (found black-box).
#[test]
fn preset_guides_are_complete() {
+39
View File
@@ -1396,6 +1396,45 @@ fn single_guide_names_follow_the_preset() {
}
}
/// Large finite guides position a visible bend even when the connector is only one EMU wide.
#[test]
fn large_connector_adjustments_keep_their_elbows_through_two_saves() {
let cases = [
("ordinary", 80.0, 50000.0),
("near control", 1588.0 / 12700.0, -12800000.0),
("near negative", 1.0 / 12700.0, -20320000000.0),
("near positive", 1.0 / 12700.0, 20320000000.0),
("zero width", 0.0, 20320000000.0),
("ordinary negative", 80.0, -20000.0),
];
let mut p = defaults::new_presentation(None);
p.slides.clear();
let mut s = new_slide(&mut p, "Connectors");
s.shapes.clear();
for (name, width, guide) in cases {
let mut sh = shape(&mut p, name, 100.0, 80.0, width, 140.0);
sh.kind = ShapeKind::Connector { start: None, end: None };
sh.geom = Geom::Preset { name: "bentConnector3".into(), adj: vec![guide] };
s.shapes.push(sh);
}
p.slides.push(Arc::new(s));
for generation in 1..=2 {
p = deckcraft_pptx::import(&deckcraft_pptx::export(&p).expect("export")).expect("reopen");
for (name, width, guide) in cases {
let sh = p.slides[0].shapes.iter().find(|sh| sh.name == name).expect("connector");
let Geom::Preset { name: preset, adj } = &sh.geom else {
panic!("preset connector");
};
assert_eq!(adj, &[guide], "{name}, save {generation}");
let xfrm = sh.xfrm.expect("transform");
assert_eq!((xfrm.w, xfrm.h), (width, 140.0));
let expected = deckcraft_geom::preset::build("bentConnector3", width, 140.0, &[guide]).expect("expected geometry");
let actual = deckcraft_geom::preset::build(preset, xfrm.w, xfrm.h, adj).expect("reopened geometry");
assert_eq!(actual.outline(), expected.outline(), "{name}, save {generation}");
}
}
}
/// #147: PowerPoint's Float Down is preset 47 (Float Up is 42); it imported as a plain fade.
#[test]
fn float_down_is_preset_47_both_ways() {