tinyvg_rs/
common.rs

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
10/// Unit may be 8, 16, or 32 bits, so we will advance the cursor conditionally.
11pub(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
20/// Page 4, VarUInt.
21/// This type is used to encode 32-bit unsigned integers while keeping the number of bytes low. It is encoded
22/// as a variable-sized integer that uses 7 bit per byte for integer bits and the 7th bit to encode that there
23/// are more bits available.
24pub(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}