Preserve inset line alignment in PPTX

This commit is contained in:
Harrison Booth committed 2026-10-30 22:48:31 -05:00
1 parent eaeb06c655
commit 727b682d17
4 files changed
+54 -3

No files matched your search

+30
View File
@@ -232,6 +232,34 @@ pub enum LineJoin {
Miter,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum LineAlign {
#[default]
Center,
Inside,
Outside,
}
impl LineAlign {
pub fn xml(self) -> &'static str {
match self {
LineAlign::Center => "ctr",
LineAlign::Inside => "in",
LineAlign::Outside => "out",
}
}
pub fn from_xml(value: &str) -> Option<Self> {
match value {
"ctr" => Some(LineAlign::Center),
"in" => Some(LineAlign::Inside),
"out" => Some(LineAlign::Outside),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Compound {
@@ -324,6 +352,7 @@ pub struct Line {
pub dash: Option<Dash>,
pub cap: Option<LineCap>,
pub join: Option<LineJoin>,
pub align: Option<LineAlign>,
pub compound: Option<Compound>,
pub head: Option<LineEnd>,
pub tail: Option<LineEnd>,
@@ -347,6 +376,7 @@ impl Line {
}
self.cap = self.cap.or(parent.cap);
self.join = self.join.or(parent.join);
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 {
+17
View File
@@ -488,6 +488,23 @@ 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 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 outside = shape(&mut p, "Outset line", 140.0, 20.0, 100.0, 100.0);
outside.line = Some(Line { align: Some(deckcraft_model::style::LineAlign::Outside), ..Default::default() });
let mut centered = shape(&mut p, "Centered line", 260.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, outside, 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], "Outset line").line.as_ref().and_then(|line| line.align), Some(deckcraft_model::style::LineAlign::Outside));
assert_eq!(by_name(&q.slides[0], "Centered line").line.as_ref().and_then(|line| line.align), Some(deckcraft_model::style::LineAlign::Center));
}
#[test]
fn pictures_media_and_ink_round_trip() {
let p = rich_deck();