Skip to main content

pptx_rs/
units.rs

1//! 单位、基本类型与颜色。
2//!
3//! 本模块集中管理 `pptx-rs` 中所有"非 OOXML 强类型但又频繁使用"的小型数据载体。
4//! 它是 `crate::lib` 中"高阶 API 层"与"OPC / OOXML 模型层"之间的语义桥梁。
5//!
6//! # PowerPoint 的几何单位
7//!
8//! OOXML 中所有几何属性(`<a:off>` / `<a:ext>` / 行高 / 列宽 / 边框 / 缩进)都是
9//! **EMU(English Metric Unit)** 整数。换算关系:
10//!
11//! | 单位      | EMU            | 备注                           |
12//! | --------- | -------------- | ------------------------------ |
13//! | 1 inch    | 914 400 EMU    | 1 in = 914 400                |
14//! | 1 cm      | 360 000 EMU    | 1 cm = 360 000                |
15//! | 1 pt      | 12 700 EMU     | 1 pt = 12 700                 |
16//! | 1 sp      | 6 350 EMU      | 1 pt = 20 sp(OpenType 子像素)|
17//!
18//! 库内统一以 `i64` 形式的 EMU 作内部表示,理由:
19//!
20//! 1. **无浮点漂移**:在缩放 / 旋转 / 多次叠加时仍能保持整型精度。
21//! 2. **互操作零成本**:与 python-pptx / Open XML SDK / LibreOffice 完全一致。
22//! 3. **可读性**:用户阅读 `Emu(914400)` 与 `Inches(1.0)` 时是等价的。
23//!
24//! # 类型分层
25//!
26//! - [`Emu`]:绝对内部单位,整数 NewType。
27//! - [`Pt`] / [`Inches`] / [`Cm`]:外部友好单位,浮点 NewType。
28//! - [`EmuPoint`]:2D 点(x, y),以 EMU 整数存储,主要给连接器/手绘 freeform 用。
29//! - [`EmuExt`]:扩展 trait,统一从 `f64` / `f32` / `i32` / `i64` / `Pt` / `Inches` / `Cm` 转换到 `Emu`。
30//! - [`RGBColor`]:sRGB 颜色三字节 0-255,对应 OOXML `<a:srgbClr val="RRGGBB"/>`。
31//!
32//! # 设计哲学
33//!
34//! - **不暴露 `f64` 形参**:高阶 API 的"位置 / 大小"参数应接受 NewType 或显式单位
35//!   转换。例如 `add_textbox(Inches(1.0), Inches(1.0), Inches(8.0), Inches(1.0))`,
36//!   让"单位混用"在编译期就被拒绝。
37//! - **浮点语义**:`f64` 在 `EmuExt` 中默认解释为"英寸"——这是最常用的隐含单位;
38//!   若需其它语义请显式包装 `Pt(...)` / `Cm(...)`。
39//! - **颜色不归此模块管**:主题色(schemeClr)、系统色(sysClr)、preset 颜色
40//!   见 [`crate::oxml::color`]。
41//!
42//! # 与 python-pptx 的对应
43//!
44//! - `pptx.util.Pt` / `pptx.util.Inches` / `pptx.util.Cm` / `pptx.util.Emu` ←→ 同名 NewType。
45//! - `pptx.util.Length` 的 `.x` / `.y` 抽象 → 本库通过 [`EmuPoint`] 暴露。
46//! - `pptx.dml.color.RGBColor` ←→ [`RGBColor`](构造时 `(r, g, b)` 元组也支持)。
47//!
48//! # 示例
49//!
50//! ```
51//! use pptx_rs::{Emu, Inches, Pt, Cm, EmuExt, RGBColor};
52//!
53//! // 隐式单位转换
54//! let w: Emu = Inches(8.5).emu();
55//! assert_eq!(w.value(), (8.5 * 914_400.0) as i64);
56//!
57//! // 跨单位混算
58//! let h = Pt(72.0).emu() + Inches(1.0).emu();
59//!
60//! // 颜色
61//! let red: RGBColor = (255, 0, 0).into();
62//! assert_eq!(red, RGBColor::RED);
63//! ```
64//!
65//! # 实现说明
66//!
67//! 本模块自 v0.5.0 起改为 re-export [`ooxml_core::units`] 的内容。
68//! 类型与 API 完全保持不变,仅是把实现下沉到 ooxml-core 公共基座,
69//! 便于未来 xlsx-rs / docx-rs 复用同一套单位系统。
70
71pub use ooxml_core::units::{Cm, Emu, EmuExt, EmuPoint, Inches, Pt, RGBColor};
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    /// 主要单位换算 round-trip 测试。
78    #[test]
79    fn unit_conversion() {
80        assert_eq!(Pt(72.0).emu().value(), 72 * 12_700);
81        assert_eq!(Inches(1.0).emu().value(), 914_400);
82        assert_eq!(Cm(1.0).emu().value(), 360_000);
83        assert!((Emu(914_400).inches() - 1.0).abs() < 1e-9);
84        assert!((Emu(12_700).pt() - 1.0).abs() < 1e-9);
85    }
86
87    /// RGBColor 基本转换测试。
88    #[test]
89    fn rgb() {
90        let c: RGBColor = (10, 20, 30).into();
91        assert_eq!(c, RGBColor(10, 20, 30));
92    }
93}