rm_lines/line.rs
1use crate::{Color, Parse, Point, Tool};
2
3use nom::{
4 combinator::map,
5 multi::length_count,
6 number::complete::{le_f32, le_u32},
7 sequence::tuple,
8};
9
10/// Data representation of a line in a reMarkable document layer
11///
12/// A `Line` combines a [Tool], a [Color], a size and a series of [Point]s.
13/// It is used to represent a continuous, joined section of [Point]s, such
14/// as a continuous pen stroke.
15#[derive(Debug, PartialEq)]
16pub struct Line {
17 pub tool: Tool,
18 pub color: Color,
19 pub size: f32,
20 pub points: Vec<Point>,
21}
22
23impl<'i> Parse<'i> for Line {
24 /// Attempts to parse a `Line` from a byte sequence
25 ///
26 /// A Line is represented by a series of little-endian 32-bit values.
27 /// 2 of these values serve an unknown purpose at this time, and most
28 /// likely map to functionality like selections, with no obvious or
29 /// useful semantic meaning in the context of a document parser.
30 ///
31 /// Points within a line are represented as a 32-bit point count,
32 /// followed by the raw [Point] values themselves.
33 fn parse(input: &'i [u8]) -> nom::IResult<&'i [u8], Self> {
34 map(
35 tuple((
36 Tool::parse,
37 Color::parse,
38 le_u32, // Unknown/padding
39 le_f32, // Line size (float)
40 le_u32, // Unknown/padding
41 length_count(le_u32, Point::parse),
42 )),
43 |(tool, color, _, size, _, points)| Line {
44 tool,
45 color,
46 size,
47 points,
48 },
49 )(input)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn test_parse() {
59 let bytes = include_bytes!("../data/test-line.rm");
60
61 let res = Line::parse(bytes);
62 assert!(res.is_ok());
63
64 let (rest, line) = res.unwrap();
65 assert_eq!(rest, &[][..]);
66 assert_eq!(line.tool, Tool::FineLiner);
67 assert_eq!(line.color, Color::Black);
68 assert_eq!(line.size, 2.);
69 assert_eq!(line.points.len(), 309);
70 }
71}