Skip to main content

easyofd_reader/
delta_tool.rs

1//! DeltaX 和 DeltaY 工具。
2//!
3//! 对应 Java: org.ofdrw.reader.DeltaTool
4//!
5//! 解析 OFD 文本对象中的 DeltaX/DeltaY 偏移数组,将压缩格式展开为
6//! 逐字符的偏移值列表。
7
8/// 获取 Delta 偏移数据。
9///
10/// 对应 Java: `DeltaTool.getDelta(ST_Array, int)`
11///
12/// OFD 的 DeltaX/DeltaY 数组支持压缩格式:当连续偏移值相同时,
13/// 用 `"g"` 标记后跟重复次数和值(如 `["g", "5", "10.0"]` 表示
14/// 5 次偏移值 10.0)。本函数将压缩格式展开为完整的偏移列表。
15///
16/// # 参数
17///
18/// - `delta`: 偏移数组的字符串表示(OFD ST_Array 格式)。
19/// - `content_length`: 文本内容长度,用于补齐不足的偏移值。
20#[must_use]
21pub fn get_delta(delta: Option<&[String]>, content_length: usize) -> Vec<f64> {
22    let mut list = Vec::new();
23
24    if let Some(array) = delta {
25        let mut i = 0;
26        while i < array.len() {
27            if array[i] == "g" && i + 2 < array.len() {
28                // 压缩格式: "g" count value
29                let count: usize = array[i + 1].parse().unwrap_or(0);
30                let value: f64 = array[i + 2].parse().unwrap_or(0.0);
31                for _ in 0..count {
32                    list.push(value);
33                }
34                i += 3;
35            } else {
36                let value: f64 = array[i].parse().unwrap_or(0.0);
37                list.push(value);
38                i += 1;
39            }
40        }
41    }
42
43    // 如果偏移值不足,用最后一个值补齐
44    let delta_size = list.len();
45    if delta_size < content_length && delta_size > 0 {
46        let last_delta = list[delta_size - 1];
47        for _ in 0..(content_length - delta_size) {
48            list.push(last_delta);
49        }
50    }
51
52    list
53}
54
55/// 从 ST_Array 字符串格式解析偏移值(直接传入原始字符串)。
56///
57/// 辅助函数,将 OFD XML 中常见的逗号或空格分隔的数字字符串解析为 f64 列表。
58#[must_use]
59pub fn parse_delta_string(s: &str, content_length: usize) -> Vec<f64> {
60    let parts: Vec<String> = s
61        .split(|c: char| c == ',' || c.is_whitespace())
62        .map(|s| s.trim().to_string())
63        .filter(|s| !s.is_empty())
64        .collect();
65    get_delta(Some(&parts), content_length)
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn test_get_delta_none() {
74        let result = get_delta(None, 5);
75        assert!(result.is_empty());
76    }
77
78    #[test]
79    fn test_get_delta_simple() {
80        let delta = vec!["10.0".into(), "12.0".into(), "11.5".into()];
81        let result = get_delta(Some(&delta), 3);
82        assert_eq!(result.len(), 3);
83        assert!((result[0] - 10.0).abs() < f64::EPSILON);
84        assert!((result[1] - 12.0).abs() < f64::EPSILON);
85        assert!((result[2] - 11.5).abs() < f64::EPSILON);
86    }
87
88    #[test]
89    fn test_get_delta_compressed() {
90        // "g" 3 10.0 表示 3 次偏移值 10.0
91        let delta = vec!["g".into(), "3".into(), "10.0".into()];
92        let result = get_delta(Some(&delta), 3);
93        assert_eq!(result.len(), 3);
94        for v in &result {
95            assert!((v - 10.0).abs() < f64::EPSILON);
96        }
97    }
98
99    #[test]
100    fn test_get_delta_pad_last() {
101        let delta = vec!["5.0".into(), "6.0".into()];
102        let result = get_delta(Some(&delta), 4);
103        assert_eq!(result.len(), 4);
104        assert!((result[0] - 5.0).abs() < f64::EPSILON);
105        assert!((result[1] - 6.0).abs() < f64::EPSILON);
106        assert!((result[2] - 6.0).abs() < f64::EPSILON);
107        assert!((result[3] - 6.0).abs() < f64::EPSILON);
108    }
109
110    #[test]
111    fn test_get_delta_empty_array() {
112        let delta: Vec<String> = vec![];
113        let result = get_delta(Some(&delta), 3);
114        assert!(result.is_empty());
115    }
116
117    #[test]
118    fn test_parse_delta_string() {
119        let result = parse_delta_string("1.0, 2.0, 3.0", 3);
120        assert_eq!(result.len(), 3);
121        assert!((result[0] - 1.0).abs() < f64::EPSILON);
122    }
123
124    #[test]
125    fn test_parse_delta_string_space_separated() {
126        let result = parse_delta_string("1.5 2.5 3.5", 3);
127        assert_eq!(result.len(), 3);
128        assert!((result[1] - 2.5).abs() < f64::EPSILON);
129    }
130}