1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | #![allow (unused)] |
5 | |
6 | use float_cmp::ApproxEq; |
7 | |
8 | use std::collections::HashMap; |
9 | |
10 | use derive_more::*; |
11 | use serde::Deserialize; |
12 | use smart_default::SmartDefault; |
13 | |
14 | #[derive (Debug, Deserialize)] |
15 | pub struct File { |
16 | pub name: String, |
17 | pub lastModified: Option<String>, |
18 | pub thumbnailURL: Option<String>, |
19 | pub version: String, |
20 | pub document: Node, |
21 | pub components: HashMap<String, Component>, |
22 | //schemaVersion: 0, |
23 | styles: HashMap<String, Style>, |
24 | } |
25 | |
26 | #[derive (Debug, Deserialize)] |
27 | pub struct Component { |
28 | pub key: String, |
29 | pub file_key: Option<String>, |
30 | pub node_id: Option<String>, |
31 | pub thumbnail_url: Option<String>, |
32 | pub name: String, |
33 | pub description: String, |
34 | pub created_at: Option<String>, |
35 | pub updated_at: Option<String>, |
36 | //pub user : User, |
37 | //pub containing_frame : Option<FrameInfo>, |
38 | //pub containing_page Option<PageInfo>, |
39 | } |
40 | |
41 | #[derive (Debug, Deserialize, Default)] |
42 | pub struct NodeCommon { |
43 | pub id: String, |
44 | pub name: String, |
45 | #[serde(default = "return_true" )] |
46 | pub visible: bool, |
47 | #[serde(default)] |
48 | pub children: Vec<Node>, |
49 | } |
50 | |
51 | #[derive (Debug, Deserialize, Default)] |
52 | pub struct LayoutConstraint { |
53 | pub vertical: String, |
54 | pub horizontal: String, |
55 | } |
56 | |
57 | fn return_true() -> bool { |
58 | true |
59 | } |
60 | fn return_one() -> f32 { |
61 | 1. |
62 | } |
63 | |
64 | #[derive (Debug, Deserialize, Default, Clone, Copy)] |
65 | pub struct Color { |
66 | pub a: f32, |
67 | pub r: f32, |
68 | pub g: f32, |
69 | pub b: f32, |
70 | } |
71 | |
72 | impl Color { |
73 | pub fn is_transparent(&self) -> bool { |
74 | self.a.approx_eq(other:0., (f32::EPSILON * 3.0, 2)) |
75 | } |
76 | } |
77 | |
78 | // Sometimes figma is having null for coordinate for some reason, just ignore that and consider it is tempty |
79 | fn deserialize_or_default<'de, T: Default + Deserialize<'de>, D: serde::Deserializer<'de>>( |
80 | de: D, |
81 | ) -> Result<T, D::Error> { |
82 | Ok(T::deserialize(deserializer:de).unwrap_or_default()) |
83 | } |
84 | |
85 | #[derive (Debug, Deserialize, Default, Clone, Copy)] |
86 | #[serde(default)] |
87 | pub struct Rectangle { |
88 | pub x: f32, |
89 | pub y: f32, |
90 | pub width: f32, |
91 | pub height: f32, |
92 | } |
93 | impl Rectangle { |
94 | pub fn origin(self) -> Vector { |
95 | Vector { x: self.x, y: self.y } |
96 | } |
97 | pub fn size(self) -> Vector { |
98 | Vector { x: self.width, y: self.height } |
99 | } |
100 | } |
101 | |
102 | #[derive (Debug, Deserialize, Default, Clone, Copy, Add, AddAssign, Neg, Sub, SubAssign)] |
103 | pub struct Vector { |
104 | pub x: f32, |
105 | pub y: f32, |
106 | } |
107 | |
108 | #[derive (Debug, Deserialize)] |
109 | pub struct Paint { |
110 | pub r#type: String, |
111 | #[serde(default = "return_true" )] |
112 | pub visible: bool, |
113 | #[serde(default = "return_one" )] |
114 | pub opacity: f32, |
115 | pub color: Option<Color>, |
116 | pub blendMode: BlendMode, |
117 | #[serde(default)] |
118 | pub gradientHandlePositions: Vec<Vector>, |
119 | #[serde(default)] |
120 | pub gradientStops: Vec<ColorStop>, |
121 | pub scaleMode: Option<String>, |
122 | pub imageTransform: Option<Transform>, |
123 | pub imageRef: Option<String>, |
124 | } |
125 | |
126 | #[derive (Debug, Deserialize)] |
127 | pub struct ColorStop { |
128 | pub position: f32, |
129 | pub color: Color, |
130 | } |
131 | |
132 | #[derive (Debug, Deserialize)] |
133 | pub struct LayoutGrid { |
134 | pub pattern: String, |
135 | pub sectionSize: f32, |
136 | pub visible: bool, |
137 | pub color: Color, |
138 | pub alignment: String, |
139 | pub gutterSize: f32, |
140 | pub offset: f32, |
141 | pub count: f32, |
142 | } |
143 | |
144 | #[derive (Debug, Deserialize)] |
145 | pub struct Effect { |
146 | pub r#type: String, |
147 | pub visible: bool, |
148 | pub radius: f32, |
149 | pub color: Option<Color>, |
150 | pub blendMode: Option<BlendMode>, |
151 | pub offset: Option<Vector>, |
152 | } |
153 | |
154 | #[derive (Debug, Deserialize, SmartDefault)] |
155 | #[serde(default)] |
156 | pub struct TypeStyle { |
157 | pub fontFamily: String, |
158 | pub fontPostScriptName: Option<String>, |
159 | pub paragraphSpacing: f32, |
160 | pub paragraphIndentNumber: f32, |
161 | pub italic: bool, |
162 | pub fontWeight: f32, |
163 | pub fontSize: f32, |
164 | #[default("ORIGINAL" )] |
165 | pub textCase: String, |
166 | #[default("NONE" )] |
167 | pub textDecoration: String, |
168 | pub textAlignHorizontal: String, |
169 | pub textAlignVertical: String, |
170 | pub letterSpacing: f32, |
171 | pub fills: Vec<Paint>, |
172 | pub lineHeightPx: f32, |
173 | #[default(100.)] |
174 | pub lineHeightPercent: f32, |
175 | pub lineHeightPercentFontSize: f32, |
176 | pub lineHeightUnit: String, |
177 | } |
178 | |
179 | #[derive (Debug, Deserialize, Default)] |
180 | #[serde(default)] |
181 | pub struct Path { |
182 | pub path: String, |
183 | pub windingRule: String, |
184 | } |
185 | |
186 | type Transform = [[f32; 3]; 2]; |
187 | |
188 | type BlendMode = String; |
189 | type EasingType = String; |
190 | |
191 | #[derive (Debug, Deserialize, Default)] |
192 | pub struct Frame { |
193 | #[serde(flatten)] |
194 | pub node: NodeCommon, |
195 | #[serde(default)] |
196 | pub locked: bool, |
197 | pub background: Vec<Paint>, |
198 | pub backgroundColor: Color, |
199 | #[serde(default)] |
200 | pub exportSettings: Vec<ExportSetting>, |
201 | pub blendMode: BlendMode, |
202 | #[serde(default)] |
203 | pub preserveRatio: bool, |
204 | pub constraints: LayoutConstraint, |
205 | pub transitionNodeID: Option<String>, |
206 | pub transitionDuration: Option<f32>, |
207 | pub transitionEasing: Option<EasingType>, |
208 | #[serde(default = "return_one" )] |
209 | pub opacity: f32, |
210 | #[serde(deserialize_with = "deserialize_or_default" )] |
211 | pub absoluteBoundingBox: Rectangle, |
212 | #[serde(deserialize_with = "deserialize_or_default" )] |
213 | pub size: Option<Vector>, |
214 | #[serde(deserialize_with = "deserialize_or_default" )] |
215 | pub relativeTransform: Option<Transform>, |
216 | pub clipsContent: bool, |
217 | #[serde(default)] |
218 | pub layoutGrids: Vec<LayoutGrid>, |
219 | #[serde(default)] |
220 | pub effects: Vec<Effect>, |
221 | #[serde(default)] |
222 | pub isMask: bool, |
223 | #[serde(default)] |
224 | pub isMaskOutline: bool, |
225 | } |
226 | |
227 | #[derive (Debug, Deserialize)] |
228 | pub struct ExportSetting { |
229 | pub suffix: String, |
230 | pub format: String, |
231 | pub constraint: Constraint, |
232 | } |
233 | |
234 | #[derive (Debug, Deserialize, Default)] |
235 | pub struct Constraint { |
236 | pub r#type: String, |
237 | pub value: f32, |
238 | } |
239 | |
240 | #[derive (Debug, Deserialize, SmartDefault)] |
241 | #[serde(default)] |
242 | pub struct VectorNode { |
243 | #[serde(flatten)] |
244 | pub node: NodeCommon, |
245 | pub locked: bool, |
246 | pub exportSettings: Vec<ExportSetting>, |
247 | pub blendMode: BlendMode, |
248 | pub preserveRatio: bool, |
249 | pub constraints: LayoutConstraint, |
250 | pub transitionNodeID: Option<String>, |
251 | pub transitionDuration: Option<f32>, |
252 | pub transitionEasing: Option<EasingType>, |
253 | #[default(1.)] |
254 | pub opacity: f32, |
255 | #[serde(deserialize_with = "deserialize_or_default" )] |
256 | pub absoluteBoundingBox: Rectangle, |
257 | pub effects: Vec<Effect>, |
258 | #[serde(deserialize_with = "deserialize_or_default" )] |
259 | pub size: Option<Vector>, |
260 | #[serde(deserialize_with = "deserialize_or_default" )] |
261 | pub relativeTransform: Option<Transform>, |
262 | pub isMask: bool, |
263 | pub fills: Vec<Paint>, |
264 | pub fillGeometry: Vec<Path>, |
265 | pub strokes: Vec<Paint>, |
266 | pub strokeWeight: f32, |
267 | #[default("NONE" )] |
268 | pub strokeCap: String, |
269 | #[default("MITER" )] |
270 | pub strokeJoin: String, |
271 | pub strokeDashes: Vec<f32>, |
272 | #[default(28.96)] |
273 | pub strokeMiterAngle: f32, |
274 | pub strokeGeometry: Vec<Path>, |
275 | pub strokeAlign: String, |
276 | pub styles: HashMap<String, String>, |
277 | } |
278 | |
279 | #[derive (Debug, Deserialize)] |
280 | #[serde(tag = "type" )] |
281 | pub enum Node { |
282 | DOCUMENT(NodeCommon), |
283 | CANVAS { |
284 | #[serde(flatten)] |
285 | node: NodeCommon, |
286 | backgroundColor: Color, |
287 | prototypeStartNodeID: Option<String>, |
288 | #[serde(default)] |
289 | exportSettings: Vec<ExportSetting>, |
290 | }, |
291 | FRAME(Frame), |
292 | GROUP(Frame), |
293 | VECTOR(VectorNode), |
294 | BOOLEAN_OPERATION { |
295 | #[serde(flatten)] |
296 | vector: VectorNode, |
297 | booleanOperation: String, |
298 | }, |
299 | STAR(VectorNode), |
300 | LINE(VectorNode), |
301 | ELLIPSE(VectorNode), |
302 | REGULAR_POLYGON(VectorNode), |
303 | RECTANGLE { |
304 | #[serde(flatten)] |
305 | vector: VectorNode, |
306 | cornerRadius: Option<f32>, |
307 | #[serde(default)] |
308 | rectangleCornerRadii: Vec<f32>, |
309 | }, |
310 | TEXT { |
311 | #[serde(flatten)] |
312 | vector: VectorNode, |
313 | characters: String, |
314 | style: TypeStyle, |
315 | characterStyleOverrides: Vec<f32>, |
316 | }, |
317 | SLICE { |
318 | #[serde(flatten)] |
319 | node: NodeCommon, |
320 | #[serde(default)] |
321 | exportSettings: Vec<ExportSetting>, |
322 | #[serde(deserialize_with = "deserialize_or_default" )] |
323 | absoluteBoundingBox: Rectangle, |
324 | #[serde(deserialize_with = "deserialize_or_default" )] |
325 | size: Option<Vector>, |
326 | #[serde(deserialize_with = "deserialize_or_default" )] |
327 | relativeTransform: Option<Transform>, |
328 | }, |
329 | COMPONENT(Frame), |
330 | INSTANCE { |
331 | #[serde(flatten)] |
332 | frame: Frame, |
333 | componentId: String, |
334 | }, |
335 | } |
336 | |
337 | impl Node { |
338 | pub fn common(&self) -> &NodeCommon { |
339 | match self { |
340 | Node::DOCUMENT(node: &NodeCommon) => node, |
341 | Node::CANVAS { node: &NodeCommon, .. } => node, |
342 | Node::FRAME(Frame { node: &NodeCommon, .. }) => node, |
343 | Node::GROUP(Frame { node: &NodeCommon, .. }) => node, |
344 | Node::VECTOR(VectorNode { node: &NodeCommon, .. }) => node, |
345 | Node::BOOLEAN_OPERATION { vector: VectorNode { node: &NodeCommon, .. }, .. } => node, |
346 | Node::STAR(VectorNode { node: &NodeCommon, .. }) => node, |
347 | Node::LINE(VectorNode { node: &NodeCommon, .. }) => node, |
348 | Node::ELLIPSE(VectorNode { node: &NodeCommon, .. }) => node, |
349 | Node::REGULAR_POLYGON(VectorNode { node: &NodeCommon, .. }) => node, |
350 | Node::RECTANGLE { vector: VectorNode { node: &NodeCommon, .. }, .. } => node, |
351 | Node::TEXT { vector: VectorNode { node: &NodeCommon, .. }, .. } => node, |
352 | Node::SLICE { node: &NodeCommon, .. } => node, |
353 | Node::COMPONENT(Frame { node: &NodeCommon, .. }) => node, |
354 | Node::INSTANCE { frame: Frame { node: &NodeCommon, .. }, .. } => node, |
355 | } |
356 | } |
357 | } |
358 | |
359 | #[derive (Debug, Deserialize)] |
360 | pub struct Style { |
361 | pub key: String, |
362 | pub name: String, |
363 | pub description: String, |
364 | pub styleType: String, |
365 | } |
366 | |