tinyvg_rs/
commands.rs

1use crate::common::{read_unit, read_variable_sized_unsigned_number, Unit};
2use crate::header::TinyVgHeader;
3use crate::TinyVgParseError;
4use byteorder::ReadBytesExt;
5use std::io::{Cursor, Read};
6
7#[repr(u8)]
8#[derive(Debug)]
9pub enum StyleType {
10    /// Colored The shape is uniformly colored with a single color.
11    Flat = 0,
12    /// Gradient The shape is colored with a linear gradient.
13    Linear = 1,
14    /// Gradient The shape is colored with a radial gradient.
15    Radial = 2
16}
17
18impl StyleType {
19    fn from_u8(value: u8) -> Self {
20        match value {
21            0 => Self::Flat,
22            1 => Self::Linear,
23            2 => Self::Radial,
24            _ => unreachable!("Style::from_u8 must be 0, 1, or 2.")
25        }
26    }
27}
28
29#[derive(Debug)]
30pub struct FlatColored {
31    pub color_index: u64
32}
33impl FlatColored {
34    pub fn read_from_cursor(cursor: &mut Cursor<&[u8]>) -> Result<FlatColored, TinyVgParseError> {
35        let color_index = read_variable_sized_unsigned_number(cursor)?;
36
37        Ok(FlatColored {
38            color_index,
39        })
40    }
41
42}
43
44#[derive(Debug)]
45pub struct LinearGradient {
46    pub point_0: Point,
47    pub point_1: Point,
48    pub color_index_0: u64,
49    pub color_index_1: u64,
50}
51
52impl LinearGradient {
53    pub fn read_from_cursor(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>) -> Result<LinearGradient, TinyVgParseError> {
54        let point_0 = Point::read_point(header, cursor)?;
55        let point_1 = Point::read_point(header, cursor)?;
56
57        let color_index_0 = read_variable_sized_unsigned_number(cursor)?;
58        let color_index_1 = read_variable_sized_unsigned_number(cursor)?;
59
60        Ok(LinearGradient {
61            point_0,
62            point_1,
63            color_index_0,
64            color_index_1,
65        })
66    }
67}
68
69#[derive(Debug)]
70pub struct RadialGradient {
71    pub point_0: Point,
72    pub point_1: Point,
73    pub color_index_0: u64,
74    pub color_index_1: u64,
75}
76
77impl RadialGradient {
78    pub fn read_from_cursor(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>) -> Result<RadialGradient, TinyVgParseError> {
79        let point_0 = Point::read_point(header, cursor)?;
80        let point_1 = Point::read_point(header, cursor)?;
81
82        let color_index_0 = read_variable_sized_unsigned_number(cursor)?;
83        let color_index_1 = read_variable_sized_unsigned_number(cursor)?;
84
85        Ok(RadialGradient {
86            point_0,
87            point_1,
88            color_index_0,
89            color_index_1,
90        })
91    }
92}
93
94#[derive(Debug)]
95pub enum Style {
96    FlatColor(FlatColored),
97    LinearGradient(LinearGradient),
98    RadialGradient(RadialGradient),
99}
100
101impl Style {
102    fn read_cursor_using_style_type(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>, style_type: &StyleType) ->  Result<Style, TinyVgParseError> {
103        match style_type {
104            StyleType::Flat   => Ok(Style::FlatColor(FlatColored::read_from_cursor(cursor)?)),
105            StyleType::Linear => Ok(Style::LinearGradient(LinearGradient::read_from_cursor(header, cursor)?)),
106            StyleType::Radial => Ok(Style::RadialGradient(RadialGradient::read_from_cursor(header, cursor)?))
107        }
108    }
109}
110
111/// The next draw command.
112#[repr(u8)]
113#[derive(Debug, PartialEq)]
114pub enum CommandType {
115    /// This command determines the end of file.
116    EndOfDocument = 0,
117
118    /// This command fills an N-gon.
119    FillPolygon = 1,
120
121    /// This command fills a set of rectangles.
122    FillRectangles = 2,
123
124    /// This command fills a free-form path.
125    FillPath = 3,
126
127    /// This command draws a set of lines.
128    DrawLines = 4,
129
130    /// This command draws the outline of a polygon.
131    DrawLineLoop = 5,
132
133    /// This command draws a list of end-to-end lines.
134    DrawLineStrip = 6,
135
136    /// This command draws a free-form path.
137    DrawLinePath = 7,
138
139    /// This command draws a filled polygon with an outline.
140    OutlineFillPolygon = 8,
141
142    /// This command draws several filled rectangles with an outline.
143    OutlineFillRectangles = 9,
144
145    /// This command combines the fill and draw line path command into one.
146    OutlineFillPath = 10,
147
148    /// This command defines the contents and glyph location for text.
149    TextHint = 11
150}
151
152impl CommandType {
153    fn from_u8(value: u8) -> Self {
154        match value {
155            0 => Self::EndOfDocument,
156            1 => Self::FillPolygon,
157            2 => Self::FillRectangles,
158            3 => Self::FillPath,
159            4 => Self::DrawLines,
160            5 => Self::DrawLineLoop,
161            6 => Self::DrawLineStrip,
162            7 => Self::DrawLinePath,
163            8 => Self::OutlineFillPolygon,
164            9 => Self::OutlineFillRectangles,
165            10 => Self::OutlineFillPath,
166            11 => Self::TextHint,
167            _ => unreachable!("Style::from_u8 must be in the range 0 to 11.")
168        }
169    }
170}
171
172#[derive(Debug, Copy, Clone,)]
173pub struct Point {
174    pub x: Unit,
175    pub y: Unit,
176}
177
178impl Point {
179    fn read_point(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>) ->  Result<Point, TinyVgParseError> {
180        let x = read_unit(header.scale, cursor, &header.coordinate_range)?;
181        let y = read_unit(header.scale, cursor, &header.coordinate_range)?;
182        let start = Point { x, y };
183        Ok(start)
184    }
185    
186    pub fn move_to(&self, point: &Point) -> Self {
187        point.clone()
188    }
189}
190
191#[derive(Debug)]
192pub struct Rectangle {
193    pub x: Unit,
194    pub y: Unit,
195    pub width: Unit,
196    pub height: Unit,
197}
198
199impl Rectangle {
200    fn read_rectangle(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>) ->  Result<Rectangle, TinyVgParseError> {
201        let x = read_unit(header.scale, cursor, &header.coordinate_range)?;
202        let y = read_unit(header.scale, cursor, &header.coordinate_range)?;
203        let width = read_unit(header.scale, cursor, &header.coordinate_range)?;
204        let height = read_unit(header.scale, cursor, &header.coordinate_range)?;
205        Ok(Rectangle { x, y, width, height})
206    }
207}
208
209#[derive(Debug)]
210pub struct Line {
211    /// Start point of the line
212    pub start: Point,
213    /// End point of the line.
214    pub end: Point,
215}
216
217impl Line {
218    fn read_line(header: &TinyVgHeader, cursor: &mut Cursor<&[u8]>) ->  Result<Line, TinyVgParseError> {
219        let start = Point::read_point(header, cursor)?;
220        let end = Point::read_point(header, cursor)?;
221        Ok(Line{ start, end })
222    }
223}
224
225#[derive(Debug)]
226pub struct FillPolygonData {
227    pub style: Style,
228    pub points: Vec<Point>,
229}
230
231#[derive(Debug)]
232pub struct FillRectanglesData {
233    pub style: Style,
234    pub rectangles: Vec<Rectangle>,
235}
236
237#[derive(Debug)]
238pub struct FillPathData {
239    pub style: Style,
240    pub path: Path,
241}
242
243#[derive(Debug)]
244pub struct DrawLinesData {
245    pub lines: Vec<Line>,
246    pub line_width: Unit,
247    pub line_style: Style,
248}
249
250#[derive(Debug)]
251pub struct DrawLineLoopData {
252    pub line_style: Style,
253    pub line_width: Unit,
254    pub points: Vec<Point>
255}
256
257#[derive(Debug)]
258pub struct DrawLineStripData {
259    pub style: Style,
260    pub line_width: Unit,
261    pub points: Vec<Point>
262}
263
264#[derive(Debug)]
265pub struct DrawLinePathData {
266    pub style: Style,
267    pub line_width: Unit,
268    pub path: Path,
269}
270
271#[derive(Debug)]
272pub struct OutlineFillPolygonData {
273    pub fill_style: Style,
274    pub line_style: Style,
275    pub line_width: Unit,
276    pub points: Vec<Point>,
277}
278
279#[derive(Debug)]
280pub struct OutlineFillRectanglesData {
281    pub fill_style: Style,
282    pub line_style: Style,
283    pub line_width: Unit,
284    pub rectangles: Vec<Rectangle>,
285}
286
287#[derive(Debug)]
288pub struct OutlineFillPathData {
289    pub path: Path,
290    pub fill_style: Style,
291    pub line_style: Style,
292    pub line_width: Unit
293}
294
295#[derive(Debug)]
296pub struct TextHintData {
297    /// The center of the descender line for the defined text.
298    pub center: Point,
299    /// The amount of degrees the text is rotated.
300    pub rotation: Unit,
301    /// The font size or distance from the ascender line to the
302    /// descender line for the text.
303    pub height: Unit,
304    pub text: String,
305    /// The number of glyphs within the text.
306    pub glyph_length: u64,
307    /// The start and end offset on the descender line from the
308    /// center for each glyph
309    pub glyph_offset: Vec<(Unit, Unit)>,
310}
311
312#[derive(Debug)]
313pub enum DrawCommand {
314    /// This command fills an N-gon.
315    FillPolygon(FillPolygonData),
316
317    /// This command fills a set of rectangles.
318    FillRectangles(FillRectanglesData),
319
320    /// This command fills a free-form path.
321    FillPath(FillPathData),
322
323    /// This command draws a set of lines.
324    DrawLines(DrawLinesData),
325
326    /// This command draws the outline of a polygon.
327    DrawLineLoop(DrawLineLoopData),
328
329    /// This command draws a list of end-to-end lines.
330    DrawLineStrip(DrawLineStripData),
331
332    /// This command draws a free-form path.
333    DrawLinePath(DrawLinePathData),
334
335    /// This command draws a filled polygon with an outline.
336    OutlineFillPolygon(OutlineFillPolygonData),
337
338    /// This command draws several filled rectangles with an outline.
339    OutlineFillRectangles(OutlineFillRectanglesData),
340
341    /// This command combines the fill and draw line path command into one.
342    OutlineFillPath(OutlineFillPathData),
343
344    /// This command only provides metadata for accessibility or text selection tools for the position and content
345    /// of text. A renderer can safely ignore this command since it must not have any effect on the resulting
346    /// graphic.
347    TextHint(TextHintData)
348}
349
350#[repr(u8)]
351#[derive(Debug, PartialEq)]
352enum PathCommandType {
353    Line = 0,
354    HorizontalLine = 1,
355    VerticalLine = 2,
356    CubicBezier = 3,
357    ArcCircle = 4,
358    ArcEllipse = 5,
359    ClosePath = 6,
360    QuadraticBezier = 7,
361}
362
363impl PathCommandType {
364    fn from_u8(value: u8) -> Self {
365        match value {
366            0 => Self::Line,
367            1 => Self::HorizontalLine,
368            2 => Self::VerticalLine,
369            3 => Self::CubicBezier,
370            4 => Self::ArcCircle,
371            5 => Self::ArcEllipse,
372            6 => Self::ClosePath,
373            7 => Self::QuadraticBezier,
374            _ => unreachable!("PathCommand::from_u8 must be in the range 0 to 7.")
375        }
376    }
377}
378
379#[derive(Debug)]
380pub struct CubicBezier {
381    pub control_point_0: Point,
382    pub control_point_1: Point,
383    pub point_1: Point,
384}
385
386#[derive(Debug)]
387pub struct ArcCircle {
388    pub large_arc: bool,
389    pub sweep: bool,
390    pub radius: Unit,
391    pub target: Point,
392}
393
394#[derive(Debug)]
395pub struct ArcEllipse {
396    pub large_arc: bool,
397    pub sweep: bool,
398    pub radius_x: Unit,
399    pub radius_y: Unit,
400    pub rotation: Unit,
401    pub target: Point,
402}
403
404#[derive(Debug)]
405pub struct QuadraticBezier {
406    pub control_point: Point,
407    pub point_1: Point,
408}
409
410#[derive(Debug)]
411pub enum PathCommand {
412    Line(Point, Option<Unit>),
413    HorizontalLine(Unit, Option<Unit>),
414    VerticalLine(Unit, Option<Unit>),
415    CubicBezier(CubicBezier, Option<Unit>),
416    ArcCircle(ArcCircle, Option<Unit>),
417    ArcEllipse(ArcEllipse, Option<Unit>),
418    ClosePath,
419    QuadraticBezier(QuadraticBezier, Option<Unit>),
420}
421
422#[derive(Debug)]
423pub struct Segment {
424    pub start: Point,
425    pub path_commands: Vec<PathCommand>,
426}
427
428#[derive(Debug)]
429pub struct Path {
430    pub segments: Vec<Segment>,
431}
432
433impl Path {
434    pub fn parse(cursor: &mut Cursor<&[u8]>, header: &TinyVgHeader, segment_count: usize) -> Result<Self, TinyVgParseError> {
435        let mut segment_command_counts: Vec<usize> = Vec::new();
436        for _ in 0..segment_count {
437            let segment_length = read_variable_sized_unsigned_number(cursor)? + 1;
438            segment_command_counts.push(segment_length as usize);
439        }
440
441        let mut segments: Vec<Segment> = Vec::new();
442
443        for i in 0..segment_count {
444            let start = Point::read_point(header, cursor)?;
445
446            let mut segment = Segment {
447                start,
448                path_commands: vec![],
449            };
450
451            let commands_count = segment_command_counts[i];
452
453            for _ in 0..commands_count {
454                let command_tag = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
455                let path_command_raw = command_tag & 0b00_00_01_11;
456                let path_command = PathCommandType::from_u8(path_command_raw);
457                let has_line_width = (command_tag & 0b00_01_00_00) != 0;
458                let mut line_width: Option<Unit> = None;
459
460                if has_line_width {
461                    // FIXME: Figure out how this should be used in the Vello example.
462                    line_width = Some(read_unit(header.scale, cursor, &header.coordinate_range)?);
463                }
464
465                match path_command {
466                    PathCommandType::Line => {
467                        let point = Point::read_point(header, cursor)?;
468                        segment.path_commands.push(PathCommand::Line(point, line_width));
469                    }
470                    PathCommandType::HorizontalLine => {
471                        let pos_x = read_unit(header.scale, cursor, &header.coordinate_range)?;
472                        segment.path_commands.push(PathCommand::HorizontalLine(pos_x, line_width));
473                    }
474                    PathCommandType::VerticalLine => {
475                        let pos_y = read_unit(header.scale, cursor, &header.coordinate_range)?;
476                        segment.path_commands.push(PathCommand::VerticalLine(pos_y, line_width));
477                    }
478                    PathCommandType::CubicBezier => {
479                        let control_0 = Point::read_point(header, cursor)?;
480                        let control_1 = Point::read_point(header, cursor)?;
481                        let point_1 = Point::read_point(header, cursor)?;
482
483                        segment.path_commands.push(PathCommand::CubicBezier(
484                            CubicBezier {
485                                control_point_0: control_0,
486                                control_point_1: control_1,
487                                point_1,
488                            }, 
489                            line_width
490                        ));
491                    }
492                    PathCommandType::ArcCircle => {
493                        let large_arc_sweep_padding = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
494                        let large_arc = (large_arc_sweep_padding & 0b00_00_00_01) != 0;
495                        let sweep = (large_arc_sweep_padding & 0b00_00_00_10) != 0;
496                        let radius = read_unit(header.scale, cursor, &header.coordinate_range)?;
497                        let target = Point::read_point(header, cursor)?;
498
499                        segment.path_commands.push(PathCommand::ArcCircle(
500                            ArcCircle {
501                                large_arc,
502                                sweep,
503                                radius,
504                                target
505                            },
506                            line_width
507                        ))
508                    }
509                    PathCommandType::ArcEllipse => {
510                        let large_arc_sweep_padding = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
511                        let large_arc = (large_arc_sweep_padding & 0b00_00_00_01) != 0;
512                        let sweep = (large_arc_sweep_padding & 0b00_00_00_10) != 0;
513
514                        let radius_x = read_unit(header.scale, cursor, &header.coordinate_range)?;
515                        let radius_y = read_unit(header.scale, cursor, &header.coordinate_range)?;
516                        let rotation = read_unit(header.scale, cursor, &header.coordinate_range)?;
517                        let target = Point::read_point(header, cursor)?;
518
519                        let arc_ellipse = ArcEllipse {
520                            large_arc,
521                            sweep,
522                            radius_x,
523                            radius_y,
524                            rotation,
525                            target,
526                        };
527                        segment.path_commands.push(PathCommand::ArcEllipse(arc_ellipse, line_width));
528                    }
529                    PathCommandType::ClosePath => {
530                        segment.path_commands.push(PathCommand::ClosePath);
531                    }
532                    PathCommandType::QuadraticBezier => {
533                        let control = Point::read_point(header, cursor)?;
534                        let point_1 = Point::read_point(header, cursor)?;
535
536                        let quadratic_bezier = QuadraticBezier {
537                            control_point: control,
538                            point_1
539                        };
540                        segment.path_commands.push(PathCommand::QuadraticBezier(quadratic_bezier, line_width));
541                    }
542                }
543            }
544
545            segments.push(segment);
546        }
547
548        Ok(Self {
549            segments,
550        })
551    }
552}
553
554pub(crate) fn parse_draw_commands(cursor: &mut Cursor<&[u8]>, header: &TinyVgHeader) -> Result<Vec<DrawCommand>, TinyVgParseError> {
555    let mut draw_commands: Vec<DrawCommand> = Vec::new();
556
557    loop {
558        let encoded_command = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
559        // bits 0–6 = command_index
560        let command_index = encoded_command & 0b00_11_11_11;
561        // bits 7-8 = prim_style_kind
562        let prim_style_kind = (encoded_command & 0b11_00_00_00) >> 6;
563
564        let command = CommandType::from_u8(command_index);
565
566        // If this command is read, the TinyVG file has ended. This command must have prim_style_kind to be
567        // set to 0, so the last byte of every TinyVG file is 0x00.
568        if matches!(command, CommandType::EndOfDocument) {
569            break;
570        }
571
572        let style_type = StyleType::from_u8(prim_style_kind);
573
574        match command {
575            CommandType::EndOfDocument => {
576                unreachable!("We should have broken out of the loop above.")
577            }
578            CommandType::FillPolygon => {
579                // The number of points in the polygon. This value is offset by 1.
580                let point_count = read_variable_sized_unsigned_number(cursor)? + 1;
581                let mut points: Vec<Point> = Vec::with_capacity(point_count as usize);
582
583                // The style that is used to fill the polygon.
584                let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
585
586                // The points of the polygon.
587                for _ in 0..point_count {
588                    let point = Point::read_point(header, cursor)?;
589                    points.push(point);
590                }
591
592                let data = FillPolygonData {
593                    style,
594                    points,
595                };
596                draw_commands.push(DrawCommand::FillPolygon(data))
597            }
598            CommandType::FillRectangles => {
599                // The number of points in the polygon. This value is offset by 1.
600                let rectangle_count = read_variable_sized_unsigned_number(cursor)? + 1;
601
602                // The style that is used to fill all rectangles.
603                let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
604                
605                // The list of rectangles to be filled.
606                let mut rectangles: Vec<Rectangle> = Vec::with_capacity(rectangle_count as usize);
607                for _ in 0..rectangle_count {
608                    // Horizontal distance of the left side to the origin.
609                    let x = read_unit(header.scale, cursor, &header.coordinate_range)?;
610                    
611                    // Vertical distance of the upper side to the origin.
612                    let y = read_unit(header.scale, cursor, &header.coordinate_range)?;
613                    
614                    // Horizontal extent of the rectangle.
615                    let width = read_unit(header.scale, cursor, &header.coordinate_range)?;
616                    
617                    // Vertical extent of the rectangle origin.
618                    let height = read_unit(header.scale, cursor, &header.coordinate_range)?;
619                    rectangles.push(Rectangle { x, y, width, height });
620                }
621
622                let data = FillRectanglesData {
623                    rectangles,
624                    style,
625                };
626                draw_commands.push(DrawCommand::FillRectangles(data))
627            }
628            CommandType::FillPath => {
629                // The number of segments in the path. This value is offset by 1.
630                let segment_count = read_variable_sized_unsigned_number(cursor)? + 1;
631                
632                // The style that is used to fill the path.
633                let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
634
635                // A path with segment_count segments.
636                let path = Path::parse(cursor, header, segment_count as usize)?;
637
638                let data = FillPathData {
639                    path,
640                    style,
641                };
642                draw_commands.push(DrawCommand::FillPath(data));
643            }
644            CommandType::DrawLines => {
645                // The number of rectangles. This value is offset by 1.
646                let line_count = read_variable_sized_unsigned_number(cursor)? + 1;
647                
648                // The style that is used to draw the all rectangles.
649                let line_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
650
651                // The width of the line.
652                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
653
654                // The list of lines.
655                let mut lines: Vec<Line> = Vec::with_capacity(line_count as usize);
656                for _ in 0..line_count {
657                    let line = Line::read_line(header, cursor)?;
658                    lines.push(line);
659                }
660                
661                let data = DrawLinesData {
662                    lines,
663                    line_width,
664                    line_style,
665                };
666                draw_commands.push(DrawCommand::DrawLines(data));
667            }
668            CommandType::DrawLineLoop => {
669                // The number of points. This value is offset by 1.
670                let point_count = read_variable_sized_unsigned_number(cursor)? + 1;
671
672                // The style that is used to draw the all rectangles.
673                let line_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
674
675                // The width of the line.
676                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
677
678                // The points of the polygon.
679                let mut points: Vec<Point> = Vec::with_capacity(point_count as usize);
680                for _ in 0..point_count {
681                    let point = Point::read_point(header, cursor)?;
682                    points.push(point);
683                }
684
685                let data = DrawLineLoopData {
686                    line_style,
687                    line_width,
688                    points,
689                };
690                draw_commands.push(DrawCommand::DrawLineLoop(data));
691            }
692            CommandType::DrawLineStrip => {
693                // The number of points. This value is offset by 1.
694                let point_count = read_variable_sized_unsigned_number(cursor)? + 1;
695
696                // The style that is used to draw the all rectangles.
697                let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
698
699                // The width of the line.
700                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
701
702                // The points of the line strip.
703                let mut points: Vec<Point> = Vec::with_capacity(point_count as usize);
704                for _ in 0..point_count {
705                    let point = Point::read_point(header, cursor)?;
706                    points.push(point);
707                }
708
709                let data = DrawLineStripData {
710                    style,
711                    line_width,
712                    points
713                };
714                draw_commands.push(DrawCommand::DrawLineStrip(data));
715            }
716            CommandType::DrawLinePath => {
717                // The number of segments in the path. This value is offset by 1.
718                let segment_count = read_variable_sized_unsigned_number(cursor)? + 1;
719
720                // The style that is used to draw the all rectangles.
721                let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
722
723                // The width of the line.
724                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
725
726                // A path with segment_count segments.
727                let path = Path::parse(cursor, header, segment_count as usize)?;
728
729                let data = DrawLinePathData {
730                    style,
731                    line_width,
732                    path,
733                };
734                draw_commands.push(DrawCommand::DrawLinePath(data));
735            }
736            CommandType::OutlineFillPolygon => {
737                let point_count_sec_style_kind = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
738                // The number of points in the polygon. This value is offset by 1.
739                let point_count = (point_count_sec_style_kind & 0b00_11_11_11) + 1;
740
741                // The secondary style used in this command.
742                let sec_style_kind = point_count_sec_style_kind & 0b11_00_00_00;
743
744                // The style that is used to fill the polygon.
745                let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
746
747                // The style that is used to draw the outline of the polygon.
748                let line_style = Style::read_cursor_using_style_type(header, cursor, &StyleType::from_u8(sec_style_kind))?;
749
750                // The width of the line.
751                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
752
753                // The set of points of this polygon.
754                let mut points: Vec<Point> = Vec::with_capacity(point_count as usize);
755                for _ in 0..point_count {
756                    let point = Point::read_point(header, cursor)?;
757                    points.push(point);
758                }
759
760                let data = OutlineFillPolygonData {
761                    points,
762                    line_width,
763                    line_style,
764                    fill_style,
765                };
766                draw_commands.push(DrawCommand::OutlineFillPolygon(data));
767            }
768            CommandType::OutlineFillRectangles => {
769                let rect_count_sec_style_kind = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
770                // The number of rectangles. This value is offset by 1.
771                let rect_count = (rect_count_sec_style_kind & 0b00_11_11_11) + 1;
772
773                // The secondary style used in this command.
774                let sec_style_kind = rect_count_sec_style_kind & 0b11_00_00_00;
775
776                // The style that is used to fill the polygon.
777                let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
778
779                // The style that is used to draw the outline of the polygon.
780                let line_style = Style::read_cursor_using_style_type(header, cursor, &StyleType::from_u8(sec_style_kind))?;
781
782                // The width of the line.
783                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
784
785                // The list of rectangles to be drawn.
786                let mut rectangles: Vec<Rectangle> = Vec::with_capacity(rect_count as usize);
787                for _ in 0..rect_count {
788                    let rectangle = Rectangle::read_rectangle(header, cursor)?;
789                    rectangles.push(rectangle);
790                }
791
792                let data = OutlineFillRectanglesData {
793                    fill_style,
794                    line_style,
795                    line_width,
796                    rectangles,
797                };
798                draw_commands.push(DrawCommand::OutlineFillRectangles(data));
799            }
800            CommandType::OutlineFillPath => {
801                let segment_count_and_sec_style_kind = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidCommand)?;
802
803                // The number of points in the polygon. This value is offset by 1
804                let segment_count = (segment_count_and_sec_style_kind & 0b00_11_11_11) + 1;
805
806                // The secondary style used in this command.
807                let sec_style_kind = segment_count_and_sec_style_kind & 0b11_00_00_00;
808                let sec_style_type = StyleType::from_u8(sec_style_kind);
809
810                // The style that is used to fill the polygon.
811                let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
812
813                // The style that is used to draw the outline of the polygon.
814                let line_style = Style::read_cursor_using_style_type(header, cursor, &sec_style_type)?;
815
816                // The width of the line.
817                let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
818
819                // The path that should be drawn
820                let path = Path::parse(cursor, header, segment_count as usize)?;
821
822                let data = OutlineFillPathData {
823                    path,
824                    fill_style,
825                    line_style,
826                    line_width,
827                };
828                draw_commands.push(DrawCommand::OutlineFillPath(data));
829            }
830
831            CommandType::TextHint => {
832                // The center of the descender line for the defined text.
833                let center = Point::read_point(header, cursor)?;
834
835                // The amount of degrees the text is rotated.
836                let rotation = read_unit(header.scale, cursor, &header.coordinate_range)?;
837
838                // The font size or distance from the ascender line to the
839                // descender line for the text.
840                let height = read_unit(header.scale, cursor, &header.coordinate_range)?;
841
842                // The number of bytes used to encode the text.
843                let text_length = read_variable_sized_unsigned_number(cursor)?;
844
845                // The UTF-8 encoded bytes corresponding to the text.
846                let mut text_buffer: Vec<u8> = vec![0; text_length as usize];
847                cursor.read_exact(text_buffer.as_mut_slice()).map_err(|_| TinyVgParseError::InvalidCommand)?;
848                let text = String::from_utf8(text_buffer).map_err(|_| TinyVgParseError::InvalidCommand)?;
849
850                // The number of glyphs within the text.
851                let glyph_length = read_variable_sized_unsigned_number(cursor)?;
852
853                // The start and end offset on the descender line from the
854                // center for each glyph.
855                let mut glyph_offset: Vec<(Unit, Unit)> = Vec::with_capacity(glyph_length as usize);
856                for _ in 0..glyph_length {
857                    let start_offset = read_unit(header.scale, cursor, &header.coordinate_range)?;
858                    let end_offset = read_unit(header.scale, cursor, &header.coordinate_range)?;
859                    glyph_offset.push((start_offset, end_offset));
860                }
861
862                let data = TextHintData {
863                    center,
864                    text,
865                    rotation,
866                    height,
867                    glyph_length,
868                    glyph_offset
869                };
870                draw_commands.push(DrawCommand::TextHint(data));
871            }
872        }
873
874    }
875
876    Ok(draw_commands)
877}