ase_swatch/
lib.rs

1#[macro_use]
2extern crate structure;
3
4pub mod types;
5
6use types::*;
7use wasm_bindgen::prelude::*;
8
9fn chunk_for_color(obj: &ObjectColor) -> Vec<u8> {
10    let mut title = obj.name.clone();
11    title.push('\0');
12    // Chunk body starts with length of title
13    let mut chunk = structure!(">H").pack(title.len() as u16).unwrap();
14
15    // Each character in title is pushed in 16 bits(two 8 bits)
16    for character in title.encode_utf16() {
17        let be_bytes = character.to_be_bytes();
18        chunk.push(be_bytes[0]);
19        chunk.push(be_bytes[1]);
20    }
21
22    let color_mode = match obj.data.mode {
23        ColorMode::Rgb => "RGB",
24        ColorMode::Lab => "LAB",
25        ColorMode::Cmyk => "CMYK",
26        ColorMode::Gray => "Gray",
27    };
28
29    // Color mode is adjusted to 4 bytes and pushed
30    let adjusted_color_mode = format!("{:<4}", color_mode);
31    chunk.append(&mut structure!("!4s").pack(adjusted_color_mode.as_bytes()).unwrap());
32
33    // Append the float values of the color
34    for value in &obj.data.values {
35        chunk.append(&mut structure!("!f").pack(*value).unwrap());
36    }
37
38    // Append object color type("Global", "Spot", "process") value
39    chunk.append(&mut structure!(">h").pack(obj.object_type as i16).unwrap());
40
41    // Color chunk starts with [0, 1]
42    let mut head_chunk = vec![0, 1];
43    // Append size of the chunk body
44    head_chunk.append(&mut structure!(">I").pack(chunk.len() as u32).unwrap());
45    // Append the chunk body
46    head_chunk.append(&mut chunk);
47
48    return head_chunk;
49}
50
51fn chunk_for_swatch(obj: &ObjectSwatch) -> Vec<u8> {
52    let mut title = obj.name.clone();
53    title.push('\0');
54    // Chunk body starts with length of title
55    let mut chunk_body = structure!(">H").pack(title.len() as u16).unwrap();
56
57    // Each character in title is pushed in 16 bits(two 8 bits)
58    for character in title.encode_utf16() {
59        let be_bytes = character.to_be_bytes();
60        chunk_body.push(be_bytes[0]);
61        chunk_body.push(be_bytes[1]);
62    }
63
64    // Chunk head starts with [192, 1], possibly an identifier for swatch data
65    let mut chunk_head = vec![192, 1];
66    // Append length of chunk body (this excludes size of individual color objects in the body)
67    chunk_head.append(&mut structure!(">I").pack(chunk_body.len() as u32).unwrap());
68
69    let mut chunk = Vec::new();
70    chunk.append(&mut chunk_head);
71    chunk.append(&mut chunk_body);
72
73    // Append each color object chunk
74    for color in &obj.swatches {
75        chunk.append(&mut chunk_for_color(&color));
76    }
77
78    // End the swatch with [192, 2, 0, 0, 0, 0]
79    chunk.append(&mut vec![192, 2]);
80    chunk.append(&mut vec![0, 0, 0, 0]);
81
82    return chunk;
83}
84
85/// Creates an ASE binary data from the Swatch and Color objects.
86/// 
87/// ## Usage:
88/// ```
89/// use ase_swatch::create_ase;
90/// use ase_swatch::types::*;
91/// let swatches = vec![
92///     ObjectSwatch {
93///         name: "Palette 1".to_string(),
94///         swatches: vec![
95///             ObjectColor {
96///                 name: "Red".to_string(),
97///                 object_type: ObjectColorType::Global,
98///                 data: Color {
99///                     mode: ColorMode::Rgb,
100///                     values: vec![1.0, 0.0, 0.0],
101///                 },
102///             },
103///             ObjectColor {
104///                 name: "Green".to_string(),
105///                 object_type: ObjectColorType::Global,
106///                 data: Color {
107///                     mode: ColorMode::Rgb,
108///                     values: vec![0.0, 1.0, 0.0],
109///                 },
110///             },
111///             ObjectColor {
112///                 name: "Blue".to_string(),
113///                 object_type: ObjectColorType::Global,
114///                 data: Color {
115///                     mode: ColorMode::Rgb,
116///                     values: vec![0.0, 0.0, 1.0],
117///                 },
118///             },
119///         ],
120///     }
121/// ];
122/// let colors = vec![
123///     ObjectColor {
124///         name: "Blue".to_string(),
125///         object_type: ObjectColorType::Global,
126///         data: Color {
127///             mode: ColorMode::Rgb,
128///             values: vec![0.0, 0.0, 1.0],
129///         },
130///     },
131/// ];
132/// let result: Vec<u8> = create_ase(&swatches, &colors);
133/// ```
134pub fn create_ase(swatch_objects: &Vec<ObjectSwatch>, color_objects: &Vec<ObjectColor>) -> Vec<u8> {
135    let s = structure!("!4sHHI");
136    let header = "ASEF".as_bytes();
137    let (v_major, v_minor) = (1, 0);
138    
139    let mut chunk_count: u32 = 0;
140    let mut chunks = Vec::new();
141    for object in swatch_objects {
142        chunk_count += 2 + object.swatches.len() as u32;
143        chunks.append(&mut chunk_for_swatch(&object));
144    }
145
146    for object in color_objects {
147        chunk_count += 1;
148        chunks.append(&mut chunk_for_color(&object));
149    }
150
151    let mut head_chunk = s.pack(header, v_major, v_minor, chunk_count).unwrap();
152    head_chunk.append(&mut chunks);
153
154    return head_chunk;
155}
156
157/// `create_ase` that can be called from JavaScript.
158#[wasm_bindgen]
159pub fn create_ase_js(swatches: &JsValue, colors: &JsValue) -> Vec<u8> {
160    let swatch_objects: Vec<ObjectSwatch> = swatches.into_serde().unwrap();
161    let color_objects: Vec<ObjectColor> = colors.into_serde().unwrap();
162
163    return create_ase(&swatch_objects, &color_objects);
164}