1use crate::commands::Point;
2use crate::header::TinyVgHeader;
3use crate::{CoordinateRange, TinyVgParseError};
4use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
5use std::io::Cursor;
6
7#[derive(Debug, Copy, Clone)]
8pub struct Unit(pub f64);
9
10pub(crate) fn read_size(coordinate_range: &CoordinateRange, cursor: &mut Cursor<&[u8]>) -> Result<u32, TinyVgParseError> {
12 let res = match coordinate_range {
13 CoordinateRange::Reduced => cursor.read_u8().map_err(|_| TinyVgParseError::InvalidHeader)? as u32,
14 CoordinateRange::Default => cursor.read_u16::<LittleEndian>().map_err(|_| TinyVgParseError::InvalidHeader)? as u32,
15 CoordinateRange::Enhanced => cursor.read_u32::<LittleEndian>().map_err(|_| TinyVgParseError::InvalidHeader)?
16 };
17 Ok(res)
18}
19
20pub(crate) fn read_variable_sized_unsigned_number(cursor: &mut Cursor<&[u8]>) -> Result<u64, TinyVgParseError> {
25 let mut count = 0u64;
26 let mut result = 0u64;
27 loop {
28 let byte = cursor.read_u8().map_err(|_| TinyVgParseError::InvalidHeader)?;
29 let val: u64 = (byte as u64 & 0x7F) << (7 * count);
30 result |= val;
31 if (byte & 0x80) == 0 {
32 break;
33 }
34 count += 1;
35 }
36
37 Ok(result)
38}
39
40
41pub(crate) fn read_unit(scale: u8, cursor: &mut Cursor<&[u8]>, coordinate_range: &CoordinateRange) -> Result<Unit, TinyVgParseError> {
42 let raw: i64;
43
44 match coordinate_range {
45 CoordinateRange::Default => raw = cursor.read_i16::<LittleEndian>().map_err(|_| TinyVgParseError::InvalidCommand)? as i64,
46 CoordinateRange::Reduced => raw = cursor.read_i8().map_err(|_| TinyVgParseError::InvalidCommand)? as i64,
47 CoordinateRange::Enhanced => raw = cursor.read_i32::<LittleEndian>().map_err(|_| TinyVgParseError::InvalidCommand)? as i64,
48 }
49
50 let units_in_css_px: f64 = raw as f64 / (1 << scale) as f64;
51
52 Ok(Unit(units_in_css_px))
53}
54
55#[allow(dead_code)]
56pub(crate) fn write_unit(
57 scale: u8,
58 cursor: &mut Cursor<Vec<u8>>,
59 coordinate_range: &CoordinateRange,
60 value: Unit,
61) -> Result<(), TinyVgParseError> {
62 let scaled = (value.0 * (1 << scale) as f64).round() as i64;
63
64 match coordinate_range {
65 CoordinateRange::Default => {
66 if scaled < i16::MIN as i64 || scaled > i16::MAX as i64 {
67 return Err(TinyVgParseError::InvalidCommand);
68 }
69 cursor.write_i16::<LittleEndian>(scaled as i16).map_err(|_| TinyVgParseError::InvalidCommand)
70 }
71 CoordinateRange::Reduced => {
72 if scaled < i8::MIN as i64 || scaled > i8::MAX as i64 {
73 return Err(TinyVgParseError::InvalidCommand);
74 }
75 cursor.write_i8(scaled as i8).map_err(|_| TinyVgParseError::InvalidCommand)
76 }
77 CoordinateRange::Enhanced => {
78 if scaled < i32::MIN as i64 || scaled > i32::MAX as i64 {
79 return Err(TinyVgParseError::InvalidCommand);
80 }
81 cursor.write_i32::<LittleEndian>(scaled as i32).map_err(|_| TinyVgParseError::InvalidCommand)
82 }
83 }
84}
85
86#[allow(dead_code)]
87pub(crate) fn write_variable_sized_unsigned_number(
88 cursor: &mut Cursor<Vec<u8>>,
89 mut value: u64,
90) -> Result<(), TinyVgParseError> {
91 loop {
92 let mut byte = (value & 0x7F) as u8;
93 value >>= 7;
94 if value != 0 {
95 byte |= 0x80;
96 }
97 cursor.write_u8(byte).map_err(|_| TinyVgParseError::InvalidHeader)?;
98 if value == 0 {
99 break;
100 }
101 }
102 Ok(())
103}
104
105#[allow(dead_code)]
106pub(crate) fn write_size(
107 range: &CoordinateRange,
108 cursor: &mut Cursor<Vec<u8>>,
109 value: u32,
110) -> Result<(), TinyVgParseError> {
111 match range {
112 CoordinateRange::Reduced => cursor.write_u8(value as u8).map_err(|_| TinyVgParseError::InvalidHeader),
113 CoordinateRange::Default => cursor.write_u16::<LittleEndian>(value as u16).map_err(|_| TinyVgParseError::InvalidHeader),
114 CoordinateRange::Enhanced => cursor.write_u32::<LittleEndian>(value).map_err(|_| TinyVgParseError::InvalidHeader),
115 }
116}
117
118#[allow(dead_code)]
119pub(crate) fn write_point(point: &Point, header: &TinyVgHeader, cursor: &mut Cursor<Vec<u8>>) -> Result<(), TinyVgParseError> {
120 write_unit(header.scale, cursor, &header.coordinate_range, point.x)?;
121 write_unit(header.scale, cursor, &header.coordinate_range, point.y)?;
122 Ok(())
123}