1use serde::{Deserialize, Serialize};
12
13use crate::model::graphics::{CtColor, CtVectorG};
14use crate::types::{StArray, StId, StLoc};
15
16#[derive(Debug, Clone, Default, Deserialize, Serialize)]
23pub struct Res {
24 #[serde(rename = "@BaseLoc")]
28 pub base_loc: StLoc,
29 #[serde(rename = "$value", default)]
31 pub children: Vec<ResChild>,
32}
33
34#[derive(Debug, Clone, Deserialize, Serialize)]
36pub enum ResChild {
37 ColorSpaces(ColorSpaces),
39 DrawParams(DrawParams),
41 Fonts(Fonts),
43 MultiMedias(MultiMedias),
45 CompositeGraphicUnits(CompositeGraphicUnits),
47}
48
49impl Res {
50 pub fn color_spaces(&self) -> impl Iterator<Item = &CtColorSpace> {
52 self.children
53 .iter()
54 .filter_map(|c| match c {
55 ResChild::ColorSpaces(g) => Some(g),
56 _ => None,
57 })
58 .flat_map(|g| g.color_spaces.iter())
59 }
60
61 pub fn draw_params(&self) -> impl Iterator<Item = &CtDrawParam> {
63 self.children
64 .iter()
65 .filter_map(|c| match c {
66 ResChild::DrawParams(g) => Some(g),
67 _ => None,
68 })
69 .flat_map(|g| g.draw_params.iter())
70 }
71
72 pub fn fonts(&self) -> impl Iterator<Item = &CtFont> {
74 self.children
75 .iter()
76 .filter_map(|c| match c {
77 ResChild::Fonts(g) => Some(g),
78 _ => None,
79 })
80 .flat_map(|g| g.fonts.iter())
81 }
82
83 pub fn multi_medias(&self) -> impl Iterator<Item = &CtMultiMedia> {
85 self.children
86 .iter()
87 .filter_map(|c| match c {
88 ResChild::MultiMedias(g) => Some(g),
89 _ => None,
90 })
91 .flat_map(|g| g.multi_medias.iter())
92 }
93
94 pub fn composite_graphic_units(&self) -> impl Iterator<Item = &CtVectorG> {
96 self.children
97 .iter()
98 .filter_map(|c| match c {
99 ResChild::CompositeGraphicUnits(g) => Some(g),
100 _ => None,
101 })
102 .flat_map(|g| g.units.iter())
103 }
104}
105
106#[derive(Debug, Clone, Default, Deserialize, Serialize)]
108pub struct CompositeGraphicUnits {
109 #[serde(rename = "CompositeGraphicUnit", default)]
111 pub units: Vec<CtVectorG>,
112}
113
114#[derive(Debug, Clone, Default, Deserialize, Serialize)]
116pub struct ColorSpaces {
117 #[serde(rename = "ColorSpace", default)]
119 pub color_spaces: Vec<CtColorSpace>,
120}
121
122#[derive(Debug, Clone, Default, Deserialize, Serialize)]
124pub struct CtColorSpace {
125 #[serde(rename = "@ID")]
127 pub id: StId,
128 #[serde(rename = "@Type")]
130 pub cs_type: String,
131 #[serde(rename = "@BitsPerComponent")]
133 pub bits_per_component: Option<u32>,
134 #[serde(rename = "@Profile")]
136 pub profile: Option<StLoc>,
137}
138
139#[derive(Debug, Clone, Default, Deserialize, Serialize)]
141pub struct DrawParams {
142 #[serde(rename = "DrawParam", default)]
144 pub draw_params: Vec<CtDrawParam>,
145}
146
147#[derive(Debug, Clone, Default, Deserialize, Serialize)]
152pub struct CtDrawParam {
153 #[serde(rename = "@ID")]
155 pub id: StId,
156 #[serde(rename = "@Relative")]
158 pub relative: Option<StId>,
159 #[serde(rename = "@LineWidth")]
161 pub line_width: Option<f64>,
162 #[serde(rename = "@Join")]
164 pub join: Option<String>,
165 #[serde(rename = "@Cap")]
167 pub cap: Option<String>,
168 #[serde(rename = "@MiterLimit")]
170 pub miter_limit: Option<f64>,
171 #[serde(rename = "@DashOffset")]
173 pub dash_offset: Option<f64>,
174 #[serde(rename = "@DashPattern")]
176 pub dash_pattern: Option<StArray<f64>>,
177 #[serde(rename = "FillColor")]
179 pub fill_color: Option<CtColor>,
180 #[serde(rename = "StrokeColor")]
182 pub stroke_color: Option<CtColor>,
183}
184
185#[derive(Debug, Clone, Default, Deserialize, Serialize)]
187pub struct Fonts {
188 #[serde(rename = "Font", default)]
190 pub fonts: Vec<CtFont>,
191}
192
193#[derive(Debug, Clone, Default, Deserialize, Serialize)]
195pub struct CtFont {
196 #[serde(rename = "@ID")]
198 pub id: StId,
199 #[serde(rename = "@FontName")]
201 pub font_name: String,
202 #[serde(rename = "@FamilyName")]
204 pub family_name: Option<String>,
205 #[serde(rename = "@Charset")]
207 pub charset: Option<String>,
208 #[serde(rename = "@Italic")]
210 pub italic: Option<bool>,
211 #[serde(rename = "@Bold")]
213 pub bold: Option<bool>,
214 #[serde(rename = "@Serif")]
216 pub serif: Option<bool>,
217 #[serde(rename = "@FixedWidth")]
219 pub fixed_width: Option<bool>,
220 #[serde(rename = "FontFile")]
222 pub font_file: Option<StLoc>,
223}
224
225#[derive(Debug, Clone, Default, Deserialize, Serialize)]
227pub struct MultiMedias {
228 #[serde(rename = "MultiMedia", default)]
230 pub multi_medias: Vec<CtMultiMedia>,
231}
232
233#[derive(Debug, Clone, Default, Deserialize, Serialize)]
235pub struct CtMultiMedia {
236 #[serde(rename = "@ID")]
238 pub id: StId,
239 #[serde(rename = "@Type")]
241 pub media_type: String,
242 #[serde(rename = "@Format")]
244 pub format: Option<String>,
245 #[serde(rename = "MediaFile")]
247 pub media_file: StLoc,
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 fn full_res() -> Res {
257 Res {
258 base_loc: StLoc::from("Res"),
259 children: vec![
260 ResChild::ColorSpaces(ColorSpaces {
261 color_spaces: vec![CtColorSpace {
262 id: StId(1),
263 cs_type: "RGB".into(),
264 ..Default::default()
265 }],
266 }),
267 ResChild::DrawParams(DrawParams {
268 draw_params: vec![CtDrawParam {
269 id: StId(2),
270 ..Default::default()
271 }],
272 }),
273 ResChild::Fonts(Fonts {
274 fonts: vec![CtFont {
275 id: StId(3),
276 font_name: "宋体".into(),
277 ..Default::default()
278 }],
279 }),
280 ResChild::MultiMedias(MultiMedias {
281 multi_medias: vec![CtMultiMedia {
282 id: StId(4),
283 media_type: "Image".into(),
284 media_file: StLoc::from("a.png"),
285 ..Default::default()
286 }],
287 }),
288 ResChild::CompositeGraphicUnits(CompositeGraphicUnits {
289 units: vec![CtVectorG::default()],
290 }),
291 ],
292 }
293 }
294
295 #[test]
296 fn iterators_flatten_each_group() {
297 let res = full_res();
298 assert_eq!(res.color_spaces().count(), 1);
299 assert_eq!(res.draw_params().count(), 1);
300 assert_eq!(res.fonts().count(), 1);
301 assert_eq!(res.multi_medias().count(), 1);
302 assert_eq!(res.composite_graphic_units().count(), 1);
303 assert_eq!(res.fonts().next().unwrap().font_name, "宋体");
305 assert_eq!(res.color_spaces().next().unwrap().cs_type, "RGB");
306 }
307
308 #[test]
309 fn iterators_skip_when_group_absent() {
310 let res = Res {
312 base_loc: StLoc::default(),
313 children: vec![ResChild::Fonts(Fonts::default())],
314 };
315 assert_eq!(res.color_spaces().count(), 0);
316 assert_eq!(res.draw_params().count(), 0);
317 assert_eq!(res.multi_medias().count(), 0);
318 assert_eq!(res.composite_graphic_units().count(), 0);
319 }
320}