PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / 1.10.20
WCPOS – Point of Sale (POS) plugin for WooCommerce v1.10.20
1.10.20 1.10.19 1.10.18 1.10.17 1.10.16 1.10.15 1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 All 164 releases
woocommerce-pos / includes / Templates / Thermal / Thermal_Bounds.php

Thermal_Bounds.php in WCPOS – Point of Sale (POS) plugin for WooCommerce 1.10.20, at includes/Templates/Thermal/Thermal_Bounds.php

207 lines 6.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Thermal Attribute Bounds.
4 *
5 * The one table of legal ranges for the numeric attributes of thermal markup.
6 *
7 * A thermal template is authored once and rendered down six paths: the merchant
8 * preview (packages/thermal-utils/src/thermal-renderer.ts), the PDF receipt
9 * (Html_Thermal_Emitter) and the four wire emitters (Escpos, Starprnt, Epos_Xml,
10 * Star_Markup). A bound that holds in only some of them is not a safety net, it
11 * is a divergence: the merchant previews 50 blank lines and the printer spools
12 * 500. So every shared bound lives here, Thermal_Markup_Parser applies it once
13 * while building the AST, and the emitters re-apply it to stay safe against
14 * hand-built ASTs rather than inventing numbers of their own.
15 *
16 * Bounds that belong to one device rather than to the markup stay in that
17 * emitter, next to the command they constrain, and say so — Starprnt's 8-module
18 * QR ceiling and its 8-dot minimum barcode height are the current examples.
19 *
20 * Keep in step with THERMAL_BOUNDS in
21 * packages/thermal-utils/src/thermal-renderer.ts, which mirrors this table.
22 *
23 * @author Paul Kilmurray <[email protected]>
24 *
25 * @see http://wcpos.com
26 * @package WCPOS\WooCommercePOS
27 */
28
29 namespace WCPOS\WooCommercePOS\Templates\Thermal;
30
31 /**
32 * Thermal_Bounds class.
33 */
34 final class Thermal_Bounds {
35
36 /**
37 * Narrowest receipt in character columns, for every lane.
38 *
39 * Deliberately 1, not the 16 the PDF page uses. This bound is applied by the
40 * PARSER, so it reaches every emitter — including Text_Thermal_Emitter, which
41 * renders plain text with no physical roll behind it and has always accepted
42 * narrow widths (`paper-width="10"` is pinned by its own tests). Raising the
43 * shared floor to 16 silently re-centred that output.
44 *
45 * The rule this PR is built on is that a bound holds on every lane or none.
46 * A floor of 16 is real, but it belongs to the medium that has a page, not to
47 * the markup — so it stays where it already was ({@see self::PAPER_WIDTH_PDF_MIN}).
48 * The hazard this PR exists to close is the top end, not the bottom.
49 */
50 public const PAPER_WIDTH_MIN = 1;
51
52 /**
53 * Narrowest receipt the PDF/preview page will lay out.
54 *
55 * Pre-existing behaviour of Html_Thermal_Emitter, preserved rather than
56 * generalised: below this its character-cell arithmetic has nothing to divide.
57 */
58 public const PAPER_WIDTH_PDF_MIN = 16;
59
60 /**
61 * Widest receipt in character columns.
62 *
63 * 120 covers the widest thermal and impact rolls sold; the character grid
64 * stops being a receipt beyond it.
65 */
66 public const PAPER_WIDTH_MAX = 120;
67
68 /**
69 * Smallest text size multiplier (normal size).
70 */
71 public const SIZE_MULTIPLIER_MIN = 1;
72
73 /**
74 * Largest text size multiplier.
75 *
76 * The ESC/POS `GS ! n` size byte carries one nibble per axis, so 8x is the
77 * ceiling the hardware can express; Star's magnification tops out lower
78 * still. Html_Thermal_Emitter renders the same 8em maximum.
79 */
80 public const SIZE_MULTIPLIER_MAX = 8;
81
82 /**
83 * Shortest 1D barcode, in dots.
84 */
85 public const BARCODE_HEIGHT_MIN = 1;
86
87 /**
88 * Tallest 1D barcode, in dots.
89 *
90 * `GS h n` is a single byte.
91 */
92 public const BARCODE_HEIGHT_MAX = 255;
93
94 /**
95 * Smallest QR module size.
96 */
97 public const QRCODE_SIZE_MIN = 1;
98
99 /**
100 * Largest QR module size.
101 *
102 * The ESC/POS `GS ( k` module-size function accepts 1-16.
103 */
104 public const QRCODE_SIZE_MAX = 16;
105
106 /**
107 * Narrowest image, in printer dots.
108 */
109 public const IMAGE_WIDTH_DOTS_MIN = 1;
110
111 /**
112 * Widest image, in printer dots.
113 *
114 * Comfortably past the 576-dot budget of an 80mm head, so it never truncates
115 * a real logo, while still bounding the em width handed to Dompdf.
116 */
117 public const IMAGE_WIDTH_DOTS_MAX = 2000;
118
119 /**
120 * Fewest lines a `<feed>` advances.
121 */
122 public const FEED_LINES_MIN = 1;
123
124 /**
125 * Most lines a `<feed>` advances.
126 *
127 * At roughly 3.5mm per line this is ~17cm of blank paper — already far more
128 * than any tear-off or cut gap a template legitimately wants, so a larger
129 * number is a typo, and an unbounded one is a hazard: every wire emitter
130 * turns `lines` straight into a loop or a str_repeat(). This is also the
131 * bound the PDF path has shipped since it was written, so pinning the rest
132 * of the paths to 50 moves the fewest of them.
133 */
134 public const FEED_LINES_MAX = 50;
135
136 /**
137 * Narrowest fixed column, in characters.
138 *
139 * A zero-width column would silently delete a semantic cell from the row.
140 */
141 public const COL_WIDTH_MIN = 1;
142
143 /**
144 * Widest fixed column, in characters.
145 *
146 * A column cannot outgrow the widest paper.
147 */
148 public const COL_WIDTH_MAX = self::PAPER_WIDTH_MAX;
149
150 /**
151 * Printable dots across 80 mm paper at 203 dpi.
152 */
153 public const DOTS_80MM = 576;
154
155 /**
156 * Printable dots across 58 mm paper at 203 dpi.
157 */
158 public const DOTS_58MM = 384;
159
160 /**
161 * Column count at or above which paper is treated as 80 mm.
162 *
163 * 58 mm rolls carry 32 columns; 80 mm rolls carry 42 or 48.
164 */
165 public const WIDE_PAPER_COLUMNS = 40;
166
167 /**
168 * The printable width of the roll, in dots.
169 *
170 * Templates size images in dots, but declare paper in character columns, so
171 * every lane that puts an image on paper has to bridge the two. Shared here
172 * because an image scaled against 576 dots on one lane and 384 on another is
173 * the same divergence this whole table exists to prevent.
174 *
175 * @param int $columns The paper width in character columns.
176 *
177 * @return int The printable width in dots.
178 */
179 public static function paper_dots( int $columns ): int {
180 return $columns >= self::WIDE_PAPER_COLUMNS ? self::DOTS_80MM : self::DOTS_58MM;
181 }
182
183 /**
184 * Clamp a value into one of the ranges above.
185 *
186 * Thermal_Markup_Parser already clamps every attribute on its way into the
187 * AST, so for parsed templates this is a no-op. It is here for ASTs built by
188 * hand — tests, and any future caller that skips the parser — because a
189 * bound the emitters do not enforce themselves is a bound that stops holding
190 * the moment someone builds a node directly.
191 *
192 * @param mixed $value The candidate value.
193 * @param int $fallback The fallback for missing/non-numeric values.
194 * @param int $min The lowest legal value.
195 * @param int $max The highest legal value.
196 *
197 * @return int The clamped integer.
198 */
199 public static function clamp_int( $value, int $fallback, int $min, int $max ): int {
200 if ( ! is_numeric( $value ) ) {
201 return $fallback;
202 }
203
204 return max( $min, min( $max, (int) $value ) );
205 }
206 }
207