1use serde::{Deserialize, Serialize};
13
14use crate::types::{StArray, StBox, StId, StRefId};
15
16#[derive(Debug, Clone, Deserialize, Serialize)]
21pub enum PageBlock {
22 #[serde(rename = "TextObject")]
24 Text(TextObject),
25 #[serde(rename = "PathObject")]
27 Path(PathObject),
28 #[serde(rename = "ImageObject")]
30 Image(ImageObject),
31 #[serde(rename = "CompositeObject")]
33 Composite(CompositeObject),
34 #[serde(rename = "PageBlock")]
36 Block(PageBlockGroup),
37}
38
39#[derive(Debug, Clone, Default, Deserialize, Serialize)]
41pub struct PageBlockGroup {
42 #[serde(rename = "@ID")]
44 pub id: Option<StId>,
45 #[serde(rename = "$value", default)]
47 pub objects: Vec<PageBlock>,
48}
49
50#[derive(Debug, Clone, Default, Deserialize, Serialize)]
55pub struct CtColor {
56 #[serde(rename = "@Value")]
58 pub value: Option<StArray<f64>>,
59 #[serde(rename = "@Index")]
61 pub index: Option<i64>,
62 #[serde(rename = "@ColorSpace")]
64 pub color_space: Option<StRefId>,
65 #[serde(rename = "@Alpha")]
67 pub alpha: Option<u8>,
68}
69
70#[derive(Debug, Clone, Default, Deserialize, Serialize)]
72pub struct TextObject {
73 #[serde(rename = "@ID")]
75 pub id: Option<StId>,
76 #[serde(rename = "@Boundary", default)]
78 pub boundary: StBox,
79 #[serde(rename = "@CTM")]
81 pub ctm: Option<StArray<f64>>,
82 #[serde(rename = "@DrawParam")]
84 pub draw_param: Option<StRefId>,
85 #[serde(rename = "@Alpha")]
87 pub alpha: Option<u8>,
88 #[serde(rename = "@Font", default)]
90 pub font: StRefId,
91 #[serde(rename = "@Size", default)]
93 pub size: f64,
94 #[serde(rename = "@Stroke")]
96 pub stroke: Option<bool>,
97 #[serde(rename = "@Fill")]
99 pub fill: Option<bool>,
100 #[serde(rename = "@HScale")]
102 pub h_scale: Option<f64>,
103 #[serde(rename = "@ReadDirection")]
107 pub read_direction: Option<i32>,
108 #[serde(rename = "@CharDirection")]
111 pub char_direction: Option<i32>,
112 #[serde(rename = "@Weight")]
114 pub weight: Option<i32>,
115 #[serde(rename = "@Italic")]
117 pub italic: Option<bool>,
118 #[serde(rename = "FillColor")]
120 pub fill_color: Option<CtColor>,
121 #[serde(rename = "StrokeColor")]
123 pub stroke_color: Option<CtColor>,
124 #[serde(rename = "CGTransform", default)]
131 pub cg_transforms: Vec<CtCgTransform>,
132 #[serde(rename = "TextCode", default)]
134 pub text_codes: Vec<TextCode>,
135}
136
137#[derive(Debug, Clone, Default, Deserialize, Serialize)]
142pub struct CtCgTransform {
143 #[serde(rename = "@CodePosition", default)]
145 pub code_position: i32,
146 #[serde(rename = "@CodeCount")]
148 pub code_count: Option<i32>,
149 #[serde(rename = "@GlyphCount")]
151 pub glyph_count: Option<i32>,
152 #[serde(rename = "Glyphs")]
154 pub glyphs: Option<StArray<u32>>,
155}
156
157#[derive(Debug, Clone, Default, Deserialize, Serialize)]
159pub struct TextCode {
160 #[serde(rename = "@X")]
162 pub x: Option<f64>,
163 #[serde(rename = "@Y")]
165 pub y: Option<f64>,
166 #[serde(rename = "@DeltaX")]
168 pub delta_x: Option<String>,
169 #[serde(rename = "@DeltaY")]
171 pub delta_y: Option<String>,
172 #[serde(rename = "$text")]
174 pub text: Option<String>,
175}
176
177#[derive(Debug, Clone, Default, Deserialize, Serialize)]
179pub struct PathObject {
180 #[serde(rename = "@ID")]
182 pub id: Option<StId>,
183 #[serde(rename = "@Boundary", default)]
185 pub boundary: StBox,
186 #[serde(rename = "@CTM")]
188 pub ctm: Option<StArray<f64>>,
189 #[serde(rename = "@DrawParam")]
191 pub draw_param: Option<StRefId>,
192 #[serde(rename = "@LineWidth")]
194 pub line_width: Option<f64>,
195 #[serde(rename = "@Cap")]
197 pub cap: Option<String>,
198 #[serde(rename = "@Join")]
200 pub join: Option<String>,
201 #[serde(rename = "@MiterLimit")]
203 pub miter_limit: Option<f64>,
204 #[serde(rename = "@DashOffset")]
206 pub dash_offset: Option<f64>,
207 #[serde(rename = "@DashPattern")]
209 pub dash_pattern: Option<StArray<f64>>,
210 #[serde(rename = "@Alpha")]
212 pub alpha: Option<u8>,
213 #[serde(rename = "@Stroke")]
215 pub stroke: Option<bool>,
216 #[serde(rename = "@Fill")]
218 pub fill: Option<bool>,
219 #[serde(rename = "@Rule")]
221 pub rule: Option<String>,
222 #[serde(rename = "FillColor")]
224 pub fill_color: Option<CtColor>,
225 #[serde(rename = "StrokeColor")]
227 pub stroke_color: Option<CtColor>,
228 #[serde(rename = "AbbreviatedData")]
230 pub abbreviated_data: Option<String>,
231}
232
233#[derive(Debug, Clone, Default, Deserialize, Serialize)]
235pub struct ImageObject {
236 #[serde(rename = "@ID")]
238 pub id: Option<StId>,
239 #[serde(rename = "@Boundary", default)]
241 pub boundary: StBox,
242 #[serde(rename = "@CTM")]
244 pub ctm: Option<StArray<f64>>,
245 #[serde(rename = "@Alpha")]
247 pub alpha: Option<u8>,
248 #[serde(rename = "@ResourceID", default)]
250 pub resource_id: StRefId,
251 #[serde(rename = "@ImageMask")]
253 pub image_mask: Option<StRefId>,
254}
255
256#[derive(Debug, Clone, Default, Deserialize, Serialize)]
261pub struct CompositeObject {
262 #[serde(rename = "@ID")]
264 pub id: Option<StId>,
265 #[serde(rename = "@Boundary", default)]
267 pub boundary: StBox,
268 #[serde(rename = "@CTM")]
270 pub ctm: Option<StArray<f64>>,
271 #[serde(rename = "@ResourceID", default)]
273 pub resource_id: StRefId,
274}
275
276pub fn parse_deltas(raw: &str) -> Vec<f64> {
281 let mut out = Vec::new();
282 let mut tokens = raw.split_whitespace();
283 while let Some(tok) = tokens.next() {
284 if tok == "g" || tok == "G" {
285 let count = tokens.next().and_then(|s| s.parse::<usize>().ok());
286 let value = tokens.next().and_then(|s| s.parse::<f64>().ok());
287 if let (Some(count), Some(value)) = (count, value) {
288 out.extend(std::iter::repeat_n(value, count));
289 }
290 } else if let Ok(v) = tok.parse::<f64>() {
291 out.push(v);
292 }
293 }
294 out
295}
296
297#[derive(Debug, Clone, Default, Deserialize, Serialize)]
305pub struct CtVectorG {
306 #[serde(rename = "@ID")]
308 pub id: StId,
309 #[serde(rename = "@Width", default)]
311 pub width: f64,
312 #[serde(rename = "@Height", default)]
314 pub height: f64,
315 #[serde(rename = "@Thumbnail")]
317 pub thumbnail: Option<StRefId>,
318 #[serde(rename = "@Substitution")]
320 pub substitution: Option<StRefId>,
321 #[serde(rename = "Content")]
323 pub content: Option<PageBlockGroup>,
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 #[test]
331 fn parse_delta_runlength() {
332 assert_eq!(parse_deltas("g 3 4.5 10"), vec![4.5, 4.5, 4.5, 10.0]);
333 assert_eq!(parse_deltas("1 2 3"), vec![1.0, 2.0, 3.0]);
334 assert!(parse_deltas("").is_empty());
335 }
336}