PPTX: a lone guide keeps the name its preset uses, adj1 or adj (#214)

The writer named a preset's only guide `adj` whenever the preset has one
adjust value, but bentConnector3, curvedConnector3, frame, mathPlus,
mathMinus and mathMultiply call it `adj1`, and PowerPoint refuses to open
a file that renames it. The names come from PowerPoint itself: every
autoshape and connector created with an adjusted first value and saved;
these six are the only single-guide presets named `adj1`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
justinjohn0306
2026-10-12 17:30:03 +05:30
co-authored by Claude Opus 5.5
parent 0ec0cc5e81
commit aaec22c681
2 changed files with 34 additions and 1 deletions
+4 -1
View File
@@ -100,7 +100,10 @@ pub fn geometry(w: &mut W, g: &Geom) {
vals.push(defaults.get(vals.len()).copied().unwrap_or(0.0));
}
w.open0("a:avLst");
let single = if defaults.is_empty() { vals.len() == 1 } else { defaults.len() == 1 };
// A lone guide is `adj`, except in these presets, which name it `adj1` (as PowerPoint
// writes them); PowerPoint won't open a file that renames it (#214).
let adj1 = matches!(name, "bentConnector3" | "curvedConnector3" | "frame" | "mathPlus" | "mathMinus" | "mathMultiply");
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 };
+30
View File
@@ -730,3 +730,33 @@ fn date_fields_keep_their_type_and_saved_text() {
assert_eq!(by_name(&q.slides[0], name).text, by_name(&p.slides[0], name).text, "{name}");
}
}
/// #214: a lone guide was always written `adj`, but these presets name it `adj1`, and PowerPoint
/// won't open the file otherwise. (Names as PowerPoint writes them.)
#[test]
fn single_guide_names_follow_the_preset() {
let mut p = defaults::new_presentation(None);
let mut s = new_slide(&mut p, "Guides");
let cases = [
("bentConnector3", "adj1"),
("curvedConnector3", "adj1"),
("frame", "adj1"),
("mathPlus", "adj1"),
("mathMinus", "adj1"),
("mathMultiply", "adj1"),
("roundRect", "adj"),
("chevron", "adj"),
];
for (prst, _) in cases {
let mut sh = shape(&mut p, prst, 0.0, 0.0, 100.0, 50.0);
sh.geom = Geom::Preset { name: prst.into(), adj: vec![20000.0] };
s.shapes.push(sh);
}
p.slides.push(Arc::new(s));
let bytes = deckcraft_pptx::export(&p).expect("export");
let mut z = zip::ZipArchive::new(std::io::Cursor::new(&bytes)).expect("zip");
let xml = std::io::read_to_string(z.by_name("ppt/slides/slide2.xml").expect("slide")).expect("read");
for (prst, gd) in cases {
assert!(xml.contains(&format!(r#"<a:prstGeom prst="{prst}"><a:avLst><a:gd name="{gd}" fmla="val 20000"/>"#)), "{prst} → {gd}: {xml}");
}
}