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