1use std::cmp::max;
7use std::collections::HashMap;
8use std::sync::Arc;
9
10use ab_glyph::{Font, PxScale, ScaleFont};
11use base64::{Engine as _, engine::general_purpose};
12use image::{
13 ImageBuffer, Rgb, RgbImage, Rgba, RgbaImage,
14 imageops::{overlay, rotate90, rotate180, rotate270},
15};
16use imageproc::drawing::{
17 draw_filled_circle_mut, draw_filled_ellipse_mut, draw_filled_rect_mut, draw_polygon_mut,
18 draw_text_mut,
19};
20use imageproc::point::Point;
21use imageproc::rect::Rect;
22use rxing::common::BitMatrix;
23use rxing::{BarcodeFormat, EncodeHintType, EncodeHintValue, EncodeHints};
24
25use super::{barcode_1d_format, barcode_cache};
26use crate::engine::{Barcode1DKind, FontManager, ZplForgeBackend};
27use crate::{ZplError, ZplResult};
28
29pub struct PngBackend {
34 canvas: RgbImage,
35 font_manager: Option<Arc<FontManager>>,
36}
37
38impl Default for PngBackend {
39 fn default() -> Self {
40 Self::new()
41 }
42}
43
44impl PngBackend {
45 pub fn new() -> Self {
47 Self {
48 canvas: ImageBuffer::new(0, 0),
49 font_manager: None,
50 }
51 }
52
53 fn xor_overlay(&mut self, src: &RgbImage, x: i64, y: i64) {
55 let (sw, sh) = src.dimensions();
56 let (cw, ch) = self.canvas.dimensions();
57
58 for sy in 0..sh {
59 let dy = y + sy as i64;
60 if dy < 0 || dy >= ch as i64 {
61 continue;
62 }
63
64 for sx in 0..sw {
65 let dx = x + sx as i64;
66 if dx < 0 || dx >= cw as i64 {
67 continue;
68 }
69
70 let src_pixel = src[(sx, sy)];
71 if src_pixel.0 != [255, 255, 255] {
72 let dest_pixel = &mut self.canvas[(dx as u32, dy as u32)];
73 dest_pixel.0[0] ^= 255;
74 dest_pixel.0[1] ^= 255;
75 dest_pixel.0[2] ^= 255;
76 }
77 }
78 }
79 }
80
81 fn invert_rect(&mut self, rect: Rect) {
83 let (cw, ch) = self.canvas.dimensions();
84 let x_start = rect.left().max(0) as u32;
85 let y_start = rect.top().max(0) as u32;
86 let x_end = (rect.right() as u32).min(cw);
87 let y_end = (rect.bottom() as u32).min(ch);
88
89 for py in y_start..y_end {
90 for px in x_start..x_end {
91 let pixel = &mut self.canvas[(px, py)];
92 pixel.0[0] ^= 255;
93 pixel.0[1] ^= 255;
94 pixel.0[2] ^= 255;
95 }
96 }
97 }
98
99 fn draw_wrapper<F>(
101 &mut self,
102 x: u32,
103 y: u32,
104 width: u32,
105 height: u32,
106 reverse_print: bool,
107 draw_op: F,
108 ) -> ZplResult<()>
109 where
110 F: FnOnce(&mut RgbImage, i32, i32),
111 {
112 if reverse_print {
113 let mut temp_buf = ImageBuffer::from_pixel(width, height, Rgb([255, 255, 255]));
114 draw_op(&mut temp_buf, 0, 0);
115 self.xor_overlay(&temp_buf, x as i64, y as i64);
116 } else {
117 draw_op(&mut self.canvas, x as i32, y as i32);
118 }
119 Ok(())
120 }
121
122 fn parse_hex_color(&self, color: &Option<String>) -> Rgb<u8> {
123 if let Some(hex) = color {
124 let hex = hex.trim_start_matches('#');
125 if hex.len() == 6 {
126 if let (Ok(r), Ok(g), Ok(b)) = (
127 u8::from_str_radix(&hex[0..2], 16),
128 u8::from_str_radix(&hex[2..4], 16),
129 u8::from_str_radix(&hex[4..6], 16),
130 ) {
131 return Rgb([r, g, b]);
132 }
133 } else if hex.len() == 3
134 && let (Ok(r), Ok(g), Ok(b)) = (
135 u8::from_str_radix(&hex[0..1], 16),
136 u8::from_str_radix(&hex[1..2], 16),
137 u8::from_str_radix(&hex[2..3], 16),
138 )
139 {
140 return Rgb([r * 17, g * 17, b * 17]);
141 }
142 }
143 Rgb([0, 0, 0])
144 }
145
146 fn get_text_width(
147 &self,
148 text: &str,
149 font_char: char,
150 height: Option<u32>,
151 width: Option<u32>,
152 ) -> u32 {
153 let font = match self.font_manager.as_ref() {
154 Some(fm) => match fm.get_font(&font_char.to_string()) {
155 Some(f) => f,
156 None => match fm.get_font("0") {
157 Some(f) => f,
158 None => return 0,
159 },
160 },
161 None => return 0,
162 };
163
164 let scale_y = height.unwrap_or(9) as f32;
165 let scale_x = width.unwrap_or(scale_y as u32) as f32;
166 let scale = PxScale {
167 x: scale_x,
168 y: scale_y,
169 };
170
171 let scaled_font = font.as_scaled(scale);
172 let mut width = 0.0;
173 let mut last_glyph_id = None;
174
175 for c in text.chars() {
176 let glyph_id = font.glyph_id(c);
177 if let Some(last) = last_glyph_id {
178 width += scaled_font.kern(last, glyph_id);
179 }
180 width += scaled_font.h_advance(glyph_id);
181 last_glyph_id = Some(glyph_id);
182 }
183
184 width.ceil() as u32
185 }
186}
187
188impl ZplForgeBackend for PngBackend {
189 fn setup_page(&mut self, width: f64, height: f64, _resolution: f32) {
190 const MAX_DIM: u32 = 8192;
192 let w = (width as u32).min(MAX_DIM);
193 let h = (height as u32).min(MAX_DIM);
194 self.canvas = ImageBuffer::from_pixel(w, h, Rgb([255, 255, 255]));
195 }
196
197 fn setup_font_manager(&mut self, font_manager: &FontManager) {
198 self.font_manager = Some(Arc::new(font_manager.clone()));
199 }
200
201 fn draw_text(
202 &mut self,
203 x: u32,
204 y: u32,
205 font: char,
206 height: Option<u32>,
207 width: Option<u32>,
208 orientation: char,
209 text: &str,
210 _reverse_print: bool,
211 color: Option<String>,
212 ) -> ZplResult<()> {
213 if text.is_empty() {
214 return Ok(());
215 }
216
217 let font_data = match self.font_manager.as_ref() {
218 Some(fm) => match fm.get_font(&font.to_string()) {
219 Some(f) => f.clone(),
220 None => match fm.get_font("0") {
221 Some(f) => f.clone(),
222 None => return Err(ZplError::FontError(format!("Font not found: {}", font))),
223 },
224 },
225 None => return Err(ZplError::FontError("Font manager not initialized".into())),
226 };
227
228 let scale_y = height.unwrap_or(9) as f32;
229 let scale_x = width.unwrap_or(scale_y as u32) as f32;
230 let scale = PxScale {
231 x: scale_x,
232 y: scale_y,
233 };
234
235 let text_color = self.parse_hex_color(&color);
236
237 if !matches!(orientation, 'R' | 'I' | 'B') {
238 draw_text_mut(
239 &mut self.canvas,
240 text_color,
241 x as i32,
242 y as i32,
243 scale,
244 &font_data,
245 text,
246 );
247 return Ok(());
248 }
249
250 let text_w = self.get_text_width(text, font, height, width).max(1);
253 let font_h = (scale_y as u32).max(1);
254 let mut tmp = RgbaImage::from_pixel(text_w, font_h, Rgba([0, 0, 0, 0]));
255 let text_rgba = Rgba([text_color.0[0], text_color.0[1], text_color.0[2], 255]);
256 draw_text_mut(&mut tmp, text_rgba, 0, 0, scale, &font_data, text);
257
258 let rotated = match orientation {
259 'R' => rotate90(&tmp),
260 'I' => rotate180(&tmp),
261 _ => rotate270(&tmp),
262 };
263
264 let (cw, ch) = self.canvas.dimensions();
265 for (sx, sy, p) in rotated.enumerate_pixels() {
266 if p.0[3] > 0 {
267 let dx = x.saturating_add(sx);
268 let dy = y.saturating_add(sy);
269 if dx < cw && dy < ch {
270 self.canvas[(dx, dy)] = Rgb([p.0[0], p.0[1], p.0[2]]);
271 }
272 }
273 }
274 Ok(())
275 }
276
277 fn draw_graphic_box(
278 &mut self,
279 x: u32,
280 y: u32,
281 width: u32,
282 height: u32,
283 thickness: u32,
284 color: char,
285 custom_color: Option<String>,
286 rounding: u32,
287 reverse_print: bool,
288 ) -> ZplResult<()> {
289 let w = max(width, 1);
290 let h = max(height, 1);
291 let t = thickness;
292 let r = (rounding as f64 * 8.0) as i32;
293
294 let (draw_color, clear_color) = if let Some(custom) = custom_color {
295 (self.parse_hex_color(&Some(custom)), Rgb([255, 255, 255]))
296 } else if color == 'B' {
297 (Rgb([0, 0, 0]), Rgb([255, 255, 255]))
298 } else {
299 (Rgb([255, 255, 255]), Rgb([0, 0, 0]))
300 };
301
302 let draw_op = |img: &mut RgbImage, px: i32, py: i32| {
303 let draw_rounded_fill =
304 |img: &mut RgbImage, px: i32, py: i32, pw: u32, ph: u32, pr: i32, pc: Rgb<u8>| {
305 if pw == 0 || ph == 0 {
306 return;
307 }
308 if pr <= 0 {
309 draw_filled_rect_mut(img, Rect::at(px, py).of_size(pw, ph), pc);
310 } else {
311 let pr = pr.max(0).min((pw / 2) as i32).min((ph / 2) as i32);
312 let inner_w = pw.saturating_sub(2 * pr as u32).max(1);
313 let inner_h = ph.saturating_sub(2 * pr as u32).max(1);
314 draw_filled_rect_mut(img, Rect::at(px + pr, py).of_size(inner_w, ph), pc);
315 draw_filled_rect_mut(img, Rect::at(px, py + pr).of_size(pw, inner_h), pc);
316 draw_filled_circle_mut(img, (px + pr, py + pr), pr, pc);
317 draw_filled_circle_mut(img, (px + pw as i32 - pr - 1, py + pr), pr, pc);
318 draw_filled_circle_mut(img, (px + pr, py + ph as i32 - pr - 1), pr, pc);
319 draw_filled_circle_mut(
320 img,
321 (px + pw as i32 - pr - 1, py + ph as i32 - pr - 1),
322 pr,
323 pc,
324 );
325 }
326 };
327
328 draw_rounded_fill(img, px, py, w, h, r, draw_color);
329 if t * 2 < w && t * 2 < h {
330 draw_rounded_fill(
331 img,
332 px + t as i32,
333 py + t as i32,
334 w - t * 2,
335 h - t * 2,
336 (r - t as i32).max(0),
337 clear_color,
338 );
339 }
340 };
341
342 self.draw_wrapper(x, y, w, h, reverse_print, draw_op)
343 }
344
345 fn draw_graphic_circle(
346 &mut self,
347 x: u32,
348 y: u32,
349 radius: u32,
350 thickness: u32,
351 _color: char,
352 custom_color: Option<String>,
353 reverse_print: bool,
354 ) -> ZplResult<()> {
355 let color = self.parse_hex_color(&custom_color);
356 let clear_color = Rgb([255, 255, 255]);
357
358 let draw_op = |img: &mut RgbImage, px: i32, py: i32| {
359 let center_x = px + radius as i32;
360 let center_y = py + radius as i32;
361 draw_filled_circle_mut(img, (center_x, center_y), radius as i32, color);
362
363 if radius > thickness {
364 draw_filled_circle_mut(
365 img,
366 (center_x, center_y),
367 (radius - thickness) as i32,
368 clear_color,
369 );
370 }
371 };
372
373 self.draw_wrapper(x, y, radius * 2, radius * 2, reverse_print, draw_op)
374 }
375
376 fn draw_graphic_ellipse(
377 &mut self,
378 x: u32,
379 y: u32,
380 width: u32,
381 height: u32,
382 thickness: u32,
383 _color: char,
384 custom_color: Option<String>,
385 reverse_print: bool,
386 ) -> ZplResult<()> {
387 let color = self.parse_hex_color(&custom_color);
388 let clear_color = Rgb([255, 255, 255]);
389
390 let draw_op = |img: &mut RgbImage, px: i32, py: i32| {
391 let rx = (width / 2) as i32;
392 let ry = (height / 2) as i32;
393 let center_x = px + rx;
394 let center_y = py + ry;
395 draw_filled_ellipse_mut(img, (center_x, center_y), rx, ry, color);
396
397 let t = thickness as i32;
398 if rx > t && ry > t {
399 draw_filled_ellipse_mut(img, (center_x, center_y), rx - t, ry - t, clear_color);
400 }
401 };
402
403 self.draw_wrapper(x, y, width, height, reverse_print, draw_op)
404 }
405
406 fn draw_graphic_field(
407 &mut self,
408 x: u32,
409 y: u32,
410 width: u32,
411 height: u32,
412 data: &[u8],
413 reverse_print: bool,
414 ) -> ZplResult<()> {
415 let draw_op = |img: &mut RgbImage, px: i32, py: i32| {
416 let row_bytes = width.div_ceil(8);
417 let (img_w, img_h) = (img.width() as i32, img.height() as i32);
418
419 for (row_idx, row_data) in data.chunks(row_bytes as usize).enumerate() {
420 let dy = py + row_idx as i32;
421 if dy < 0 || dy >= img_h || row_idx as u32 >= height {
422 continue;
423 }
424
425 for (byte_idx, &byte) in row_data.iter().enumerate() {
426 if byte == 0 {
427 continue;
428 }
429 let base_x = px + (byte_idx as i32 * 8);
430 for bit_idx in 0..8 {
431 let col_idx = byte_idx as u32 * 8 + bit_idx;
432 if col_idx >= width {
433 break;
434 }
435
436 if (byte & (0x80 >> bit_idx)) != 0 {
437 let dx = base_x + bit_idx as i32;
438 if dx >= 0 && dx < img_w {
439 img[(dx as u32, dy as u32)] = Rgb([0, 0, 0]);
440 }
441 }
442 }
443 }
444 }
445 };
446
447 self.draw_wrapper(x, y, width, height, reverse_print, draw_op)
448 }
449
450 fn draw_graphic_image_custom(
451 &mut self,
452 x: u32,
453 y: u32,
454 width: u32,
455 height: u32,
456 data: &str,
457 ) -> ZplResult<()> {
458 let image_data = general_purpose::STANDARD
459 .decode(data.trim())
460 .map_err(|e| ZplError::ImageError(format!("Failed to decode base64: {}", e)))?;
461
462 let img = image::load_from_memory(&image_data)
463 .map_err(|e| ZplError::ImageError(format!("Failed to load image: {}", e)))?
464 .to_rgb8();
465
466 let (orig_w, orig_h) = img.dimensions();
467 let (target_w, target_h) = match (width, height) {
468 (0, 0) => (orig_w, orig_h),
469 (w, 0) => {
470 let h = (orig_h as f32 * (w as f32 / orig_w as f32)).round() as u32;
471 (w, h)
472 }
473 (0, h) => {
474 let w = (orig_w as f32 * (h as f32 / orig_h as f32)).round() as u32;
475 (w, h)
476 }
477 (w, h) => (w, h),
478 };
479
480 let resized_img = if target_w != orig_w || target_h != orig_h {
481 image::imageops::resize(
482 &img,
483 target_w,
484 target_h,
485 image::imageops::FilterType::Lanczos3,
486 )
487 } else {
488 img
489 };
490
491 overlay(&mut self.canvas, &resized_img, x as i64, y as i64);
492 Ok(())
493 }
494
495 fn draw_code128(
496 &mut self,
497 x: u32,
498 y: u32,
499 orientation: char,
500 height: u32,
501 module_width: u32,
502 interpretation_line: char,
503 interpretation_line_above: char,
504 _check_digit: char,
505 _mode: char,
506 data: &str,
507 reverse_print: bool,
508 ) -> ZplResult<()> {
509 let (clean_data, hint_val) = if let Some(stripped) = data.strip_prefix(">:") {
510 (stripped, Some("B"))
511 } else if let Some(stripped) = data.strip_prefix(">;") {
512 (stripped, Some("C"))
513 } else if let Some(stripped) = data.strip_prefix(">9") {
514 (stripped, Some("A"))
515 } else {
516 (data, Some("B")) };
518
519 let hints = hint_val.map(|v| {
520 let mut h = HashMap::new();
521 h.insert(
522 EncodeHintType::FORCE_CODE_SET,
523 EncodeHintValue::ForceCodeSet(v.to_string()),
524 );
525 EncodeHints::from(h)
526 });
527
528 self.draw_1d_barcode(
529 x,
530 y,
531 orientation,
532 height,
533 module_width,
534 clean_data,
535 BarcodeFormat::CODE_128,
536 reverse_print,
537 interpretation_line,
538 interpretation_line_above,
539 hints,
540 hint_val.unwrap_or(""),
541 )
542 }
543
544 fn draw_qr_code(
545 &mut self,
546 x: u32,
547 y: u32,
548 orientation: char,
549 _model: u32,
550 magnification: u32,
551 error_correction: char,
552 _mask: u32,
553 data: &str,
554 reverse_print: bool,
555 ) -> ZplResult<()> {
556 let level = match error_correction {
557 'L' => "L",
558 'M' => "M",
559 'Q' => "Q",
560 'H' => "H",
561 _ => "M",
562 };
563
564 let mut hints = HashMap::new();
565 hints.insert(
566 EncodeHintType::ERROR_CORRECTION,
567 EncodeHintValue::ErrorCorrection(level.to_string()),
568 );
569 hints.insert(
570 EncodeHintType::MARGIN,
571 EncodeHintValue::Margin("0".to_owned()),
572 );
573 let hints: EncodeHints = hints.into();
574
575 let bit_matrix = barcode_cache::encode_cached(
576 BarcodeFormat::QR_CODE,
577 data,
578 &format!("ec:{}", level),
579 Some(&hints),
580 )?;
581
582 let mag = max(magnification, 1);
583 self.fill_matrix_cells(x, y, orientation, mag, mag, &bit_matrix, reverse_print);
584 Ok(())
585 }
586
587 fn draw_datamatrix(
588 &mut self,
589 x: u32,
590 y: u32,
591 orientation: char,
592 module_size: u32,
593 data: &str,
594 reverse_print: bool,
595 ) -> ZplResult<()> {
596 let bit_matrix = barcode_cache::encode_cached(BarcodeFormat::DATA_MATRIX, data, "", None)?;
597
598 let m = max(module_size, 1);
599 self.fill_matrix_cells(x, y, orientation, m, m, &bit_matrix, reverse_print);
600 Ok(())
601 }
602
603 fn draw_pdf417(
604 &mut self,
605 x: u32,
606 y: u32,
607 orientation: char,
608 row_height: u32,
609 module_width: u32,
610 security_level: u32,
611 data: &str,
612 reverse_print: bool,
613 ) -> ZplResult<()> {
614 let mut hints = HashMap::new();
615 hints.insert(
616 EncodeHintType::ERROR_CORRECTION,
617 EncodeHintValue::ErrorCorrection(security_level.min(8).to_string()),
618 );
619 hints.insert(
620 EncodeHintType::MARGIN,
621 EncodeHintValue::Margin("0".to_owned()),
622 );
623 let hints: EncodeHints = hints.into();
624
625 let bit_matrix = barcode_cache::encode_cached(
626 BarcodeFormat::PDF_417,
627 data,
628 &format!("ec:{}", security_level.min(8)),
629 Some(&hints),
630 )?;
631
632 let cw = max(module_width, 1);
633 let ch = max(row_height, 1);
634 self.fill_matrix_cells(x, y, orientation, cw, ch, &bit_matrix, reverse_print);
635 Ok(())
636 }
637
638 fn draw_code39(
639 &mut self,
640 x: u32,
641 y: u32,
642 orientation: char,
643 _check_digit: char,
644 height: u32,
645 module_width: u32,
646 interpretation_line: char,
647 interpretation_line_above: char,
648 data: &str,
649 reverse_print: bool,
650 ) -> ZplResult<()> {
651 self.draw_1d_barcode(
652 x,
653 y,
654 orientation,
655 height,
656 module_width,
657 data,
658 BarcodeFormat::CODE_39,
659 reverse_print,
660 interpretation_line,
661 interpretation_line_above,
662 None,
663 "",
664 )
665 }
666
667 fn draw_barcode_1d(
668 &mut self,
669 kind: Barcode1DKind,
670 x: u32,
671 y: u32,
672 orientation: char,
673 height: u32,
674 module_width: u32,
675 interpretation_line: char,
676 interpretation_line_above: char,
677 data: &str,
678 reverse_print: bool,
679 ) -> ZplResult<()> {
680 self.draw_1d_barcode(
681 x,
682 y,
683 orientation,
684 height,
685 module_width,
686 data,
687 barcode_1d_format(kind),
688 reverse_print,
689 interpretation_line,
690 interpretation_line_above,
691 None,
692 "",
693 )
694 }
695
696 fn draw_graphic_diagonal(
697 &mut self,
698 x: u32,
699 y: u32,
700 width: u32,
701 height: u32,
702 thickness: u32,
703 color: char,
704 custom_color: Option<String>,
705 diagonal_orientation: char,
706 reverse_print: bool,
707 ) -> ZplResult<()> {
708 let draw_color = if custom_color.is_some() {
709 self.parse_hex_color(&custom_color)
710 } else if color == 'W' {
711 Rgb([255, 255, 255])
712 } else {
713 Rgb([0, 0, 0])
714 };
715
716 let w = max(width, 1) as i32;
717 let h = max(height, 1) as i32;
718 let t = (max(thickness, 1) as i32).min(w);
719
720 let draw_op = move |img: &mut RgbImage, px: i32, py: i32| {
721 let pts = if diagonal_orientation == 'L' {
723 [
725 Point::new(px, py),
726 Point::new(px + t, py),
727 Point::new(px + w, py + h),
728 Point::new(px + w - t, py + h),
729 ]
730 } else {
731 [
733 Point::new(px, py + h),
734 Point::new(px + t, py + h),
735 Point::new(px + w, py),
736 Point::new(px + w - t, py),
737 ]
738 };
739 draw_polygon_mut(img, &pts, draw_color);
740 };
741
742 self.draw_wrapper(x, y, w as u32, h as u32, reverse_print, draw_op)
743 }
744
745 fn finalize(&mut self) -> ZplResult<Vec<u8>> {
746 let mut bytes = Vec::new();
747 let mut cursor = std::io::Cursor::new(&mut bytes);
748 self.canvas
749 .write_to(&mut cursor, image::ImageFormat::Png)
750 .map_err(|e| ZplError::BackendError(format!("Failed to write PNG: {}", e)))?;
751 Ok(bytes)
752 }
753}
754
755impl PngBackend {
756 #[allow(clippy::too_many_arguments)]
760 fn fill_matrix_cells(
761 &mut self,
762 x: u32,
763 y: u32,
764 orientation: char,
765 cell_w: u32,
766 cell_h: u32,
767 bit_matrix: &BitMatrix,
768 reverse_print: bool,
769 ) {
770 let bw = bit_matrix.getWidth();
771 let bh = bit_matrix.getHeight();
772 let full_w = bw * cell_w;
773 let full_h = bh * cell_h;
774
775 for gy in 0..bh {
776 for gx in 0..bw {
777 if !bit_matrix.get(gx, gy) {
778 continue;
779 }
780 let lx = (gx * cell_w) as i32;
781 let ly = (gy * cell_h) as i32;
782 let (w, h) = (cell_w, cell_h);
783 let rect = match orientation {
784 'R' => {
785 let nx = full_h as i32 - (ly + h as i32);
786 Rect::at(x as i32 + nx, y as i32 + lx).of_size(h, w)
787 }
788 'I' => {
789 let nx = full_w as i32 - (lx + w as i32);
790 let ny = full_h as i32 - (ly + h as i32);
791 Rect::at(x as i32 + nx, y as i32 + ny).of_size(w, h)
792 }
793 'B' => {
794 let ny = full_w as i32 - (lx + w as i32);
795 Rect::at(x as i32 + ly, y as i32 + ny).of_size(h, w)
796 }
797 _ => Rect::at(x as i32 + lx, y as i32 + ly).of_size(w, h),
798 };
799 if reverse_print {
800 self.invert_rect(rect);
801 } else {
802 draw_filled_rect_mut(&mut self.canvas, rect, Rgb([0, 0, 0]));
803 }
804 }
805 }
806 }
807
808 #[allow(clippy::too_many_arguments)]
809 fn draw_1d_barcode(
810 &mut self,
811 x: u32,
812 y: u32,
813 orientation: char,
814 height: u32,
815 module_width: u32,
816 data: &str,
817 format: BarcodeFormat,
818 reverse_print: bool,
819 interpretation_line: char,
820 interpretation_line_above: char,
821 hints: Option<EncodeHints>,
822 hints_key: &str,
823 ) -> ZplResult<()> {
824 let bit_matrix = barcode_cache::encode_cached(format, data, hints_key, hints.as_ref())?;
825
826 let mw = max(module_width, 1);
827 let bh = height;
828 let bw = bit_matrix.getWidth() * mw;
829
830 let (full_w, full_h) = match orientation {
831 'N' | 'I' => (bw, bh),
832 'R' | 'B' => (bh, bw),
833 _ => (bw, bh),
834 };
835
836 let transform_rect = |lx: i32, ly: i32, w: u32, h: u32| -> Rect {
837 match orientation {
838 'N' => Rect::at(x as i32 + lx, y as i32 + ly).of_size(w, h),
839 'R' => {
840 let new_x = bh as i32 - (ly + h as i32);
841 let new_y = lx;
842 Rect::at(x as i32 + new_x, y as i32 + new_y).of_size(h, w)
843 }
844 'I' => {
845 let new_x = bw as i32 - (lx + w as i32);
846 let new_y = bh as i32 - (ly + h as i32);
847 Rect::at(x as i32 + new_x, y as i32 + new_y).of_size(w, h)
848 }
849 'B' => {
850 let new_x = ly;
851 let new_y = bw as i32 - (lx + w as i32);
852 Rect::at(x as i32 + new_x, y as i32 + new_y).of_size(h, w)
853 }
854 _ => Rect::at(x as i32 + lx, y as i32 + ly).of_size(w, h),
855 }
856 };
857
858 for gx in 0..bit_matrix.getWidth() {
859 if bit_matrix.get(gx, 0) {
860 let rect = transform_rect((gx * mw) as i32, 0, mw, bh);
861 if reverse_print {
862 self.invert_rect(rect);
863 } else {
864 draw_filled_rect_mut(&mut self.canvas, rect, Rgb([0, 0, 0]));
865 }
866 }
867 }
868
869 if interpretation_line == 'Y' {
870 let font_char = '0';
871 let text_h = 18;
872 let text_y = if interpretation_line_above == 'Y' {
873 y.saturating_sub(text_h)
874 } else {
875 y + full_h
876 } + 6;
877
878 let text_width = self.get_text_width(data, font_char, Some(text_h), None);
879 let text_x = if full_w > text_width {
880 x + (full_w - text_width) / 2
881 } else {
882 x
883 };
884
885 self.draw_text(
886 text_x,
887 text_y,
888 font_char,
889 Some(text_h),
890 None,
891 'N',
892 data,
893 false,
894 None,
895 )?;
896 }
897
898 Ok(())
899 }
900}