mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-31 14:07:06 -06:00
Preserve bullet follow-text resets in PPTX
This commit is contained in:
@@ -236,10 +236,13 @@ pub struct ParaProps {
|
||||
pub space_after: Option<Spacing>,
|
||||
pub bullet: Option<Bullet>,
|
||||
pub bullet_font: Option<String>,
|
||||
pub bullet_font_follow_text: Option<bool>,
|
||||
/// `None` = follow text colour.
|
||||
pub bullet_color: Option<ColorRef>,
|
||||
pub bullet_color_follow_text: Option<bool>,
|
||||
/// Bullet size as a fraction of the text size.
|
||||
pub bullet_size: Option<f64>,
|
||||
pub bullet_size_follow_text: Option<bool>,
|
||||
pub tabs: Option<Vec<TabStop>>,
|
||||
pub default_tab: Option<f64>,
|
||||
pub rtl: Option<bool>,
|
||||
@@ -263,13 +266,15 @@ impl ParaProps {
|
||||
if self.bullet.is_none() {
|
||||
self.bullet.clone_from(&p.bullet);
|
||||
}
|
||||
if self.bullet_font.is_none() {
|
||||
if self.bullet_font.is_none() && self.bullet_font_follow_text.is_none() {
|
||||
self.bullet_font.clone_from(&p.bullet_font);
|
||||
}
|
||||
if self.bullet_color.is_none() {
|
||||
if self.bullet_color.is_none() && self.bullet_color_follow_text.is_none() {
|
||||
self.bullet_color.clone_from(&p.bullet_color);
|
||||
}
|
||||
self.bullet_size = self.bullet_size.or(p.bullet_size);
|
||||
if self.bullet_size_follow_text.is_none() {
|
||||
self.bullet_size = self.bullet_size.or(p.bullet_size);
|
||||
}
|
||||
if self.tabs.is_none() {
|
||||
self.tabs.clone_from(&p.tabs);
|
||||
}
|
||||
@@ -632,3 +637,31 @@ impl TextBody {
|
||||
n + self.paragraphs.len().saturating_sub(1)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use deckcraft_color::Rgba;
|
||||
|
||||
#[test]
|
||||
fn explicit_bullet_follow_text_resets_block_inheritance() {
|
||||
let parent = ParaProps {
|
||||
bullet_font: Some("Inter".into()),
|
||||
bullet_color: Some(ColorRef::rgb(Rgba::rgb(0, 0, 0))),
|
||||
bullet_size: Some(2.0),
|
||||
..Default::default()
|
||||
};
|
||||
let mut child = ParaProps {
|
||||
bullet_font_follow_text: Some(true),
|
||||
bullet_color_follow_text: Some(true),
|
||||
bullet_size_follow_text: Some(true),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
child.inherit(&parent);
|
||||
|
||||
assert_eq!(child.bullet_font, None);
|
||||
assert_eq!(child.bullet_color, None);
|
||||
assert_eq!(child.bullet_size, None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,8 +431,11 @@ pub fn ppr(imp: &mut Imp, part: &Part, p: &El) -> (Option<u8>, ParaProps, Option
|
||||
}
|
||||
}
|
||||
"buFont" => pp.bullet_font = e.attr("typeface").map(String::from),
|
||||
"buFontTx" => pp.bullet_font_follow_text = Some(true),
|
||||
"buClr" => pp.bullet_color = color(e),
|
||||
"buClrTx" => pp.bullet_color_follow_text = Some(true),
|
||||
"buSzPct" => pp.bullet_size = pct(e, "val").map(|v| v.clamp(0.25, 4.0)),
|
||||
"buSzTx" => pp.bullet_size_follow_text = Some(true),
|
||||
"tabLst" => {
|
||||
pp.tabs = Some(
|
||||
e.children_named("tab")
|
||||
|
||||
@@ -533,8 +533,11 @@ pub fn ppr(w: &mut W, x: &mut Exp, o: &mut Out, tag: &str, level: Option<u8>, p:
|
||||
|| p.space_after.is_some()
|
||||
|| p.bullet.is_some()
|
||||
|| p.bullet_font.is_some()
|
||||
|| p.bullet_font_follow_text.is_some()
|
||||
|| p.bullet_color.is_some()
|
||||
|| p.bullet_color_follow_text.is_some()
|
||||
|| p.bullet_size.is_some()
|
||||
|| p.bullet_size_follow_text.is_some()
|
||||
|| p.tabs.is_some()
|
||||
|| def.is_some();
|
||||
if !has_kids {
|
||||
@@ -551,15 +554,21 @@ pub fn ppr(w: &mut W, x: &mut Exp, o: &mut Out, tag: &str, level: Option<u8>, p:
|
||||
if let Some(s) = &p.space_after {
|
||||
spacing(w, "a:spcAft", s);
|
||||
}
|
||||
if let Some(c) = &p.bullet_color {
|
||||
if p.bullet_color_follow_text == Some(true) {
|
||||
w.empty0("a:buClrTx");
|
||||
} else if let Some(c) = &p.bullet_color {
|
||||
w.open0("a:buClr");
|
||||
color(w, c);
|
||||
w.close("a:buClr");
|
||||
}
|
||||
if let Some(s) = p.bullet_size {
|
||||
if p.bullet_size_follow_text == Some(true) {
|
||||
w.empty0("a:buSzTx");
|
||||
} else if let Some(s) = p.bullet_size {
|
||||
w.val("a:buSzPct", pct(s.clamp(0.25, 4.0)));
|
||||
}
|
||||
if let Some(f) = &p.bullet_font {
|
||||
if p.bullet_font_follow_text == Some(true) {
|
||||
w.empty0("a:buFontTx");
|
||||
} else if let Some(f) = &p.bullet_font {
|
||||
w.empty("a:buFont", A::new().a("typeface", f));
|
||||
}
|
||||
match &p.bullet {
|
||||
|
||||
@@ -488,6 +488,33 @@ fn shapes_fills_lines_effects_round_trip() {
|
||||
assert_eq!(by_name(&q.slides[1], "Group").id, by_name(&p.slides[1], "Group").id);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bullet_follow_text_resets_survive_two_pptx_round_trips() {
|
||||
let mut p = Presentation::default();
|
||||
let mut textbox = shape(&mut p, "Follow text bullets", 20.0, 20.0, 400.0, 100.0);
|
||||
textbox.text = Some(TextBody {
|
||||
paragraphs: vec![Paragraph {
|
||||
props: deckcraft_model::text::ParaProps {
|
||||
bullet: Some(Bullet::Char { char: "•".into() }),
|
||||
bullet_color_follow_text: Some(true),
|
||||
bullet_size_follow_text: Some(true),
|
||||
bullet_font_follow_text: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
runs: vec![Run::with("Bullet text", RunProps::default())],
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
});
|
||||
Arc::make_mut(&mut p.slides[0]).shapes.push(textbox);
|
||||
|
||||
let q = round(&round(&p));
|
||||
let props = &by_name(&q.slides[0], "Follow text bullets").text.as_ref().expect("text").paragraphs[0].props;
|
||||
assert_eq!(props.bullet_color_follow_text, Some(true));
|
||||
assert_eq!(props.bullet_size_follow_text, Some(true));
|
||||
assert_eq!(props.bullet_font_follow_text, Some(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pictures_media_and_ink_round_trip() {
|
||||
let p = rich_deck();
|
||||
|
||||
Reference in New Issue
Block a user