1<?php
2/**
3 * This file is part of escpos-php: PHP receipt printer library for use with
4 * ESC/POS-compatible thermal and impact printers.
5 *
6 * Copyright (c) 2014-18 Michael Billington < michael.billington@gmail.com >,
7 * incorporating modifications by others. See CONTRIBUTORS.md for a full list.
8 *
9 * This software is distributed under the terms of the MIT license. See LICENSE.md
10 * for details.
11 */
12
13namespace Mike42\Escpos;
14
15use Exception;
16use InvalidArgumentException;
17use Mike42\Escpos\PrintBuffers\PrintBuffer;
18use Mike42\Escpos\PrintBuffers\EscposPrintBuffer;
19use Mike42\Escpos\PrintConnectors\PrintConnector;
20use Mike42\Escpos\CapabilityProfile;
21
22/**
23 * Main class for ESC/POS code generation
24 */
25class Printer
26{
27    /**
28     * ASCII null control character
29     */
30    const NUL = "\x00";
31
32    /**
33     * ASCII linefeed control character
34     */
35    const LF = "\x0a";
36
37    /**
38     * ASCII escape control character
39     */
40    const ESC = "\x1b";
41
42    /**
43     * ASCII form separator control character
44     */
45    const FS = "\x1c";
46
47    /**
48     * ASCII form feed control character
49     */
50    const FF = "\x0c";
51
52    /**
53     * ASCII group separator control character
54     */
55    const GS = "\x1d";
56
57    /**
58     * ASCII data link escape control character
59     */
60    const DLE = "\x10";
61
62    /**
63     * ASCII end of transmission control character
64     */
65    const EOT = "\x04";
66
67    /**
68     * Indicates UPC-A barcode when used with Printer::barcode
69     */
70    const BARCODE_UPCA = 65;
71
72    /**
73     * Indicates UPC-E barcode when used with Printer::barcode
74     */
75    const BARCODE_UPCE = 66;
76
77    /**
78     * Indicates JAN13 barcode when used with Printer::barcode
79     */
80    const BARCODE_JAN13 = 67;
81
82    /**
83     * Indicates JAN8 barcode when used with Printer::barcode
84     */
85    const BARCODE_JAN8 = 68;
86
87    /**
88     * Indicates CODE39 barcode when used with Printer::barcode
89     */
90    const BARCODE_CODE39 = 69;
91
92    /**
93     * Indicates ITF barcode when used with Printer::barcode
94     */
95    const BARCODE_ITF = 70;
96
97    /**
98     * Indicates CODABAR barcode when used with Printer::barcode
99     */
100    const BARCODE_CODABAR = 71;
101
102    /**
103     * Indicates CODE93 barcode when used with Printer::barcode
104     */
105    const BARCODE_CODE93 = 72;
106
107    /**
108     * Indicates CODE128 barcode when used with Printer::barcode
109     */
110    const BARCODE_CODE128 = 73;
111
112    /**
113     * Indicates that HRI (human-readable interpretation) text should not be
114     * printed, when used with Printer::setBarcodeTextPosition
115     */
116    const BARCODE_TEXT_NONE = 0;
117
118    /**
119     * Indicates that HRI (human-readable interpretation) text should be printed
120     * above a barcode, when used with Printer::setBarcodeTextPosition
121     */
122    const BARCODE_TEXT_ABOVE = 1;
123
124    /**
125     * Indicates that HRI (human-readable interpretation) text should be printed
126     * below a barcode, when used with Printer::setBarcodeTextPosition
127     */
128    const BARCODE_TEXT_BELOW = 2;
129
130    /**
131     * Use the first color (usually black), when used with Printer::setColor
132     */
133    const COLOR_1 = 0;
134
135    /**
136     * Use the second color (usually red or blue), when used with Printer::setColor
137     */
138    const COLOR_2 = 1;
139
140    /**
141     * Make a full cut, when used with Printer::cut
142     */
143    const CUT_FULL = 65;
144
145    /**
146     * Make a partial cut, when used with Printer::cut
147     */
148    const CUT_PARTIAL = 66;
149
150    /**
151     * Use Font A, when used with Printer::setFont
152     */
153    const FONT_A = 0;
154
155    /**
156     * Use Font B, when used with Printer::setFont
157     */
158    const FONT_B = 1;
159
160    /**
161     * Use Font C, when used with Printer::setFont
162     */
163    const FONT_C = 2;
164
165    /**
166     * Use default (high density) image size, when used with Printer::graphics,
167     * Printer::bitImage or Printer::bitImageColumnFormat
168     */
169    const IMG_DEFAULT = 0;
170
171    /**
172     * Use lower horizontal density for image printing, when used with Printer::graphics,
173     * Printer::bitImage or Printer::bitImageColumnFormat
174     */
175    const IMG_DOUBLE_WIDTH = 1;
176
177    /**
178     * Use lower vertical density for image printing, when used with Printer::graphics,
179     * Printer::bitImage or Printer::bitImageColumnFormat
180     */
181    const IMG_DOUBLE_HEIGHT = 2;
182
183    /**
184     * Align text to the left, when used with Printer::setJustification
185     */
186    const JUSTIFY_LEFT = 0;
187
188    /**
189     * Center text, when used with Printer::setJustification
190     */
191    const JUSTIFY_CENTER = 1;
192
193    /**
194     * Align text to the right, when used with Printer::setJustification
195     */
196    const JUSTIFY_RIGHT = 2;
197
198    /**
199     * Use Font A, when used with Printer::selectPrintMode
200     */
201    const MODE_FONT_A = 0;
202
203    /**
204     * Use Font B, when used with Printer::selectPrintMode
205     */
206    const MODE_FONT_B = 1;
207
208    /**
209     * Use text emphasis, when used with Printer::selectPrintMode
210     */
211    const MODE_EMPHASIZED = 8;
212
213    /**
214     * Use double height text, when used with Printer::selectPrintMode
215     */
216    const MODE_DOUBLE_HEIGHT = 16;
217
218    /**
219     * Use double width text, when used with Printer::selectPrintMode
220     */
221    const MODE_DOUBLE_WIDTH = 32;
222
223    /**
224     * Underline text, when used with Printer::selectPrintMode
225     */
226    const MODE_UNDERLINE = 128;
227
228    /**
229     * Indicates standard PDF417 code
230     */
231    const PDF417_STANDARD = 0;
232
233    /**
234     * Indicates truncated PDF417 code
235     */
236    const PDF417_TRUNCATED = 1;
237
238    /**
239     * Indicates error correction level L when used with Printer::qrCode
240     */
241    const QR_ECLEVEL_L = 0;
242
243    /**
244     * Indicates error correction level M when used with Printer::qrCode
245     */
246    const QR_ECLEVEL_M = 1;
247
248    /**
249     * Indicates error correction level Q when used with Printer::qrCode
250     */
251    const QR_ECLEVEL_Q = 2;
252
253    /**
254     * Indicates error correction level H when used with Printer::qrCode
255     */
256    const QR_ECLEVEL_H = 3;
257
258    /**
259     * Indicates QR model 1 when used with Printer::qrCode
260     */
261    const QR_MODEL_1 = 1;
262
263    /**
264     * Indicates QR model 2 when used with Printer::qrCode
265     */
266    const QR_MODEL_2 = 2;
267
268    /**
269     * Indicates micro QR code when used with Printer::qrCode
270     */
271    const QR_MICRO = 3;
272
273    /**
274     * Indicates a request for printer status when used with
275     * Printer::getPrinterStatus (experimental)
276     */
277    const STATUS_PRINTER = 1;
278
279    /**
280     * Indicates a request for printer offline cause when used with
281     * Printer::getPrinterStatus (experimental)
282     */
283    const STATUS_OFFLINE_CAUSE = 2;
284
285    /**
286     * Indicates a request for error cause when used with Printer::getPrinterStatus
287     * (experimental)
288     */
289    const STATUS_ERROR_CAUSE = 3;
290
291    /**
292     * Indicates a request for error cause when used with Printer::getPrinterStatus
293     * (experimental)
294     */
295    const STATUS_PAPER_ROLL = 4;
296
297    /**
298     * Indicates a request for ink A status when used with Printer::getPrinterStatus
299     * (experimental)
300     */
301    const STATUS_INK_A = 7;
302
303    /**
304     * Indicates a request for ink B status when used with Printer::getPrinterStatus
305     * (experimental)
306     */
307    const STATUS_INK_B = 6;
308
309    /**
310     * Indicates a request for peeler status when used with Printer::getPrinterStatus
311     * (experimental)
312     */
313    const STATUS_PEELER = 8;
314
315    /**
316     * Indicates no underline when used with Printer::setUnderline
317     */
318    const UNDERLINE_NONE = 0;
319
320    /**
321     * Indicates single underline when used with Printer::setUnderline
322     */
323    const UNDERLINE_SINGLE = 1;
324
325    /**
326     * Indicates double underline when used with Printer::setUnderline
327     */
328    const UNDERLINE_DOUBLE = 2;
329
330    /**
331     * @var PrintBuffer|null $buffer
332     *  The printer's output buffer.
333     */
334    protected $buffer;
335
336    /**
337     * @var PrintConnector $connector
338     *  Connector showing how to print to this printer
339     */
340    protected $connector;
341
342    /**
343     * @var CapabilityProfile $profile
344     *  Profile showing supported features for this printer
345     */
346    protected $profile;
347
348    /**
349     * @var int $characterTable
350     *  Current character code table
351     */
352    protected $characterTable;
353
354    /**
355     * Construct a new print object
356     *
357     * @param PrintConnector $connector The PrintConnector to send data to. If not set, output is sent to standard output.
358     * @param CapabilityProfile|null $profile Supported features of this printer. If not set, the "default" CapabilityProfile will be used, which is suitable for Epson printers.
359     * @throws InvalidArgumentException
360     */
361    public function __construct(PrintConnector $connector, CapabilityProfile $profile = null)
362    {
363        /* Set connector */
364        $this -> connector = $connector;
365
366        /* Set capability profile */
367        if ($profile === null) {
368            $profile = CapabilityProfile::load('default');
369        }
370        $this -> profile = $profile;
371        /* Set buffer */
372        $buffer = new EscposPrintBuffer();
373        $this -> buffer = null;
374        $this -> setPrintBuffer($buffer);
375        $this -> initialize();
376    }
377
378    /**
379     * Print a barcode.
380     *
381     * @param string $content The information to encode.
382     * @param int $type The barcode standard to output. Supported values are
383     * `Printer::BARCODE_UPCA`, `Printer::BARCODE_UPCE`, `Printer::BARCODE_JAN13`,
384     * `Printer::BARCODE_JAN8`, `Printer::BARCODE_CODE39`, `Printer::BARCODE_ITF`,
385     * `Printer::BARCODE_CODABAR`, `Printer::BARCODE_CODE93`, and `Printer::BARCODE_CODE128`.
386     * If not specified, `Printer::BARCODE_CODE39` will be used. Note that some
387     * barcode formats only support specific lengths or sets of characters, and that
388     * available barcode types vary between printers.
389     * @throws InvalidArgumentException Where the length or characters used in $content is invalid for the requested barcode format.
390     */
391    public function barcode($content, $type = Printer::BARCODE_CODE39)
392    {
393        /* Validate input */
394        self::validateInteger($type, 65, 73, __FUNCTION__, "Barcode type");
395        $len = strlen($content);
396        switch ($type) {
397            case self::BARCODE_UPCA:
398                self::validateInteger($len, 11, 12, __FUNCTION__, "UPCA barcode content length");
399                self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{11,12}$/", "UPCA barcode content");
400                break;
401            case self::BARCODE_UPCE:
402                self::validateIntegerMulti($len, [[6, 8], [11, 12]], __FUNCTION__, "UPCE barcode content length");
403                self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{6,8}|[0-9]{11,12})$/", "UPCE barcode content");
404                break;
405            case self::BARCODE_JAN13:
406                self::validateInteger($len, 12, 13, __FUNCTION__, "JAN13 barcode content length");
407                self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{12,13}$/", "JAN13 barcode content");
408                break;
409            case self::BARCODE_JAN8:
410                self::validateInteger($len, 7, 8, __FUNCTION__, "JAN8 barcode content length");
411                self::validateStringRegex($content, __FUNCTION__, "/^[0-9]{7,8}$/", "JAN8 barcode content");
412                break;
413            case self::BARCODE_CODE39:
414                self::validateInteger($len, 1, 255, __FUNCTION__, "CODE39 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format.
415                self::validateStringRegex($content, __FUNCTION__, "/^([0-9A-Z \$\%\+\-\.\/]+|\*[0-9A-Z \$\%\+\-\.\/]+\*)$/", "CODE39 barcode content");
416                break;
417            case self::BARCODE_ITF:
418                self::validateInteger($len, 2, 255, __FUNCTION__, "ITF barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format.
419                self::validateStringRegex($content, __FUNCTION__, "/^([0-9]{2})+$/", "ITF barcode content");
420                break;
421            case self::BARCODE_CODABAR:
422                self::validateInteger($len, 1, 255, __FUNCTION__, "Codabar barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format.
423                self::validateStringRegex($content, __FUNCTION__, "/^[A-Da-d][0-9\$\+\-\.\/\:]+[A-Da-d]$/", "Codabar barcode content");
424                break;
425            case self::BARCODE_CODE93:
426                self::validateInteger($len, 1, 255, __FUNCTION__, "Code93 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format.
427                self::validateStringRegex($content, __FUNCTION__, "/^[\\x00-\\x7F]+$/", "Code93 barcode content");
428                break;
429            case self::BARCODE_CODE128:
430                self::validateInteger($len, 1, 255, __FUNCTION__, "Code128 barcode content length"); // 255 is a limitation of the "function b" command, not the barcode format.
431                // The CODE128 encoder is quite complex, so only a very basic header-check is applied here.
432                self::validateStringRegex($content, __FUNCTION__, "/^\{[A-C][\\x00-\\x7F]+$/", "Code128 barcode content");
433                break;
434        }
435        if (!$this -> profile -> getSupportsBarcodeB()) {
436            // A simpler barcode command which supports fewer codes
437            self::validateInteger($type, 65, 71, __FUNCTION__);
438            $this -> connector -> write(self::GS . "k" . chr($type - 65) . $content . self::NUL);
439            return;
440        }
441        // More advanced function B, used in preference
442        $this -> connector -> write(self::GS . "k" . chr($type) . chr(strlen($content)) . $content);
443    }
444
445    /**
446     * Print an image, using the older "bit image" command. This creates padding on the right of the image,
447     * if its width is not divisible by 8.
448     *
449     * Should only be used if your printer does not support the graphics() command.
450     * See also bitImageColumnFormat().
451     *
452     * @param EscposImage $img The image to print
453     * @param int $size Size modifier for the image. Must be either `Printer::IMG_DEFAULT`
454     *  (default), or any combination of the `Printer::IMG_DOUBLE_HEIGHT` and
455     *  `Printer::IMG_DOUBLE_WIDTH` flags.
456     */
457    public function bitImage(EscposImage $img, $size = Printer::IMG_DEFAULT)
458    {
459        self::validateInteger($size, 0, 3, __FUNCTION__);
460        $rasterData = $img -> toRasterFormat();
461        $header = Printer::dataHeader([$img -> getWidthBytes(), $img -> getHeight()], true);
462        $this -> connector -> write(self::GS . "v0" . chr($size) . $header);
463        $this -> connector -> write($rasterData);
464    }
465
466    /**
467     * Print an image, using the older "bit image" command in column format.
468     *
469     * Should only be used if your printer does not support the graphics() or
470     * bitImage() commands.
471     *
472     * @param EscposImage $img The image to print
473     * @param int $size Size modifier for the image. Must be either `Printer::IMG_DEFAULT`
474     *  (default), or any combination of the `Printer::IMG_DOUBLE_HEIGHT` and
475     *  `Printer::IMG_DOUBLE_WIDTH` flags.
476     */
477    public function bitImageColumnFormat(EscposImage $img, $size = Printer::IMG_DEFAULT)
478    {
479        $highDensityVertical = ! (($size & self::IMG_DOUBLE_HEIGHT) == Printer::IMG_DOUBLE_HEIGHT);
480        $highDensityHorizontal = ! (($size & self::IMG_DOUBLE_WIDTH) == Printer::IMG_DOUBLE_WIDTH);
481        // Experimental column format printing
482        // This feature is not yet complete and may produce unpredictable results.
483        $this -> setLineSpacing(16); // 16-dot line spacing. This is the correct value on both TM-T20 and TM-U220
484        // Header and density code (0, 1, 32, 33) re-used for every line
485        $densityCode = ($highDensityHorizontal ? 1 : 0) + ($highDensityVertical ? 32 : 0);
486        $colFormatData = $img -> toColumnFormat($highDensityVertical);
487        $header = Printer::dataHeader([$img -> getWidth()], true);
488        foreach ($colFormatData as $line) {
489            // Print each line, double density etc for printing are set here also
490            $this -> connector -> write(self::ESC . "*" . chr($densityCode) . $header . $line);
491            $this -> feed();
492            // sleep(0.1); // Reduces the amount of trouble that a TM-U220 has keeping up with large images
493        }
494        $this -> setLineSpacing(); // Revert to default line spacing
495    }
496
497    /**
498     * Close the underlying buffer. With some connectors, the
499     * job will not actually be sent to the printer until this is called.
500     */
501    public function close()
502    {
503        $this -> connector -> finalize();
504    }
505
506    /**
507     * Cut the paper.
508     *
509     * @param int $mode Cut mode, either Printer::CUT_FULL or Printer::CUT_PARTIAL. If not specified, `Printer::CUT_FULL` will be used.
510     * @param int $lines Number of lines to feed
511     */
512    public function cut($mode = Printer::CUT_FULL, $lines = 3)
513    {
514        // TODO validation on cut() inputs
515        $this -> connector -> write(self::GS . "V" . chr($mode) . chr($lines));
516    }
517
518    /**
519     * Print and feed line / Print and feed n lines.
520     *
521     * @param int $lines Number of lines to feed
522     */
523    public function feed($lines = 1)
524    {
525        self::validateInteger($lines, 1, 255, __FUNCTION__);
526        if ($lines <= 1) {
527            $this -> connector -> write(self::LF);
528        } else {
529            $this -> connector -> write(self::ESC . "d" . chr($lines));
530        }
531    }
532
533    /**
534     * Some printers require a form feed to release the paper. On most printers, this
535     * command is only useful in page mode, which is not implemented in this driver.
536     */
537    public function feedForm()
538    {
539        $this -> connector -> write(self::FF);
540    }
541
542    /**
543     * Some slip printers require `ESC q` sequence to release the paper.
544     */
545    public function release()
546    {
547        $this -> connector -> write(self::ESC . chr(113));
548    }
549
550    /**
551     * Print and reverse feed n lines.
552     *
553     * @param int $lines number of lines to feed. If not specified, 1 line will be fed.
554     */
555    public function feedReverse($lines = 1)
556    {
557        self::validateInteger($lines, 1, 255, __FUNCTION__);
558        $this -> connector -> write(self::ESC . "e" . chr($lines));
559    }
560
561    /**
562     * @return number
563     */
564    public function getCharacterTable()
565    {
566        return $this -> characterTable;
567    }
568
569    /**
570     * @return PrintBuffer
571     */
572    public function getPrintBuffer()
573    {
574        return $this -> buffer;
575    }
576
577    /**
578     * @return PrintConnector
579     */
580    public function getPrintConnector()
581    {
582        return $this -> connector;
583    }
584
585    /**
586     * @return CapabilityProfile
587     */
588    public function getPrinterCapabilityProfile()
589    {
590        return $this -> profile;
591    }
592
593    /**
594     * Print an image to the printer.
595     *
596     * Size modifiers are:
597     * - Printer::IMG_DEFAULT (leave image at original size)
598     * - Printer::IMG_DOUBLE_WIDTH
599     * - Printer::IMG_DOUBLE_HEIGHT
600     *
601     * See the example/ folder for detailed examples.
602     *
603     * The functions bitImage() and bitImageColumnFormat() take the same
604     * parameters, and can be used if your printer doesn't support the newer
605     * graphics commands.
606     *
607     * @param EscposImage $img The image to print.
608     * @param int $size Size modifier for the image. Must be either `Printer::IMG_DEFAULT`
609     *  (default), or any combination of the `Printer::IMG_DOUBLE_HEIGHT` and
610     *  `Printer::IMG_DOUBLE_WIDTH` flags.
611     */
612    public function graphics(EscposImage $img, $size = Printer::IMG_DEFAULT)
613    {
614        self::validateInteger($size, 0, 3, __FUNCTION__);
615        $rasterData = $img -> toRasterFormat();
616        $imgHeader = Printer::dataHeader([$img -> getWidth(), $img -> getHeight()], true);
617        $tone = '0';
618        $colors = '1';
619        $xm = (($size & self::IMG_DOUBLE_WIDTH) == Printer::IMG_DOUBLE_WIDTH) ? chr(2) : chr(1);
620        $ym = (($size & self::IMG_DOUBLE_HEIGHT) == Printer::IMG_DOUBLE_HEIGHT) ? chr(2) : chr(1);
621        $header = $tone . $xm . $ym . $colors . $imgHeader;
622        $this -> wrapperSendGraphicsData('0', 'p', $header . $rasterData);
623        $this -> wrapperSendGraphicsData('0', '2');
624    }
625
626    /**
627     * Initialize printer. This resets formatting back to the defaults.
628     */
629    public function initialize()
630    {
631        $this -> connector -> write(self::ESC . "@");
632        $this -> characterTable = 0;
633    }
634
635    /**
636     * Print a two-dimensional data code using the PDF417 standard.
637     *
638     * @param string $content Text or numbers to store in the code
639     * @param int $width Width of a module (pixel) in the printed code.
640     *  Default is 3 dots.
641     * @param int $heightMultiplier Multiplier for height of a module.
642     *  Default is 3 times the width.
643     * @param int $dataColumnCount Number of data columns to use. 0 (default)
644     *  is to auto-calculate. Smaller numbers will result in a narrower code,
645     *  making larger pixel sizes possible. Larger numbers require smaller pixel sizes.
646     * @param float $ec Error correction ratio, from 0.01 to 4.00. Default is 0.10 (10%).
647     * @param int $options Standard code Printer::PDF417_STANDARD with
648     *  start/end bars, or truncated code Printer::PDF417_TRUNCATED with start bars only.
649     * @throws Exception If this profile indicates that PDF417 code is not supported
650     */
651    public function pdf417Code($content, $width = 3, $heightMultiplier = 3, $dataColumnCount = 0, $ec = 0.10, $options = Printer::PDF417_STANDARD)
652    {
653        self::validateString($content, __FUNCTION__, 'content');
654        self::validateInteger($width, 2, 8, __FUNCTION__, 'width');
655        self::validateInteger($heightMultiplier, 2, 8, __FUNCTION__, 'heightMultiplier');
656        self::validateInteger($dataColumnCount, 0, 30, __FUNCTION__, 'dataColumnCount');
657        self::validateFloat($ec, 0.01, 4.00, __FUNCTION__, 'ec');
658        self::validateInteger($options, 0, 1, __FUNCTION__, 'options');
659        if ($content == "") {
660            return;
661        }
662        if (!$this -> profile -> getSupportsPdf417Code()) {
663            // TODO use software rendering via a library instead
664            throw new Exception("PDF417 codes are not supported on your printer.");
665        }
666        $cn = '0'; // Code type for pdf417 code
667        // Select model: standard or truncated
668        $this -> wrapperSend2dCodeData(chr(70), $cn, chr($options));
669        // Column count
670        $this -> wrapperSend2dCodeData(chr(65), $cn, chr($dataColumnCount));
671        // Set dot sizes
672        $this -> wrapperSend2dCodeData(chr(67), $cn, chr($width));
673        $this -> wrapperSend2dCodeData(chr(68), $cn, chr($heightMultiplier));
674        // Set error correction ratio: 1% to 400%
675        $ec_int = (int)ceil(floatval($ec) * 10);
676        $this -> wrapperSend2dCodeData(chr(69), $cn, chr($ec_int), '1');
677        // Send content & print
678        $this -> wrapperSend2dCodeData(chr(80), $cn, $content, '0');
679        $this -> wrapperSend2dCodeData(chr(81), $cn, '', '0');
680    }
681
682    /**
683     * Generate a pulse, for opening a cash drawer if one is connected.
684     * The default settings should open an Epson drawer.
685     *
686     * @param int $pin 0 or 1, for pin 2 or pin 5 kick-out connector respectively.
687     * @param int $on_ms pulse ON time, in milliseconds.
688     * @param int $off_ms pulse OFF time, in milliseconds.
689     */
690    public function pulse($pin = 0, $on_ms = 120, $off_ms = 240)
691    {
692        self::validateInteger($pin, 0, 1, __FUNCTION__);
693        self::validateInteger($on_ms, 1, 511, __FUNCTION__);
694        self::validateInteger($off_ms, 1, 511, __FUNCTION__);
695        $this -> connector -> write(self::ESC . "p" . chr($pin + 48) . chr($on_ms / 2) . chr($off_ms / 2));
696    }
697
698    /**
699     * Print the given data as a QR code on the printer.
700     *
701     * @param string $content The content of the code. Numeric data will be more efficiently compacted.
702     * @param int $ec Error-correction level to use. One of Printer::QR_ECLEVEL_L (default), Printer::QR_ECLEVEL_M, Printer::QR_ECLEVEL_Q or Printer::QR_ECLEVEL_H. Higher error correction results in a less compact code.
703     * @param int $size Pixel size to use. Must be 1-16 (default 3)
704     * @param int $model QR code model to use. Must be one of Printer::QR_MODEL_1, Printer::QR_MODEL_2 (default) or Printer::QR_MICRO (not supported by all printers).
705     */
706    public function qrCode($content, $ec = Printer::QR_ECLEVEL_L, $size = 3, $model = Printer::QR_MODEL_2)
707    {
708        self::validateString($content, __FUNCTION__);
709        self::validateInteger($ec, 0, 3, __FUNCTION__);
710        self::validateInteger($size, 1, 16, __FUNCTION__);
711        self::validateInteger($model, 1, 3, __FUNCTION__);
712        if ($content == "") {
713            return;
714        }
715        if (!$this -> profile -> getSupportsQrCode()) {
716            // TODO use software rendering via phpqrcode instead
717            throw new Exception("QR codes are not supported on your printer.");
718        }
719        $cn = '1'; // Code type for QR code
720        // Select model: 1, 2 or micro.
721        $this -> wrapperSend2dCodeData(chr(65), $cn, chr(48 + $model) . chr(0));
722        // Set dot size.
723        $this -> wrapperSend2dCodeData(chr(67), $cn, chr($size));
724        // Set error correction level: L, M, Q, or H
725        $this -> wrapperSend2dCodeData(chr(69), $cn, chr(48 + $ec));
726        // Send content & print
727        $this -> wrapperSend2dCodeData(chr(80), $cn, $content, '0');
728        $this -> wrapperSend2dCodeData(chr(81), $cn, '', '0');
729    }
730
731    /**
732     * Switch character table (code page) manually. Used in conjunction with textRaw() to
733     * print special characters which can't be encoded automatically.
734     *
735     * @param int $table The table to select. Available code tables are model-specific.
736     */
737    public function selectCharacterTable($table = 0)
738    {
739        self::validateInteger($table, 0, 255, __FUNCTION__);
740        $supported = $this -> profile -> getCodePages();
741        if (!isset($supported[$table])) {
742            throw new InvalidArgumentException("There is no code table $table allowed by this printer's capability profile.");
743        }
744        $this -> characterTable = $table;
745        if ($this -> profile -> getSupportsStarCommands()) {
746            /* Not an ESC/POS command: STAR printers stash all the extra code pages under a different command. */
747            $this -> connector -> write(self::ESC . self::GS . "t" . chr($table));
748            return;
749        }
750        $this -> connector -> write(self::ESC . "t" . chr($table));
751    }
752
753    /**
754     * Select print mode(s).
755     *
756     * Several MODE_* constants can be OR'd together passed to this function's `$mode` argument. The valid modes are:
757     *  - Printer::MODE_FONT_A
758     *  - Printer::MODE_FONT_B
759     *  - Printer::MODE_EMPHASIZED
760     *  - Printer::MODE_DOUBLE_HEIGHT
761     *  - Printer::MODE_DOUBLE_WIDTH
762     *  - Printer::MODE_UNDERLINE
763     *
764     * @param int $mode The mode to use. Default is Printer::MODE_FONT_A, with no special formatting. This has a similar effect to running initialize().
765     */
766    public function selectPrintMode($mode = Printer::MODE_FONT_A)
767    {
768        $allModes = Printer::MODE_FONT_B | self::MODE_EMPHASIZED | self::MODE_DOUBLE_HEIGHT | self::MODE_DOUBLE_WIDTH | self::MODE_UNDERLINE;
769        if (!is_integer($mode) || $mode < 0 || ($mode & $allModes) != $mode) {
770            throw new InvalidArgumentException("Invalid mode");
771        }
772
773        $this -> connector -> write(self::ESC . "!" . chr($mode));
774    }
775
776    /**
777     * Set barcode height.
778     *
779     * @param int $height Height in dots. If not specified, 8 will be used.
780     */
781    public function setBarcodeHeight($height = 8)
782    {
783        self::validateInteger($height, 1, 255, __FUNCTION__);
784        $this -> connector -> write(self::GS . "h" . chr($height));
785    }
786
787    /**
788     * Set barcode bar width.
789     *
790     * @param int $width Bar width in dots. If not specified, 3 will be used.
791     *  Values above 6 appear to have no effect.
792     */
793    public function setBarcodeWidth($width = 3)
794    {
795        self::validateInteger($width, 1, 255, __FUNCTION__);
796        $this -> connector -> write(self::GS . "w" . chr($width));
797    }
798
799    /**
800     * Set the position for the Human Readable Interpretation (HRI) of barcode characters.
801     *
802     * @param int $position. Use Printer::BARCODE_TEXT_NONE to hide the text (default),
803     *  or any combination of Printer::BARCODE_TEXT_ABOVE and Printer::BARCODE_TEXT_BELOW
804     *  flags to display the text.
805     */
806    public function setBarcodeTextPosition($position = Printer::BARCODE_TEXT_NONE)
807    {
808        self::validateInteger($position, 0, 3, __FUNCTION__, "Barcode text position");
809        $this -> connector -> write(self::GS . "H" . chr($position));
810    }
811
812    /**
813     * Turn double-strike mode on/off.
814     *
815     * @param boolean $on true for double strike, false for no double strike
816     */
817    public function setDoubleStrike($on = true)
818    {
819        self::validateBoolean($on, __FUNCTION__);
820        $this -> connector -> write(self::ESC . "G". ($on ? chr(1) : chr(0)));
821    }
822
823    /**
824     * Select print color on printers that support multiple colors.
825     *
826     * @param int $color Color to use. Must be either Printer::COLOR_1 (default), or Printer::COLOR_2.
827     */
828    public function setColor($color = Printer::COLOR_1)
829    {
830        self::validateInteger($color, 0, 1, __FUNCTION__, "Color");
831        $this -> connector -> write(self::ESC . "r" . chr($color));
832    }
833
834    /**
835     * Turn emphasized mode on/off.
836     *
837     *  @param boolean $on true for emphasis, false for no emphasis
838     */
839    public function setEmphasis($on = true)
840    {
841        self::validateBoolean($on, __FUNCTION__);
842        $this -> connector -> write(self::ESC . "E". ($on ? chr(1) : chr(0)));
843    }
844
845    /**
846     * Select font. Most printers have two fonts (Fonts A and B), and some have a third (Font C).
847     *
848     * @param int $font The font to use. Must be either Printer::FONT_A, Printer::FONT_B, or Printer::FONT_C.
849     */
850    public function setFont($font = Printer::FONT_A)
851    {
852        self::validateInteger($font, 0, 2, __FUNCTION__);
853        $this -> connector -> write(self::ESC . "M" . chr($font));
854    }
855
856    /**
857     * Select justification.
858     *
859     * @param int $justification One of Printer::JUSTIFY_LEFT, Printer::JUSTIFY_CENTER, or Printer::JUSTIFY_RIGHT.
860     */
861    public function setJustification($justification = Printer::JUSTIFY_LEFT)
862    {
863        self::validateInteger($justification, 0, 2, __FUNCTION__);
864        $this -> connector -> write(self::ESC . "a" . chr($justification));
865    }
866
867    /**
868     * Set the height of the line.
869     *
870     * Some printers will allow you to overlap lines with a smaller line feed.
871     *
872     * @param int|null $height The height of each line, in dots. If not set, the printer
873     *  will reset to its default line spacing.
874     */
875    public function setLineSpacing($height = null)
876    {
877        if ($height === null) {
878            // Reset to default
879            $this -> connector -> write(self::ESC . "2"); // Revert to default line spacing
880            return;
881        }
882        self::validateInteger($height, 1, 255, __FUNCTION__);
883        $this -> connector -> write(self::ESC . "3" . chr($height));
884    }
885
886    /**
887     * Set print area left margin. Reset to default with Printer::initialize()
888     *
889     * @param int $margin The left margin to set on to the print area, in dots.
890     */
891    public function setPrintLeftMargin($margin = 0)
892    {
893        self::validateInteger($margin, 0, 65535, __FUNCTION__);
894        $this -> connector -> write(Printer::GS . 'L' . self::intLowHigh($margin, 2));
895    }
896
897    /**
898     * Set print area width. This can be used to add a right margin to the print area.
899     * Reset to default with Printer::initialize()
900     *
901     * @param int $width The width of the page print area, in dots.
902     */
903    public function setPrintWidth($width = 512)
904    {
905        self::validateInteger($width, 1, 65535, __FUNCTION__);
906         $this -> connector -> write(Printer::GS . 'W' . self::intLowHigh($width, 2));
907    }
908
909    /**
910     * Attach a different print buffer to the printer. Buffers are responsible for handling text output to the printer.
911     *
912     * @param PrintBuffer $buffer The buffer to use.
913     * @throws InvalidArgumentException Where the buffer is already attached to a different printer.
914     */
915    public function setPrintBuffer(PrintBuffer $buffer)
916    {
917        if ($buffer === $this -> buffer) {
918            return;
919        }
920        if ($buffer -> getPrinter() != null) {
921            throw new InvalidArgumentException("This buffer is already attached to a printer.");
922        }
923        if ($this -> buffer !== null) {
924            $this -> buffer -> setPrinter(null);
925        }
926        $this -> buffer = $buffer;
927        $this -> buffer -> setPrinter($this);
928    }
929
930    /**
931     * Set black/white reverse mode on or off. In this mode, text is printed white on a black background.
932     *
933     * @param boolean $on True to enable, false to disable.
934     */
935    public function setReverseColors($on = true)
936    {
937        self::validateBoolean($on, __FUNCTION__);
938        $this -> connector -> write(self::GS . "B" . ($on ? chr(1) : chr(0)));
939    }
940
941    /**
942     * Set the size of text, as a multiple of the normal size.
943     *
944     * @param int $widthMultiplier Multiple of the regular height to use (range 1 - 8)
945     * @param int $heightMultiplier Multiple of the regular height to use (range 1 - 8)
946     */
947    public function setTextSize($widthMultiplier, $heightMultiplier)
948    {
949        self::validateInteger($widthMultiplier, 1, 8, __FUNCTION__);
950        self::validateInteger($heightMultiplier, 1, 8, __FUNCTION__);
951        $c = pow(2, 4) * ($widthMultiplier - 1) + ($heightMultiplier - 1);
952        $this -> connector -> write(self::GS . "!" . chr($c));
953    }
954
955    /**
956     * Set underline for printed text.
957     *
958     * Argument can be true/false, or one of UNDERLINE_NONE,
959     * UNDERLINE_SINGLE or UNDERLINE_DOUBLE.
960     *
961     * @param int $underline Either true/false, or one of Printer::UNDERLINE_NONE, Printer::UNDERLINE_SINGLE or Printer::UNDERLINE_DOUBLE. Defaults to Printer::UNDERLINE_SINGLE.
962     */
963    public function setUnderline($underline = Printer::UNDERLINE_SINGLE)
964    {
965        /* Map true/false to underline constants */
966        if ($underline === true) {
967            $underline = Printer::UNDERLINE_SINGLE;
968        } elseif ($underline === false) {
969            $underline = Printer::UNDERLINE_NONE;
970        }
971        /* Set the underline */
972        self::validateInteger($underline, 0, 2, __FUNCTION__);
973        $this -> connector -> write(self::ESC . "-" . chr($underline));
974    }
975
976    /**
977     * Print each line upside-down (180 degrees rotated).
978     *
979     * @param boolean $on True to enable, false to disable.
980     */
981    public function setUpsideDown($on = true)
982    {
983        self::validateBoolean($on, __FUNCTION__);
984        $this -> connector -> write(self::ESC . "{" . ($on ? chr(1) : chr(0)));
985    }
986
987    /**
988     * Add text to the buffer.
989     *
990     * Text should either be followed by a line-break, or feed() should be called
991     * after this to clear the print buffer.
992     *
993     * @param string $str Text to print
994     */
995    public function text($str = "")
996    {
997        self::validateString($str, __FUNCTION__);
998        $this -> buffer -> writeText((string)$str);
999    }
1000
1001    /**
1002     * Add Chinese text to the buffer. This is a specific workaround for the common Zijang printer- The printer will be switched to a two-byte mode and sent GBK-encoded text.
1003     *
1004     * Support for this will be merged into a print buffer.
1005     *
1006     * @param string $str Text to print, as UTF-8
1007     */
1008    public function textChinese($str = "")
1009    {
1010        self::validateString($str, __FUNCTION__);
1011        $this -> connector -> write(self::FS . "&");
1012        $str = iconv("UTF-8", "GBK//IGNORE", $str);
1013        $this -> buffer -> writeTextRaw((string)$str);
1014        $this -> connector -> write(self::FS . ".");
1015    }
1016
1017    /**
1018     * Add text to the buffer without attempting to interpret chararacter codes.
1019     *
1020     * Text should either be followed by a line-break, or feed() should be called
1021     * after this to clear the print buffer.
1022     *
1023     * @param string $str Text to print
1024     */
1025    public function textRaw($str = "")
1026    {
1027        self::validateString($str, __FUNCTION__);
1028        $this -> buffer -> writeTextRaw((string)$str);
1029    }
1030
1031    /**
1032     * Wrapper for GS ( k, to calculate and send correct data length.
1033     *
1034     * @param string $fn Function to use
1035     * @param string $cn Output code type. Affects available data
1036     * @param string $data Data to send.
1037     * @param string $m Modifier/variant for function. Often '0' where used.
1038     * @throws InvalidArgumentException Where the input lengths are bad.
1039     */
1040    protected function wrapperSend2dCodeData($fn, $cn, $data = '', $m = '')
1041    {
1042        if (strlen($m) > 1 || strlen($cn) != 1 || strlen($fn) != 1) {
1043            throw new InvalidArgumentException("wrapperSend2dCodeData: cn and fn must be one character each.");
1044        }
1045        $header = $this -> intLowHigh(strlen($data) + strlen($m) + 2, 2);
1046        $this -> connector -> write(self::GS . "(k" . $header . $cn . $fn . $m . $data);
1047    }
1048
1049    /**
1050     * Wrapper for GS ( L, to calculate and send correct data length.
1051     *
1052     * @param string $m Modifier/variant for function. Usually '0'.
1053     * @param string $fn Function number to use, as character.
1054     * @param string $data Data to send.
1055     * @throws InvalidArgumentException Where the input lengths are bad.
1056     */
1057    protected function wrapperSendGraphicsData($m, $fn, $data = '')
1058    {
1059        if (strlen($m) != 1 || strlen($fn) != 1) {
1060            throw new InvalidArgumentException("wrapperSendGraphicsData: m and fn must be one character each.");
1061        }
1062        $header = $this -> intLowHigh(strlen($data) + 2, 2);
1063        $this -> connector -> write(self::GS . "(L" . $header . $m . $fn . $data);
1064    }
1065
1066    /**
1067     * Convert widths and heights to characters. Used before sending graphics to set the size.
1068     *
1069     * @param array $inputs
1070     * @param boolean $long True to use 4 bytes, false to use 2
1071     * @return string
1072     */
1073    protected static function dataHeader(array $inputs, $long = true)
1074    {
1075        $outp = [];
1076        foreach ($inputs as $input) {
1077            if ($long) {
1078                $outp[] = Printer::intLowHigh($input, 2);
1079            } else {
1080                self::validateInteger($input, 0, 255, __FUNCTION__);
1081                $outp[] = chr($input);
1082            }
1083        }
1084        return implode("", $outp);
1085    }
1086
1087    /**
1088     * Generate two characters for a number: In lower and higher parts, or more parts as needed.
1089     *
1090     * @param int $input Input number
1091     * @param int $length The number of bytes to output (1 - 4).
1092     */
1093    protected static function intLowHigh($input, $length)
1094    {
1095        $maxInput = (256 << ($length * 8) - 1);
1096        self::validateInteger($length, 1, 4, __FUNCTION__);
1097        self::validateInteger($input, 0, $maxInput, __FUNCTION__);
1098        $outp = "";
1099        for ($i = 0; $i < $length; $i++) {
1100            $outp .= chr($input % 256);
1101            $input = (int)($input / 256);
1102        }
1103        return $outp;
1104    }
1105
1106    /**
1107     * Throw an exception if the argument given is not a boolean
1108     *
1109     * @param boolean $test the input to test
1110     * @param string $source the name of the function calling this
1111     */
1112    protected static function validateBoolean($test, $source)
1113    {
1114        if (!($test === true || $test === false)) {
1115            throw new InvalidArgumentException("Argument to $source must be a boolean");
1116        }
1117    }
1118
1119    /**
1120     * Throw an exception if the argument given is not a float within the specified range
1121     *
1122     * @param float $test the input to test
1123     * @param float $min the minimum allowable value (inclusive)
1124     * @param float $max the maximum allowable value (inclusive)
1125     * @param string $source the name of the function calling this
1126     * @param string $argument the name of the invalid parameter
1127     */
1128    protected static function validateFloat($test, $min, $max, $source, $argument = "Argument")
1129    {
1130        if (!is_numeric($test)) {
1131            throw new InvalidArgumentException("$argument given to $source must be a float, but '$test' was given.");
1132        }
1133        if ($test < $min || $test > $max) {
1134            throw new InvalidArgumentException("$argument given to $source must be in range $min to $max, but $test was given.");
1135        }
1136    }
1137
1138    /**
1139     * Throw an exception if the argument given is not an integer within the specified range
1140     *
1141     * @param int $test the input to test
1142     * @param int $min the minimum allowable value (inclusive)
1143     * @param int $max the maximum allowable value (inclusive)
1144     * @param string $source the name of the function calling this
1145     * @param string $argument the name of the invalid parameter
1146     */
1147    protected static function validateInteger($test, $min, $max, $source, $argument = "Argument")
1148    {
1149        self::validateIntegerMulti($test, [[$min, $max]], $source, $argument);
1150    }
1151
1152    /**
1153     * Throw an exception if the argument given is not an integer within one of the specified ranges
1154     *
1155     * @param int $test the input to test
1156     * @param array $ranges array of two-item min/max ranges.
1157     * @param string $source the name of the function calling this
1158     * @param string $source the name of the function calling this
1159     * @param string $argument the name of the invalid parameter
1160     */
1161    protected static function validateIntegerMulti($test, array $ranges, $source, $argument = "Argument")
1162    {
1163        if (!is_integer($test)) {
1164            throw new InvalidArgumentException("$argument given to $source must be a number, but '$test' was given.");
1165        }
1166        $match = false;
1167        foreach ($ranges as $range) {
1168            $match |= $test >= $range[0] && $test <= $range[1];
1169        }
1170        if (!$match) {
1171            // Put together a good error "range 1-2 or 4-6"
1172            $rangeStr = "range ";
1173            for ($i = 0; $i < count($ranges); $i++) {
1174                $rangeStr .= $ranges[$i][0] . "-" . $ranges[$i][1];
1175                if ($i == count($ranges) - 1) {
1176                    continue;
1177                } elseif ($i == count($ranges) - 2) {
1178                    $rangeStr .= " or ";
1179                } else {
1180                    $rangeStr .= ", ";
1181                }
1182            }
1183            throw new InvalidArgumentException("$argument given to $source must be in $rangeStr, but $test was given.");
1184        }
1185    }
1186
1187    /**
1188     * Throw an exception if the argument given can't be cast to a string
1189     *
1190     * @param string $test the input to test
1191     * @param string $source the name of the function calling this
1192     * @param string $argument the name of the parameter being validated
1193     * @throws InvalidArgumentException Where the argument is not valid
1194     */
1195    protected static function validateString($test, $source, $argument = "Argument")
1196    {
1197        if (is_object($test) && !method_exists($test, '__toString')) {
1198            throw new InvalidArgumentException("$argument to $source must be a string");
1199        }
1200    }
1201
1202    /**
1203     * Throw an exception if the argument doesn't match the given regex.
1204     *
1205     * @param string $test the input to test
1206     * @param string $source the name of the function calling this
1207     * @param string $regex valid values for this attribute, as a regex
1208     * @param string $argument the name of the parameter being validated
1209     * @throws InvalidArgumentException Where the argument is not valid
1210     */
1211    protected static function validateStringRegex($test, $source, $regex, $argument = "Argument")
1212    {
1213        if (preg_match($regex, $test) === 0) {
1214            throw new InvalidArgumentException("$argument given to $source is invalid. It should match regex '$regex', but '$test' was given.");
1215        }
1216    }
1217}
1218