mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-05 09:59:18 +00:00
Merge pull request #1710
# Conflicts: # crates/model/src/resolve.rs # crates/model/src/table.rs # crates/model/src/text.rs # crates/pptx/src/read/dml.rs # crates/pptx/src/read/shapes.rs # crates/pptx/src/write/dml.rs # crates/pptx/src/write/shapes.rs # crates/pptx/tests/it/roundtrip.rs
This commit is contained in:
commit
907ebd2de0
8 files changed
+96
-1
No files matched your search
@@ -250,6 +250,8 @@ pub fn body(ctx: &Ctx, shape: &Shape) -> BodyProps {
|
||||
upright: Some(false),
|
||||
space_first_last: Some(false),
|
||||
compat_ln_spc: None,
|
||||
vert_overflow: None,
|
||||
horz_overflow: None,
|
||||
});
|
||||
b
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ pub struct Cell {
|
||||
pub v_merge: bool,
|
||||
pub vertical: Option<crate::text::TextDir>,
|
||||
pub anchor_ctr: Option<bool>,
|
||||
pub horz_overflow: Option<crate::text::TextOverflow>,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
|
||||
@@ -122,6 +122,45 @@ pub struct BodyProps {
|
||||
/// Space before the first paragraph and after the last count too (`spcFirstLastPara`).
|
||||
pub space_first_last: Option<bool>,
|
||||
pub compat_ln_spc: Option<bool>,
|
||||
pub vert_overflow: Option<TextOverflow>,
|
||||
pub horz_overflow: Option<TextOverflow>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum TextOverflow {
|
||||
Overflow,
|
||||
Clip,
|
||||
Ellipsis,
|
||||
}
|
||||
|
||||
impl TextOverflow {
|
||||
pub fn xml(self) -> &'static str {
|
||||
match self {
|
||||
Self::Overflow => "overflow",
|
||||
Self::Clip => "clip",
|
||||
Self::Ellipsis => "ellipsis",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_xml(value: &str) -> Option<Self> {
|
||||
Some(match value {
|
||||
"overflow" => Self::Overflow,
|
||||
"clip" => Self::Clip,
|
||||
"ellipsis" => Self::Ellipsis,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// A `horzOverflow` value: `ST_TextHorzOverflowType` has only `overflow` and `clip`.
|
||||
pub fn from_horz_xml(value: &str) -> Option<Self> {
|
||||
Self::from_xml(value).filter(|v| *v != Self::Ellipsis)
|
||||
}
|
||||
|
||||
/// The `horzOverflow` attribute to write, leaving out `ellipsis`, which is vertical only.
|
||||
pub fn horz_xml(v: Option<Self>) -> Option<&'static str> {
|
||||
v.filter(|v| *v != Self::Ellipsis).map(Self::xml)
|
||||
}
|
||||
}
|
||||
|
||||
impl BodyProps {
|
||||
@@ -146,6 +185,8 @@ impl BodyProps {
|
||||
self.upright = self.upright.or(p.upright);
|
||||
self.space_first_last = self.space_first_last.or(p.space_first_last);
|
||||
self.compat_ln_spc = self.compat_ln_spc.or(p.compat_ln_spc);
|
||||
self.vert_overflow = self.vert_overflow.or(p.vert_overflow);
|
||||
self.horz_overflow = self.horz_overflow.or(p.horz_overflow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -378,6 +378,8 @@ pub fn body_pr(b: &El) -> BodyProps {
|
||||
upright: b.bool("upright"),
|
||||
space_first_last: b.bool("spcFirstLastPara"),
|
||||
compat_ln_spc: b.bool("compatLnSpc"),
|
||||
vert_overflow: b.attr("vertOverflow").and_then(deckcraft_model::text::TextOverflow::from_xml),
|
||||
horz_overflow: b.attr("horzOverflow").and_then(deckcraft_model::text::TextOverflow::from_horz_xml),
|
||||
..Default::default()
|
||||
};
|
||||
for e in b.elements() {
|
||||
|
||||
@@ -807,6 +807,7 @@ fn cell(imp: &mut Imp, part: &Part, tc: &El) -> Cell {
|
||||
}
|
||||
c.anchor = pr.attr("anchor").and_then(Anchor::from_xml);
|
||||
c.anchor_ctr = pr.bool("anchorCtr");
|
||||
c.horz_overflow = pr.attr("horzOverflow").and_then(deckcraft_model::text::TextOverflow::from_horz_xml);
|
||||
c.vertical = pr.attr("vert").and_then(TextDir::from_xml);
|
||||
for (i, n) in ["lnL", "lnR", "lnT", "lnB"].iter().enumerate() {
|
||||
if let Some(ln) = pr.child(n)
|
||||
|
||||
@@ -478,6 +478,9 @@ pub fn body_pr(w: &mut W, b: &BodyProps) {
|
||||
.b("upright", b.upright)
|
||||
.b("spcFirstLastPara", b.space_first_last)
|
||||
.b("compatLnSpc", b.compat_ln_spc);
|
||||
.b("spcFirstLastPara", b.space_first_last)
|
||||
.o("vertOverflow", b.vert_overflow.map(|v| v.xml()))
|
||||
.o("horzOverflow", deckcraft_model::text::TextOverflow::horz_xml(b.horz_overflow));
|
||||
let warp = b.warp.as_deref().filter(|s| !s.is_empty() && s.chars().all(|c| c.is_ascii_alphanumeric()));
|
||||
if warp.is_none() && b.autofit.is_none() {
|
||||
w.empty("a:bodyPr", a);
|
||||
|
||||
@@ -748,6 +748,7 @@ fn table(w: &mut W, x: &mut Exp, o: &mut Out, t: &Table) {
|
||||
}),
|
||||
);
|
||||
a = a.b("anchorCtr", c.anchor_ctr);
|
||||
a = a.o("horzOverflow", deckcraft_model::text::TextOverflow::horz_xml(c.horz_overflow));
|
||||
w.open("a:tcPr", a);
|
||||
for (tag, l) in [
|
||||
("a:lnL", &c.borders[0]),
|
||||
|
||||
@@ -12,7 +12,7 @@ use deckcraft_model::style::{
|
||||
};
|
||||
use deckcraft_model::table::Table;
|
||||
use deckcraft_model::text::{
|
||||
Action, Align, Anchor, AutoFit, BodyProps, Bullet, Caps, Hyperlink, Paragraph, Run, RunKind, RunProps, Spacing, Strike, TextBody,
|
||||
Action, Align, Anchor, AutoFit, BodyProps, Bullet, Caps, Hyperlink, Paragraph, Run, RunKind, RunProps, Spacing, Strike, TextBody, TextOverflow,
|
||||
};
|
||||
use deckcraft_model::{
|
||||
Background, Comment, CustomPath, CustomShow, Geom, InkStroke, MediaClip, Presentation, Section, Shape, ShapeId, ShapeKind, Slide, SlideId,
|
||||
@@ -561,6 +561,50 @@ fn compatible_line_spacing_mode_survives_two_pptx_round_trips() {
|
||||
assert_eq!(by_name(slide, "Disabled").text.as_ref().expect("text").body.compat_ln_spc, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn text_overflow_policies_survive_two_pptx_round_trips() {
|
||||
let mut p = Presentation::default();
|
||||
let mut slide = new_slide(&mut p, "Text overflow");
|
||||
let mut text = shape(&mut p, "Text", 10.0, 10.0, 200.0, 80.0);
|
||||
let mut body = TextBody::from_text("policy");
|
||||
body.body.vert_overflow = Some(TextOverflow::Clip);
|
||||
body.body.horz_overflow = Some(TextOverflow::Overflow);
|
||||
text.text = Some(body);
|
||||
let mut table_shape = shape(&mut p, "Table", 10.0, 100.0, 200.0, 80.0);
|
||||
let mut table = Table::new(1, 1, 200.0, 80.0);
|
||||
table.rows[0].cells[0].horz_overflow = Some(TextOverflow::Clip);
|
||||
table_shape.kind = ShapeKind::Table(table);
|
||||
slide.shapes = vec![text, table_shape];
|
||||
p.slides.push(Arc::new(slide));
|
||||
|
||||
let twice = round(&round(&p));
|
||||
let slide = twice.slides.last().expect("added slide");
|
||||
let text = by_name(slide, "Text").text.as_ref().expect("text body");
|
||||
assert_eq!(text.body.vert_overflow, Some(TextOverflow::Clip));
|
||||
assert_eq!(text.body.horz_overflow, Some(TextOverflow::Overflow));
|
||||
let ShapeKind::Table(table) = &by_name(slide, "Table").kind else { panic!("table") };
|
||||
assert_eq!(table.rows[0].cells[0].horz_overflow, Some(TextOverflow::Clip));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn horizontal_ellipsis_is_not_written_because_only_vertical_overflow_has_it() {
|
||||
let mut p = Presentation::default();
|
||||
let mut slide = new_slide(&mut p, "Horizontal ellipsis");
|
||||
let mut text = shape(&mut p, "Text", 10.0, 10.0, 200.0, 80.0);
|
||||
let mut body = TextBody::from_text("policy");
|
||||
body.body.vert_overflow = Some(TextOverflow::Ellipsis);
|
||||
body.body.horz_overflow = Some(TextOverflow::Ellipsis);
|
||||
text.text = Some(body);
|
||||
slide.shapes = vec![text];
|
||||
p.slides.push(Arc::new(slide));
|
||||
|
||||
let once = round(&p);
|
||||
let slide = once.slides.last().expect("added slide");
|
||||
let text = by_name(slide, "Text").text.as_ref().expect("text body");
|
||||
assert_eq!(text.body.vert_overflow, Some(TextOverflow::Ellipsis));
|
||||
assert_eq!(text.body.horz_overflow, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shapes_fills_lines_effects_round_trip() {
|
||||
let p = rich_deck();
|
||||
|
||||
Reference in new issue
Block a user