1use crate::error::{Result, VisioError};
7use crate::model::*;
8use crate::vsd::records::*;
9use crate::vsd::shapes::*;
10use std::io::Read;
11
12pub fn parse_vsd(data: &[u8]) -> Result<Document> {
14 let cursor = std::io::Cursor::new(data);
15 let mut comp = cfb::CompoundFile::open(cursor).map_err(|e| VisioError::Cfb(e.to_string()))?;
16
17 let mut stream_data = Vec::new();
18 {
19 let mut stream = comp
20 .open_stream("/VisioDocument")
21 .map_err(|e| VisioError::Cfb(format!("Cannot open VisioDocument stream: {}", e)))?;
22 stream
23 .read_to_end(&mut stream_data)
24 .map_err(VisioError::Io)?;
25 }
26
27 let mut parser = VsdParser::new(&stream_data);
28 parser.parse()?;
29 Ok(parser.into_document())
30}
31
32#[allow(dead_code)]
33struct VsdParser<'a> {
34 data: &'a [u8],
35 pages: Vec<Page>,
36 current_page: Option<Page>,
37 current_shape: Option<CurrentShape>,
38 current_geom: Option<VsdGeomSection>,
39 colors: Vec<String>, fonts: std::collections::HashMap<u32, String>,
41 names: std::collections::HashMap<u32, String>,
42}
43
44struct CurrentShape {
45 shape_id: u32,
46 shape_type: String,
47 xform: VsdXForm,
48 text_xform: Option<VsdTextXForm>,
49 xform_1d: Option<VsdXForm1D>,
50 text: String,
51 geometry: Vec<VsdGeomSection>,
52 char_formats: Vec<VsdCharFormat>,
53 para_formats: Vec<VsdParaFormat>,
54 line_weight: f64,
55 line_color: String,
56 line_pattern: i32,
57 fill_fg: String,
58 fill_bg: String,
59 fill_pattern: i32,
60 shadow_color: String,
61 shadow_pattern: i32,
62 shadow_offset_x: f64,
63 shadow_offset_y: f64,
64 foreign_data: Option<VsdForeignData>,
65 layer_member: String,
66}
67
68impl Default for CurrentShape {
69 fn default() -> Self {
70 Self {
71 shape_id: 0,
72 shape_type: "Shape".to_string(),
73 xform: VsdXForm::default(),
74 text_xform: None,
75 xform_1d: None,
76 text: String::new(),
77 geometry: Vec::new(),
78 char_formats: Vec::new(),
79 para_formats: Vec::new(),
80 line_weight: 0.01,
81 line_color: "#000000".to_string(),
82 line_pattern: 1,
83 fill_fg: String::new(),
84 fill_bg: String::new(),
85 fill_pattern: 1,
86 shadow_color: String::new(),
87 shadow_pattern: 0,
88 shadow_offset_x: 0.0,
89 shadow_offset_y: 0.0,
90 foreign_data: None,
91 layer_member: String::new(),
92 }
93 }
94}
95
96impl<'a> VsdParser<'a> {
97 fn new(data: &'a [u8]) -> Self {
98 Self {
99 data,
100 pages: Vec::new(),
101 current_page: None,
102 current_shape: None,
103 current_geom: None,
104 colors: Vec::new(),
105 fonts: std::collections::HashMap::new(),
106 names: std::collections::HashMap::new(),
107 }
108 }
109
110 fn parse(&mut self) -> Result<()> {
111 if self.data.len() < 0x36 {
112 return Err(VisioError::InvalidFile("File too small".to_string()));
113 }
114 self.parse_chunks_linear(0x36);
116 self.flush_shape();
117 Ok(())
118 }
119
120 fn into_document(self) -> Document {
121 let mut doc = Document::default();
122 doc.pages = self.pages;
123 doc
124 }
125
126 fn flush_shape(&mut self) {
127 if let Some(cs) = self.current_shape.take() {
128 let mut geom = cs.geometry;
129 if let Some(g) = self.current_geom.take() {
130 geom.push(g);
131 }
132 let shape = vsd_shape_to_model(
133 &cs.xform,
134 &cs.text,
135 &geom,
136 cs.line_weight,
137 &cs.line_color,
138 cs.line_pattern,
139 &cs.fill_fg,
140 &cs.fill_bg,
141 cs.fill_pattern,
142 cs.shape_id,
143 &cs.shape_type,
144 cs.text_xform.as_ref(),
145 cs.xform_1d.as_ref(),
146 &cs.char_formats,
147 &cs.para_formats,
148 &cs.shadow_color,
149 cs.shadow_pattern,
150 cs.shadow_offset_x,
151 cs.shadow_offset_y,
152 cs.foreign_data.as_ref(),
153 &cs.layer_member,
154 );
155 if let Some(page) = &mut self.current_page {
156 page.shapes.push(shape);
157 }
158 }
159 self.current_geom = None;
160 }
161
162 fn parse_chunks_linear(&mut self, start: usize) {
163 let mut offset = start;
164 while offset + 19 < self.data.len() {
165 while offset < self.data.len() && self.data[offset] == 0 {
167 offset += 1;
168 }
169 if offset + 19 > self.data.len() {
170 break;
171 }
172
173 let hdr = match parse_chunk_header(self.data, offset) {
174 Some((h, new_off)) => {
175 offset = new_off;
176 h
177 }
178 None => break,
179 };
180
181 let end_pos = offset + hdr.data_length as usize + hdr.trailer as usize;
182 if end_pos > self.data.len() {
183 break;
184 }
185
186 let chunk_data = &self.data[offset..offset + hdr.data_length as usize];
187 self.handle_chunk(&hdr, chunk_data);
188 offset = end_pos;
189 }
190 }
191
192 fn handle_chunk(&mut self, hdr: &ChunkHeader, data: &[u8]) {
193 match hdr.chunk_type {
194 VSD_SHAPE_GROUP | VSD_SHAPE_SHAPE | VSD_SHAPE_FOREIGN => {
195 self.flush_shape();
196 let mut cs = CurrentShape::default();
197 cs.shape_id = hdr.record_id;
198 if hdr.chunk_type == VSD_SHAPE_GROUP {
199 cs.shape_type = "Group".to_string();
200 } else if hdr.chunk_type == VSD_SHAPE_FOREIGN {
201 cs.shape_type = "Foreign".to_string();
202 }
203 self.current_shape = Some(cs);
204 }
205 VSD_PAGE_PROPS => self.read_page_props(data),
206 VSD_XFORM_DATA => self.read_xform(data),
207 VSD_TEXT_XFORM => self.read_text_xform(data),
208 VSD_XFORM_1D => self.read_xform_1d(data),
209 VSD_TEXT => self.read_text(data),
210 VSD_GEOMETRY => self.read_geometry(data),
211 VSD_MOVE_TO => self.read_geom_row("MoveTo", data),
212 VSD_LINE_TO => self.read_geom_row("LineTo", data),
213 VSD_ARC_TO => self.read_geom_row("ArcTo", data),
214 VSD_ELLIPSE => self.read_geom_row("Ellipse", data),
215 VSD_ELLIPTICAL_ARC_TO => self.read_geom_row("EllipticalArcTo", data),
216 VSD_SPLINE_START => self.read_geom_row("SplineStart", data),
217 VSD_SPLINE_KNOT => self.read_geom_row("SplineKnot", data),
218 VSD_INFINITE_LINE => self.read_geom_row("InfiniteLine", data),
219 VSD_NURBS_TO => self.read_nurbs_to(data),
220 VSD_POLYLINE_TO => self.read_polyline_to(data),
221 VSD_LINE => self.read_line_fmt(data),
222 VSD_FILL_AND_SHADOW => self.read_fill(data),
223 VSD_CHAR_IX => self.read_char_ix(data),
224 VSD_PARA_IX => self.read_para_ix(data),
225 VSD_LAYER_MEMBERSHIP => self.read_layer_membership(data),
226 VSD_FOREIGN_DATA_TYPE => self.read_foreign_data_type(data),
227 VSD_FOREIGN_DATA => self.read_foreign_data(data),
228 VSD_PAGE => {
229 self.flush_shape();
230 let page = Page::default();
231 self.current_page = Some(page);
232 }
233 VSD_PAGE_SHEET => {
234 if self.current_page.is_none() {
235 self.current_page = Some(Page::default());
236 }
237 }
238 _ => {}
239 }
240
241 if hdr.chunk_type == VSD_PAGE && self.current_page.is_some() {
243 }
245 }
246
247 fn read_page_props(&mut self, data: &[u8]) {
248 if self.current_page.is_none() {
249 self.current_page = Some(Page::default());
250 }
251 let page = self.current_page.as_mut().unwrap();
252 let mut off = 1usize;
253 if let Some(w) = read_double(data, off) {
254 page.width = w;
255 }
256 off += 9;
257 if let Some(h) = read_double(data, off) {
258 page.height = h;
259 }
260 }
261
262 fn read_xform(&mut self, data: &[u8]) {
263 let cs = match &mut self.current_shape {
264 Some(s) => s,
265 None => return,
266 };
267 let mut off = 1usize;
268 if let Some(v) = read_double(data, off) {
269 cs.xform.pin_x = v;
270 }
271 off += 9;
272 if let Some(v) = read_double(data, off) {
273 cs.xform.pin_y = v;
274 }
275 off += 9;
276 if let Some(v) = read_double(data, off) {
277 cs.xform.width = v;
278 }
279 off += 9;
280 if let Some(v) = read_double(data, off) {
281 cs.xform.height = v;
282 }
283 off += 9;
284 if let Some(v) = read_double(data, off) {
285 cs.xform.loc_pin_x = v;
286 }
287 off += 9;
288 if let Some(v) = read_double(data, off) {
289 cs.xform.loc_pin_y = v;
290 }
291 off += 9;
292 if let Some(v) = read_double(data, off) {
293 cs.xform.angle = v;
294 }
295 off += 8;
296 if off < data.len() {
297 cs.xform.flip_x = data[off] != 0;
298 off += 1;
299 }
300 if off < data.len() {
301 cs.xform.flip_y = data[off] != 0;
302 }
303 }
304
305 fn read_text_xform(&mut self, data: &[u8]) {
306 let cs = match &mut self.current_shape {
307 Some(s) => s,
308 None => return,
309 };
310 let mut txf = VsdTextXForm::default();
311 let mut off = 1usize;
312 if let Some(v) = read_double(data, off) {
313 txf.txt_pin_x = v;
314 }
315 off += 9;
316 if let Some(v) = read_double(data, off) {
317 txf.txt_pin_y = v;
318 }
319 off += 9;
320 if let Some(v) = read_double(data, off) {
321 txf.txt_width = v;
322 }
323 off += 9;
324 if let Some(v) = read_double(data, off) {
325 txf.txt_height = v;
326 }
327 cs.text_xform = Some(txf);
328 }
329
330 fn read_xform_1d(&mut self, data: &[u8]) {
331 let cs = match &mut self.current_shape {
332 Some(s) => s,
333 None => return,
334 };
335 let mut xf = VsdXForm1D::default();
336 let mut off = 1usize;
337 if let Some(v) = read_double(data, off) {
338 xf.begin_x = v;
339 }
340 off += 9;
341 if let Some(v) = read_double(data, off) {
342 xf.begin_y = v;
343 }
344 off += 9;
345 if let Some(v) = read_double(data, off) {
346 xf.end_x = v;
347 }
348 off += 9;
349 if let Some(v) = read_double(data, off) {
350 xf.end_y = v;
351 }
352 cs.xform_1d = Some(xf);
353 }
354
355 fn read_text(&mut self, data: &[u8]) {
356 let cs = match &mut self.current_shape {
357 Some(s) => s,
358 None => return,
359 };
360 if data.len() < 8 {
361 return;
362 }
363 let text_data = &data[8..];
364 if text_data.len() >= 2 {
366 let chars: Vec<u16> = text_data
367 .chunks(2)
368 .map(|c| {
369 if c.len() == 2 {
370 u16::from_le_bytes([c[0], c[1]])
371 } else {
372 0
373 }
374 })
375 .collect();
376 if let Ok(s) = String::from_utf16(&chars) {
377 let trimmed = s.trim_end_matches('\0').to_string();
378 if !trimmed.is_empty() && !trimmed.chars().all(|c| c == '\u{FFFD}') {
379 cs.text = trimmed;
380 return;
381 }
382 }
383 }
384 cs.text = String::from_utf8_lossy(text_data)
386 .trim_end_matches('\0')
387 .to_string();
388 }
389
390 fn read_geometry(&mut self, data: &[u8]) {
391 if self.current_shape.is_none() {
392 return;
393 }
394 if let Some(g) = self.current_geom.take() {
396 if let Some(cs) = &mut self.current_shape {
397 cs.geometry.push(g);
398 }
399 }
400 let mut geom = VsdGeomSection::default();
401 if !data.is_empty() {
402 let flags = data[0];
403 geom.no_fill = flags & 1 != 0;
404 geom.no_line = flags & 2 != 0;
405 geom.no_show = flags & 4 != 0;
406 }
407 self.current_geom = Some(geom);
408 }
409
410 fn ensure_geom(&mut self) -> bool {
411 if self.current_shape.is_none() {
412 return false;
413 }
414 if self.current_geom.is_none() {
415 self.current_geom = Some(VsdGeomSection::default());
416 }
417 true
418 }
419
420 fn read_geom_row(&mut self, row_type: &str, data: &[u8]) {
421 if !self.ensure_geom() {
422 return;
423 }
424 let mut row = VsdGeomRow::default();
425 row.row_type = row_type.to_string();
426 let mut off = 1usize;
427 if let Some(v) = read_double(data, off) {
428 row.x = v;
429 }
430 off += 9;
431 if let Some(v) = read_double(data, off) {
432 row.y = v;
433 }
434 off += 9;
435 match row_type {
436 "ArcTo" | "SplineStart" | "SplineKnot" | "InfiniteLine" => {
437 if let Some(v) = read_double(data, off) {
438 row.a = v;
439 }
440 off += 9;
441 if let Some(v) = read_double(data, off) {
442 row.b = v;
443 }
444 off += 9;
445 }
446 "Ellipse" | "EllipticalArcTo" => {
447 if let Some(v) = read_double(data, off) {
448 row.a = v;
449 }
450 off += 9;
451 if let Some(v) = read_double(data, off) {
452 row.b = v;
453 }
454 off += 9;
455 if let Some(v) = read_double(data, off) {
456 row.c = v;
457 }
458 off += 9;
459 if let Some(v) = read_double(data, off) {
460 row.d = v;
461 }
462 }
463 _ => {}
464 }
465 if let Some(geom) = &mut self.current_geom {
466 geom.rows.push(row);
467 }
468 }
469
470 fn read_nurbs_to(&mut self, data: &[u8]) {
471 if !self.ensure_geom() {
472 return;
473 }
474 let mut row = VsdGeomRow::default();
475 row.row_type = "NURBSTo".to_string();
476 let mut off = 1usize;
477 if let Some(v) = read_double(data, off) {
478 row.x = v;
479 }
480 off += 9;
481 if let Some(v) = read_double(data, off) {
482 row.y = v;
483 }
484 off += 9;
485 if let Some(v) = read_double(data, off) {
486 row.knot_last = v;
487 }
488 off += 9;
489 if off + 2 <= data.len() {
490 row.degree = u16::from_le_bytes([data[off], data[off + 1]]);
491 off += 2;
492 }
493 if off < data.len() {
494 row.x_type = data[off];
495 off += 1;
496 }
497 if off < data.len() {
498 row.y_type = data[off];
499 off += 1;
500 }
501 while off + 32 <= data.len() {
503 let knot = read_double(data, off).unwrap_or(0.0);
504 off += 8;
505 let weight = read_double(data, off).unwrap_or(0.0);
506 off += 8;
507 let px = read_double(data, off).unwrap_or(0.0);
508 off += 8;
509 let py = read_double(data, off).unwrap_or(0.0);
510 off += 8;
511 row.points.push((px, py, knot, weight));
512 }
513 if let Some(geom) = &mut self.current_geom {
514 geom.rows.push(row);
515 }
516 }
517
518 fn read_polyline_to(&mut self, data: &[u8]) {
519 if !self.ensure_geom() {
520 return;
521 }
522 let mut row = VsdGeomRow::default();
523 row.row_type = "PolylineTo".to_string();
524 let mut off = 1usize;
525 if let Some(v) = read_double(data, off) {
526 row.x = v;
527 }
528 off += 9;
529 if let Some(v) = read_double(data, off) {
530 row.y = v;
531 }
532 off += 9;
533 if off < data.len() {
534 row.x_type = data[off];
535 off += 1;
536 }
537 if off < data.len() {
538 row.y_type = data[off];
539 off += 1;
540 }
541 while off + 16 <= data.len() {
542 let px = read_double(data, off).unwrap_or(0.0);
543 off += 8;
544 let py = read_double(data, off).unwrap_or(0.0);
545 off += 8;
546 row.points.push((px, py, 0.0, 0.0));
547 }
548 if let Some(geom) = &mut self.current_geom {
549 geom.rows.push(row);
550 }
551 }
552
553 fn read_line_fmt(&mut self, data: &[u8]) {
554 let cs = match &mut self.current_shape {
555 Some(s) => s,
556 None => return,
557 };
558 let mut off = 1usize;
559 if let Some(v) = read_double(data, off) {
560 cs.line_weight = v;
561 }
562 off += 9;
563 if off + 3 <= data.len() {
564 let r = data[off];
565 let g = data[off + 1];
566 let b = data[off + 2];
567 cs.line_color = format!("#{:02X}{:02X}{:02X}", r, g, b);
568 off += 4; }
570 if off < data.len() {
571 cs.line_pattern = data[off] as i32;
572 }
573 }
574
575 fn read_fill(&mut self, data: &[u8]) {
576 let cs = match &mut self.current_shape {
577 Some(s) => s,
578 None => return,
579 };
580 let mut off = 1usize;
581 if off + 3 <= data.len() {
582 cs.fill_fg = format!(
583 "#{:02X}{:02X}{:02X}",
584 data[off],
585 data[off + 1],
586 data[off + 2]
587 );
588 off += 4;
589 }
590 off += 1;
591 if off + 3 <= data.len() {
592 cs.fill_bg = format!(
593 "#{:02X}{:02X}{:02X}",
594 data[off],
595 data[off + 1],
596 data[off + 2]
597 );
598 off += 4;
599 }
600 if off < data.len() {
601 cs.fill_pattern = data[off] as i32;
602 off += 1;
603 }
604 if off + 12 <= data.len() {
606 off += 1;
607 if off + 3 <= data.len() {
608 cs.shadow_color = format!(
609 "#{:02X}{:02X}{:02X}",
610 data[off],
611 data[off + 1],
612 data[off + 2]
613 );
614 off += 4;
615 }
616 if off < data.len() {
617 cs.shadow_pattern = data[off] as i32;
618 off += 1;
619 }
620 off += 1;
621 if let Some(v) = read_double(data, off) {
622 cs.shadow_offset_x = v;
623 }
624 off += 9;
625 if let Some(v) = read_double(data, off) {
626 cs.shadow_offset_y = v;
627 }
628 }
629 }
630
631 fn read_char_ix(&mut self, data: &[u8]) {
632 let cs = match &mut self.current_shape {
633 Some(s) => s,
634 None => return,
635 };
636 if data.len() < 12 {
637 return;
638 }
639 let mut fmt = VsdCharFormat::default();
640 let mut off = 0usize;
641 fmt.char_count = read_u32(data, off);
642 off += 4;
643 fmt.font_id = read_u16(data, off);
644 off += 3;
645 if off + 3 <= data.len() {
646 fmt.color_r = data[off];
647 fmt.color_g = data[off + 1];
648 fmt.color_b = data[off + 2];
649 off += 4;
650 }
651 if off < data.len() {
652 let mods = data[off];
653 fmt.bold = mods & 1 != 0;
654 fmt.italic = mods & 2 != 0;
655 fmt.underline = mods & 4 != 0;
656 off += 5;
657 }
658 if let Some(v) = read_double(data, off) {
659 fmt.font_size = v;
660 }
661 cs.char_formats.push(fmt);
662 }
663
664 fn read_para_ix(&mut self, data: &[u8]) {
665 let cs = match &mut self.current_shape {
666 Some(s) => s,
667 None => return,
668 };
669 if data.len() < 8 {
670 return;
671 }
672 let mut pf = VsdParaFormat::default();
673 let mut off = 0usize;
674 pf.char_count = read_u32(data, off);
675 off += 5;
676 if let Some(v) = read_double(data, off) {
677 pf.indent_first = v;
678 }
679 off += 9;
680 if let Some(v) = read_double(data, off) {
681 pf.indent_left = v;
682 }
683 off += 9;
684 if let Some(v) = read_double(data, off) {
685 pf.indent_right = v;
686 }
687 off += 9;
688 if let Some(v) = read_double(data, off) {
689 pf.spacing_line = v;
690 }
691 off += 9;
692 off += 18;
694 off += 1;
695 if off < data.len() {
696 pf.horiz_align = data[off];
697 off += 2;
698 }
699 if off < data.len() {
700 pf.bullet = data[off];
701 }
702 cs.para_formats.push(pf);
703 }
704
705 fn read_layer_membership(&mut self, data: &[u8]) {
706 let cs = match &mut self.current_shape {
707 Some(s) => s,
708 None => return,
709 };
710 if data.len() < 2 {
711 return;
712 }
713 let chars: Vec<u16> = data
714 .chunks(2)
715 .map(|c| {
716 if c.len() == 2 {
717 u16::from_le_bytes([c[0], c[1]])
718 } else {
719 0
720 }
721 })
722 .collect();
723 if let Ok(s) = String::from_utf16(&chars) {
724 cs.layer_member = s.trim_end_matches('\0').trim().to_string();
725 }
726 }
727
728 fn read_foreign_data_type(&mut self, data: &[u8]) {
729 let cs = match &mut self.current_shape {
730 Some(s) => s,
731 None => return,
732 };
733 if cs.foreign_data.is_none() {
734 cs.foreign_data = Some(VsdForeignData::default());
735 }
736 let off = 34; if off + 2 <= data.len() {
739 let img_type = u16::from_le_bytes([data[off], data[off + 1]]);
740 let fd = cs.foreign_data.as_mut().unwrap();
741 let (dt, fmt) = match img_type {
742 0 => ("img", "emf"),
743 1 => ("img", "wmf"),
744 2 => ("img", "bmp"),
745 3 => ("ole", "ole"),
746 4 => ("img", "jpg"),
747 5 => ("img", "png"),
748 6 => ("img", "gif"),
749 7 => ("img", "tiff"),
750 _ => ("img", "png"),
751 };
752 fd.data_type = dt.to_string();
753 fd.img_format = fmt.to_string();
754 }
755 }
756
757 fn read_foreign_data(&mut self, data: &[u8]) {
758 let cs = match &mut self.current_shape {
759 Some(s) => s,
760 None => return,
761 };
762 if cs.foreign_data.is_none() {
763 cs.foreign_data = Some(VsdForeignData::default());
764 }
765 cs.foreign_data.as_mut().unwrap().data = data.to_vec();
766 }
767}
768
769fn read_double(data: &[u8], offset: usize) -> Option<f64> {
772 if offset + 8 > data.len() {
773 return None;
774 }
775 Some(f64::from_le_bytes(
776 data[offset..offset + 8].try_into().ok()?,
777 ))
778}
779
780fn read_u32(data: &[u8], offset: usize) -> u32 {
781 if offset + 4 > data.len() {
782 return 0;
783 }
784 u32::from_le_bytes(data[offset..offset + 4].try_into().unwrap_or([0; 4]))
785}
786
787fn read_u16(data: &[u8], offset: usize) -> u16 {
788 if offset + 2 > data.len() {
789 return 0;
790 }
791 u16::from_le_bytes(data[offset..offset + 2].try_into().unwrap_or([0; 2]))
792}
793
794fn parse_chunk_header(data: &[u8], offset: usize) -> Option<(ChunkHeader, usize)> {
795 if offset + 19 > data.len() {
796 return None;
797 }
798 let mut off = offset;
799 let mut hdr = ChunkHeader::default();
800 hdr.chunk_type = read_u32(data, off);
801 off += 4;
802 hdr.record_id = read_u32(data, off);
803 off += 4;
804 hdr.list_flag = read_u32(data, off);
805 off += 4;
806
807 if hdr.list_flag != 0 || LIST_TRAILER_TYPES.contains(&hdr.chunk_type) {
808 hdr.trailer += 8;
809 }
810
811 hdr.data_length = read_u32(data, off);
812 off += 4;
813 hdr.level = read_u16(data, off);
814 off += 2;
815 hdr.unknown = if off < data.len() { data[off] } else { 0 };
816 off += 1;
817
818 if hdr.list_flag != 0
819 || (hdr.level == 2 && hdr.unknown == 0x55)
820 || (hdr.level == 2 && hdr.unknown == 0x54 && hdr.chunk_type == 0xAA)
821 || (hdr.level == 3 && hdr.unknown != 0x50 && hdr.unknown != 0x54)
822 {
823 hdr.trailer += 4;
824 }
825
826 for &tt in TRAILER_TYPES {
827 if hdr.chunk_type == tt && hdr.trailer != 12 && hdr.trailer != 4 {
828 hdr.trailer += 4;
829 break;
830 }
831 }
832
833 if NO_TRAILER_TYPES.contains(&hdr.chunk_type) {
834 hdr.trailer = 0;
835 }
836
837 Some((hdr, off))
838}