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        interpretation_line: char,
136        interpretation_line_above: char,
137        data: String,
138        reverse_print: bool,
139        condition: Option<(String, String)>,
140    },
141    /// Draws a diagonal line (`^GD`).
142    GraphicDiagonal {
143        x: u32,
144        y: u32,
145        width: u32,
146        height: u32,
147        thickness: u32,
148        color: char,
149        custom_color: Option<String>,
150        /// Leaning: 'R' (`/`) or 'L' (`\`).
151        diagonal_orientation: char,
152        reverse_print: bool,
153        condition: Option<(String, String)>,
154    },
155    /// Draws a Data Matrix (ECC 200) barcode.
156    DataMatrix {
157        x: u32,
158        y: u32,
159        orientation: char,
160        /// Module size in dots (`^BX` dimensional height).
161        module_size: u32,
162        data: String,
163        reverse_print: bool,
164        condition: Option<(String, String)>,
165    },
166    /// Draws a PDF417 barcode.
167    Pdf417 {
168        x: u32,
169        y: u32,
170        orientation: char,
171        /// Row height in dots.
172        row_height: u32,
173        /// Module width in dots (from `^BY`).
174        module_width: u32,
175        /// Error correction security level (0-8).
176        security_level: u32,
177        data: String,
178        reverse_print: bool,
179        condition: Option<(String, String)>,
180    },
181    /// Draws a Code 39 barcode.
182    Code39 {
183        x: u32,
184        y: u32,
185        orientation: char,
186        check_digit: char,
187        height: u32,
188        module_width: u32,
189        interpretation_line: char,
190        interpretation_line_above: char,
191        data: String,
192        reverse_print: bool,
193        condition: Option<(String, String)>,
194    },
195}
196
197/// `^FB` field-block formatting parameters.
198#[derive(Debug, Clone, Copy, PartialEq)]
199pub struct TextBlock {
200    /// Block width in dots; lines wrap to fit it.
201    pub width: u32,
202    /// Maximum number of lines (default 1).
203    pub max_lines: u32,
204    /// Extra space added between lines, in dots.
205    pub line_spacing: i32,
206    /// Justification: 'L', 'C', 'R' or 'J' (J renders as L).
207    pub justification: char,
208    /// Hanging indent applied from the second line onwards, in dots.
209    pub indent: u32,
210}
211
212/// Represents common printer resolutions.
213#[derive(Debug, Clone, Copy, PartialEq)]
214pub enum Resolution {
215    /// 152 DPI (6 dots/mm)
216    Dpi152,
217    /// 203 DPI (8 dots/mm) - Zebra Standard
218    Dpi203,
219    /// 300 DPI (12 dots/mm)
220    Dpi300,
221    /// 600 DPI (24 dots/mm)
222    Dpi600,
223    /// Custom DPI value
224    Custom(f32),
225}
226
227impl Resolution {
228    /// Returns the dots per millimeter for this resolution.
229    pub fn dpmm(&self) -> f32 {
230        match self {
231            Resolution::Dpi152 => 6.0,
232            Resolution::Dpi203 => 8.0,
233            Resolution::Dpi300 => 12.0,
234            Resolution::Dpi600 => 24.0,
235            Resolution::Custom(val) => val / 25.4,
236        }
237    }
238
239    /// Returns the dots per inch for this resolution.
240    pub fn dpi(&self) -> f32 {
241        match self {
242            Resolution::Dpi152 => 152.0,
243            Resolution::Dpi203 => 203.2,
244            Resolution::Dpi300 => 304.8,
245            Resolution::Dpi600 => 609.6,
246            Resolution::Custom(val) => *val,
247        }
248    }
249}
250
251/// Physical units of measurement supported by the engine.
252#[derive(Debug, Clone, Copy, PartialEq)]
253pub enum Unit {
254    /// Raw dots.
255    Dots(u32),
256    /// Inches.
257    Inches(f32),
258    /// Millimeters.
259    Millimeters(f32),
260    /// Centimeters.
261    Centimeters(f32),
262}
263
264impl Unit {
265    /// Converts the unit to dots based on the provided resolution.
266    pub fn to_dots(&self, resolution: Resolution) -> u32 {
267        match self {
268            Unit::Dots(dots) => *dots,
269            Unit::Inches(inches) => (inches.max(0.0) * resolution.dpi()).round() as u32,
270            Unit::Millimeters(mm) => (mm.max(0.0) * resolution.dpmm()).round() as u32,
271            Unit::Centimeters(cm) => (cm.max(0.0) * 10.0 * resolution.dpmm()).round() as u32,
272        }
273    }
274}