Merge pull request #1590

Preserve inset line alignment in PPTX

# Conflicts:
#	crates/model/src/style.rs
#	crates/pptx/tests/it/roundtrip.rs
This commit is contained in:
Brandon Thomas
2026-10-31 00:51:48 -04:00
4 changed files with 50 additions and 3 deletions
+27
View File
@@ -232,6 +232,31 @@ pub enum LineJoin {
Miter,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum LineAlign {
#[default]
Center,
Inside,
}
impl LineAlign {
pub fn xml(self) -> &'static str {
match self {
LineAlign::Center => "ctr",
LineAlign::Inside => "in",
}
}
pub fn from_xml(value: &str) -> Option<Self> {
match value {
"ctr" => Some(LineAlign::Center),
"in" => Some(LineAlign::Inside),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Compound {
@@ -325,6 +350,7 @@ pub struct Line {
pub cap: Option<LineCap>,
pub join: Option<LineJoin>,
pub miter_limit: Option<u32>,
pub align: Option<LineAlign>,
pub compound: Option<Compound>,
pub head: Option<LineEnd>,
pub tail: Option<LineEnd>,
@@ -349,6 +375,7 @@ impl Line {
self.cap = self.cap.or(parent.cap);
self.join = self.join.or(parent.join);
self.miter_limit = self.miter_limit.or(parent.miter_limit);
self.align = self.align.or(parent.align);
self.compound = self.compound.or(parent.compound);
if self.head.is_none() {
self.head.clone_from(&parent.head);
+3 -2
View File
@@ -3,8 +3,8 @@
use deckcraft_color::{ColorTransform, Rgba, SchemeSlot};
use deckcraft_geom::{Xfrm, emu_to_pt};
use deckcraft_model::style::{
ColorBase, ColorRef, Compound, Dash, Effects, Fill, Glow, Gradient, GradientShape, GradientStop, Line, LineCap, LineEnd, LineJoin, PatternFill,
PictureFill, PictureMode, Reflection, Shadow,
ColorBase, ColorRef, Compound, Dash, Effects, Fill, Glow, Gradient, GradientShape, GradientStop, Line, LineAlign, LineCap, LineEnd, LineJoin,
PatternFill, PictureFill, PictureMode, Reflection, Shadow,
};
use deckcraft_model::text::{
Action, Align, Anchor, AutoFit, BodyProps, Bullet, Caps, Hyperlink, LevelStyle, ListStyle, ParaProps, Paragraph, Run, RunKind, RunProps, Spacing,
@@ -222,6 +222,7 @@ pub fn line(imp: &mut Imp, part: &Part, ln: &El) -> Line {
_ => LineCap::Flat,
});
l.compound = ln.attr("cmpd").map(Compound::from_xml);
l.align = ln.attr("algn").and_then(LineAlign::from_xml);
l.fill = fill_of(imp, part, ln);
for e in ln.elements() {
match e.local() {
+4 -1
View File
@@ -1,7 +1,9 @@
//! DrawingML writing: colours, fills, lines, effects, transforms and text.
use deckcraft_geom::{Xfrm, pt_to_emu};
use deckcraft_model::style::{ColorBase, ColorRef, Compound, Dash, Effects, Fill, GradientShape, Line, LineCap, LineJoin, PictureFill, PictureMode};
use deckcraft_model::style::{
ColorBase, ColorRef, Compound, Dash, Effects, Fill, GradientShape, Line, LineAlign, LineCap, LineJoin, PictureFill, PictureMode,
};
use deckcraft_model::text::{
Action, AutoFit, BodyProps, Bullet, Caps, Hyperlink, ListStyle, ParaProps, Paragraph, RunKind, RunProps, Spacing, Strike, TextBody,
};
@@ -208,6 +210,7 @@ pub fn line(w: &mut W, x: &mut Exp, o: &mut Out, tag: &str, l: &Line) {
LineCap::Flat => "flat",
}),
)
.o("algn", l.align.map(LineAlign::xml))
.o("cmpd", l.compound.map(Compound::xml));
w.open(tag, a);
if let Some(f) = &l.fill {
+16
View File
@@ -559,6 +559,22 @@ fn bullet_follow_text_resets_survive_two_pptx_round_trips() {
assert_eq!(explicit.bullet_color, Some(deckcraft_model::style::ColorRef::rgb(deckcraft_color::Rgba::rgb(255, 0, 0))));
}
#[test]
fn line_alignment_survives_two_pptx_round_trips() {
let mut p = Presentation::default();
let mut inside = shape(&mut p, "Inset line", 20.0, 20.0, 100.0, 100.0);
inside.line = Some(Line { align: Some(deckcraft_model::style::LineAlign::Inside), ..Default::default() });
let mut centered = shape(&mut p, "Centered line", 140.0, 20.0, 100.0, 100.0);
centered.line = Some(Line { align: Some(deckcraft_model::style::LineAlign::Center), ..Default::default() });
Arc::make_mut(&mut p.slides[0]).shapes.extend([inside, centered]);
let q = round(&round(&p));
assert_eq!(by_name(&q.slides[0], "Inset line").line.as_ref().and_then(|line| line.align), Some(deckcraft_model::style::LineAlign::Inside));
assert_eq!(by_name(&q.slides[0], "Centered line").line.as_ref().and_then(|line| line.align), Some(deckcraft_model::style::LineAlign::Center));
assert_eq!(deckcraft_model::style::LineAlign::from_xml("out"), None);
}
#[test]
fn pictures_media_and_ink_round_trip() {
let p = rich_deck();