1use quick_xml::events::{BytesStart, BytesText, Event};
8use quick_xml::name::QName;
9use quick_xml::{Reader, Writer};
10
11#[derive(Debug, Default, Clone, PartialEq)]
17pub struct VmlDrawing {
18 pub xmlns_v: String,
19 pub xmlns_o: String,
20 pub xmlns_x: String,
21 pub xmlns_mv: Option<String>,
22 pub shape_layout: Option<VmlShapeLayout>,
23 pub shape_type: Option<VmlShapeType>,
24 pub shape: Vec<VmlShape>,
25}
26
27#[derive(Debug, Default, Clone, PartialEq)]
29pub struct VmlShapeLayout {
30 pub ext: String,
31 pub idmap: Option<VmlIdmap>,
32}
33
34#[derive(Debug, Default, Clone, PartialEq)]
36pub struct VmlIdmap {
37 pub ext: String,
38 pub data: i32,
39}
40
41#[derive(Debug, Default, Clone, PartialEq)]
43pub struct VmlShapeType {
44 pub id: String,
45 pub coord_size: String,
46 pub spt: i32,
47 pub prefer_relative: Option<String>,
48 pub path: String,
49 pub filled: Option<String>,
50 pub stroked: Option<String>,
51 pub stroke: Option<VmlStroke>,
52 pub formulas: Option<VmlFormulas>,
53 pub v_path: Option<VmlPath>,
54 pub lock: Option<VmlLock>,
55}
56
57#[derive(Debug, Default, Clone, PartialEq)]
59pub struct VmlStroke {
60 pub join_style: String,
61}
62
63#[derive(Debug, Default, Clone, PartialEq)]
65pub struct VmlPath {
66 pub extrusion_ok: Option<String>,
67 pub gradient_shape_ok: Option<String>,
68 pub connect_type: String,
69}
70
71#[derive(Debug, Default, Clone, PartialEq)]
73pub struct VmlFormulas {
74 pub formula: Vec<VmlFormula>,
75}
76
77#[derive(Debug, Default, Clone, PartialEq)]
78pub struct VmlFormula {
79 pub equation: String,
80}
81
82#[derive(Debug, Default, Clone, PartialEq)]
84pub struct VmlLock {
85 pub ext: String,
86 pub rotation: Option<String>,
87 pub aspect_ratio: Option<String>,
88}
89
90#[derive(Debug, Default, Clone, PartialEq)]
92pub struct VmlImageData {
93 pub id: Option<String>,
94 pub src: Option<String>,
95 pub rel_id: Option<String>,
96 pub title: Option<String>,
97 pub crop_top: Option<String>,
98 pub crop_left: Option<String>,
99 pub crop_bottom: Option<String>,
100 pub crop_right: Option<String>,
101 pub gain: Option<String>,
102 pub black_level: Option<String>,
103 pub gamma: Option<String>,
104 pub grayscale: Option<String>,
105 pub bilevel: Option<String>,
106}
107
108#[derive(Debug, Default, Clone, PartialEq)]
112pub struct VmlShape {
113 pub id: String,
114 pub spid: Option<String>,
115 pub shape_type: String,
116 pub style: String,
117 pub button: Option<String>,
118 pub filled: Option<String>,
119 pub fill_color: Option<String>,
120 pub inset_mode: Option<String>,
121 pub stroked: Option<String>,
122 pub stroke_color: Option<String>,
123 pub inner_xml: String,
124}
125
126impl VmlLock {
131 pub(crate) fn write_xml<W: std::io::Write>(&self, w: &mut Writer<W>) {
132 let mut e = BytesStart::new("o:lock");
133 if !self.ext.is_empty() {
134 e.push_attribute(("v:ext", self.ext.as_str()));
135 }
136 if let Some(v) = &self.rotation {
137 e.push_attribute(("rotation", v.as_str()));
138 }
139 if let Some(v) = &self.aspect_ratio {
140 e.push_attribute(("aspectratio", v.as_str()));
141 }
142 w.write_event(Event::Empty(e)).ok();
143 }
144
145 pub(crate) fn to_xml_string(&self) -> String {
146 let mut buf = Vec::new();
147 self.write_xml(&mut Writer::new(&mut buf));
148 String::from_utf8_lossy(&buf).to_string()
149 }
150}
151
152impl VmlImageData {
153 pub(crate) fn write_xml<W: std::io::Write>(&self, w: &mut Writer<W>) {
154 let mut e = BytesStart::new("v:imagedata");
155 if let Some(v) = &self.id {
156 e.push_attribute(("id", v.as_str()));
157 }
158 if let Some(v) = &self.src {
159 e.push_attribute(("src", v.as_str()));
160 }
161 if let Some(v) = &self.rel_id {
162 e.push_attribute(("o:relid", v.as_str()));
163 }
164 if let Some(v) = &self.title {
165 e.push_attribute(("o:title", v.as_str()));
166 }
167 if let Some(v) = &self.crop_top {
168 e.push_attribute(("croptop", v.as_str()));
169 }
170 if let Some(v) = &self.crop_left {
171 e.push_attribute(("cropleft", v.as_str()));
172 }
173 if let Some(v) = &self.crop_bottom {
174 e.push_attribute(("cropbottom", v.as_str()));
175 }
176 if let Some(v) = &self.crop_right {
177 e.push_attribute(("cropright", v.as_str()));
178 }
179 if let Some(v) = &self.gain {
180 e.push_attribute(("gain", v.as_str()));
181 }
182 if let Some(v) = &self.black_level {
183 e.push_attribute(("blacklevel", v.as_str()));
184 }
185 if let Some(v) = &self.gamma {
186 e.push_attribute(("gamma", v.as_str()));
187 }
188 if let Some(v) = &self.grayscale {
189 e.push_attribute(("grayscale", v.as_str()));
190 }
191 if let Some(v) = &self.bilevel {
192 e.push_attribute(("bilevel", v.as_str()));
193 }
194 w.write_event(Event::Empty(e)).ok();
195 }
196
197 pub(crate) fn to_xml_string(&self) -> String {
198 let mut buf = Vec::new();
199 self.write_xml(&mut Writer::new(&mut buf));
200 String::from_utf8_lossy(&buf).to_string()
201 }
202}
203
204impl VmlDrawing {
205 pub fn to_xml(&self) -> Vec<u8> {
207 let mut out = Vec::new();
208 out.extend_from_slice(crate::constants::XML_HEADER.as_bytes());
209 out.push(b'\n');
210 let mut w = Writer::new_with_indent(&mut out, b' ', 2);
211
212 let mut root = BytesStart::new("xml");
213 root.push_attribute(("xmlns:v", self.xmlns_v.as_str()));
214 root.push_attribute(("xmlns:o", self.xmlns_o.as_str()));
215 root.push_attribute(("xmlns:x", self.xmlns_x.as_str()));
216 if let Some(ns) = &self.xmlns_mv {
217 root.push_attribute(("xmlns:mv", ns.as_str()));
218 }
219 w.write_event(Event::Start(root)).ok();
220
221 if let Some(layout) = &self.shape_layout {
222 layout.write_xml(&mut w);
223 }
224 if let Some(st) = &self.shape_type {
225 st.write_xml(&mut w);
226 }
227 for shape in &self.shape {
228 shape.write_xml(&mut w);
229 }
230
231 w.write_event(Event::End(QName(b"xml").into())).ok();
232 out
233 }
234
235 pub fn from_xml(data: &[u8]) -> Result<Self, quick_xml::Error> {
237 let mut reader = Reader::from_reader(data);
238 reader.config_mut().trim_text(true);
239 let mut buf = Vec::new();
240 let mut drawing = VmlDrawing::default();
241 let mut depth = 0usize;
242
243 loop {
244 let event = reader.read_event_into(&mut buf)?;
245 match event {
246 Event::Start(e) => {
247 let name = e.name();
248 let local = local_name(name.as_ref());
249 if local == b"xml" && depth == 0 {
250 parse_root_attrs(&e, &mut drawing);
251 depth += 1;
252 } else if local == b"shapelayout" {
253 drawing.shape_layout = Some(parse_shape_layout(&e, &mut reader)?);
254 } else if local == b"shapetype" {
255 drawing.shape_type = Some(parse_shape_type(&e, &mut reader)?);
256 } else if local == b"shape" {
257 drawing.shape.push(parse_shape(&e, &mut reader)?);
258 } else {
259 depth += 1;
260 }
261 }
262 Event::Empty(e) => {
263 let name = e.name();
264 let local = local_name(name.as_ref());
265 if local == b"xml" && depth == 0 {
266 parse_root_attrs(&e, &mut drawing);
267 } else if local == b"shapelayout" {
268 drawing.shape_layout = Some(parse_shape_layout(&e, &mut reader)?);
269 } else if local == b"shapetype" {
270 drawing.shape_type = Some(parse_shape_type(&e, &mut reader)?);
271 } else if local == b"shape" {
272 let mut shape = parse_shape_attrs(&e);
273 shape.inner_xml = String::new();
274 drawing.shape.push(shape);
275 }
276 }
277 Event::End(e) => {
278 let name = e.name();
279 if local_name(name.as_ref()) == b"xml" {
280 depth = depth.saturating_sub(1);
281 }
282 }
283 Event::Eof => break,
284 _ => {}
285 }
286 buf.clear();
287 }
288 Ok(drawing)
289 }
290}
291
292impl VmlShapeLayout {
297 fn write_xml<W: std::io::Write>(&self, w: &mut Writer<W>) {
298 let mut start = BytesStart::new("o:shapelayout");
299 if !self.ext.is_empty() {
300 start.push_attribute(("v:ext", self.ext.as_str()));
301 }
302 w.write_event(Event::Start(start)).ok();
303 if let Some(idmap) = &self.idmap {
304 let mut e = BytesStart::new("o:idmap");
305 if !idmap.ext.is_empty() {
306 e.push_attribute(("v:ext", idmap.ext.as_str()));
307 }
308 e.push_attribute(("data", idmap.data.to_string().as_str()));
309 w.write_event(Event::Empty(e)).ok();
310 }
311 w.write_event(Event::End(QName(b"o:shapelayout").into()))
312 .ok();
313 }
314}
315
316impl VmlShapeType {
317 fn write_xml<W: std::io::Write>(&self, w: &mut Writer<W>) {
318 let mut start = BytesStart::new("v:shapetype");
319 if !self.id.is_empty() {
320 start.push_attribute(("id", self.id.as_str()));
321 }
322 if !self.coord_size.is_empty() {
323 start.push_attribute(("coordsize", self.coord_size.as_str()));
324 }
325 if self.spt != 0 {
326 start.push_attribute(("o:spt", self.spt.to_string().as_str()));
327 }
328 if let Some(v) = &self.prefer_relative {
329 start.push_attribute(("o:preferrelative", v.as_str()));
330 }
331 if !self.path.is_empty() {
332 start.push_attribute(("path", self.path.as_str()));
333 }
334 if let Some(v) = &self.filled {
335 start.push_attribute(("filled", v.as_str()));
336 }
337 if let Some(v) = &self.stroked {
338 start.push_attribute(("stroked", v.as_str()));
339 }
340 w.write_event(Event::Start(start)).ok();
341 if let Some(stroke) = &self.stroke {
342 let mut e = BytesStart::new("v:stroke");
343 e.push_attribute(("joinstyle", stroke.join_style.as_str()));
344 w.write_event(Event::Empty(e)).ok();
345 }
346 if let Some(formulas) = &self.formulas {
347 w.write_event(Event::Start(BytesStart::new("v:formulas")))
348 .ok();
349 for f in &formulas.formula {
350 let mut e = BytesStart::new("v:f");
351 e.push_attribute(("eqn", f.equation.as_str()));
352 w.write_event(Event::Empty(e)).ok();
353 }
354 w.write_event(Event::End(QName(b"v:formulas").into())).ok();
355 }
356 if let Some(path) = &self.v_path {
357 let mut e = BytesStart::new("v:path");
358 if let Some(v) = &path.extrusion_ok {
359 e.push_attribute(("o:extrusionok", v.as_str()));
360 }
361 if let Some(v) = &path.gradient_shape_ok {
362 e.push_attribute(("gradientshapeok", v.as_str()));
363 }
364 e.push_attribute(("o:connecttype", path.connect_type.as_str()));
365 w.write_event(Event::Empty(e)).ok();
366 }
367 if let Some(lock) = &self.lock {
368 lock.write_xml(w);
369 }
370 w.write_event(Event::End(QName(b"v:shapetype").into())).ok();
371 }
372}
373
374impl VmlShape {
375 fn write_xml<W: std::io::Write>(&self, w: &mut Writer<W>) {
376 let mut start = BytesStart::new("v:shape");
377 if !self.id.is_empty() {
378 start.push_attribute(("id", self.id.as_str()));
379 }
380 if !self.shape_type.is_empty() {
381 start.push_attribute(("type", self.shape_type.as_str()));
382 }
383 if !self.style.is_empty() {
384 start.push_attribute(("style", self.style.as_str()));
385 }
386 if let Some(v) = &self.spid {
387 start.push_attribute(("o:spid", v.as_str()));
388 }
389 if let Some(v) = &self.button {
390 start.push_attribute(("o:button", v.as_str()));
391 }
392 if let Some(v) = &self.filled {
393 start.push_attribute(("filled", v.as_str()));
394 }
395 if let Some(v) = &self.fill_color {
396 start.push_attribute(("fillcolor", v.as_str()));
397 }
398 if let Some(v) = &self.inset_mode {
399 start.push_attribute(("o:insetmode", v.as_str()));
400 }
401 if let Some(v) = &self.stroked {
402 start.push_attribute(("stroked", v.as_str()));
403 }
404 if let Some(v) = &self.stroke_color {
405 start.push_attribute(("strokecolor", v.as_str()));
406 }
407 w.write_event(Event::Start(start)).ok();
408 if !self.inner_xml.is_empty() {
409 w.write_event(Event::Text(BytesText::from_escaped(
410 self.inner_xml.as_str(),
411 )))
412 .ok();
413 }
414 w.write_event(Event::End(QName(b"v:shape").into())).ok();
415 }
416}
417
418fn local_name(name: &[u8]) -> &[u8] {
423 if let Some(pos) = name.iter().rposition(|&b| b == b':') {
424 &name[pos + 1..]
425 } else {
426 name
427 }
428}
429
430fn parse_root_attrs(e: &BytesStart<'_>, drawing: &mut VmlDrawing) {
431 for attr in e.attributes().flatten() {
432 let key = String::from_utf8_lossy(attr.key.as_ref());
433 let value = String::from_utf8_lossy(&attr.value).to_string();
434 if key == "xmlns:v" {
435 drawing.xmlns_v = value;
436 } else if key == "xmlns:o" {
437 drawing.xmlns_o = value;
438 } else if key == "xmlns:x" {
439 drawing.xmlns_x = value;
440 } else if key == "xmlns:mv" {
441 drawing.xmlns_mv = Some(value);
442 }
443 }
444}
445
446fn parse_shape_layout<R: std::io::BufRead>(
447 e: &BytesStart<'_>,
448 reader: &mut Reader<R>,
449) -> Result<VmlShapeLayout, quick_xml::Error> {
450 let mut layout = VmlShapeLayout::default();
451 for attr in e.attributes().flatten() {
452 let key = String::from_utf8_lossy(attr.key.as_ref());
453 if key.ends_with(":ext") || key == "ext" {
454 layout.ext = String::from_utf8_lossy(&attr.value).to_string();
455 }
456 }
457 let mut buf = Vec::new();
458 loop {
459 match reader.read_event_into(&mut buf)? {
460 Event::Start(e) if local_name(e.name().as_ref()) == b"idmap" => {
461 let mut idmap = VmlIdmap::default();
462 for attr in e.attributes().flatten() {
463 let key = String::from_utf8_lossy(attr.key.as_ref());
464 if key.ends_with(":ext") || key == "ext" {
465 idmap.ext = String::from_utf8_lossy(&attr.value).to_string();
466 } else if key == "data" {
467 idmap.data = String::from_utf8_lossy(&attr.value).parse().unwrap_or(0);
468 }
469 }
470 layout.idmap = Some(idmap);
471 }
472 Event::End(e) if local_name(e.name().as_ref()) == b"shapelayout" => break,
473 Event::Eof => break,
474 _ => {}
475 }
476 buf.clear();
477 }
478 Ok(layout)
479}
480
481fn parse_shape_type<R: std::io::BufRead>(
482 e: &BytesStart<'_>,
483 reader: &mut Reader<R>,
484) -> Result<VmlShapeType, quick_xml::Error> {
485 let mut st = VmlShapeType::default();
486 for attr in e.attributes().flatten() {
487 let key = String::from_utf8_lossy(attr.key.as_ref());
488 let value = String::from_utf8_lossy(&attr.value).to_string();
489 if key == "id" {
490 st.id = value;
491 } else if key == "coordsize" {
492 st.coord_size = value;
493 } else if key.ends_with(":spt") || key == "spt" {
494 st.spt = value.parse().unwrap_or(0);
495 } else if key.ends_with(":preferrelative") || key == "preferrelative" {
496 st.prefer_relative = Some(value);
497 } else if key == "path" {
498 st.path = value;
499 } else if key == "filled" {
500 st.filled = Some(value);
501 } else if key == "stroked" {
502 st.stroked = Some(value);
503 }
504 }
505
506 let mut buf = Vec::new();
507 loop {
508 match reader.read_event_into(&mut buf)? {
509 Event::Empty(e) => {
510 let name = e.name();
511 let local = local_name(name.as_ref());
512 parse_shape_type_child(local, &e, &mut st);
513 }
514 Event::Start(e) => {
515 let name = e.name();
516 let local = local_name(name.as_ref());
517 if local == b"formulas" {
518 st.formulas = Some(parse_formulas(reader)?);
519 } else {
520 parse_shape_type_child(local, &e, &mut st);
521 }
522 }
523 Event::End(e) => {
524 let name = e.name();
525 if local_name(name.as_ref()) == b"shapetype" {
526 break;
527 }
528 }
529 Event::Eof => break,
530 _ => {}
531 }
532 buf.clear();
533 }
534 Ok(st)
535}
536
537fn parse_shape_type_child(local: &[u8], e: &BytesStart<'_>, st: &mut VmlShapeType) {
538 if local == b"stroke" {
539 let mut stroke = VmlStroke::default();
540 for attr in e.attributes().flatten() {
541 let key = String::from_utf8_lossy(attr.key.as_ref());
542 if key == "joinstyle" {
543 stroke.join_style = String::from_utf8_lossy(&attr.value).to_string();
544 }
545 }
546 st.stroke = Some(stroke);
547 } else if local == b"path" {
548 let mut path = VmlPath {
549 connect_type: String::new(),
550 ..Default::default()
551 };
552 for attr in e.attributes().flatten() {
553 let key = String::from_utf8_lossy(attr.key.as_ref());
554 if key.ends_with(":extrusionok") || key == "extrusionok" {
555 path.extrusion_ok = Some(String::from_utf8_lossy(&attr.value).to_string());
556 } else if key == "gradientshapeok" {
557 path.gradient_shape_ok = Some(String::from_utf8_lossy(&attr.value).to_string());
558 } else if key.ends_with(":connecttype") || key == "connecttype" {
559 path.connect_type = String::from_utf8_lossy(&attr.value).to_string();
560 }
561 }
562 st.v_path = Some(path);
563 } else if local == b"lock" {
564 let mut lock = VmlLock::default();
565 for attr in e.attributes().flatten() {
566 let key = String::from_utf8_lossy(attr.key.as_ref());
567 let value = Some(String::from_utf8_lossy(&attr.value).to_string());
568 if key.ends_with(":ext") || key == "ext" {
569 lock.ext = value.unwrap_or_default();
570 } else if key == "rotation" {
571 lock.rotation = value;
572 } else if key == "aspectratio" {
573 lock.aspect_ratio = value;
574 }
575 }
576 st.lock = Some(lock);
577 }
578}
579
580fn parse_formulas<R: std::io::BufRead>(
581 reader: &mut Reader<R>,
582) -> Result<VmlFormulas, quick_xml::Error> {
583 let mut formulas = VmlFormulas::default();
584 let mut buf = Vec::new();
585 loop {
586 match reader.read_event_into(&mut buf)? {
587 Event::Empty(e) | Event::Start(e) if local_name(e.name().as_ref()) == b"f" => {
588 for attr in e.attributes().flatten() {
589 let key = String::from_utf8_lossy(attr.key.as_ref());
590 if key == "eqn" {
591 formulas.formula.push(VmlFormula {
592 equation: String::from_utf8_lossy(&attr.value).to_string(),
593 });
594 }
595 }
596 }
597 Event::End(e) if local_name(e.name().as_ref()) == b"formulas" => break,
598 Event::Eof => break,
599 _ => {}
600 }
601 buf.clear();
602 }
603 Ok(formulas)
604}
605
606fn parse_shape_attrs(e: &BytesStart<'_>) -> VmlShape {
607 let mut shape = VmlShape::default();
608 for attr in e.attributes().flatten() {
609 let key = String::from_utf8_lossy(attr.key.as_ref());
610 let value = String::from_utf8_lossy(&attr.value).to_string();
611 if key == "id" {
612 shape.id = value;
613 } else if key == "type" {
614 shape.shape_type = value;
615 } else if key == "style" {
616 shape.style = value;
617 } else if key.ends_with(":spid") || key == "spid" {
618 shape.spid = Some(value);
619 } else if key.ends_with(":button") || key == "button" {
620 shape.button = Some(value);
621 } else if key == "filled" {
622 shape.filled = Some(value);
623 } else if key == "fillcolor" {
624 shape.fill_color = Some(value);
625 } else if key.ends_with(":insetmode") || key == "insetmode" {
626 shape.inset_mode = Some(value);
627 } else if key == "stroked" {
628 shape.stroked = Some(value);
629 } else if key == "strokecolor" {
630 shape.stroke_color = Some(value);
631 }
632 }
633 shape
634}
635
636fn parse_shape<R: std::io::BufRead>(
637 e: &BytesStart<'_>,
638 reader: &mut Reader<R>,
639) -> Result<VmlShape, quick_xml::Error> {
640 let mut shape = parse_shape_attrs(e);
641 let mut inner = Vec::new();
642 let mut inner_writer = Writer::new(&mut inner);
643 let mut depth = 1usize;
644 let mut buf = Vec::new();
645
646 loop {
647 match reader.read_event_into(&mut buf)? {
648 Event::Start(ref se) => {
649 inner_writer.write_event(Event::Start(se.clone())).ok();
650 if local_name(se.name().as_ref()) == b"shape" {
651 depth += 1;
652 }
653 }
654 Event::Empty(ref see) => {
655 inner_writer.write_event(Event::Empty(see.clone())).ok();
656 }
657 Event::Text(ref te) => {
658 inner_writer.write_event(Event::Text(te.clone())).ok();
659 }
660 Event::End(ref ee) => {
661 if local_name(ee.name().as_ref()) == b"shape" {
662 depth -= 1;
663 if depth == 0 {
664 break;
665 }
666 }
667 inner_writer.write_event(Event::End(ee.clone())).ok();
668 }
669 Event::Eof => break,
670 _ => {}
671 }
672 buf.clear();
673 }
674 shape.inner_xml = String::from_utf8_lossy(&inner).trim().to_string();
675 Ok(shape)
676}