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 Flat = 0,
12 Linear = 1,
14 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#[repr(u8)]
113#[derive(Debug, PartialEq)]
114pub enum CommandType {
115 EndOfDocument = 0,
117
118 FillPolygon = 1,
120
121 FillRectangles = 2,
123
124 FillPath = 3,
126
127 DrawLines = 4,
129
130 DrawLineLoop = 5,
132
133 DrawLineStrip = 6,
135
136 DrawLinePath = 7,
138
139 OutlineFillPolygon = 8,
141
142 OutlineFillRectangles = 9,
144
145 OutlineFillPath = 10,
147
148 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 pub start: Point,
213 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 pub center: Point,
299 pub rotation: Unit,
301 pub height: Unit,
304 pub text: String,
305 pub glyph_length: u64,
307 pub glyph_offset: Vec<(Unit, Unit)>,
310}
311
312#[derive(Debug)]
313pub enum DrawCommand {
314 FillPolygon(FillPolygonData),
316
317 FillRectangles(FillRectanglesData),
319
320 FillPath(FillPathData),
322
323 DrawLines(DrawLinesData),
325
326 DrawLineLoop(DrawLineLoopData),
328
329 DrawLineStrip(DrawLineStripData),
331
332 DrawLinePath(DrawLinePathData),
334
335 OutlineFillPolygon(OutlineFillPolygonData),
337
338 OutlineFillRectangles(OutlineFillRectanglesData),
340
341 OutlineFillPath(OutlineFillPathData),
343
344 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 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 let command_index = encoded_command & 0b00_11_11_11;
561 let prim_style_kind = (encoded_command & 0b11_00_00_00) >> 6;
563
564 let command = CommandType::from_u8(command_index);
565
566 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 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 let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
585
586 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 let rectangle_count = read_variable_sized_unsigned_number(cursor)? + 1;
601
602 let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
604
605 let mut rectangles: Vec<Rectangle> = Vec::with_capacity(rectangle_count as usize);
607 for _ in 0..rectangle_count {
608 let x = read_unit(header.scale, cursor, &header.coordinate_range)?;
610
611 let y = read_unit(header.scale, cursor, &header.coordinate_range)?;
613
614 let width = read_unit(header.scale, cursor, &header.coordinate_range)?;
616
617 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 let segment_count = read_variable_sized_unsigned_number(cursor)? + 1;
631
632 let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
634
635 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 let line_count = read_variable_sized_unsigned_number(cursor)? + 1;
647
648 let line_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
650
651 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
653
654 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 let point_count = read_variable_sized_unsigned_number(cursor)? + 1;
671
672 let line_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
674
675 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
677
678 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 let point_count = read_variable_sized_unsigned_number(cursor)? + 1;
695
696 let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
698
699 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
701
702 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 let segment_count = read_variable_sized_unsigned_number(cursor)? + 1;
719
720 let style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
722
723 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
725
726 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 let point_count = (point_count_sec_style_kind & 0b00_11_11_11) + 1;
740
741 let sec_style_kind = point_count_sec_style_kind & 0b11_00_00_00;
743
744 let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
746
747 let line_style = Style::read_cursor_using_style_type(header, cursor, &StyleType::from_u8(sec_style_kind))?;
749
750 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
752
753 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 let rect_count = (rect_count_sec_style_kind & 0b00_11_11_11) + 1;
772
773 let sec_style_kind = rect_count_sec_style_kind & 0b11_00_00_00;
775
776 let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
778
779 let line_style = Style::read_cursor_using_style_type(header, cursor, &StyleType::from_u8(sec_style_kind))?;
781
782 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
784
785 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 let segment_count = (segment_count_and_sec_style_kind & 0b00_11_11_11) + 1;
805
806 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 let fill_style = Style::read_cursor_using_style_type(header, cursor, &style_type)?;
812
813 let line_style = Style::read_cursor_using_style_type(header, cursor, &sec_style_type)?;
815
816 let line_width = read_unit(header.scale, cursor, &header.coordinate_range)?;
818
819 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 let center = Point::read_point(header, cursor)?;
834
835 let rotation = read_unit(header.scale, cursor, &header.coordinate_range)?;
837
838 let height = read_unit(header.scale, cursor, &header.coordinate_range)?;
841
842 let text_length = read_variable_sized_unsigned_number(cursor)?;
844
845 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 let glyph_length = read_variable_sized_unsigned_number(cursor)?;
852
853 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}