1use crate::binary::{Reader, Writer, be_f64_at, be_u32_at};
26use crate::error::{Result, bail};
27
28pub const FLAG_CLOSED: u32 = 0x1000;
30
31#[derive(Debug, Clone, PartialEq)]
33pub struct VectorObject {
34 pub header: Vec<u8>,
36 pub point_size: usize,
38 pub points: Vec<Vec<u8>>,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize)]
44pub struct Point {
45 pub x: f64,
47 pub y: f64,
49 pub bbox: [i32; 4],
51 pub flags: u32,
53 pub values: [f32; 13],
55}
56
57fn put(data: &mut [u8], offset: usize, bytes: &[u8]) {
58 if let Some(target) = data.get_mut(offset..offset + bytes.len()) {
59 target.copy_from_slice(bytes);
60 }
61}
62
63impl VectorObject {
64 pub fn header_size(&self) -> usize {
66 self.header.len()
67 }
68
69 pub fn flags(&self) -> u32 {
71 be_u32_at(&self.header, 20)
72 }
73
74 pub fn is_closed(&self) -> bool {
76 self.flags() & FLAG_CLOSED != 0
77 }
78
79 pub fn bbox(&self) -> [i32; 4] {
81 [0, 4, 8, 12].map(|i| be_u32_at(&self.header, 24 + i) as i32)
82 }
83
84 pub fn set_bbox(&mut self, bbox: [i32; 4]) {
86 for (i, value) in bbox.iter().enumerate() {
87 put(&mut self.header, 24 + 4 * i, &value.to_be_bytes());
88 }
89 }
90
91 pub fn main_color(&self) -> [u8; 3] {
94 [0, 4, 8].map(|i| (be_u32_at(&self.header, 40 + i) >> 24) as u8)
95 }
96
97 pub fn sub_color(&self) -> [u8; 3] {
99 [0, 4, 8].map(|i| (be_u32_at(&self.header, 52 + i) >> 24) as u8)
100 }
101
102 pub fn opacity(&self) -> f64 {
104 be_f64_at(&self.header, 64)
105 }
106
107 fn brush_size_offset(&self) -> Option<usize> {
110 match self.header.len() {
111 size @ (88 | 92 | 96) => Some(size - 12),
112 _ => None,
113 }
114 }
115
116 pub fn brush_size(&self) -> Option<f64> {
118 self.brush_size_offset().map(|offset| be_f64_at(&self.header, offset))
119 }
120
121 pub fn set_brush_size(&mut self, size: f64) {
123 if let Some(offset) = self.brush_size_offset() {
124 put(&mut self.header, offset, &size.to_be_bytes());
125 }
126 }
127
128 pub fn brush_styles(&self) -> Vec<u32> {
130 let end = self.brush_size_offset().unwrap_or(76).min(self.header.len());
131 (72..end).step_by(4).map(|offset| be_u32_at(&self.header, offset)).collect()
132 }
133
134 pub fn shape_kind(&self) -> Option<u32> {
138 (self.header.len() == 96).then(|| be_u32_at(&self.header, 72))
139 }
140
141 pub fn is_balloon_tail(&self) -> bool {
143 self.shape_kind().is_some_and(|kind| kind & 0xff == 0x11)
144 }
145
146 pub fn point(&self, index: usize) -> Option<Point> {
148 let raw = self.points.get(index)?;
149 let mut values = [0f32; 13];
150 for (i, value) in values.iter_mut().enumerate() {
151 *value = f32::from_bits(be_u32_at(raw, 36 + 4 * i));
152 }
153 Some(Point {
154 x: be_f64_at(raw, 0),
155 y: be_f64_at(raw, 8),
156 bbox: [16, 20, 24, 28].map(|offset| be_u32_at(raw, offset) as i32),
157 flags: be_u32_at(raw, 32),
158 values,
159 })
160 }
161
162 pub fn coordinates(&self) -> Vec<(f64, f64)> {
164 self.points.iter().map(|raw| (be_f64_at(raw, 0), be_f64_at(raw, 8))).collect()
165 }
166
167 pub fn transform(&mut self, scale: f64, dx: f64, dy: f64) {
170 let map = |bbox: [i32; 4]| {
171 [
172 (bbox[0] as f64 * scale + dx).floor() as i32,
173 (bbox[1] as f64 * scale + dy).floor() as i32,
174 (bbox[2] as f64 * scale + dx).ceil() as i32,
175 (bbox[3] as f64 * scale + dy).ceil() as i32,
176 ]
177 };
178 self.set_bbox(map(self.bbox()));
179 if let Some(size) = self.brush_size() {
180 self.set_brush_size(size * scale);
181 }
182 for raw in &mut self.points {
183 let x = be_f64_at(raw, 0) * scale + dx;
184 let y = be_f64_at(raw, 8) * scale + dy;
185 put(raw, 0, &x.to_be_bytes());
186 put(raw, 8, &y.to_be_bytes());
187 let bbox = map([16, 20, 24, 28].map(|offset| be_u32_at(raw, offset) as i32));
188 for (i, value) in bbox.iter().enumerate() {
189 put(raw, 16 + 4 * i, &value.to_be_bytes());
190 }
191 if raw.len() >= 104 {
192 let cx = be_f64_at(raw, 88) * scale + dx;
193 let cy = be_f64_at(raw, 96) * scale + dy;
194 put(raw, 88, &cx.to_be_bytes());
195 put(raw, 96, &cy.to_be_bytes());
196 }
197 }
198 }
199}
200
201#[derive(Debug, Clone, PartialEq)]
203pub struct VectorData {
204 pub lead: u32,
206 pub objects: Vec<VectorObject>,
208 pub trailing: Vec<u8>,
210}
211
212impl VectorData {
213 pub fn parse(payload: &[u8]) -> Result<Self> {
215 let mut reader = Reader::new(payload);
216 let lead = reader.be_u32()?;
217 let length = reader.be_u32()? as usize;
218 let end = 8usize
219 .checked_add(length)
220 .filter(|end| *end <= payload.len())
221 .ok_or_else(|| crate::Error::format("VectorData length exceeds the payload"))?;
222 let mut objects = Vec::new();
223 while reader.position() < end {
224 let start = reader.position();
225 let header_size = Reader::at(payload, start).be_u32()? as usize;
226 let point_size = Reader::at(payload, start + 8).be_u32()? as usize;
227 let point_count = Reader::at(payload, start + 16).be_u32()? as usize;
228 if header_size < 24 || point_size < 16 {
229 bail!("Vector object at {start}: header {header_size} / point {point_size} bytes");
230 }
231 if point_count.checked_mul(point_size).is_none_or(|bytes| start + header_size + bytes > end) {
232 bail!("Vector object at {start} overruns the data");
233 }
234 let header = reader.bytes(header_size)?.to_vec();
235 let mut points = Vec::with_capacity(point_count);
236 for _ in 0..point_count {
237 points.push(reader.bytes(point_size)?.to_vec());
238 }
239 objects.push(VectorObject { header, point_size, points });
240 }
241 Ok(VectorData { lead, objects, trailing: payload[end..].to_vec() })
242 }
243
244 pub fn to_bytes(&self) -> Vec<u8> {
246 let mut body = Writer::new();
247 for object in &self.objects {
248 body.bytes(&object.header);
249 for point in &object.points {
250 body.bytes(point);
251 }
252 }
253 let mut writer = Writer::new();
254 writer.be_u32(self.lead).be_u32(body.len() as u32).bytes(&body.data).bytes(&self.trailing);
255 writer.into_inner()
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 fn object(points: &[(f64, f64)]) -> VectorObject {
264 let mut header = vec![0u8; 88];
265 put(&mut header, 0, &88u32.to_be_bytes());
266 put(&mut header, 4, &72u32.to_be_bytes());
267 put(&mut header, 8, &88u32.to_be_bytes());
268 put(&mut header, 12, &88u32.to_be_bytes());
269 put(&mut header, 16, &(points.len() as u32).to_be_bytes());
270 put(&mut header, 20, &0x2081u32.to_be_bytes());
271 put(&mut header, 76, &10f64.to_be_bytes());
272 let points = points
273 .iter()
274 .map(|(x, y)| {
275 let mut raw = vec![0u8; 88];
276 put(&mut raw, 0, &x.to_be_bytes());
277 put(&mut raw, 8, &y.to_be_bytes());
278 raw
279 })
280 .collect();
281 VectorObject { header, point_size: 88, points }
282 }
283
284 #[test]
285 fn vector_data_round_trips() {
286 let data = VectorData { lead: 0, objects: vec![object(&[(1.0, 2.0), (3.5, 4.5)])], trailing: vec![] };
287 let bytes = data.to_bytes();
288 let parsed = VectorData::parse(&bytes).unwrap();
289 assert_eq!(parsed, data);
290 assert_eq!(parsed.objects[0].coordinates(), vec![(1.0, 2.0), (3.5, 4.5)]);
291 assert_eq!(parsed.objects[0].brush_size(), Some(10.0));
292 assert!(VectorData::parse(&bytes[..bytes.len() - 3]).is_err());
293 }
294
295 #[test]
296 fn transform_moves_points_and_size() {
297 let mut item = object(&[(1.0, 2.0)]);
298 item.transform(2.0, 10.0, 20.0);
299 assert_eq!(item.coordinates(), vec![(12.0, 24.0)]);
300 assert_eq!(item.brush_size(), Some(20.0));
301 }
302}