Skip to main content

zpl_forge/engine/
common.rs

1pub use crate::ast::commons::Barcode1DKind;
2
3/// Represents a self-contained ZPL instruction ready for rendering.
4///
5/// Unlike AST commands, instructions are calculated based on the cumulative
6/// state of the parser (e.g., coordinates are absolute, fonts are resolved).
7#[derive(Debug)]
8pub enum ZplInstruction {
9    /// Starts a new page. Emitted between consecutive `^XA...^XZ` blocks.
10    ///
11    /// Backends that support multi-page output (PDF) start a fresh page;
12    /// single-surface backends (PNG) may ignore it.
13    PageBreak,
14    /// Renders a text field.
15    Text {
16        /// Absolute X coordinate.
17        x: u32,
18        /// Absolute Y coordinate.
19        y: u32,
20        /// Font identifier.
21        font: char,
22        /// Height in dots.
23        height: Option<u32>,
24        /// Width in dots.
25        width: Option<u32>,
26        /// Field orientation from `^A` (N, R, I, B).
27        orientation: char,
28        /// Text content.
29        text: String,
30        /// Whether to print white-on-black.
31        reverse_print: bool,
32        /// Custom text color.
33        color: Option<String>,
34        /// `^FB` block formatting (wrap, max lines, justification).
35        block: Option<TextBlock>,
36        /// Condition for this instruction.
37        condition: Option<(String, String)>,
38    },
39    /// Draws a rectangular box.
40    GraphicBox {
41        x: u32,
42        y: u32,
43        width: u32,
44        height: u32,
45        thickness: u32,
46        color: char,
47        custom_color: Option<String>,
48        rounding: u32,
49        reverse_print: bool,
50        condition: Option<(String, String)>,
51    },
52    /// Draws a circle.
53    GraphicCircle {
54        x: u32,
55        y: u32,
56        radius: u32,
57        thickness: u32,
58        color: char,
59        custom_color: Option<String>,
60        reverse_print: bool,
61        condition: Option<(String, String)>,
62    },
63    /// Draws an ellipse.
64    GraphicEllipse {
65        x: u32,
66        y: u32,
67        width: u32,
68        height: u32,
69        thickness: u32,
70        color: char,
71        custom_color: Option<String>,
72        reverse_print: bool,
73        condition: Option<(String, String)>,
74    },
75    /// Renders a bitmap graphic.
76    GraphicField {
77        x: u32,
78        y: u32,
79        width: u32,
80        height: u32,
81        data: Vec<u8>,
82        reverse_print: bool,
83        condition: Option<(String, String)>,
84    },
85    /// Renders a custom color image (extension).
86    CustomImage {
87        /// Absolute X coordinate.
88        x: u32,
89        /// Absolute Y coordinate.
90        y: u32,
91        /// Requested width (0 for natural/proportional).
92        width: u32,
93        /// Requested height (0 for natural/proportional).
94        height: u32,
95        /// Base64 encoded image data.
96        data: String,
97        condition: Option<(String, String)>,
98    },
99    /// Draws a Code 128 barcode.
100    Code128 {
101        x: u32,
102        y: u32,
103        orientation: char,
104        height: u32,
105        module_width: u32,
106        interpretation_line: char,
107        interpretation_line_above: char,
108        check_digit: char,
109        mode: char,
110        data: String,
111        reverse_print: bool,
112        condition: Option<(String, String)>,
113    },
114    /// Draws a QR Code.
115    QRCode {
116        x: u32,
117        y: u32,
118        orientation: char,
119        model: u32,
120        magnification: u32,
121        error_correction: char,
122        mask: u32,
123        data: String,
124        reverse_print: bool,
125        condition: Option<(String, String)>,
126    },
127    /// Draws a generic 1-D barcode (EAN-13, UPC-A, ITF, Code 93).
128    Barcode1D {
129        kind: Barcode1DKind,
130        x: u32,
131        y: u32,
132        orientation: char,
133        height: u32,
134        module_width: u32,
135        /// `^BY` wide:narrow ratio. ZPL defaults to 3.0.
136        ratio: f32,
137        /// Check-digit mode as spelled by the symbology's own command.
138        check_digit: char,
139        interpretation_line: char,
140        interpretation_line_above: char,
141        data: String,
142        reverse_print: bool,
143        condition: Option<(String, String)>,
144    },
145    /// Draws a MicroPDF417 2D barcode (`^BF`).
146    MicroPdf417 {
147        x: u32,
148        y: u32,
149        orientation: char,
150        height: u32,
151        mode: u32,
152        data: String,
153        reverse_print: bool,
154        condition: Option<(String, String)>,
155    },
156    /// Draws an Aztec Code 2D barcode (`^B0`/`^BO`).
157    AztecCode {
158        x: u32,
159        y: u32,
160        orientation: char,
161        magnification: u32,
162        data: String,
163        reverse_print: bool,
164        condition: Option<(String, String)>,
165    },
166    /// Draws a diagonal line (`^GD`).
167    GraphicDiagonal {
168        x: u32,
169        y: u32,
170        width: u32,
171        height: u32,
172        thickness: u32,
173        color: char,
174        custom_color: Option<String>,
175        /// Leaning: 'R' (`/`) or 'L' (`\`).
176        diagonal_orientation: char,
177        reverse_print: bool,
178        condition: Option<(String, String)>,
179    },
180    /// Draws a Data Matrix (ECC 200) barcode.
181    DataMatrix {
182        x: u32,
183        y: u32,
184        orientation: char,
185        /// Module size in dots (`^BX` dimensional height).
186        module_size: u32,
187        data: String,
188        reverse_print: bool,
189        condition: Option<(String, String)>,
190    },
191    /// Draws a PDF417 barcode.
192    Pdf417 {
193        x: u32,
194        y: u32,
195        orientation: char,
196        /// Row height in dots.
197        row_height: u32,
198        /// Module width in dots (from `^BY`).
199        module_width: u32,
200        /// Error correction security level (0-8).
201        security_level: u32,
202        data: String,
203        reverse_print: bool,
204        condition: Option<(String, String)>,
205    },
206    /// Draws a Code 39 barcode.
207    Code39 {
208        x: u32,
209        y: u32,
210        orientation: char,
211        check_digit: char,
212        height: u32,
213        module_width: u32,
214        /// `^BY` wide:narrow ratio. ZPL defaults to 3.0.
215        ratio: f32,
216        interpretation_line: char,
217        interpretation_line_above: char,
218        data: String,
219        reverse_print: bool,
220        condition: Option<(String, String)>,
221    },
222}
223
224/// `^FB` field-block formatting parameters.
225#[derive(Debug, Clone, Copy, PartialEq)]
226pub struct TextBlock {
227    /// Block width in dots; lines wrap to fit it.
228    pub width: u32,
229    /// Maximum number of lines (default 1).
230    pub max_lines: u32,
231    /// Extra space added between lines, in dots.
232    pub line_spacing: i32,
233    /// Justification: 'L', 'C', 'R' or 'J' (J renders as L).
234    pub justification: char,
235    /// Hanging indent applied from the second line onwards, in dots.
236    pub indent: u32,
237}
238
239/// Represents common printer resolutions.
240#[derive(Debug, Clone, Copy, PartialEq)]
241pub enum Resolution {
242    /// 152 DPI (6 dots/mm)
243    Dpi152,
244    /// 203 DPI (8 dots/mm) - Zebra Standard
245    Dpi203,
246    /// 300 DPI (12 dots/mm)
247    Dpi300,
248    /// 600 DPI (24 dots/mm)
249    Dpi600,
250    /// Custom DPI value
251    Custom(f32),
252}
253
254impl Resolution {
255    /// Returns the dots per millimeter for this resolution.
256    pub fn dpmm(&self) -> f32 {
257        match self {
258            Resolution::Dpi152 => 6.0,
259            Resolution::Dpi203 => 8.0,
260            Resolution::Dpi300 => 12.0,
261            Resolution::Dpi600 => 24.0,
262            Resolution::Custom(val) => val / 25.4,
263        }
264    }
265
266    /// Returns the dots per inch for this resolution.
267    ///
268    /// These are the *nominal* ratings Zebra prints on its hardware, not the
269    /// exact `dpmm * 25.4` conversions (which would give 203.2 / 304.8 /
270    /// 609.6). Firmware maps inches to dots with the nominal value, so a 4 x 6
271    /// in label is exactly 812 x 1218 dots at 8 dpmm rather than 813 x 1219.
272    /// Using the exact conversion put every canvas one dot over.
273    pub fn dpi(&self) -> f32 {
274        match self {
275            Resolution::Dpi152 => 152.0,
276            Resolution::Dpi203 => 203.0,
277            Resolution::Dpi300 => 300.0,
278            Resolution::Dpi600 => 600.0,
279            Resolution::Custom(val) => *val,
280        }
281    }
282}
283
284/// Physical units of measurement supported by the engine.
285#[derive(Debug, Clone, Copy, PartialEq)]
286pub enum Unit {
287    /// Raw dots.
288    Dots(u32),
289    /// Inches.
290    Inches(f32),
291    /// Millimeters.
292    Millimeters(f32),
293    /// Centimeters.
294    Centimeters(f32),
295}
296
297impl Unit {
298    /// Converts the unit to dots based on the provided resolution.
299    pub fn to_dots(&self, resolution: Resolution) -> u32 {
300        match self {
301            Unit::Dots(dots) => *dots,
302            Unit::Inches(inches) => (inches.max(0.0) * resolution.dpi()).round() as u32,
303            Unit::Millimeters(mm) => (mm.max(0.0) * resolution.dpmm()).round() as u32,
304            Unit::Centimeters(cm) => (cm.max(0.0) * 10.0 * resolution.dpmm()).round() as u32,
305        }
306    }
307}