mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-13 13:43:50 +00:00
Merge pull request #2062 from cloutier/fix/2060-large-adjustment-guides
PPTX: preserve large connector adjustment guides
This commit is contained in:
2 files changed
+59
-1
No files matched your search
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in new issue
Block a user