Skip to main content

pptx_rs/oxml/
simpletypes.rs

1//! 简单 OOXML 类型。
2//!
3//! 包括文本对齐、字体、颜色等"枚举"型属性。
4//!
5//! 本模块与 python-pptx 中 `pptx.enum.text`、`pptx.enum.shapes` 等
6//! 子包对标,但本库倾向于把"枚举"集中在 `simpletypes` 一处,便于查找。
7//!
8//! # 设计要点
9//!
10//! - **全部派生 `Copy + Eq + Hash`**:便于 `match` / `BTreeMap` 索引。
11//! - **`as_str()` 返回 `&'static str`**:零分配序列化。
12//! - **`FromStr` 实现**:解析时把 OOXML 字面量转回枚举。
13//! - **未知值兜底**:用 [`PresetGeometry::Other`] 等变体吸收未识别值,
14//!   避免解析阶段直接 panic;与 python-pptx 的容错策略一致。
15
16use std::str::FromStr;
17
18use crate::oxml::color::SchemeColor;
19use crate::oxml::sppr::Dash;
20
21/// 段落水平对齐方式(`algn` 属性)。
22#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
23pub enum Alignment {
24    /// 左对齐。
25    #[default]
26    Left,
27    /// 居中。
28    Center,
29    /// 右对齐。
30    Right,
31    /// 两端对齐。
32    Justify,
33    /// 分散对齐。
34    Distribute,
35}
36
37impl Alignment {
38    /// 转 OOXML 字面量。
39    pub fn as_str(self) -> &'static str {
40        match self {
41            Alignment::Left => "l",
42            Alignment::Center => "ctr",
43            Alignment::Right => "r",
44            Alignment::Justify => "just",
45            Alignment::Distribute => "dist",
46        }
47    }
48}
49
50impl FromStr for Alignment {
51    type Err = ();
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        Ok(match s {
54            "l" => Alignment::Left,
55            "ctr" => Alignment::Center,
56            "r" => Alignment::Right,
57            "just" => Alignment::Justify,
58            "dist" => Alignment::Distribute,
59            _ => return Err(()),
60        })
61    }
62}
63
64/// 几何预设形状名(`prstGeom prst="..."`)。
65///
66/// 仅包含 python-pptx 文档过的常用子集,足以应付大部分 case。
67/// 全部 200+ 形状见 ECMA-376 Part 1, §19.3 起的 `ST_PresetShape`。
68#[derive(Copy, Clone, Debug, Eq, PartialEq)]
69pub enum PresetGeometry {
70    /// 矩形。
71    Rectangle,
72    /// 圆角矩形。
73    RoundRectangle,
74    /// 椭圆。
75    Ellipse,
76    /// 直线。
77    Line,
78    /// 直角三角形。
79    RightTriangle,
80    /// 菱形。
81    Diamond,
82    /// 平行四边形。
83    Parallelogram,
84    /// 梯形。
85    Trapezoid,
86    /// 六边形。
87    Hexagon,
88    /// 八边形。
89    Octagon,
90    /// 五角星。
91    Star5,
92    /// 六角星。
93    Star6,
94    /// 十角星。
95    Star10,
96    /// 普通箭头。
97    Arrow,
98    /// 右箭头。
99    RightArrow,
100    /// 左箭头。
101    LeftArrow,
102    /// 上箭头。
103    UpArrow,
104    /// 下箭头。
105    DownArrow,
106    /// 弯箭头。
107    BentArrow,
108    /// 弧形右箭头。
109    CurvedRightArrow,
110    /// 矩形标注。
111    Callout1,
112    /// 圆角矩形标注。
113    Callout2,
114    /// 云形。
115    Cloud,
116    /// 心形。
117    Heart,
118    /// 闪电。
119    LightningBolt,
120    /// 太阳。
121    Sun,
122    /// 月亮。
123    Moon,
124    /// 笑脸。
125    SmileyFace,
126    /// 甜甜圈。
127    Donut,
128    /// 立方体。
129    Cube,
130    /// 圆柱。
131    Can,
132    /// 斜面。
133    Bevel,
134    /// 禁止符号。
135    NoSmoking,
136    /// 弧块。
137    BlockArc,
138    /// 加号。
139    Plus,
140    /// 减号。
141    Minus,
142    /// 牌匾。
143    Plaque,
144    /// 波浪。
145    Wave,
146    /// 饼图。
147    Pie,
148    /// 斜面 2。
149    Bevel2,
150    /// 折角。
151    FoldedCorner,
152    /// 五边形。
153    Pentagon,
154    /// 人字形。
155    Chevron,
156    /// 右方括号。
157    RightBracket,
158    /// 左方括号。
159    LeftBracket,
160    /// 双方括号。
161    DoubleBracket,
162    /// 直线连接器。
163    StraightConnector1,
164    /// 折线连接器(2 段,无调整点)。
165    BentConnector2,
166    /// 折线连接器(3 段,1 个调整点)。
167    BentConnector3,
168    /// 折线连接器(4 段,2 个调整点)。
169    BentConnector4,
170    /// 折线连接器(5 段,3 个调整点)。
171    BentConnector5,
172    /// 曲线连接器(2 段,无调整点)。
173    CurvedConnector2,
174    /// 曲线连接器(3 段,1 个调整点)。
175    CurvedConnector3,
176    /// 曲线连接器(4 段,2 个调整点)。
177    CurvedConnector4,
178    /// 曲线连接器(5 段,3 个调整点)。
179    CurvedConnector5,
180    /// 任意未识别形状的兜底(实际写出 `prst="rect"`)。
181    Other,
182}
183
184impl PresetGeometry {
185    /// 转 OOXML 字面量。
186    pub fn as_str(self) -> &'static str {
187        use PresetGeometry::*;
188        match self {
189            Rectangle => "rect",
190            RoundRectangle => "roundRect",
191            Ellipse => "ellipse",
192            Line => "line",
193            RightTriangle => "rtTriangle",
194            Diamond => "diamond",
195            Parallelogram => "parallelogram",
196            Trapezoid => "trapezoid",
197            Hexagon => "hexagon",
198            Octagon => "octagon",
199            Star5 => "star5",
200            Star6 => "star6",
201            Star10 => "star10",
202            Arrow => "arrow",
203            RightArrow => "rightArrow",
204            LeftArrow => "leftArrow",
205            UpArrow => "upArrow",
206            DownArrow => "downArrow",
207            BentArrow => "bentArrow",
208            CurvedRightArrow => "curvedRightArrow",
209            Callout1 => "wedgeRectCallout",
210            Callout2 => "wedgeRoundRectCallout",
211            Cloud => "cloud",
212            Heart => "heart",
213            LightningBolt => "lightningBolt",
214            Sun => "sun",
215            Moon => "moon",
216            SmileyFace => "smileyFace",
217            Donut => "donut",
218            Cube => "cube",
219            Can => "can",
220            Bevel => "bevel",
221            NoSmoking => "noSmoking",
222            BlockArc => "blockArc",
223            Plus => "mathPlus",
224            Minus => "mathMinus",
225            Plaque => "plaque",
226            Wave => "wave",
227            Pie => "pie",
228            Bevel2 => "bevel2",
229            FoldedCorner => "foldedCorner",
230            Pentagon => "homePlate",
231            Chevron => "chevron",
232            RightBracket => "rightBracket",
233            LeftBracket => "leftBracket",
234            DoubleBracket => "doubleBracket",
235            StraightConnector1 => "straightConnector1",
236            BentConnector2 => "bentConnector2",
237            BentConnector3 => "bentConnector3",
238            BentConnector4 => "bentConnector4",
239            BentConnector5 => "bentConnector5",
240            CurvedConnector2 => "curvedConnector2",
241            CurvedConnector3 => "curvedConnector3",
242            CurvedConnector4 => "curvedConnector4",
243            CurvedConnector5 => "curvedConnector5",
244            Other => "rect",
245        }
246    }
247}
248
249impl FromStr for PresetGeometry {
250    type Err = ();
251    fn from_str(s: &str) -> Result<Self, Self::Err> {
252        use PresetGeometry::*;
253        Ok(match s {
254            "rect" => Rectangle,
255            "roundRect" => RoundRectangle,
256            "ellipse" => Ellipse,
257            "line" => Line,
258            "rtTriangle" => RightTriangle,
259            "diamond" => Diamond,
260            "parallelogram" => Parallelogram,
261            "trapezoid" => Trapezoid,
262            "hexagon" => Hexagon,
263            "octagon" => Octagon,
264            "star5" => Star5,
265            "star6" => Star6,
266            "star10" => Star10,
267            "arrow" => Arrow,
268            "rightArrow" => RightArrow,
269            "leftArrow" => LeftArrow,
270            "upArrow" => UpArrow,
271            "downArrow" => DownArrow,
272            "bentArrow" => BentArrow,
273            "curvedRightArrow" => CurvedRightArrow,
274            "wedgeRectCallout" => Callout1,
275            "wedgeRoundRectCallout" => Callout2,
276            "cloud" => Cloud,
277            "heart" => Heart,
278            "lightningBolt" => LightningBolt,
279            "sun" => Sun,
280            "moon" => Moon,
281            "smileyFace" => SmileyFace,
282            "donut" => Donut,
283            "cube" => Cube,
284            "can" => Can,
285            "bevel" => Bevel,
286            "noSmoking" => NoSmoking,
287            "blockArc" => BlockArc,
288            "mathPlus" => Plus,
289            "mathMinus" => Minus,
290            "plaque" => Plaque,
291            "wave" => Wave,
292            "pie" => Pie,
293            "bevel2" => Bevel2,
294            "foldedCorner" => FoldedCorner,
295            "homePlate" => Pentagon,
296            "chevron" => Chevron,
297            "rightBracket" => RightBracket,
298            "leftBracket" => LeftBracket,
299            "doubleBracket" => DoubleBracket,
300            "straightConnector1" => StraightConnector1,
301            "bentConnector2" => BentConnector2,
302            "bentConnector3" => BentConnector3,
303            "bentConnector4" => BentConnector4,
304            "bentConnector5" => BentConnector5,
305            "curvedConnector2" => CurvedConnector2,
306            "curvedConnector3" => CurvedConnector3,
307            "curvedConnector4" => CurvedConnector4,
308            "curvedConnector5" => CurvedConnector5,
309            _ => Other,
310        })
311    }
312}
313
314/// 文本下划线样式(`a:rPr u="..."`)。
315#[derive(Copy, Clone, Debug, Eq, PartialEq)]
316pub enum Underline {
317    /// 无下划线。
318    None,
319    /// 单线下划线。
320    Single,
321    /// 双线下划线。
322    Double,
323    /// 加粗下划线。
324    Heavy,
325    /// 点状下划线。
326    Dotted,
327    /// 虚线下划线。
328    Dashed,
329    /// 波浪下划线。
330    Wavy,
331}
332
333impl Underline {
334    /// 转 OOXML 字面量。
335    pub fn as_str(self) -> &'static str {
336        match self {
337            Underline::None => "none",
338            Underline::Single => "sng",
339            Underline::Double => "dbl",
340            Underline::Heavy => "heavy",
341            Underline::Dotted => "dotted",
342            Underline::Dashed => "dash",
343            Underline::Wavy => "wavy",
344        }
345    }
346}
347
348impl FromStr for Underline {
349    type Err = ();
350    fn from_str(s: &str) -> Result<Self, Self::Err> {
351        Ok(match s {
352            "none" => Underline::None,
353            "sng" => Underline::Single,
354            "dbl" => Underline::Double,
355            "heavy" => Underline::Heavy,
356            "dotted" => Underline::Dotted,
357            "dash" => Underline::Dashed,
358            "wavy" => Underline::Wavy,
359            _ => return Err(()),
360        })
361    }
362}
363
364/// 端帽样式(`a:ln cap="..."`)。
365#[derive(Copy, Clone, Debug, Eq, PartialEq)]
366pub enum Cap {
367    /// 平头(`flat`)。
368    Flat,
369    /// 小头(`small`)。
370    Small,
371    /// 大头(`all`)。
372    All,
373}
374
375impl Cap {
376    /// 转 OOXML 字面量。
377    pub fn as_str(self) -> &'static str {
378        match self {
379            Cap::Flat => "flat",
380            Cap::Small => "small",
381            Cap::All => "all",
382        }
383    }
384}
385
386/// 段落属性方向(竖排/横排,`a:bodyPr vert="..."`)。
387#[derive(Copy, Clone, Debug, Eq, PartialEq)]
388pub enum TextDirection {
389    /// 水平。
390    Horizontal,
391    /// 竖排。
392    Vertical,
393    /// 东亚竖排(旋转 270°)。
394    VerticalEastAsia,
395}
396
397impl TextDirection {
398    /// 转 OOXML 字面量。
399    pub fn as_str(self) -> &'static str {
400        match self {
401            TextDirection::Horizontal => "horz",
402            TextDirection::Vertical => "vert",
403            TextDirection::VerticalEastAsia => "vert270",
404        }
405    }
406}
407
408/// 文本框自动调整策略(`MSO_AUTO_SIZE`)。
409///
410/// 对标 python-pptx 中 `pptx.enum.text.MSO_AUTO_SIZE`。
411/// 序列化时分别落到 `<a:bodyPr>` 内的 `<a:normAutofit>` / `<a:spAutoFit>` 子元素。
412#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
413pub enum MsoAutoSize {
414    /// 不自动调整(不写 autofit 子元素)。
415    #[default]
416    None,
417    /// 文字溢出时调整形状(`spAutoFit`)。
418    ShapeToFitText,
419    /// 文字溢出时缩小字号(`normAutofit`)。
420    TextToFitShape,
421}
422
423impl MsoAutoSize {
424    /// 转 OOXML 字面量(`a:bodyPr` 直接属性或子元素标签)。
425    ///
426    /// - `None` → 无输出;
427    /// - `ShapeToFitText` → `<a:spAutoFit/>`;
428    /// - `TextToFitShape` → `<a:normAutofit/>`。
429    pub fn tag_name(self) -> Option<&'static str> {
430        match self {
431            MsoAutoSize::None => None,
432            MsoAutoSize::ShapeToFitText => Some("a:spAutoFit"),
433            MsoAutoSize::TextToFitShape => Some("a:normAutofit"),
434        }
435    }
436}
437
438/// 文本框垂直对齐(`MSO_ANCHOR`)。
439///
440/// 对标 python-pptx 中 `pptx.enum.text.MSO_ANCHOR`。
441/// 序列化时落到 `<a:bodyPr anchor="...">`。
442#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
443pub enum MsoAnchor {
444    /// 顶端对齐。
445    #[default]
446    Top,
447    /// 居中对齐。
448    Middle,
449    /// 底端对齐。
450    Bottom,
451}
452
453impl MsoAnchor {
454    /// 转 OOXML 字面量。
455    pub fn as_str(self) -> &'static str {
456        match self {
457            MsoAnchor::Top => "t",
458            MsoAnchor::Middle => "ctr",
459            MsoAnchor::Bottom => "b",
460        }
461    }
462}
463
464impl FromStr for MsoAnchor {
465    type Err = ();
466    fn from_str(s: &str) -> Result<Self, Self::Err> {
467        Ok(match s {
468            "t" => MsoAnchor::Top,
469            "ctr" => MsoAnchor::Middle,
470            "b" => MsoAnchor::Bottom,
471            _ => return Err(()),
472        })
473    }
474}
475
476/// 连接器类型(`MSO_CONNECTOR_TYPE`)。
477///
478/// 对标 python-pptx 中 `pptx.enum.shapes.MSO_CONNECTOR_TYPE`。
479/// 序列化时落到 `<a:prstGeom prst="...">` 的 `prst` 属性。
480#[derive(Copy, Clone, Debug, Eq, PartialEq)]
481pub enum MsoConnectorType {
482    /// 直线(`straightConnector1`)。
483    Straight,
484    /// 单折线(`bentConnector2`)。
485    Elbow,
486    /// 曲线(`curvedConnector2`)。
487    Curve,
488    /// 弯曲连接器 3 段(`bentConnector3`,带 1 个调整点)。
489    BentConnector3,
490    /// 弯曲连接器 4 段(`bentConnector4`,带 2 个调整点)。
491    BentConnector4,
492    /// 弯曲连接器 5 段(`bentConnector5`,带 3 个调整点)。
493    BentConnector5,
494    /// 曲线连接器 3 段(`curvedConnector3`)。
495    CurvedConnector3,
496    /// 曲线连接器 4 段(`curvedConnector4`)。
497    CurvedConnector4,
498    /// 曲线连接器 5 段(`curvedConnector5`)。
499    CurvedConnector5,
500}
501
502impl MsoConnectorType {
503    /// 转 OOXML 字面量。
504    pub fn as_str(self) -> &'static str {
505        match self {
506            MsoConnectorType::Straight => "straightConnector1",
507            MsoConnectorType::Elbow => "bentConnector2",
508            MsoConnectorType::Curve => "curvedConnector2",
509            MsoConnectorType::BentConnector3 => "bentConnector3",
510            MsoConnectorType::BentConnector4 => "bentConnector4",
511            MsoConnectorType::BentConnector5 => "bentConnector5",
512            MsoConnectorType::CurvedConnector3 => "curvedConnector3",
513            MsoConnectorType::CurvedConnector4 => "curvedConnector4",
514            MsoConnectorType::CurvedConnector5 => "curvedConnector5",
515        }
516    }
517}
518
519impl FromStr for MsoConnectorType {
520    type Err = ();
521    fn from_str(s: &str) -> Result<Self, Self::Err> {
522        Ok(match s {
523            "straightConnector1" => MsoConnectorType::Straight,
524            "bentConnector2" => MsoConnectorType::Elbow,
525            "curvedConnector2" => MsoConnectorType::Curve,
526            "bentConnector3" => MsoConnectorType::BentConnector3,
527            "bentConnector4" => MsoConnectorType::BentConnector4,
528            "bentConnector5" => MsoConnectorType::BentConnector5,
529            "curvedConnector3" => MsoConnectorType::CurvedConnector3,
530            "curvedConnector4" => MsoConnectorType::CurvedConnector4,
531            "curvedConnector5" => MsoConnectorType::CurvedConnector5,
532            _ => return Err(()),
533        })
534    }
535}
536
537/// 形状类型(`MSO_SHAPE_TYPE`)。
538///
539/// 对标 python-pptx 中 `pptx.enum.shapes.MSO_SHAPE_TYPE`。
540/// 本枚举用于 `Shape::shape_type` 的返回值。
541#[derive(Copy, Clone, Debug, Eq, PartialEq)]
542pub enum MsoShapeType {
543    /// 自由形状(`AUTO_SHAPE` / `FREE_FORM`)。
544    AutoShape,
545    /// 文本框。
546    TextBox,
547    /// 占位符。
548    Placeholder,
549    /// 图片。
550    Picture,
551    /// 组合。
552    Group,
553    /// 连接器。
554    Connector,
555    /// 表格(GraphicFrame)。
556    GraphicFrame,
557}
558
559impl MsoShapeType {
560    /// 转小写字符串(与 [`crate::shape::ShapeKind::shape_type`] 对齐)。
561    pub fn as_str(self) -> &'static str {
562        match self {
563            MsoShapeType::AutoShape => "auto_shape",
564            MsoShapeType::TextBox => "text_box",
565            MsoShapeType::Placeholder => "placeholder",
566            MsoShapeType::Picture => "picture",
567            MsoShapeType::Group => "group",
568            MsoShapeType::Connector => "connector",
569            MsoShapeType::GraphicFrame => "table",
570        }
571    }
572}
573
574/// 自动换行模式(`<a:bodyPr wrap="...">`)。
575#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
576pub enum TextWrapping {
577    /// 不自动换行(`wrap="none"`)。
578    None,
579    /// 方形换行(`wrap="square"`)——OOXML 默认行为。
580    #[default]
581    Square,
582}
583
584impl TextWrapping {
585    /// 转 OOXML 字面量。
586    pub fn as_str(self) -> &'static str {
587        match self {
588            TextWrapping::None => "none",
589            TextWrapping::Square => "square",
590        }
591    }
592}
593
594/// 制表位对齐类型(`<a:tab algn="...">`)。
595///
596/// 对标 python-pptx `pptx.enum.text.PP_ALIGN` 在制表位场景下的子集。
597/// OOXML 中 `<a:tab>` 元素的 `algn` 属性取值:`l` / `ctr` / `r` / `dec`。
598#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
599pub enum TabAlignment {
600    /// 左对齐制表位(`algn="l"`)——默认。
601    #[default]
602    Left,
603    /// 居中对齐制表位(`algn="ctr"`)。
604    Center,
605    /// 右对齐制表位(`algn="r"`)。
606    Right,
607    /// 小数点对齐制表位(`algn="dec"`),常用于数字列表。
608    Decimal,
609}
610
611impl TabAlignment {
612    /// 转 OOXML 字面量。
613    pub fn as_str(self) -> &'static str {
614        match self {
615            TabAlignment::Left => "l",
616            TabAlignment::Center => "ctr",
617            TabAlignment::Right => "r",
618            TabAlignment::Decimal => "dec",
619        }
620    }
621}
622
623impl FromStr for TabAlignment {
624    type Err = ();
625    fn from_str(s: &str) -> Result<Self, Self::Err> {
626        Ok(match s {
627            "l" => TabAlignment::Left,
628            "ctr" => TabAlignment::Center,
629            "r" => TabAlignment::Right,
630            "dec" => TabAlignment::Decimal,
631            _ => return Err(()),
632        })
633    }
634}
635
636// ====================================================================
637// 以下枚举对标 python-pptx `pptx.enum.*`,是 0.1.x 末段补全:
638//   - MsoFillType / MsoThemeColorIndex / MsoColorType / MsoLineDashStyle
639//   - PpAlign / PpPlaceholderType / MsoShapeType 完整版
640// 主要为"python-pptx 风格 API"提供枚举来源;序列化时仍走 [`as_str`]
641// 返回的字面量。
642// ====================================================================
643
644/// 填充类型(`MSO_FILL_TYPE`)。
645///
646/// 对标 python-pptx 中 `pptx.enum.dml.MSO_FILL_TYPE`。
647/// 主要用于 `FillFormat.type` 的只读视图。
648#[derive(Copy, Clone, Debug, Eq, PartialEq)]
649pub enum MsoFillType {
650    /// 背景自动。
651    Background,
652    /// 渐变。
653    Gradient,
654    /// 图案。
655    Pattern,
656    /// 图片。
657    Picture,
658    /// 实色。
659    Solid,
660    /// 继承。
661    Inherit,
662    /// 自动(由 Office 决定)。
663    Mixed,
664}
665
666impl MsoFillType {
667    /// 转 OOXML 字面量(仅用于调试 / 诊断)。
668    pub fn as_str(self) -> &'static str {
669        match self {
670            MsoFillType::Background => "background",
671            MsoFillType::Gradient => "gradient",
672            MsoFillType::Pattern => "pattern",
673            MsoFillType::Picture => "picture",
674            MsoFillType::Solid => "solid",
675            MsoFillType::Inherit => "inherit",
676            MsoFillType::Mixed => "mixed",
677        }
678    }
679}
680
681/// 主题色索引(`MSO_THEME_COLOR_INDEX`)。
682///
683/// 对标 python-pptx 中 `pptx.enum.dml.MSO_THEME_COLOR_INDEX`。
684/// 与 [`SchemeColor`] 区别:本枚举是"运行时颜色索引",`SchemeColor`
685/// 是"OOXML 字面量"——两者一一对应。
686#[derive(Copy, Clone, Debug, Eq, PartialEq)]
687pub enum MsoThemeColorIndex {
688    /// 背景 1(Not scheme color)。
689    NotThemeColor,
690    /// 背景 1。
691    Background1,
692    /// 背景 2。
693    Background2,
694    /// 文本 1。
695    Text1,
696    /// 文本 2。
697    Text2,
698    /// 强调色 1。
699    Accent1,
700    /// 强调色 2。
701    Accent2,
702    /// 强调色 3。
703    Accent3,
704    /// 强调色 4。
705    Accent4,
706    /// 强调色 5。
707    Accent5,
708    /// 强调色 6。
709    Accent6,
710    /// 超链接。
711    Hyperlink,
712    /// 已访问超链接。
713    FollowedHyperlink,
714    /// 浅色 1。
715    Light1,
716    /// 浅色 2。
717    Light2,
718    /// 深色 1。
719    Dark1,
720    /// 深色 2。
721    Dark2,
722    /// 混合。
723    Mixed,
724}
725
726impl MsoThemeColorIndex {
727    /// 转 OOXML 字面量(与 [`SchemeColor::as_str`] 一致)。
728    pub fn as_str(self) -> Option<&'static str> {
729        match self {
730            MsoThemeColorIndex::NotThemeColor => None,
731            MsoThemeColorIndex::Background1 => Some("bg1"),
732            MsoThemeColorIndex::Background2 => Some("bg2"),
733            MsoThemeColorIndex::Text1 => Some("tx1"),
734            MsoThemeColorIndex::Text2 => Some("tx2"),
735            MsoThemeColorIndex::Accent1 => Some("accent1"),
736            MsoThemeColorIndex::Accent2 => Some("accent2"),
737            MsoThemeColorIndex::Accent3 => Some("accent3"),
738            MsoThemeColorIndex::Accent4 => Some("accent4"),
739            MsoThemeColorIndex::Accent5 => Some("accent5"),
740            MsoThemeColorIndex::Accent6 => Some("accent6"),
741            MsoThemeColorIndex::Hyperlink => Some("hlink"),
742            MsoThemeColorIndex::FollowedHyperlink => Some("folHlink"),
743            MsoThemeColorIndex::Light1 => Some("lt1"),
744            MsoThemeColorIndex::Light2 => Some("lt2"),
745            MsoThemeColorIndex::Dark1 => Some("dk1"),
746            MsoThemeColorIndex::Dark2 => Some("dk2"),
747            MsoThemeColorIndex::Mixed => None,
748        }
749    }
750
751    /// 从 [`SchemeColor`] 映射回来(与 `as_str` 互逆)。
752    pub fn from_scheme(c: SchemeColor) -> Self {
753        match c {
754            SchemeColor::Background1 => MsoThemeColorIndex::Background1,
755            SchemeColor::Background2 => MsoThemeColorIndex::Background2,
756            SchemeColor::Text1 => MsoThemeColorIndex::Text1,
757            SchemeColor::Text2 => MsoThemeColorIndex::Text2,
758            SchemeColor::Accent1 => MsoThemeColorIndex::Accent1,
759            SchemeColor::Accent2 => MsoThemeColorIndex::Accent2,
760            SchemeColor::Accent3 => MsoThemeColorIndex::Accent3,
761            SchemeColor::Accent4 => MsoThemeColorIndex::Accent4,
762            SchemeColor::Accent5 => MsoThemeColorIndex::Accent5,
763            SchemeColor::Accent6 => MsoThemeColorIndex::Accent6,
764            SchemeColor::Hlink => MsoThemeColorIndex::Hyperlink,
765            SchemeColor::FolHlink => MsoThemeColorIndex::FollowedHyperlink,
766            SchemeColor::Lt1 => MsoThemeColorIndex::Light1,
767            SchemeColor::Lt2 => MsoThemeColorIndex::Light2,
768            SchemeColor::Dk1 => MsoThemeColorIndex::Dark1,
769            SchemeColor::Dk2 => MsoThemeColorIndex::Dark2,
770        }
771    }
772}
773
774/// 颜色类型(`MSO_COLOR_TYPE`)。
775///
776/// 对标 python-pptx 中 `pptx.enum.dml.MSO_COLOR_TYPE`。
777#[derive(Copy, Clone, Debug, Eq, PartialEq)]
778pub enum MsoColorType {
779    /// 系统窗口主题色。
780    SystemColor,
781    /// sRGB 颜色。
782    Rgb,
783    /// 预设颜色。
784    PresetColor,
785    /// 主题色(schemeClr)。
786    SchemeColor,
787    /// 混合。
788    Mixed,
789}
790
791impl MsoColorType {
792    /// 转字符串。
793    pub fn as_str(self) -> &'static str {
794        match self {
795            MsoColorType::SystemColor => "system",
796            MsoColorType::Rgb => "rgb",
797            MsoColorType::PresetColor => "preset",
798            MsoColorType::SchemeColor => "scheme",
799            MsoColorType::Mixed => "mixed",
800        }
801    }
802}
803
804/// 段落水平对齐(`PP_ALIGN`)——python-pptx 风格别名。
805///
806/// 对标 python-pptx 中 `pptx.enum.text.PP_ALIGN`。本枚举与 [`Alignment`]
807/// 1:1 对应,但**仅**作为面向用户的"python-pptx 同名"别名。
808pub type PpAlign = Alignment;
809
810/// 占位符类型(`PP_PLACEHOLDER`)。
811///
812/// 对标 python-pptx 中 `pptx.enum.shapes.PP_PLACEHOLDER`。
813/// 注意本枚举只覆盖**常见**的占位符类型;其它取值统一收为 [`PpPlaceholderType::Other`]。
814#[derive(Copy, Clone, Debug, Eq, PartialEq)]
815pub enum PpPlaceholderType {
816    /// 标题。
817    Title,
818    /// 正文。
819    Body,
820    /// 中心文本。
821    CenterTitle,
822    /// 子标题。
823    Subtitle,
824    /// 日期。
825    Date,
826    /// 幻灯片编号。
827    SlideNumber,
828    /// 页脚。
829    Footer,
830    /// 页眉。
831    Header,
832    /// 对象(表格/图/...)。
833    Object,
834    /// 图表。
835    Chart,
836    /// 表格。
837    Table,
838    /// 剪贴画。
839    ClipArt,
840    /// 组织结构图。
841    OrgChart,
842    /// 媒体剪辑。
843    MediaClip,
844    /// 图片占位符(`pic`,TODO-007)。
845    ///
846    /// 对应 PowerPoint "图片占位符"——版式中的 `<p:ph type="pic"/>`,
847    /// 用户点击后弹出"插入图片"对话框。
848    Picture,
849    /// 其它(任意未识别的 `type` 字符串)。
850    Other,
851}
852
853impl PpPlaceholderType {
854    /// 转 OOXML `<p:ph type="...">` 字面量。
855    pub fn as_str(self) -> &'static str {
856        match self {
857            PpPlaceholderType::Title => "title",
858            PpPlaceholderType::Body => "body",
859            PpPlaceholderType::CenterTitle => "ctrTitle",
860            PpPlaceholderType::Subtitle => "subTitle",
861            PpPlaceholderType::Date => "dt",
862            PpPlaceholderType::SlideNumber => "sldNum",
863            PpPlaceholderType::Footer => "ftr",
864            PpPlaceholderType::Header => "hdr",
865            PpPlaceholderType::Object => "obj",
866            PpPlaceholderType::Chart => "chart",
867            PpPlaceholderType::Table => "tbl",
868            PpPlaceholderType::ClipArt => "clipArt",
869            PpPlaceholderType::OrgChart => "orgChart",
870            PpPlaceholderType::MediaClip => "media",
871            PpPlaceholderType::Picture => "pic",
872            // Other 回落到 body——这是 OOXML 规范的默认 placeholder 类型,
873            // 也是 PowerPoint 在用户新增占位符时实际写入的值。
874            PpPlaceholderType::Other => "body",
875        }
876    }
877
878    /// 从 OOXML `<p:ph type="...">` 字面量解析(TODO-007)。
879    ///
880    /// 与 [`as_str`](Self::as_str) 互逆;未识别的字符串回落到 [`Other`](Self::Other)。
881    ///
882    /// 注:方法名为 `parse` 而非 `from_str`,以避免与 `std::str::FromStr` trait 冲突。
883    pub fn parse(s: &str) -> Self {
884        match s {
885            "title" => PpPlaceholderType::Title,
886            "body" => PpPlaceholderType::Body,
887            "ctrTitle" => PpPlaceholderType::CenterTitle,
888            "subTitle" => PpPlaceholderType::Subtitle,
889            "dt" => PpPlaceholderType::Date,
890            "sldNum" => PpPlaceholderType::SlideNumber,
891            "ftr" => PpPlaceholderType::Footer,
892            "hdr" => PpPlaceholderType::Header,
893            "obj" => PpPlaceholderType::Object,
894            "chart" => PpPlaceholderType::Chart,
895            "tbl" => PpPlaceholderType::Table,
896            "clipArt" => PpPlaceholderType::ClipArt,
897            "orgChart" => PpPlaceholderType::OrgChart,
898            "media" => PpPlaceholderType::MediaClip,
899            "pic" => PpPlaceholderType::Picture,
900            _ => PpPlaceholderType::Other,
901        }
902    }
903}
904
905/// 线型虚实(`MSO_LINE_DASH_STYLE`)。
906///
907/// 对标 python-pptx 中 `pptx.enum.dml.MSO_LINE_DASH_STYLE`。
908/// 与 [`Dash`] 关系:`Dash` 是"已实现序列化"的精简子集,
909/// `MsoLineDashStyle` 是"完整 ECMA-376"枚举——两者一一对应。
910#[derive(Copy, Clone, Debug, Eq, PartialEq)]
911pub enum MsoLineDashStyle {
912    Solid,
913    Dash,
914    DashDot,
915    DashDotDot,
916    Dot,
917    LongDash,
918    LongDashDot,
919    LongDashDotDot,
920    RoundDot,
921    SysDash,
922    SysDashDot,
923    SysDashDotDot,
924    SysDot,
925    Mixed,
926}
927
928impl MsoLineDashStyle {
929    /// 转 OOXML `<a:prstDash val="...">` 字面量。
930    pub fn as_str(self) -> &'static str {
931        match self {
932            MsoLineDashStyle::Solid => "solid",
933            MsoLineDashStyle::Dash => "dash",
934            MsoLineDashStyle::DashDot => "dashDot",
935            MsoLineDashStyle::DashDotDot => "dashDotDot",
936            MsoLineDashStyle::Dot => "dot",
937            MsoLineDashStyle::LongDash => "lgDash",
938            MsoLineDashStyle::LongDashDot => "lgDashDot",
939            MsoLineDashStyle::LongDashDotDot => "lgDashDotDot",
940            MsoLineDashStyle::RoundDot => "sysDot",
941            MsoLineDashStyle::SysDash => "sysDash",
942            MsoLineDashStyle::SysDashDot => "sysDashDot",
943            MsoLineDashStyle::SysDashDotDot => "sysDashDotDot",
944            MsoLineDashStyle::SysDot => "sysDot",
945            MsoLineDashStyle::Mixed => "solid",
946        }
947    }
948}
949
950impl From<MsoLineDashStyle> for Dash {
951    fn from(s: MsoLineDashStyle) -> Self {
952        match s {
953            MsoLineDashStyle::Solid => Dash::Solid,
954            MsoLineDashStyle::Dash => Dash::Dash,
955            MsoLineDashStyle::DashDot => Dash::DashDot,
956            MsoLineDashStyle::DashDotDot => Dash::LgDashDotDot,
957            MsoLineDashStyle::Dot => Dash::Dot,
958            MsoLineDashStyle::LongDash => Dash::LgDash,
959            MsoLineDashStyle::LongDashDot => Dash::LgDashDot,
960            MsoLineDashStyle::LongDashDotDot => Dash::LgDashDotDot,
961            MsoLineDashStyle::RoundDot => Dash::SysDot,
962            MsoLineDashStyle::SysDash => Dash::SysDash,
963            MsoLineDashStyle::SysDashDot => Dash::SysDashDot,
964            MsoLineDashStyle::SysDashDotDot => Dash::SysDashDotDot,
965            MsoLineDashStyle::SysDot => Dash::SysDot,
966            MsoLineDashStyle::Mixed => Dash::Solid,
967        }
968    }
969}