boox_note_parser/
shape.rs1use zip::ZipArchive;
2
3use crate::{
4 id::{PointsUuid, ShapeGroupUuid, StrokeUuid},
5 json::Dimensions,
6 shape::json::{DisplayScale, LineStyle, LineStyleContainer},
7 utils::{convert_timestamp_to_datetime, parse_json},
8};
9
10#[derive(Debug, Clone)]
11pub struct ShapeGroup {
12 shapes: Vec<Shape>,
13}
14
15impl ShapeGroup {
16 pub fn read(mut reader: impl std::io::Read) -> crate::error::Result<Self> {
17 let mut buf = Vec::new();
18 reader.read_to_end(&mut buf)?;
19
20 let reader = std::io::Cursor::new(buf);
21
22 let mut archive = ZipArchive::new(reader)?;
23
24 let mut reader = archive.by_index(0)?;
25
26 let container = protobuf::ShapeContainer::read(&mut reader)?;
27 let shapes = container
28 .shapes
29 .iter()
30 .map(Shape::from_protobuf)
31 .collect::<crate::error::Result<_>>()?;
32 Ok(Self { shapes })
33 }
34
35 pub fn shapes(&self) -> &[Shape] {
36 &self.shapes
37 }
38}
39
40#[derive(Debug, Clone)]
41pub struct Shape {
42 pub stroke_id: StrokeUuid,
43 pub created: chrono::DateTime<chrono::Utc>,
44 pub modified: chrono::DateTime<chrono::Utc>,
45 pub unknown: i64,
46 pub stroke_width: f32,
47 pub bbox: Dimensions,
48 pub render_scale: DisplayScale,
49 pub z_order: i64,
50 pub points_id: Option<PointsUuid>,
51 pub line_style: Option<LineStyle>,
52 pub shape_group_id: ShapeGroupUuid,
53 pub points_json: String,
54}
55
56impl Shape {
57 fn from_protobuf(shape: &protobuf::Shape) -> crate::error::Result<Self> {
58 Ok(Self {
59 stroke_id: StrokeUuid::from_str(&shape.stroke_uuid)?,
60 created: convert_timestamp_to_datetime(shape.created)?,
61 modified: convert_timestamp_to_datetime(shape.modified)?,
62 unknown: shape.unknown,
63 stroke_width: shape.stroke_width,
64 bbox: parse_json(&shape.bbox_json)?,
65 render_scale: parse_json(&shape.render_scale_json)?,
66 z_order: shape.z_order,
67 points_id: if shape.points_uuid.is_empty() {
68 None
69 } else {
70 Some(PointsUuid::from_str(&shape.points_uuid)?)
71 },
72 line_style: if shape.line_style_json.is_empty() {
73 None
74 } else {
75 let line_style_container: LineStyleContainer = parse_json(&shape.line_style_json)?;
76 Some(line_style_container.line_style)
77 },
78 shape_group_id: ShapeGroupUuid::from_str(&shape.shape_group_uuid)?,
79 points_json: shape.empty_array_json.clone(),
80 })
81 }
82}
83
84mod json {
85 use serde::Deserialize;
86
87 #[derive(Debug, Clone, Deserialize)]
88 #[serde(rename_all = "camelCase")]
89 pub struct DisplayScale {
90 display_scale: f32,
91 max_pressure: f32,
92 revised_display_scale: f32,
93 source: u32,
94 }
95
96 #[derive(Debug, Clone, Deserialize)]
97 #[serde(rename_all = "camelCase")]
98 pub struct LineStyleContainer {
99 pub line_style: LineStyle,
100 }
101
102 #[derive(Debug, Clone, Deserialize)]
103 #[serde(rename_all = "camelCase")]
104 pub struct LineStyle {
105 pub phase: f32,
106 pub type_: u8,
107 }
108}
109
110mod protobuf {
111 use prost::Message;
112
113 use crate::error::Result;
114
115 #[derive(Clone, PartialEq, Message)]
116 pub struct ShapeContainer {
117 #[prost(message, repeated, tag = "1")]
118 pub shapes: Vec<Shape>,
119 }
120
121 impl ShapeContainer {
122 pub fn read(mut reader: impl std::io::Read) -> Result<Self> {
123 let mut buf = Vec::new();
124 reader.read_to_end(&mut buf)?;
125 Ok(ShapeContainer::decode(&buf[..])?)
126 }
127 }
128
129 #[derive(Clone, PartialEq, Message)]
130 pub struct Shape {
131 #[prost(string, tag = "1")]
133 pub stroke_uuid: String,
134 #[prost(uint64, tag = "2")]
136 pub created: u64,
137 #[prost(uint64, tag = "3")]
139 pub modified: u64,
140 #[prost(sint64, tag = "4")]
142 pub unknown: i64,
143 #[prost(float, tag = "5")]
145 pub stroke_width: f32,
146 #[prost(string, tag = "7")]
148 pub bbox_json: String,
149 #[prost(string, tag = "11")]
151 pub render_scale_json: String,
152 #[prost(sint64, tag = "12")]
154 pub z_order: i64,
155 #[prost(string, tag = "16")]
157 pub points_uuid: String,
158 #[prost(string, tag = "17")]
160 pub line_style_json: String,
161 #[prost(string, tag = "18")]
163 pub shape_group_uuid: String,
164 #[prost(string, tag = "21")]
166 pub empty_array_json: String,
167 }
168}