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