Skip to main content

office_rs/common/
types.rs

1//! 通用数据类型定义
2//!
3//! 本模块定义了Office文档处理中常用的基础数据类型,包括:
4//! - 颜色类型(RGB、主题色、索引色)
5//! - 字体类型(字体名称、大小、样式)
6//! - 尺寸类型(长度单位、边距、间距)
7//! - 对齐和样式类型
8//! - Office文档通用结构定义
9
10// 注意:std::fmt 和 std::str::FromStr 在当前实现中未使用,但保留以备将来扩展
11
12/// RGB颜色表示
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct RgbColor {
15    pub red: u8,
16    pub green: u8,
17    pub blue: u8,
18}
19
20/// RGBA颜色表示(包含透明度)
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct RgbaColor {
23    pub red: u8,
24    pub green: u8,
25    pub blue: u8,
26    pub alpha: u8, // 0 = 完全透明, 255 = 完全不透明
27}
28
29impl RgbColor {
30    /// 创建新的RGB颜色
31    pub fn new(red: u8, green: u8, blue: u8) -> Self {
32        Self { red, green, blue }
33    }
34
35    /// 从十六进制字符串创建颜色(如 "FF0000" 表示红色)
36    pub fn from_hex(hex: &str) -> Result<Self, String> {
37        let hex = hex.trim_start_matches('#');
38        if hex.len() != 6 {
39            return Err("Hex color must be 6 characters long".to_string());
40        }
41
42        let red = u8
43            ::from_str_radix(&hex[0..2], 16)
44            .map_err(|_| "Invalid red component".to_string())?;
45        let green = u8
46            ::from_str_radix(&hex[2..4], 16)
47            .map_err(|_| "Invalid green component".to_string())?;
48        let blue = u8
49            ::from_str_radix(&hex[4..6], 16)
50            .map_err(|_| "Invalid blue component".to_string())?;
51
52        Ok(Self::new(red, green, blue))
53    }
54
55    /// 转换为十六进制字符串
56    pub fn to_hex(&self) -> String {
57        format!("{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
58    }
59
60    /// 转换为RGBA颜色(不透明)
61    pub fn to_rgba(&self) -> RgbaColor {
62        RgbaColor::new(self.red, self.green, self.blue, 255)
63    }
64
65    /// 常用颜色常量
66    pub const BLACK: Self = Self {
67        red: 0,
68        green: 0,
69        blue: 0,
70    };
71    pub const WHITE: Self = Self {
72        red: 255,
73        green: 255,
74        blue: 255,
75    };
76    pub const RED: Self = Self {
77        red: 255,
78        green: 0,
79        blue: 0,
80    };
81    pub const GREEN: Self = Self {
82        red: 0,
83        green: 255,
84        blue: 0,
85    };
86    pub const BLUE: Self = Self {
87        red: 0,
88        green: 0,
89        blue: 255,
90    };
91}
92
93impl RgbaColor {
94    /// 创建新的RGBA颜色
95    pub fn new(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
96        Self {
97            red,
98            green,
99            blue,
100            alpha,
101        }
102    }
103
104    /// 从十六进制字符串创建颜色(如 "FF0000FF" 表示不透明红色)
105    pub fn from_hex(hex: &str) -> Result<Self, String> {
106        let hex = hex.trim_start_matches('#');
107        if hex.len() != 8 {
108            return Err("Hex RGBA color must be 8 characters long".to_string());
109        }
110
111        let red = u8
112            ::from_str_radix(&hex[0..2], 16)
113            .map_err(|_| "Invalid red component".to_string())?;
114        let green = u8
115            ::from_str_radix(&hex[2..4], 16)
116            .map_err(|_| "Invalid green component".to_string())?;
117        let blue = u8
118            ::from_str_radix(&hex[4..6], 16)
119            .map_err(|_| "Invalid blue component".to_string())?;
120        let alpha = u8
121            ::from_str_radix(&hex[6..8], 16)
122            .map_err(|_| "Invalid alpha component".to_string())?;
123
124        Ok(Self::new(red, green, blue, alpha))
125    }
126
127    /// 转换为十六进制字符串
128    pub fn to_hex(&self) -> String {
129        format!("{:02X}{:02X}{:02X}{:02X}", self.red, self.green, self.blue, self.alpha)
130    }
131
132    /// 获取透明度百分比(0.0-1.0)
133    pub fn alpha_percent(&self) -> f32 {
134        (self.alpha as f32) / 255.0
135    }
136
137    /// 从透明度百分比设置alpha值
138    pub fn with_alpha_percent(mut self, alpha: f32) -> Self {
139        self.alpha = (alpha.clamp(0.0, 1.0) * 255.0) as u8;
140        self
141    }
142
143    /// 转换为RGB颜色(丢弃透明度)
144    pub fn to_rgb(&self) -> RgbColor {
145        RgbColor::new(self.red, self.green, self.blue)
146    }
147
148    /// 常用RGBA颜色常量
149    pub const TRANSPARENT: Self = Self {
150        red: 0,
151        green: 0,
152        blue: 0,
153        alpha: 0,
154    };
155    pub const BLACK: Self = Self {
156        red: 0,
157        green: 0,
158        blue: 0,
159        alpha: 255,
160    };
161    pub const WHITE: Self = Self {
162        red: 255,
163        green: 255,
164        blue: 255,
165        alpha: 255,
166    };
167    pub const RED: Self = Self {
168        red: 255,
169        green: 0,
170        blue: 0,
171        alpha: 255,
172    };
173    pub const GREEN: Self = Self {
174        red: 0,
175        green: 255,
176        blue: 0,
177        alpha: 255,
178    };
179    pub const BLUE: Self = Self {
180        red: 0,
181        green: 0,
182        blue: 255,
183        alpha: 255,
184    };
185}
186
187/// 主题颜色类型
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
189pub enum ThemeColor {
190    /// 深色1(通常是黑色)
191    Dark1,
192    /// 浅色1(通常是白色)
193    Light1,
194    /// 深色2
195    Dark2,
196    /// 浅色2
197    Light2,
198    /// 强调色1-6
199    Accent1,
200    Accent2,
201    Accent3,
202    Accent4,
203    Accent5,
204    Accent6,
205    /// 超链接颜色
206    Hyperlink,
207    /// 已访问超链接颜色
208    FollowedHyperlink,
209}
210
211/// 颜色类型枚举
212#[derive(Debug, Clone, PartialEq)]
213pub enum Color {
214    /// RGB颜色
215    Rgb(RgbColor),
216    /// RGBA颜色(包含透明度)
217    Rgba(RgbaColor),
218    /// 主题颜色
219    Theme(ThemeColor),
220    /// 索引颜色(用于兼容旧格式)
221    Indexed(u8),
222    /// 自动颜色
223    Auto,
224}
225
226/// 字体大小(以磅为单位)
227#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
228pub struct FontSize(pub f32);
229
230impl FontSize {
231    /// 创建字体大小
232    pub fn new(size: f32) -> Self {
233        Self(size.max(0.0))
234    }
235
236    /// 获取大小值
237    pub fn value(&self) -> f32 {
238        self.0
239    }
240
241    /// 常用字体大小
242    pub const SMALL: Self = Self(8.0);
243    pub const NORMAL: Self = Self(11.0);
244    pub const MEDIUM: Self = Self(12.0);
245    pub const LARGE: Self = Self(14.0);
246    pub const EXTRA_LARGE: Self = Self(18.0);
247}
248
249/// 下划线样式
250#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
251pub enum UnderlineStyle {
252    None,
253    Single,
254    Double,
255    Thick,
256    Dotted,
257    DottedHeavy,
258    Dashed,
259    DashedHeavy,
260    DashLong,
261    DashLongHeavy,
262    DotDash,
263    DotDashHeavy,
264    DotDotDash,
265    DotDotDashHeavy,
266    Wave,
267    WaveHeavy,
268    WaveDouble,
269}
270
271/// 字体样式
272#[derive(Debug, Clone, PartialEq)]
273pub struct FontStyle {
274    pub bold: bool,
275    pub italic: bool,
276    pub underline: UnderlineStyle,
277    pub strikethrough: bool,
278    pub superscript: bool,
279    pub subscript: bool,
280    pub small_caps: bool,
281    pub all_caps: bool,
282}
283
284impl Default for FontStyle {
285    fn default() -> Self {
286        Self {
287            bold: false,
288            italic: false,
289            underline: UnderlineStyle::None,
290            strikethrough: false,
291            superscript: false,
292            subscript: false,
293            small_caps: false,
294            all_caps: false,
295        }
296    }
297}
298
299impl FontStyle {
300    /// 创建粗体样式
301    pub fn bold() -> Self {
302        Self {
303            bold: true,
304            italic: false,
305            underline: UnderlineStyle::None,
306            strikethrough: false,
307            superscript: false,
308            subscript: false,
309            small_caps: false,
310            all_caps: false,
311        }
312    }
313
314    /// 创建斜体样式
315    pub fn italic() -> Self {
316        Self {
317            bold: false,
318            italic: true,
319            underline: UnderlineStyle::None,
320            strikethrough: false,
321            superscript: false,
322            subscript: false,
323            small_caps: false,
324            all_caps: false,
325        }
326    }
327}
328
329/// 字体定义
330#[derive(Debug, Clone, PartialEq)]
331pub struct Font {
332    pub name: String,
333    pub size: FontSize,
334    pub style: FontStyle,
335    pub color: Color,
336}
337
338impl Default for Font {
339    fn default() -> Self {
340        Self {
341            name: "Calibri".to_string(),
342            size: FontSize::NORMAL,
343            style: FontStyle::default(),
344            color: Color::Auto,
345        }
346    }
347}
348
349/// 长度单位
350#[derive(Debug, Clone, Copy, PartialEq)]
351pub enum LengthUnit {
352    /// 磅(1/72英寸)
353    Points(f32),
354    /// 像素
355    Pixels(f32),
356    /// 英寸
357    Inches(f32),
358    /// 厘米
359    Centimeters(f32),
360    /// 毫米
361    Millimeters(f32),
362    /// EMU(English Metric Unit,1/914400英寸)
363    Emu(i32),
364}
365
366impl LengthUnit {
367    /// 转换为磅
368    pub fn to_points(&self) -> f32 {
369        match self {
370            LengthUnit::Points(p) => *p,
371            LengthUnit::Pixels(px) => *px * 0.75, // 假设96 DPI
372            LengthUnit::Inches(inch) => *inch * 72.0,
373            LengthUnit::Centimeters(cm) => *cm * 28.35,
374            LengthUnit::Millimeters(mm) => *mm * 2.835,
375            LengthUnit::Emu(emu) => (*emu as f32) / 12700.0,
376        }
377    }
378
379    /// 转换为EMU
380    pub fn to_emu(&self) -> i32 {
381        (self.to_points() * 12700.0) as i32
382    }
383}
384
385/// 边距定义
386#[derive(Debug, Clone, Copy, PartialEq)]
387pub struct Margin {
388    pub top: LengthUnit,
389    pub right: LengthUnit,
390    pub bottom: LengthUnit,
391    pub left: LengthUnit,
392}
393
394impl Margin {
395    /// 创建统一边距
396    pub fn uniform(margin: LengthUnit) -> Self {
397        Self {
398            top: margin,
399            right: margin,
400            bottom: margin,
401            left: margin,
402        }
403    }
404
405    /// 创建零边距
406    pub fn zero() -> Self {
407        Self::uniform(LengthUnit::Points(0.0))
408    }
409}
410
411/// 水平对齐方式
412#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
413pub enum HorizontalAlignment {
414    Left,
415    Center,
416    Right,
417    Justify,
418    Distributed,
419}
420
421/// 垂直对齐方式
422#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
423pub enum VerticalAlignment {
424    Top,
425    Middle,
426    Bottom,
427    Justify,
428    Distributed,
429}
430
431/// 边框样式
432#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
433pub enum BorderStyle {
434    None,
435    Thin,
436    Medium,
437    Thick,
438    Double,
439    Dotted,
440    Dashed,
441    DashDot,
442    DashDotDot,
443}
444
445/// 边框定义
446#[derive(Debug, Clone, PartialEq)]
447pub struct Border {
448    pub style: BorderStyle,
449    pub color: Color,
450    pub width: LengthUnit,
451}
452
453impl Default for Border {
454    fn default() -> Self {
455        Self {
456            style: BorderStyle::None,
457            color: Color::Auto,
458            width: LengthUnit::Points(0.0),
459        }
460    }
461}
462
463/// 边框集合
464#[derive(Debug, Clone, PartialEq)]
465pub struct Borders {
466    pub top: Border,
467    pub right: Border,
468    pub bottom: Border,
469    pub left: Border,
470}
471
472impl Default for Borders {
473    fn default() -> Self {
474        Self {
475            top: Border::default(),
476            right: Border::default(),
477            bottom: Border::default(),
478            left: Border::default(),
479        }
480    }
481}
482
483/// 填充模式
484#[derive(Debug, Clone, PartialEq)]
485pub enum FillPattern {
486    /// 无填充
487    None,
488    /// 纯色填充
489    Solid(Color),
490    /// 渐变填充
491    Gradient {
492        start_color: Color,
493        end_color: Color,
494        angle: f32, // 角度(度)
495    },
496    /// 图案填充
497    Pattern {
498        pattern_type: PatternType,
499        foreground_color: Color,
500        background_color: Color,
501    },
502}
503
504/// 图案类型
505#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
506pub enum PatternType {
507    Solid,
508    Gray75,
509    Gray50,
510    Gray25,
511    Gray125,
512    Gray0625,
513    HorizontalStripe,
514    VerticalStripe,
515    ReverseDiagonalStripe,
516    DiagonalStripe,
517    DiagonalCrosshatch,
518    ThickDiagonalCrosshatch,
519}
520
521/// 页面方向
522#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
523pub enum PageOrientation {
524    Portrait,
525    Landscape,
526}
527
528/// 页面大小
529#[derive(Debug, Clone, Copy, PartialEq)]
530pub struct PageSize {
531    pub width: LengthUnit,
532    pub height: LengthUnit,
533    pub orientation: PageOrientation,
534}
535
536impl PageSize {
537    /// A4纸张大小
538    pub fn a4() -> Self {
539        Self {
540            width: LengthUnit::Millimeters(210.0),
541            height: LengthUnit::Millimeters(297.0),
542            orientation: PageOrientation::Portrait,
543        }
544    }
545
546    /// Letter纸张大小
547    pub fn letter() -> Self {
548        Self {
549            width: LengthUnit::Inches(8.5),
550            height: LengthUnit::Inches(11.0),
551            orientation: PageOrientation::Portrait,
552        }
553    }
554}
555
556/// 文档属性
557#[derive(Debug, Clone, PartialEq)]
558pub struct DocumentProperties {
559    pub title: Option<String>,
560    pub author: Option<String>,
561    pub subject: Option<String>,
562    pub keywords: Option<String>,
563    pub description: Option<String>,
564    pub category: Option<String>,
565    pub created: Option<chrono::DateTime<chrono::Utc>>,
566    pub modified: Option<chrono::DateTime<chrono::Utc>>,
567}
568
569impl Default for DocumentProperties {
570    fn default() -> Self {
571        Self {
572            title: None,
573            author: None,
574            subject: None,
575            keywords: None,
576            description: None,
577            category: None,
578            created: None,
579            modified: None,
580        }
581    }
582}
583
584/// 坐标点
585#[derive(Debug, Clone, Copy, PartialEq)]
586pub struct Point {
587    pub x: LengthUnit,
588    pub y: LengthUnit,
589}
590
591/// 尺寸
592#[derive(Debug, Clone, Copy, PartialEq)]
593pub struct Size {
594    pub width: LengthUnit,
595    pub height: LengthUnit,
596}
597
598/// 矩形区域
599#[derive(Debug, Clone, Copy, PartialEq)]
600pub struct Rectangle {
601    pub position: Point,
602    pub size: Size,
603}
604
605/// 列表类型
606#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
607pub enum ListType {
608    /// 无序列表
609    Bullet,
610    /// 有序列表(数字)
611    Number,
612    /// 字母列表(小写)
613    LowerAlpha,
614    /// 字母列表(大写)
615    UpperAlpha,
616    /// 罗马数字(小写)
617    LowerRoman,
618    /// 罗马数字(大写)
619    UpperRoman,
620}
621
622/// 列表样式
623#[derive(Debug, Clone, PartialEq)]
624pub struct ListStyle {
625    pub list_type: ListType,
626    pub level: u8,
627    pub indent: LengthUnit,
628    pub bullet_char: Option<char>,
629    pub number_format: Option<String>,
630}
631
632impl Default for ListStyle {
633    fn default() -> Self {
634        Self {
635            list_type: ListType::Bullet,
636            level: 0,
637            indent: LengthUnit::Points(18.0),
638            bullet_char: Some('•'),
639            number_format: None,
640        }
641    }
642}
643
644/// 表格边框样式
645#[derive(Debug, Clone, PartialEq)]
646pub struct TableBorderStyle {
647    pub outer: Borders,
648    pub inner_horizontal: Border,
649    pub inner_vertical: Border,
650}
651
652impl Default for TableBorderStyle {
653    fn default() -> Self {
654        Self {
655            outer: Borders::default(),
656            inner_horizontal: Border::default(),
657            inner_vertical: Border::default(),
658        }
659    }
660}
661
662/// 表格样式
663#[derive(Debug, Clone, PartialEq)]
664pub struct TableStyle {
665    pub borders: TableBorderStyle,
666    pub cell_padding: Margin,
667    pub cell_spacing: LengthUnit,
668    pub background_color: Option<Color>,
669    pub stripe_rows: bool,
670    pub stripe_columns: bool,
671}
672
673impl Default for TableStyle {
674    fn default() -> Self {
675        Self {
676            borders: TableBorderStyle::default(),
677            cell_padding: Margin::uniform(LengthUnit::Points(2.0)),
678            cell_spacing: LengthUnit::Points(0.0),
679            background_color: None,
680            stripe_rows: false,
681            stripe_columns: false,
682        }
683    }
684}
685
686/// 文本装饰
687#[derive(Debug, Clone, PartialEq)]
688pub struct TextDecoration {
689    pub shadow: bool,
690    pub emboss: bool,
691    pub imprint: bool,
692    pub outline: bool,
693    pub glow: Option<Color>,
694    pub reflection: bool,
695}
696
697impl Default for TextDecoration {
698    fn default() -> Self {
699        Self {
700            shadow: false,
701            emboss: false,
702            imprint: false,
703            outline: false,
704            glow: None,
705            reflection: false,
706        }
707    }
708}
709
710/// 段落样式
711#[derive(Debug, Clone, PartialEq)]
712pub struct ParagraphStyle {
713    pub alignment: HorizontalAlignment,
714    pub line_spacing: LineSpacing,
715    pub space_before: LengthUnit,
716    pub space_after: LengthUnit,
717    pub first_line_indent: LengthUnit,
718    pub left_indent: LengthUnit,
719    pub right_indent: LengthUnit,
720    pub keep_together: bool,
721    pub keep_with_next: bool,
722    pub page_break_before: bool,
723}
724
725impl Default for ParagraphStyle {
726    fn default() -> Self {
727        Self {
728            alignment: HorizontalAlignment::Left,
729            line_spacing: LineSpacing::Single,
730            space_before: LengthUnit::Points(0.0),
731            space_after: LengthUnit::Points(0.0),
732            first_line_indent: LengthUnit::Points(0.0),
733            left_indent: LengthUnit::Points(0.0),
734            right_indent: LengthUnit::Points(0.0),
735            keep_together: false,
736            keep_with_next: false,
737            page_break_before: false,
738        }
739    }
740}
741
742/// 行间距类型
743#[derive(Debug, Clone, Copy, PartialEq)]
744pub enum LineSpacing {
745    /// 单倍行距
746    Single,
747    /// 1.5倍行距
748    OneAndHalf,
749    /// 双倍行距
750    Double,
751    /// 多倍行距
752    Multiple(f32),
753    /// 固定行距(磅)
754    Exact(f32),
755    /// 最小行距(磅)
756    AtLeast(f32),
757}
758
759#[cfg(test)]
760mod tests {
761    use super::*;
762
763    #[test]
764    fn test_rgb_color() {
765        let color = RgbColor::new(255, 128, 64);
766        assert_eq!(color.to_hex(), "FF8040");
767
768        let parsed = RgbColor::from_hex("FF8040").unwrap();
769        assert_eq!(parsed, color);
770
771        let parsed_with_hash = RgbColor::from_hex("#FF8040").unwrap();
772        assert_eq!(parsed_with_hash, color);
773    }
774
775    #[test]
776    fn test_font_size() {
777        let size = FontSize::new(12.0);
778        assert_eq!(size.value(), 12.0);
779
780        // 负数应该被转换为0
781        let negative_size = FontSize::new(-5.0);
782        assert_eq!(negative_size.value(), 0.0);
783    }
784
785    #[test]
786    fn test_length_unit_conversion() {
787        let points = LengthUnit::Points(72.0);
788        assert_eq!(points.to_points(), 72.0);
789
790        let inches = LengthUnit::Inches(1.0);
791        assert_eq!(inches.to_points(), 72.0);
792
793        let cm = LengthUnit::Centimeters(2.54);
794        assert!((cm.to_points() - 72.009).abs() < 0.1);
795    }
796
797    #[test]
798    fn test_margin() {
799        let uniform = Margin::uniform(LengthUnit::Points(10.0));
800        assert_eq!(uniform.top.to_points(), 10.0);
801        assert_eq!(uniform.right.to_points(), 10.0);
802        assert_eq!(uniform.bottom.to_points(), 10.0);
803        assert_eq!(uniform.left.to_points(), 10.0);
804
805        let zero = Margin::zero();
806        assert_eq!(zero.top.to_points(), 0.0);
807    }
808
809    #[test]
810    fn test_page_size() {
811        let a4 = PageSize::a4();
812        assert_eq!(a4.orientation, PageOrientation::Portrait);
813        // 210mm = 210 * 2.835 = 595.35 points (允许小的浮点误差)
814        assert!((a4.width.to_points() - 595.35).abs() < 0.1);
815
816        let letter = PageSize::letter();
817        assert_eq!(letter.width.to_points(), 612.0); // 8.5 inches in points
818    }
819
820    #[test]
821    fn test_font_style() {
822        let default_style = FontStyle::default();
823        assert!(!default_style.bold);
824        assert!(!default_style.italic);
825        assert_eq!(default_style.underline, UnderlineStyle::None);
826        assert!(!default_style.strikethrough);
827        assert!(!default_style.superscript);
828        assert!(!default_style.subscript);
829
830        let bold_style = FontStyle::bold();
831        assert!(bold_style.bold);
832        assert!(!bold_style.italic);
833
834        let italic_style = FontStyle::italic();
835        assert!(!italic_style.bold);
836        assert!(italic_style.italic);
837    }
838
839    #[test]
840    fn test_rgba_color() {
841        let rgba = RgbaColor::new(255, 0, 0, 128);
842        assert_eq!(rgba.red, 255);
843        assert_eq!(rgba.green, 0);
844        assert_eq!(rgba.blue, 0);
845        assert_eq!(rgba.alpha, 128);
846
847        // 测试透明度百分比
848        assert_eq!(rgba.alpha_percent(), 0.5019608); // 128/255
849
850        // 测试十六进制转换
851        assert_eq!(rgba.to_hex(), "FF000080");
852
853        // 测试从十六进制创建
854        let rgba_from_hex = RgbaColor::from_hex("FF000080").unwrap();
855        assert_eq!(rgba_from_hex, rgba);
856
857        // 测试RGB转换
858        let rgb = rgba.to_rgb();
859        assert_eq!(rgb.red, 255);
860        assert_eq!(rgb.green, 0);
861        assert_eq!(rgb.blue, 0);
862
863        // 测试透明度百分比设置
864        let semi_transparent = RgbaColor::RED.with_alpha_percent(0.5);
865        assert_eq!(semi_transparent.alpha, 127); // 0.5 * 255 = 127.5 -> 127
866    }
867
868    #[test]
869    fn test_color_enum() {
870        let rgb_color = Color::Rgb(RgbColor::RED);
871        let rgba_color = Color::Rgba(RgbaColor::RED);
872        let theme_color = Color::Theme(ThemeColor::Accent1);
873        let auto_color = Color::Auto;
874
875        // 确保不同类型的颜色不相等
876        assert_ne!(rgb_color, rgba_color);
877        assert_ne!(rgb_color, theme_color);
878        assert_ne!(rgb_color, auto_color);
879    }
880
881    #[test]
882    fn test_underline_style() {
883        let mut style = FontStyle::default();
884        assert_eq!(style.underline, UnderlineStyle::None);
885
886        style.underline = UnderlineStyle::Single;
887        assert_eq!(style.underline, UnderlineStyle::Single);
888
889        style.underline = UnderlineStyle::Double;
890        assert_eq!(style.underline, UnderlineStyle::Double);
891    }
892
893    #[test]
894    fn test_list_style() {
895        let default_list = ListStyle::default();
896        assert_eq!(default_list.list_type, ListType::Bullet);
897        assert_eq!(default_list.level, 0);
898        assert_eq!(default_list.bullet_char, Some('•'));
899
900        let numbered_list = ListStyle {
901            list_type: ListType::Number,
902            level: 1,
903            indent: LengthUnit::Points(36.0),
904            bullet_char: None,
905            number_format: Some("1.".to_string()),
906        };
907        assert_eq!(numbered_list.list_type, ListType::Number);
908        assert_eq!(numbered_list.level, 1);
909    }
910
911    #[test]
912    fn test_line_spacing() {
913        let single = LineSpacing::Single;
914        let double = LineSpacing::Double;
915        let multiple = LineSpacing::Multiple(1.5);
916        let exact = LineSpacing::Exact(12.0);
917
918        assert_ne!(single, double);
919
920        if let LineSpacing::Multiple(factor) = multiple {
921            assert_eq!(factor, 1.5);
922        } else {
923            panic!("Expected Multiple variant");
924        }
925
926        if let LineSpacing::Exact(points) = exact {
927            assert_eq!(points, 12.0);
928        } else {
929            panic!("Expected Exact variant");
930        }
931    }
932}