use serde::{Deserialize, Serialize};
use crate::types::{StArray, StBox, StId, StRefId};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum PageBlock {
#[serde(rename = "TextObject")]
Text(TextObject),
#[serde(rename = "PathObject")]
Path(PathObject),
#[serde(rename = "ImageObject")]
Image(ImageObject),
#[serde(rename = "CompositeObject")]
Composite(CompositeObject),
#[serde(rename = "PageBlock")]
Block(PageBlockGroup),
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct PageBlockGroup {
#[serde(rename = "@ID")]
pub id: Option<StId>,
#[serde(rename = "$value", default)]
pub objects: Vec<PageBlock>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtColor {
#[serde(rename = "@Value")]
pub value: Option<StArray<f64>>,
#[serde(rename = "@Index")]
pub index: Option<i64>,
#[serde(rename = "@ColorSpace")]
pub color_space: Option<StRefId>,
#[serde(rename = "@Alpha")]
pub alpha: Option<u8>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct TextObject {
#[serde(rename = "@ID")]
pub id: Option<StId>,
#[serde(rename = "@Boundary", default)]
pub boundary: StBox,
#[serde(rename = "@CTM")]
pub ctm: Option<StArray<f64>>,
#[serde(rename = "@DrawParam")]
pub draw_param: Option<StRefId>,
#[serde(rename = "@Alpha")]
pub alpha: Option<u8>,
#[serde(rename = "@Font", default)]
pub font: StRefId,
#[serde(rename = "@Size", default)]
pub size: f64,
#[serde(rename = "@Stroke")]
pub stroke: Option<bool>,
#[serde(rename = "@Fill")]
pub fill: Option<bool>,
#[serde(rename = "@HScale")]
pub h_scale: Option<f64>,
#[serde(rename = "@ReadDirection")]
pub read_direction: Option<i32>,
#[serde(rename = "@CharDirection")]
pub char_direction: Option<i32>,
#[serde(rename = "@Weight")]
pub weight: Option<i32>,
#[serde(rename = "@Italic")]
pub italic: Option<bool>,
#[serde(rename = "FillColor")]
pub fill_color: Option<CtColor>,
#[serde(rename = "StrokeColor")]
pub stroke_color: Option<CtColor>,
#[serde(rename = "CGTransform", default)]
pub cg_transforms: Vec<CtCgTransform>,
#[serde(rename = "TextCode", default)]
pub text_codes: Vec<TextCode>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtCgTransform {
#[serde(rename = "@CodePosition", default)]
pub code_position: i32,
#[serde(rename = "@CodeCount")]
pub code_count: Option<i32>,
#[serde(rename = "@GlyphCount")]
pub glyph_count: Option<i32>,
#[serde(rename = "Glyphs")]
pub glyphs: Option<StArray<u32>>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct TextCode {
#[serde(rename = "@X")]
pub x: Option<f64>,
#[serde(rename = "@Y")]
pub y: Option<f64>,
#[serde(rename = "@DeltaX")]
pub delta_x: Option<String>,
#[serde(rename = "@DeltaY")]
pub delta_y: Option<String>,
#[serde(rename = "$text")]
pub text: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct PathObject {
#[serde(rename = "@ID")]
pub id: Option<StId>,
#[serde(rename = "@Boundary", default)]
pub boundary: StBox,
#[serde(rename = "@CTM")]
pub ctm: Option<StArray<f64>>,
#[serde(rename = "@DrawParam")]
pub draw_param: Option<StRefId>,
#[serde(rename = "@LineWidth")]
pub line_width: Option<f64>,
#[serde(rename = "@Cap")]
pub cap: Option<String>,
#[serde(rename = "@Join")]
pub join: Option<String>,
#[serde(rename = "@MiterLimit")]
pub miter_limit: Option<f64>,
#[serde(rename = "@DashOffset")]
pub dash_offset: Option<f64>,
#[serde(rename = "@DashPattern")]
pub dash_pattern: Option<StArray<f64>>,
#[serde(rename = "@Alpha")]
pub alpha: Option<u8>,
#[serde(rename = "@Stroke")]
pub stroke: Option<bool>,
#[serde(rename = "@Fill")]
pub fill: Option<bool>,
#[serde(rename = "@Rule")]
pub rule: Option<String>,
#[serde(rename = "FillColor")]
pub fill_color: Option<CtColor>,
#[serde(rename = "StrokeColor")]
pub stroke_color: Option<CtColor>,
#[serde(rename = "AbbreviatedData")]
pub abbreviated_data: Option<String>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct ImageObject {
#[serde(rename = "@ID")]
pub id: Option<StId>,
#[serde(rename = "@Boundary", default)]
pub boundary: StBox,
#[serde(rename = "@CTM")]
pub ctm: Option<StArray<f64>>,
#[serde(rename = "@Alpha")]
pub alpha: Option<u8>,
#[serde(rename = "@ResourceID", default)]
pub resource_id: StRefId,
#[serde(rename = "@ImageMask")]
pub image_mask: Option<StRefId>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CompositeObject {
#[serde(rename = "@ID")]
pub id: Option<StId>,
#[serde(rename = "@Boundary", default)]
pub boundary: StBox,
#[serde(rename = "@CTM")]
pub ctm: Option<StArray<f64>>,
#[serde(rename = "@ResourceID", default)]
pub resource_id: StRefId,
}
pub fn parse_deltas(raw: &str) -> Vec<f64> {
let mut out = Vec::new();
let mut tokens = raw.split_whitespace();
while let Some(tok) = tokens.next() {
if tok == "g" || tok == "G" {
let count = tokens.next().and_then(|s| s.parse::<usize>().ok());
let value = tokens.next().and_then(|s| s.parse::<f64>().ok());
if let (Some(count), Some(value)) = (count, value) {
out.extend(std::iter::repeat_n(value, count));
}
} else if let Ok(v) = tok.parse::<f64>() {
out.push(v);
}
}
out
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtVectorG {
#[serde(rename = "@ID")]
pub id: StId,
#[serde(rename = "@Width", default)]
pub width: f64,
#[serde(rename = "@Height", default)]
pub height: f64,
#[serde(rename = "@Thumbnail")]
pub thumbnail: Option<StRefId>,
#[serde(rename = "@Substitution")]
pub substitution: Option<StRefId>,
#[serde(rename = "Content")]
pub content: Option<PageBlockGroup>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_delta_runlength() {
assert_eq!(parse_deltas("g 3 4.5 10"), vec![4.5, 4.5, 4.5, 10.0]);
assert_eq!(parse_deltas("1 2 3"), vec![1.0, 2.0, 3.0]);
assert!(parse_deltas("").is_empty());
}
}