1use crate::{ZplError, ZplResult};
2use image::GenericImageView;
3
4pub fn zpl_decode(encoded_str: &str, bytes_per_row: usize) -> Vec<u8> {
17 let mut output: Vec<u8> = Vec::new();
18 let mut multiplier: usize = 0;
19 let mut high_nibble: Option<u8> = None;
20 let mut last_was_row_terminator = false;
21
22 const MAX_DECODED_SIZE: usize = 10 * 1024 * 1024; for c in encoded_str.chars() {
26 if output.len() > MAX_DECODED_SIZE {
27 break;
28 }
29
30 match c {
31 'G'..='Y' => multiplier = multiplier.saturating_add((c as usize - 'G' as usize) + 1),
32 'g'..='z' => {
33 multiplier = multiplier.saturating_add(((c as usize - 'g' as usize) + 1) * 20)
34 }
35
36 ':' => {
37 if let Some(high) = high_nibble {
38 output.push(high << 4);
39 high_nibble = None;
40 }
41
42 if bytes_per_row > 0 {
43 let current_row_pos = output.len() % bytes_per_row;
44 let total_repeats = if multiplier == 0 { 1 } else { multiplier };
45 let mut repeats_done = 0;
46
47 if current_row_pos > 0 {
48 let missing = bytes_per_row - current_row_pos;
49 let current_row_start = output.len() - current_row_pos;
50
51 if current_row_start >= bytes_per_row {
52 let prev_row_start = current_row_start - bytes_per_row;
53 let copy_start = prev_row_start + current_row_pos;
54 let copy_end = prev_row_start + bytes_per_row;
55
56 let suffix = output[copy_start..copy_end].to_vec();
57 output.extend_from_slice(&suffix);
58 } else {
59 output.extend(std::iter::repeat_n(0x00, missing));
60 }
61 repeats_done += 1;
62 }
63
64 if repeats_done < total_repeats {
65 let remaining = total_repeats - repeats_done;
66 let remaining = remaining.min(1000);
68
69 if output.len() >= bytes_per_row {
70 let start = output.len() - bytes_per_row;
71 let last_row = output[start..].to_vec();
72 for _ in 0..remaining {
73 if output.len() + last_row.len() > MAX_DECODED_SIZE {
74 break;
75 }
76 output.extend_from_slice(&last_row);
77 }
78 } else {
79 let empty_row = vec![0u8; bytes_per_row];
80 for _ in 0..remaining {
81 if output.len() + empty_row.len() > MAX_DECODED_SIZE {
82 break;
83 }
84 output.extend_from_slice(&empty_row);
85 }
86 }
87 }
88 }
89 multiplier = 0;
90 last_was_row_terminator = true;
91 }
92
93 c if c.is_ascii_hexdigit() => {
94 let val = c.to_digit(16).unwrap_or(0) as u8;
95 let count = if multiplier == 0 { 1 } else { multiplier };
96 multiplier = 0;
97
98 let count = count.min(10000);
100
101 for _ in 0..count {
102 if output.len() >= MAX_DECODED_SIZE {
103 break;
104 }
105 if let Some(high) = high_nibble {
106 output.push((high << 4) | val);
107 high_nibble = None;
108 } else {
109 high_nibble = Some(val);
110 }
111 }
112 last_was_row_terminator = false;
113 }
114
115 ',' => {
116 if let Some(high) = high_nibble {
117 output.push(high << 4);
118 high_nibble = None;
119 }
120
121 if bytes_per_row > 0 {
122 let current_row_pos = output.len() % bytes_per_row;
123 if current_row_pos != 0 {
124 let padding = bytes_per_row - current_row_pos;
125 output.extend(std::iter::repeat_n(0x00, padding));
126 } else if last_was_row_terminator {
127 output.extend(std::iter::repeat_n(0x00, bytes_per_row));
128 }
129 }
130 multiplier = 0;
131 last_was_row_terminator = true;
132 }
133
134 '!' => {
135 if let Some(high) = high_nibble {
136 output.push((high << 4) | 0x0F);
137 high_nibble = None;
138 }
139
140 if bytes_per_row > 0 {
141 let current_row_pos = output.len() % bytes_per_row;
142 if current_row_pos != 0 {
143 let padding = bytes_per_row - current_row_pos;
144 output.extend(std::iter::repeat_n(0xFF, padding));
145 } else if last_was_row_terminator {
146 output.extend(std::iter::repeat_n(0xFF, bytes_per_row));
147 }
148 }
149 multiplier = 0;
150 last_was_row_terminator = true;
151 }
152
153 _ => {}
154 }
155 }
156
157 if let Some(high) = high_nibble {
158 output.push(high << 4);
159 }
160
161 output
162}
163
164pub fn zpl_encode(image_bytes: &[u8]) -> ZplResult<(String, usize, usize)> {
181 let img = image::load_from_memory(image_bytes)
182 .map_err(|e| ZplError::ImageError(format!("Failed to load image from bytes: {}", e)))?;
183
184 let (width, height) = img.dimensions();
185 let luma_img = img.to_luma8();
186 let bytes_per_row = (width as usize).div_ceil(8);
187 let total_bytes = bytes_per_row * height as usize;
188 let mut bitmap = vec![0u8; total_bytes];
189
190 for (y, row) in luma_img.rows().enumerate() {
191 let row_offset = y * bytes_per_row;
192 for (x, pixel) in row.enumerate() {
193 if pixel.0[0] < 128 {
196 let byte_idx = row_offset + (x / 8);
197 let bit_idx = 7 - (x % 8);
198 bitmap[byte_idx] |= 1 << bit_idx;
199 }
200 }
201 }
202
203 let hex_str = hex::encode_upper(bitmap);
204 let mut encoded = String::new();
205 let chars: Vec<char> = hex_str.chars().collect();
206
207 let mut i = 0;
208 while i < chars.len() {
209 let mut count = 1;
210 while i + count < chars.len() && chars[i + count] == chars[i] && count < 400 {
211 count += 1;
212 }
213
214 if count > 1 {
215 let mut remaining = count;
216
217 while remaining >= 20 {
219 let factor = (remaining / 20).min(20);
220 let repeat_char = (b'g' + (factor as u8) - 1) as char;
221 encoded.push(repeat_char);
222 remaining -= factor * 20;
223 }
224
225 if remaining > 0 {
227 let repeat_char = (b'G' + (remaining as u8) - 1) as char;
228 encoded.push(repeat_char);
229 }
230
231 encoded.push(chars[i]);
232 } else {
233 encoded.push(chars[i]);
234 }
235
236 i += count;
237 }
238
239 Ok((encoded, total_bytes, bytes_per_row))
240}