Skip to main content

pptx_rs/oxml/
color.rs

1//! 颜色:`a:srgbClr` / `a:schemeClr` / `a:prstClr` 统一表达。
2//!
3//! OOXML 中颜色出现在多处(填充、文本、边框、效果、表格单元格背景等),
4//! 但所有"颜色值"最终都落在三种元素上:
5//!
6//! - `<a:srgbClr val="RRGGBB"/>`:绝对 sRGB 颜色;
7//! - `<a:schemeClr val="..."/>`:主题色(间接引用 `theme1.xml`);
8//! - `<a:prstClr val="..."/>`:147 个预设颜色之一。
9//!
10//! 本模块用 [`Color`] 枚举统一表达这三种 + "无颜色"。
11
12use std::str::FromStr;
13
14use crate::units::RGBColor;
15
16/// 一个颜色(`a:solidFill` 内的颜色)。
17///
18/// `#[default]` 选 [`Color::None`] 是为了与"未设置"语义对齐。
19#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
20pub enum Color {
21    /// 主题颜色(引用主题/母版中的 schemeClr)。
22    Scheme(SchemeColor),
23    /// 预设颜色(`prstClr`)。
24    Preset(PresetColor),
25    /// sRGB 颜色。
26    RGB(RGBColor),
27    /// 暂未填充。
28    #[default]
29    None,
30}
31
32impl Color {
33    /// 写一段 XML(写到指定 tag,例如 `a:srgbClr`)。
34    ///
35    /// `Color::None` 不写任何字节。
36    pub fn write_xml(&self, w: &mut super::writer::XmlWriter, tag: &str) {
37        match self {
38            Color::None => {} // 不写
39            Color::Scheme(s) => {
40                w.open_with(tag, &[("val", s.as_str())]);
41                // 可选 lumMod/lumOff/shade/tint 暂略
42                w.close(tag);
43            }
44            Color::Preset(p) => {
45                w.open_with(tag, &[("val", p.as_str())]);
46                w.close(tag);
47            }
48            Color::RGB(c) => {
49                w.open(tag);
50                w.empty_with(
51                    "a:srgbClr",
52                    &[("val", &format!("{:02X}{:02X}{:02X}", c.0, c.1, c.2))],
53                );
54                w.close(tag);
55            }
56        }
57    }
58
59    /// 写为 `<a:solidFill><a:??>...</a:??></a:solidFill>`。
60    ///
61    /// `Color::None` 写为 `<a:noFill/>`(语义上"无填充")。
62    pub fn write_solid_fill(&self, w: &mut super::writer::XmlWriter) {
63        self.write_solid_fill_with_alpha(w, None);
64    }
65
66    /// 写为 `<a:solidFill><a:??>...<a:alpha val="..."/></a:??></a:solidFill>`。
67    ///
68    /// 与 [`Self::write_solid_fill`] 相同,但额外在颜色元素内写入 `<a:alpha>` 子元素。
69    ///
70    /// # 参数
71    /// - `alpha`:透明度值(0-100000),`None` 表示不写 alpha。
72    ///   - 0 = 完全不透明
73    ///   - 100000 = 完全透明
74    ///   - 30000 = 30% 不透明(70% 透明),常用于水印
75    pub fn write_solid_fill_with_alpha(
76        &self,
77        w: &mut super::writer::XmlWriter,
78        alpha: Option<i32>,
79    ) {
80        match self {
81            Color::None => {
82                w.empty("a:noFill");
83            }
84            Color::RGB(c) => {
85                w.open("a:solidFill");
86                let val_s = format!("{:02X}{:02X}{:02X}", c.0, c.1, c.2);
87                if alpha.is_some() {
88                    w.open_with("a:srgbClr", &[("val", val_s.as_str())]);
89                    if let Some(a) = alpha {
90                        w.empty_with("a:alpha", &[("val", a.to_string().as_str())]);
91                    }
92                    w.close("a:srgbClr");
93                } else {
94                    w.empty_with("a:srgbClr", &[("val", val_s.as_str())]);
95                }
96                w.close("a:solidFill");
97            }
98            Color::Scheme(s) => {
99                w.open("a:solidFill");
100                if alpha.is_some() {
101                    w.open_with("a:schemeClr", &[("val", s.as_str())]);
102                    if let Some(a) = alpha {
103                        w.empty_with("a:alpha", &[("val", a.to_string().as_str())]);
104                    }
105                    w.close("a:schemeClr");
106                } else {
107                    w.empty_with("a:schemeClr", &[("val", s.as_str())]);
108                }
109                w.close("a:solidFill");
110            }
111            Color::Preset(p) => {
112                w.open("a:solidFill");
113                if alpha.is_some() {
114                    w.open_with("a:prstClr", &[("val", p.as_str())]);
115                    if let Some(a) = alpha {
116                        w.empty_with("a:alpha", &[("val", a.to_string().as_str())]);
117                    }
118                    w.close("a:prstClr");
119                } else {
120                    w.empty_with("a:prstClr", &[("val", p.as_str())]);
121                }
122                w.close("a:solidFill");
123            }
124        }
125    }
126}
127
128/// 主题色(`schemeClr val`)。
129///
130/// 完整列表见 ECMA-376 Part 1, §20.1.2.3.22。
131#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
132pub enum SchemeColor {
133    /// 背景 1。
134    Background1,
135    /// 背景 2。
136    Background2,
137    /// 文本 1。
138    Text1,
139    /// 文本 2。
140    Text2,
141    /// 强调色 1。
142    Accent1,
143    /// 强调色 2。
144    Accent2,
145    /// 强调色 3。
146    Accent3,
147    /// 强调色 4。
148    Accent4,
149    /// 强调色 5。
150    Accent5,
151    /// 强调色 6。
152    Accent6,
153    /// 超链接。
154    Hlink,
155    /// 已访问超链接。
156    FolHlink,
157    /// 浅色 1。
158    Lt1,
159    /// 浅色 2。
160    Lt2,
161    /// 深色 1。
162    Dk1,
163    /// 深色 2。
164    Dk2,
165}
166
167impl SchemeColor {
168    /// 转 OOXML 字面量。
169    pub fn as_str(self) -> &'static str {
170        match self {
171            SchemeColor::Background1 => "bg1",
172            SchemeColor::Background2 => "bg2",
173            SchemeColor::Text1 => "tx1",
174            SchemeColor::Text2 => "tx2",
175            SchemeColor::Accent1 => "accent1",
176            SchemeColor::Accent2 => "accent2",
177            SchemeColor::Accent3 => "accent3",
178            SchemeColor::Accent4 => "accent4",
179            SchemeColor::Accent5 => "accent5",
180            SchemeColor::Accent6 => "accent6",
181            SchemeColor::Hlink => "hlink",
182            SchemeColor::FolHlink => "folHlink",
183            SchemeColor::Lt1 => "lt1",
184            SchemeColor::Lt2 => "lt2",
185            SchemeColor::Dk1 => "dk1",
186            SchemeColor::Dk2 => "dk2",
187        }
188    }
189}
190
191impl FromStr for SchemeColor {
192    type Err = ();
193    fn from_str(s: &str) -> Result<Self, Self::Err> {
194        Ok(match s {
195            "bg1" => SchemeColor::Background1,
196            "bg2" => SchemeColor::Background2,
197            "tx1" => SchemeColor::Text1,
198            "tx2" => SchemeColor::Text2,
199            "accent1" => SchemeColor::Accent1,
200            "accent2" => SchemeColor::Accent2,
201            "accent3" => SchemeColor::Accent3,
202            "accent4" => SchemeColor::Accent4,
203            "accent5" => SchemeColor::Accent5,
204            "accent6" => SchemeColor::Accent6,
205            "hlink" => SchemeColor::Hlink,
206            "folHlink" => SchemeColor::FolHlink,
207            "lt1" => SchemeColor::Lt1,
208            "lt2" => SchemeColor::Lt2,
209            "dk1" => SchemeColor::Dk1,
210            "dk2" => SchemeColor::Dk2,
211            _ => return Err(()),
212        })
213    }
214}
215
216/// 预设颜色(`prstClr val`)。
217///
218/// 完整列表见 ECMA-376 Part 1, §20.1.2.3.23。
219/// 本枚举仅暴露 ECMA-376 中规定的 147 个命名颜色;与 python-pptx
220/// 的 `MSO_THEME_COLOR` 不同,**这是颜色值而非主题色**。
221#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
222pub enum PresetColor {
223    AliceBlue,
224    AntiqueWhite,
225    Aqua,
226    Aquamarine,
227    Azure,
228    Beige,
229    Bisque,
230    Black,
231    BlanchedAlmond,
232    Blue,
233    BlueViolet,
234    Brown,
235    BurlyWood,
236    CadetBlue,
237    Chartreuse,
238    Chocolate,
239    Coral,
240    CornflowerBlue,
241    Cornsilk,
242    Crimson,
243    Cyan,
244    DarkBlue,
245    DarkCyan,
246    DarkGoldenrod,
247    DarkGray,
248    DarkGreen,
249    DarkGrey,
250    DarkKhaki,
251    DarkMagenta,
252    DarkOliveGreen,
253    DarkOrange,
254    DarkOrchid,
255    DarkRed,
256    DarkSalmon,
257    DarkSeaGreen,
258    DarkSlateBlue,
259    DarkSlateGray,
260    DarkSlateGrey,
261    DarkTurquoise,
262    DarkViolet,
263    DeepPink,
264    DeepSkyBlue,
265    DimGray,
266    DimGrey,
267    DodgerBlue,
268    Firebrick,
269    FloralWhite,
270    ForestGreen,
271    Fuchsia,
272    Gainsboro,
273    GhostWhite,
274    Gold,
275    Goldenrod,
276    Gray,
277    Green,
278    GreenYellow,
279    Grey,
280    Honeydew,
281    HotPink,
282    IndianRed,
283    Indigo,
284    Ivory,
285    Khaki,
286    Lavender,
287    LavenderBlush,
288    LawnGreen,
289    LemonChiffon,
290    LightBlue,
291    LightCoral,
292    LightCyan,
293    LightGoldenrodYellow,
294    LightGray,
295    LightGreen,
296    LightGrey,
297    LightPink,
298    LightSalmon,
299    LightSeaGreen,
300    LightSkyBlue,
301    LightSlateGray,
302    LightSlateGrey,
303    LightSteelBlue,
304    LightYellow,
305    Lime,
306    LimeGreen,
307    Linen,
308    Magenta,
309    Maroon,
310    MediumAquamarine,
311    MediumBlue,
312    MediumOrchid,
313    MediumPurple,
314    MediumSeaGreen,
315    MediumSlateBlue,
316    MediumSpringGreen,
317    MediumTurquoise,
318    MediumVioletRed,
319    MidnightBlue,
320    MintCream,
321    MistyRose,
322    Moccasin,
323    NavajoWhite,
324    Navy,
325    OldLace,
326    Olive,
327    OliveDrab,
328    Orange,
329    OrangeRed,
330    Orchid,
331    PaleGoldenrod,
332    PaleGreen,
333    PaleTurquoise,
334    PaleVioletRed,
335    PapayaWhip,
336    PeachPuff,
337    Peru,
338    Pink,
339    Plum,
340    PowderBlue,
341    Purple,
342    Red,
343    RosyBrown,
344    RoyalBlue,
345    SaddleBrown,
346    Salmon,
347    SandyBrown,
348    SeaGreen,
349    SeaShell,
350    Sienna,
351    Silver,
352    SkyBlue,
353    SlateBlue,
354    SlateGray,
355    SlateGrey,
356    Snow,
357    SpringGreen,
358    SteelBlue,
359    Tan,
360    Teal,
361    Thistle,
362    Tomato,
363    Turquoise,
364    Violet,
365    Wheat,
366    White,
367    WhiteSmoke,
368    Yellow,
369    YellowGreen,
370}
371
372impl PresetColor {
373    /// 转 OOXML 字面量(camelCase)。
374    pub fn as_str(self) -> &'static str {
375        // 用 serde-like 派生? 直接展开更稳
376        match self {
377            PresetColor::AliceBlue => "aliceBlue",
378            PresetColor::AntiqueWhite => "antiqueWhite",
379            PresetColor::Aqua => "aqua",
380            PresetColor::Aquamarine => "aquamarine",
381            PresetColor::Azure => "azure",
382            PresetColor::Beige => "beige",
383            PresetColor::Bisque => "bisque",
384            PresetColor::Black => "black",
385            PresetColor::BlanchedAlmond => "blanchedAlmond",
386            PresetColor::Blue => "blue",
387            PresetColor::BlueViolet => "blueViolet",
388            PresetColor::Brown => "brown",
389            PresetColor::BurlyWood => "burlyWood",
390            PresetColor::CadetBlue => "cadetBlue",
391            PresetColor::Chartreuse => "chartreuse",
392            PresetColor::Chocolate => "chocolate",
393            PresetColor::Coral => "coral",
394            PresetColor::CornflowerBlue => "cornflowerBlue",
395            PresetColor::Cornsilk => "cornsilk",
396            PresetColor::Crimson => "crimson",
397            PresetColor::Cyan => "cyan",
398            PresetColor::DarkBlue => "darkBlue",
399            PresetColor::DarkCyan => "darkCyan",
400            PresetColor::DarkGoldenrod => "darkGoldenrod",
401            PresetColor::DarkGray => "darkGray",
402            PresetColor::DarkGreen => "darkGreen",
403            PresetColor::DarkGrey => "darkGrey",
404            PresetColor::DarkKhaki => "darkKhaki",
405            PresetColor::DarkMagenta => "darkMagenta",
406            PresetColor::DarkOliveGreen => "darkOliveGreen",
407            PresetColor::DarkOrange => "darkOrange",
408            PresetColor::DarkOrchid => "darkOrchid",
409            PresetColor::DarkRed => "darkRed",
410            PresetColor::DarkSalmon => "darkSalmon",
411            PresetColor::DarkSeaGreen => "darkSeaGreen",
412            PresetColor::DarkSlateBlue => "darkSlateBlue",
413            PresetColor::DarkSlateGray => "darkSlateGray",
414            PresetColor::DarkSlateGrey => "darkSlateGrey",
415            PresetColor::DarkTurquoise => "darkTurquoise",
416            PresetColor::DarkViolet => "darkViolet",
417            PresetColor::DeepPink => "deepPink",
418            PresetColor::DeepSkyBlue => "deepSkyBlue",
419            PresetColor::DimGray => "dimGray",
420            PresetColor::DimGrey => "dimGrey",
421            PresetColor::DodgerBlue => "dodgerBlue",
422            PresetColor::Firebrick => "firebrick",
423            PresetColor::FloralWhite => "floralWhite",
424            PresetColor::ForestGreen => "forestGreen",
425            PresetColor::Fuchsia => "fuchsia",
426            PresetColor::Gainsboro => "gainsboro",
427            PresetColor::GhostWhite => "ghostWhite",
428            PresetColor::Gold => "gold",
429            PresetColor::Goldenrod => "goldenrod",
430            PresetColor::Gray => "gray",
431            PresetColor::Green => "green",
432            PresetColor::GreenYellow => "greenYellow",
433            PresetColor::Grey => "grey",
434            PresetColor::Honeydew => "honeydew",
435            PresetColor::HotPink => "hotPink",
436            PresetColor::IndianRed => "indianRed",
437            PresetColor::Indigo => "indigo",
438            PresetColor::Ivory => "ivory",
439            PresetColor::Khaki => "khaki",
440            PresetColor::Lavender => "lavender",
441            PresetColor::LavenderBlush => "lavenderBlush",
442            PresetColor::LawnGreen => "lawnGreen",
443            PresetColor::LemonChiffon => "lemonChiffon",
444            PresetColor::LightBlue => "lightBlue",
445            PresetColor::LightCoral => "lightCoral",
446            PresetColor::LightCyan => "lightCyan",
447            PresetColor::LightGoldenrodYellow => "lightGoldenrodYellow",
448            PresetColor::LightGray => "lightGray",
449            PresetColor::LightGreen => "lightGreen",
450            PresetColor::LightGrey => "lightGrey",
451            PresetColor::LightPink => "lightPink",
452            PresetColor::LightSalmon => "lightSalmon",
453            PresetColor::LightSeaGreen => "lightSeaGreen",
454            PresetColor::LightSkyBlue => "lightSkyBlue",
455            PresetColor::LightSlateGray => "lightSlateGray",
456            PresetColor::LightSlateGrey => "lightSlateGrey",
457            PresetColor::LightSteelBlue => "lightSteelBlue",
458            PresetColor::LightYellow => "lightYellow",
459            PresetColor::Lime => "lime",
460            PresetColor::LimeGreen => "limeGreen",
461            PresetColor::Linen => "linen",
462            PresetColor::Magenta => "magenta",
463            PresetColor::Maroon => "maroon",
464            PresetColor::MediumAquamarine => "mediumAquamarine",
465            PresetColor::MediumBlue => "mediumBlue",
466            PresetColor::MediumOrchid => "mediumOrchid",
467            PresetColor::MediumPurple => "mediumPurple",
468            PresetColor::MediumSeaGreen => "mediumSeaGreen",
469            PresetColor::MediumSlateBlue => "mediumSlateBlue",
470            PresetColor::MediumSpringGreen => "mediumSpringGreen",
471            PresetColor::MediumTurquoise => "mediumTurquoise",
472            PresetColor::MediumVioletRed => "mediumVioletRed",
473            PresetColor::MidnightBlue => "midnightBlue",
474            PresetColor::MintCream => "mintCream",
475            PresetColor::MistyRose => "mistyRose",
476            PresetColor::Moccasin => "moccasin",
477            PresetColor::NavajoWhite => "navajoWhite",
478            PresetColor::Navy => "navy",
479            PresetColor::OldLace => "oldLace",
480            PresetColor::Olive => "olive",
481            PresetColor::OliveDrab => "oliveDrab",
482            PresetColor::Orange => "orange",
483            PresetColor::OrangeRed => "orangeRed",
484            PresetColor::Orchid => "orchid",
485            PresetColor::PaleGoldenrod => "paleGoldenrod",
486            PresetColor::PaleGreen => "paleGreen",
487            PresetColor::PaleTurquoise => "paleTurquoise",
488            PresetColor::PaleVioletRed => "paleVioletRed",
489            PresetColor::PapayaWhip => "papayaWhip",
490            PresetColor::PeachPuff => "peachPuff",
491            PresetColor::Peru => "peru",
492            PresetColor::Pink => "pink",
493            PresetColor::Plum => "plum",
494            PresetColor::PowderBlue => "powderBlue",
495            PresetColor::Purple => "purple",
496            PresetColor::Red => "red",
497            PresetColor::RosyBrown => "rosyBrown",
498            PresetColor::RoyalBlue => "royalBlue",
499            PresetColor::SaddleBrown => "saddleBrown",
500            PresetColor::Salmon => "salmon",
501            PresetColor::SandyBrown => "sandyBrown",
502            PresetColor::SeaGreen => "seaGreen",
503            PresetColor::SeaShell => "seaShell",
504            PresetColor::Sienna => "sienna",
505            PresetColor::Silver => "silver",
506            PresetColor::SkyBlue => "skyBlue",
507            PresetColor::SlateBlue => "slateBlue",
508            PresetColor::SlateGray => "slateGray",
509            PresetColor::SlateGrey => "slateGrey",
510            PresetColor::Snow => "snow",
511            PresetColor::SpringGreen => "springGreen",
512            PresetColor::SteelBlue => "steelBlue",
513            PresetColor::Tan => "tan",
514            PresetColor::Teal => "teal",
515            PresetColor::Thistle => "thistle",
516            PresetColor::Tomato => "tomato",
517            PresetColor::Turquoise => "turquoise",
518            PresetColor::Violet => "violet",
519            PresetColor::Wheat => "wheat",
520            PresetColor::White => "white",
521            PresetColor::WhiteSmoke => "whiteSmoke",
522            PresetColor::Yellow => "yellow",
523            PresetColor::YellowGreen => "yellowGreen",
524        }
525    }
526}
527
528impl FromStr for PresetColor {
529    type Err = ();
530    fn from_str(s: &str) -> Result<Self, Self::Err> {
531        // 简化: 接受所有 147 种预设名; 用 lower-case 比较
532        let all = [
533            ("aliceBlue", PresetColor::AliceBlue),
534            ("antiqueWhite", PresetColor::AntiqueWhite),
535            ("aqua", PresetColor::Aqua),
536            ("aquamarine", PresetColor::Aquamarine),
537            ("azure", PresetColor::Azure),
538            ("beige", PresetColor::Beige),
539            ("bisque", PresetColor::Bisque),
540            ("black", PresetColor::Black),
541            ("blanchedAlmond", PresetColor::BlanchedAlmond),
542            ("blue", PresetColor::Blue),
543            ("blueViolet", PresetColor::BlueViolet),
544            ("brown", PresetColor::Brown),
545            ("burlyWood", PresetColor::BurlyWood),
546            ("cadetBlue", PresetColor::CadetBlue),
547            ("chartreuse", PresetColor::Chartreuse),
548            ("chocolate", PresetColor::Chocolate),
549            ("coral", PresetColor::Coral),
550            ("cornflowerBlue", PresetColor::CornflowerBlue),
551            ("cornsilk", PresetColor::Cornsilk),
552            ("crimson", PresetColor::Crimson),
553            ("cyan", PresetColor::Cyan),
554            ("darkBlue", PresetColor::DarkBlue),
555            ("darkCyan", PresetColor::DarkCyan),
556            ("darkGoldenrod", PresetColor::DarkGoldenrod),
557            ("darkGray", PresetColor::DarkGray),
558            ("darkGreen", PresetColor::DarkGreen),
559            ("darkGrey", PresetColor::DarkGrey),
560            ("darkKhaki", PresetColor::DarkKhaki),
561            ("darkMagenta", PresetColor::DarkMagenta),
562            ("darkOliveGreen", PresetColor::DarkOliveGreen),
563            ("darkOrange", PresetColor::DarkOrange),
564            ("darkOrchid", PresetColor::DarkOrchid),
565            ("darkRed", PresetColor::DarkRed),
566            ("darkSalmon", PresetColor::DarkSalmon),
567            ("darkSeaGreen", PresetColor::DarkSeaGreen),
568            ("darkSlateBlue", PresetColor::DarkSlateBlue),
569            ("darkSlateGray", PresetColor::DarkSlateGray),
570            ("darkSlateGrey", PresetColor::DarkSlateGrey),
571            ("darkTurquoise", PresetColor::DarkTurquoise),
572            ("darkViolet", PresetColor::DarkViolet),
573            ("deepPink", PresetColor::DeepPink),
574            ("deepSkyBlue", PresetColor::DeepSkyBlue),
575            ("dimGray", PresetColor::DimGray),
576            ("dimGrey", PresetColor::DimGrey),
577            ("dodgerBlue", PresetColor::DodgerBlue),
578            ("firebrick", PresetColor::Firebrick),
579            ("floralWhite", PresetColor::FloralWhite),
580            ("forestGreen", PresetColor::ForestGreen),
581            ("fuchsia", PresetColor::Fuchsia),
582            ("gainsboro", PresetColor::Gainsboro),
583            ("ghostWhite", PresetColor::GhostWhite),
584            ("gold", PresetColor::Gold),
585            ("goldenrod", PresetColor::Goldenrod),
586            ("gray", PresetColor::Gray),
587            ("green", PresetColor::Green),
588            ("greenYellow", PresetColor::GreenYellow),
589            ("grey", PresetColor::Grey),
590            ("honeydew", PresetColor::Honeydew),
591            ("hotPink", PresetColor::HotPink),
592            ("indianRed", PresetColor::IndianRed),
593            ("indigo", PresetColor::Indigo),
594            ("ivory", PresetColor::Ivory),
595            ("khaki", PresetColor::Khaki),
596            ("lavender", PresetColor::Lavender),
597            ("lavenderBlush", PresetColor::LavenderBlush),
598            ("lawnGreen", PresetColor::LawnGreen),
599            ("lemonChiffon", PresetColor::LemonChiffon),
600            ("lightBlue", PresetColor::LightBlue),
601            ("lightCoral", PresetColor::LightCoral),
602            ("lightCyan", PresetColor::LightCyan),
603            ("lightGoldenrodYellow", PresetColor::LightGoldenrodYellow),
604            ("lightGray", PresetColor::LightGray),
605            ("lightGreen", PresetColor::LightGreen),
606            ("lightGrey", PresetColor::LightGrey),
607            ("lightPink", PresetColor::LightPink),
608            ("lightSalmon", PresetColor::LightSalmon),
609            ("lightSeaGreen", PresetColor::LightSeaGreen),
610            ("lightSkyBlue", PresetColor::LightSkyBlue),
611            ("lightSlateGray", PresetColor::LightSlateGray),
612            ("lightSlateGrey", PresetColor::LightSlateGrey),
613            ("lightSteelBlue", PresetColor::LightSteelBlue),
614            ("lightYellow", PresetColor::LightYellow),
615            ("lime", PresetColor::Lime),
616            ("limeGreen", PresetColor::LimeGreen),
617            ("linen", PresetColor::Linen),
618            ("magenta", PresetColor::Magenta),
619            ("maroon", PresetColor::Maroon),
620            ("mediumAquamarine", PresetColor::MediumAquamarine),
621            ("mediumBlue", PresetColor::MediumBlue),
622            ("mediumOrchid", PresetColor::MediumOrchid),
623            ("mediumPurple", PresetColor::MediumPurple),
624            ("mediumSeaGreen", PresetColor::MediumSeaGreen),
625            ("mediumSlateBlue", PresetColor::MediumSlateBlue),
626            ("mediumSpringGreen", PresetColor::MediumSpringGreen),
627            ("mediumTurquoise", PresetColor::MediumTurquoise),
628            ("mediumVioletRed", PresetColor::MediumVioletRed),
629            ("midnightBlue", PresetColor::MidnightBlue),
630            ("mintCream", PresetColor::MintCream),
631            ("mistyRose", PresetColor::MistyRose),
632            ("moccasin", PresetColor::Moccasin),
633            ("navajoWhite", PresetColor::NavajoWhite),
634            ("navy", PresetColor::Navy),
635            ("oldLace", PresetColor::OldLace),
636            ("olive", PresetColor::Olive),
637            ("oliveDrab", PresetColor::OliveDrab),
638            ("orange", PresetColor::Orange),
639            ("orangeRed", PresetColor::OrangeRed),
640            ("orchid", PresetColor::Orchid),
641            ("paleGoldenrod", PresetColor::PaleGoldenrod),
642            ("paleGreen", PresetColor::PaleGreen),
643            ("paleTurquoise", PresetColor::PaleTurquoise),
644            ("paleVioletRed", PresetColor::PaleVioletRed),
645            ("papayaWhip", PresetColor::PapayaWhip),
646            ("peachPuff", PresetColor::PeachPuff),
647            ("peru", PresetColor::Peru),
648            ("pink", PresetColor::Pink),
649            ("plum", PresetColor::Plum),
650            ("powderBlue", PresetColor::PowderBlue),
651            ("purple", PresetColor::Purple),
652            ("red", PresetColor::Red),
653            ("rosyBrown", PresetColor::RosyBrown),
654            ("royalBlue", PresetColor::RoyalBlue),
655            ("saddleBrown", PresetColor::SaddleBrown),
656            ("salmon", PresetColor::Salmon),
657            ("sandyBrown", PresetColor::SandyBrown),
658            ("seaGreen", PresetColor::SeaGreen),
659            ("seaShell", PresetColor::SeaShell),
660            ("sienna", PresetColor::Sienna),
661            ("silver", PresetColor::Silver),
662            ("skyBlue", PresetColor::SkyBlue),
663            ("slateBlue", PresetColor::SlateBlue),
664            ("slateGray", PresetColor::SlateGray),
665            ("slateGrey", PresetColor::SlateGrey),
666            ("snow", PresetColor::Snow),
667            ("springGreen", PresetColor::SpringGreen),
668            ("steelBlue", PresetColor::SteelBlue),
669            ("tan", PresetColor::Tan),
670            ("teal", PresetColor::Teal),
671            ("thistle", PresetColor::Thistle),
672            ("tomato", PresetColor::Tomato),
673            ("turquoise", PresetColor::Turquoise),
674            ("violet", PresetColor::Violet),
675            ("wheat", PresetColor::Wheat),
676            ("white", PresetColor::White),
677            ("whiteSmoke", PresetColor::WhiteSmoke),
678            ("yellow", PresetColor::Yellow),
679            ("yellowGreen", PresetColor::YellowGreen),
680        ];
681        for (k, v) in &all {
682            if *k == s {
683                return Ok(*v);
684            }
685        }
686        Err(())
687    }
688}
689
690// ====================================================================
691// 高阶颜色 API:对标 python-pptx 中 `pptx.dml.color.ColorFormat`。
692// ====================================================================
693
694/// 颜色高阶视图(`pptx.dml.color.ColorFormat`)。
695///
696/// 与 [`Color`] 区别:
697///
698/// - [`Color`] 是"OOXML 模型层"表达,**只关心值**;
699/// - [`ColorFormat`] 是"高阶 API 层"包装,**关心"颜色如何被使用"**——
700///   例如字体前景色、字体背景高亮、形状填充、形状边框等,引用同一个
701///   [`Color`] 时通过 `&mut Color` 共享。
702///
703/// # 与 python-pptx 的对应
704///
705/// - `pptx.dml.color.ColorFormat` ←→ [`ColorFormat`];
706/// - `font.color.rgb = RGBColor(...)` ←→ `color_format.set_rgb(...)`。
707///
708/// # 设计要点
709///
710/// - **借用 + 透明代理**:构造时传入 `&mut Color`,**所有写操作直接修改
711///   原值**;这与 python-pptx 的 `font.color.rgb = X` 行为一致——
712///   `font.color` 是个 proxy,实际写回的是底层 `a:srgbClr` 元素。
713/// - **零分配**:除读取时的临时 `String` 外不触发堆分配。
714/// - **类型安全**:所有 setter 接受 [`Color`] / `RGBColor` / [`SchemeColor`]
715///   / [`PresetColor`] 任一;`From<impl Into<Color>>` 自动转换。
716#[derive(Debug)]
717pub struct ColorFormat<'a> {
718    /// 借用底层颜色(OXML 模型层)。所有写都走这个引用。
719    color: &'a mut Color,
720    /// 父上下文是"什么角色"——决定 type / 行为(仅供 hint / 调试)。
721    role: ColorRole,
722}
723
724/// 颜色在文档中的角色(仅 hint,不参与序列化)。
725///
726/// python-pptx 中用 `color.type` 反映"是前景 / 背景 / 文本"等角色;
727/// 本枚举用于 [`ColorFormat::role`] 的返回值。
728#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
729pub enum ColorRole {
730    /// 前景 / 文本。
731    #[default]
732    Foreground,
733    /// 背景。
734    Background,
735    /// 主题色(schemeClr)。
736    Theme,
737    /// 未知 / 不关心。
738    Unknown,
739}
740
741impl<'a> ColorFormat<'a> {
742    /// 构造一个颜色格式视图。
743    ///
744    /// # 参数
745    /// - `color`:底层颜色的可变引用;
746    /// - `role`:角色(默认 [`ColorRole::Foreground`])。
747    pub fn new(color: &'a mut Color) -> Self {
748        ColorFormat {
749            color,
750            role: ColorRole::Foreground,
751        }
752    }
753    /// 构造时指定角色。
754    pub fn with_role(color: &'a mut Color, role: ColorRole) -> Self {
755        ColorFormat { color, role }
756    }
757
758    /// 角色(python-pptx `ColorFormat.type` 的简化版)。
759    pub fn role(&self) -> ColorRole {
760        self.role
761    }
762
763    /// 当前颜色的"类型"(python-pptx `color.type`)。
764    pub fn color_type(&self) -> super::simpletypes::MsoColorType {
765        match self.color {
766            Color::None => super::simpletypes::MsoColorType::Mixed,
767            Color::Scheme(_) => super::simpletypes::MsoColorType::SchemeColor,
768            Color::Preset(_) => super::simpletypes::MsoColorType::PresetColor,
769            Color::RGB(_) => super::simpletypes::MsoColorType::Rgb,
770        }
771    }
772
773    /// 是否已设置颜色(不等于 [`Color::None`])。
774    pub fn is_set(&self) -> bool {
775        !matches!(self.color, Color::None)
776    }
777
778    // --------- 读 ---------
779
780    /// 取内部颜色克隆。
781    pub fn color(&self) -> Color {
782        self.color.clone()
783    }
784    /// 若当前是 RGB,取出 sRGB;否则返回 `None`。
785    pub fn rgb(&self) -> Option<RGBColor> {
786        match &*self.color {
787            Color::RGB(c) => Some(*c),
788            _ => None,
789        }
790    }
791    /// 若当前是 theme color,取出 scheme 枚举;否则 `None`。
792    pub fn theme_color(&self) -> Option<super::simpletypes::MsoThemeColorIndex> {
793        match &*self.color {
794            Color::Scheme(s) => Some(super::simpletypes::MsoThemeColorIndex::from_scheme(*s)),
795            _ => None,
796        }
797    }
798
799    /// 亮度调整(python-pptx 风格)。
800    ///
801    /// 返回当前颜色按 `lumMod` / `lumOff` 调整后的"近似"亮度。
802    /// **注意**:本方法只计算并返回 f32 (0.0..=1.0),**不**修改底层
803    /// `Color`。如需在 XML 中表达亮度调整,请在 [`Color`] 上加修饰子元素
804    /// (路线图 0.2.0)。
805    pub fn brightness(&self) -> f32 {
806        match &*self.color {
807            Color::RGB(c) => {
808                // 用 sRGB → Y (ITU-R BT.601) 亮度公式
809                let y = 0.299 * c.0 as f32 + 0.587 * c.1 as f32 + 0.114 * c.2 as f32;
810                y / 255.0
811            }
812            Color::Scheme(s) => match s {
813                // 给主题色一个粗略亮度估计(仅供 UI 调试用,非严格计算)
814                SchemeColor::Background1 | SchemeColor::Text1 | SchemeColor::Lt1 => 1.0,
815                SchemeColor::Background2 | SchemeColor::Text2 | SchemeColor::Lt2 => 0.9,
816                SchemeColor::Dk1 | SchemeColor::Dk2 => 0.1,
817                _ => 0.5,
818            },
819            Color::Preset(p) => preset_brightness(*p),
820            Color::None => 0.0,
821        }
822    }
823
824    // --------- 写 ---------
825
826    /// 直接覆盖为任意 [`Color`]。对应 `color = some_color`。
827    pub fn set(&mut self, c: impl Into<Color>) {
828        *self.color = c.into();
829    }
830
831    /// 设为 sRGB 颜色(对应 python-pptx `color.rgb = RGBColor(r, g, b)`)。
832    pub fn set_rgb(&mut self, c: impl Into<RGBColor>) {
833        *self.color = Color::RGB(c.into());
834    }
835
836    /// 设为预设颜色。
837    pub fn set_preset(&mut self, p: PresetColor) {
838        *self.color = Color::Preset(p);
839    }
840
841    /// 设为 schemeClr 主题色。
842    pub fn set_theme(&mut self, t: super::simpletypes::MsoThemeColorIndex) {
843        if let Some(s) = t.as_str() {
844            // 用字面量反查回 SchemeColor
845            if let Ok(sc) = s.parse::<SchemeColor>() {
846                *self.color = Color::Scheme(sc);
847            }
848        }
849    }
850
851    /// 重置为未设置。
852    pub fn clear(&mut self) {
853        *self.color = Color::None;
854    }
855}
856
857/// 预设颜色亮度估算(粗略)。
858fn preset_brightness(p: PresetColor) -> f32 {
859    use PresetColor::*;
860    // 把"亮色"统一判定为亮度 1,"深色"为 0
861    match p {
862        White | WhiteSmoke | Snow | GhostWhite | Azure | Ivory | MintCream | Honeydew
863        | FloralWhite | AliceBlue | Lavender | LavenderBlush | MistyRose | SeaShell | Linen
864        | OldLace | Cornsilk | LemonChiffon | LightYellow | Beige | PapayaWhip | BlanchedAlmond
865        | Bisque | PeachPuff | NavajoWhite | Wheat | LightGoldenrodYellow | AntiqueWhite
866        | Gainsboro | LightGray | LightGrey | PowderBlue | LightCyan | LightBlue | LightPink
867        | LightCoral | LightSalmon | LightSkyBlue | LightSteelBlue | LightSeaGreen
868        | PaleGoldenrod | PaleGreen | PaleTurquoise | PaleVioletRed => 0.9,
869        Black | MidnightBlue | Navy | DarkBlue | DarkRed | DarkGreen | DarkMagenta | DarkViolet
870        | Indigo | DarkSlateBlue | DarkSlateGray | DarkSlateGrey | DarkCyan | DarkOliveGreen
871        | DarkKhaki | Maroon | Purple | DarkGoldenrod | DarkOrchid | Firebrick | SaddleBrown
872        | Sienna | Brown => 0.1,
873        _ => 0.5,
874    }
875}
876
877impl<'a> From<&'a mut Color> for ColorFormat<'a> {
878    fn from(c: &'a mut Color) -> Self {
879        ColorFormat::new(c)
880    }
881}