Completed
Pull Request — master (#35)
by Roberto
03:15
created

DefaultPrinter::barcode()   B

Complexity

Conditions 5
Paths 8

Size

Total Lines 40
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 40
rs 8.439
cc 5
eloc 30
nc 8
nop 6
1
<?php
2
3
namespace Posprint\Printers;
4
5
/*
6
 * Default class for POS thermal printers.
7
 * 
8
 * From this class all other are extended.
9
 * In the child classes should be included all the commands that are different
10
 * from those in this class, especially those specific to particular brand and 
11
 * model of printer
12
 * 
13
 * NOTE: It was built around the commands of the Epson TM-T20,
14
 * so in theory the Epson class will be almost empty just extending this class.
15
 * 
16
 * CodePage default WINDOWS-1250
17
 * CountyPage default LATIN
18
 * 
19
 * @category   NFePHP
20
 * @package    Posprint
21
 * @copyright  Copyright (c) 2016
22
 * @license    http://www.gnu.org/licenses/lesser.html LGPL v3
23
 * @author     Roberto L. Machado <linux.rlm at gmail dot com>
24
 * @link       http://github.com/nfephp-org/posprint for the canonical source repository
25
 */
26
27
use Posprint\Connectors\ConnectorInterface;
28
use Posprint\Connectors\Buffer;
29
use Posprint\Graphics\Graphics;
30
use RuntimeException;
31
use InvalidArgumentException;
32
33
abstract class DefaultPrinter implements PrinterInterface
34
{
35
    //set standards
36
    const NUL = "\x0"; //Nulo
37
    const EOT = "\x4"; //EOT fim da transmissão
38
    const ENQ = "\x5"; //ENQ colocar na fila Pedido de status 1
39
    const HT = "\x9"; //tabulação horizontal
40
    const VT = "\xb"; //tabulação vertical
41
    const LF = "\x0a"; //Inicia a impressão e avança uma linha
42
    const FF = "\x0c"; //avança pagina
43
    const CR = "\x0d"; //retorno de carro
44
    const DLE = "\x10"; //Data Link Escape
45
    const CAN = "\x18"; //CAN Cancela linha enviada
46
    const BEL = "\x07"; //BEL sinal sonoro
47
    const ESC = "\x1b"; //escape
48
    const FS = "\x1c"; //FS
49
    const GS = "\x1d"; //GS
50
    const SO = "\x0e"; //SO Inicia modo expandido
51
    const DC1 = "\x11"; //DC1 Inicia modo enfatizado
52
    const DC2 = "\x12"; //DC2 Cancela modo condensado
53
    const DC3 = "\x13"; //DC3 Cancela modo enfatizado
54
    const DC4 = "\x14"; //DC4 Controle de dispositivo 4 Inicia modo normal
55
    const SI = "\x0f"; //Seleciona modo condensado
56
    const EM = "\x19"; //Avança 4 linhas
57
    const DEL = "\x7f"; //Cancela último caracter
58
    const SYN = "\x16"; //Sincronismo
59
    const NOTRANS = false; //not translate characters codepage
60
    const TRANS = true; //perform a character convertion to codepage
61
62
    // Cut types 
63
    const CUT_FULL = 65;
64
    const CUT_PARTIAL = 66;
65
66
    //1D barcode types
67
    const UPC_A = 'A';
68
    const UPC_E = 'B';
69
    const EAN13 = 'C';
70
    const EAN8 = 'D';
71
    const CODE39 = 'E';
72
    const ITF = 'F';
73
    const CODABAR = 'G';
74
    const CODE93 = 'H';
75
    const CODE128 = 'I';
76
    const GS1_128 = 'J';
77
    const GS1_DataBar_Omnidirectional = 'K';
78
    const GS1_DataBar_Truncated = 'L';
79
    const GS1_DataBar_Limited = 'M';
80
    const GS1_DataBar_Expanded = 'N';
81
82
    /**
83
     * List all available region pages.
84
     *
85
     * @var array
86
     */
87
    protected $aRegion = array(
88
        'USA',
89
        'FRANCE',
90
        'GERMANY',
91
        'UK',
92
        'DENMARK',
93
        'SWEDEN',
94
        'ITALY',
95
        'SPAIN',
96
        'JAPAN',
97
        'NORWAY',
98
        'DENMARK2',
99
        'SPAIN2',
100
        'LATIN',
101
        'KOREA',
102
        'SLOVENIA',
103
        'CHINA',
104
        'VIETNAM',
105
        'ARABIA',
106
    );
107
108
    /**
109
     * List all available code pages.
110
     *
111
     * @var array
112
     */
113
    protected $aCodePage = array(
114
        'CP437' => array('conv' => '437', 'table' => '0', 'desc' => 'PC437: USA, Standard Europe'),
115
        'CP850' => array('conv' => '850', 'table' => '2', 'desc' => 'PC850: Multilingual'),
116
        'CP860' => array('conv' => '860', 'table' => '3', 'desc' => 'PC860: Portuguese'),
117
        'CP863' => array('conv' => '863', 'table' => '4', 'desc' => 'PC863: Canadian-French'),
118
        'CP865' => array('conv' => '865', 'table' => '5', 'desc' => 'PC865: Nordic'),
119
        'CP851' => array('conv' => '851', 'table' => '11', 'desc' => 'PC851: Greek'),
120
        'CP853' => array('conv' => '853', 'table' => '12', 'desc' => 'PC853: Turkish'),
121
        'CP857' => array('conv' => '857', 'table' => '13', 'desc' => 'PC857: Turkish'),
122
        'CP737' => array('conv' => '737', 'table' => '14', 'desc' => 'PC737: Greek'),
123
        'ISO8859-7' => array('conv' => 'ISO8859-7', 'table' => '15', 'desc' => 'ISO8859-7: Greek'),
124
        'CP866' => array('conv' => '866', 'table' => '17', 'desc' => 'PC866: Cyrillic #2'),
125
        'CP852' => array('conv' => '852', 'table' => '18', 'desc' => 'PC852: Latin2'),
126
        'CP858' => array('conv' => '858', 'table' => '19', 'desc' => 'PC858: Euro'),
127
        'CP720' => array('conv' => '720', 'table' => '32', 'desc' => 'PC720: Arabic'),
128
        'CP855' => array('conv' => '855', 'table' => '34', 'desc' => 'PC855: Cyrillic'),
129
        'CP861' => array('conv' => '861', 'table' => '35', 'desc' => 'PC861: Icelandic'),
130
        'CP862' => array('conv' => '862', 'table' => '36', 'desc' => 'PC862: Hebrew'),
131
        'CP864' => array('conv' => '864', 'table' => '37', 'desc' => 'PC864: Arabic'),
132
        'CP869' => array('conv' => '869', 'table' => '38', 'desc' => 'PC869: Greek'),
133
        'ISO8859-2' => array('conv' => 'ISO8859-2', 'table' => '39', 'desc' => 'ISO8859-2: Latin2'),
134
        'ISO8859-15' => array('conv' => 'ISO8859-15', 'table' => '40', 'desc' => 'ISO8859-15: Latin9'),
135
        'WINDOWS-1250' => array('conv' => 'WINDOWS-1250', 'table' => '45', 'desc' => 'WPC1250: Latin2'),
136
        'WINDOWS-1251' => array('conv' => 'WINDOWS-1251', 'table' => '46', 'desc' => 'WPC1251: Cyrillic'),
137
        'WINDOWS-1252' => array('conv' => 'WINDOWS-1252', 'table' => '47', 'desc' => 'WPC1253: Greek'),
138
        'WINDOWS-1254' => array('conv' => 'WINDOWS-1254', 'table' => '48', 'desc' => 'WPC1254: Turkish'),
139
        'WINDOWS-1255' => array('conv' => 'WINDOWS-1255', 'table' => '49', 'desc' => 'WPC1255: Hebrew'),
140
        'WINDOWS-1256' => array('conv' => 'WINDOWS-1256', 'table' => '50', 'desc' => 'WPC1256: Arabic'),
141
        'WINDOWS-1257' => array('conv' => 'WINDOWS-1257', 'table' => '51', 'desc' => 'WPC1257: Baltic Rim'),
142
        'WINDOWS-1258' => array('conv' => 'WINDOWS-1258', 'table' => '52', 'desc' => 'WPC1258: Vietnamese'),
143
    );
144
    /**
145
     * Seleted code page
146
     * Defined in printer class.
147
     *
148
     * @var string
149
     */
150
    protected $codepage = 'CP437';
151
    /**
152
     * Number of codpage in printer memory.
153
     *
154
     * @var int
155
     */
156
    protected $charsetTableNum = 0;
157
    /**
158
     * Selected Region character page
159
     * Defined in printer class.
160
     *
161
     * @var string
162
     */
163
    protected $region = 'LATIN';
164
    /**
165
     * List all avaiable fonts
166
     * @var array
167
     */
168
    protected $aFont = array(0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 97 => 'SA', 98 => 'SB');
169
    /**
170
     * Selected internal font.
171
     *
172
     * @var string
173
     */
174
    protected $font = 'A';           
175
    /**
176
     * Resolution in dpi.
177
     *
178
     * @var int
179
     */
180
    public $dpi = 203; //dots per inch
181
    /**
182
     * Resolution in dpmm.
183
     *
184
     * @var int
185
     */
186
    public $dpmm = 8; //dots per mm
187
    /**
188
     * Maximum width paper.
189
     *
190
     * @var int
191
     */
192
    public $widthMaxmm = 80;//mm
193
    /**
194
     * Selected Width paper.
195
     *
196
     * @var int
197
     */
198
    public $widthPaper = 80;//mm
199
    /**
200
     * Maximum width for printed area.
201
     *
202
     * @var int
203
     */
204
    public $widthPrint = 72;//mm
205
    /**
206
     * Maximum width for printed area in dots.
207
     *
208
     * @var int
209
     */
210
    public $widthMaxdots = 576;//dots
211
    /**
212
     * Maximum number of characters per line.
213
     *
214
     * @var int
215
     */
216
    public $maxchars = 48;//max characters per line
217
218
    //protected property standards
219
    /**
220
     * Connector to printer.
221
     *
222
     * @var ConnectosInterface
223
     */
224
    protected $connector = null;
225
    /**
226
     * Seleted printer mode.
227
     *
228
     * @var string
229
     */
230
    protected $printerMode = 'normal';
231
    /**
232
     * Selected bold mode.
233
     *
234
     * @var bool
235
     */
236
    protected $boldMode = false;
237
    /**
238
     * Selected reverse colors mode.
239
     *
240
     * @var bool
241
     */
242
    protected $reverseColors = false;
243
    /**
244
     * Selected under lined mode.
245
     * 
246
     * @var bool
247
     */
248
    protected $underlineMode = false;
249
    /**
250
     * Selected rotate 90 degrees mode
251
     * @var bool
252
     */
253
    protected $rotateMode = false;
254
    /**
255
     * Buffer class.
256
     *
257
     * @var Connectors\Buffer
258
     */
259
    protected $buffer = null;
260
261
    /**
262
     * Class constructor
263
     * Instantiates the data buffer.
264
     *
265
     * @param ConnectorInterface $conn
266
     */
267
    public function __construct(ConnectorInterface $conn = null)
268
    {
269
        if (!is_null($conn)) {
270
            $this->connector = $conn;
0 ignored issues
show
Documentation Bug introduced by
It seems like $conn of type object<Posprint\Connectors\ConnectorInterface> is incompatible with the declared type object<Posprint\Printers\ConnectosInterface> of property $connector.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
271
        }
272
        $this->buffer = new Buffer();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Posprint\Connectors\Buffer() of type object<Posprint\Connectors\Buffer> is incompatible with the declared type object<Posprint\Printers\Connectors\Buffer> of property $buffer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
273
    }
274
275
    /**
276
     * Returns a default region for codepage
277
     * if param $region is null will return actual default region from class
278
     * if param $region is 'all' will return a array with all avaiable regions
279
     * if param $region is a string will set the region parameter of class and returns it.
280
     * NOTE: This command do not set the printer, only class parameters
281
     * 
282
     * @param string $region
283
     * @return string|array
284
     */
285
    public function defaultRegionPage($region = null)
286
    {
287
        if (!is_null($region)) {
288
            $region = strtoupper(trim($region));
289
            if ($region == 'ALL') {
290
                return $this->aRegion;
291
            }
292
            //set $this->region
293
            $reg = array_search($region, $this->aRegion, true);
294
            if ($reg !== false) {
295
                $this->region = $region;
296
            }
297
        }
298
        return $this->region;
299
    }
300
301
    /**
302
     * Returns a default codepage
303
     * if param $codepage is null will return actual default codepage from class
304
     * if param $codepage is 'all' will return a array with all avaiable codepages
305
     * if param $codepage is a string will set the codepage parameter of class and returns it.
306
     * NOTE: This command do not set the printer, only class parameters
307
     * 
308
     * @param string $codepage
309
     * @return string|array
310
     */
311
    public function defaultCodePage($codepage = null)
312
    {
313
        if (!is_null($codepage)) {
314
            $codepage = strtoupper(trim($codepage));
315
            if ($codepage == 'ALL') {
316
                return array_keys($this->aCodePage);
317
            }
318
            //set $this->codepage
319
            $reg = array_search($codepage, $this->aCodePage, true);
320
            if ($reg !== false) {
321
                $this->codepage = $codepage;
322
                $table = $this->aCodePage[$codepage];
323
                $this->charsetTableNum = $table['table'];
324
            }
325
        }
326
        return $this->codepage;
327
    }
328
329
    /**
330
     * Set a codepage table in printer.
331
     *
332
     * @param string $codepage
333
     */
334
    public function setCodePage($codepage = null)
335
    {
336
        $codepage = $this->defaultCodePage($codepage);
0 ignored issues
show
Unused Code introduced by
$codepage is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
337
        $this->buffer->write(self::ESC.'t'.chr($this->tableNum));
0 ignored issues
show
Bug introduced by
The property tableNum does not seem to exist. Did you mean charsetTableNum?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
338
    }
339
340
    /**
341
     * Set a region page.
342
     * The numeric key of array $this->aRegion is the command parameter.
343
     *
344
     * @param string $region
345
     */
346
    public function setRegionPage($region = null)
347
    {
348
        $region = $this->defaultRegionPage($region);
349
        $mode = array_keys($this->aRegion, $region, true);
350
        $this->buffer->write(self::ESC.'R'.chr($mode));
351
    }
352
    
353
    /**
354
     * Returns the default printer font 
355
     * A - Font A (12 x 24)
356
     * B - Font B (9 x 17)
357
     * C - Font C
358
     * D - Font D
359
     * E - Font E
360
     * Special A
361
     * Special B
362
     * Default Font A.
363
     * if param $font is null will return actual default font from class
364
     * if param $font is 'all' will return a array with all avaiable printer fonts
365
     * if param $font is a string will set the font parameter of class and returns it.
366
     * NOTE: This command do not set the printer, only class parameters
367
     * 
368
     * @param string $font
369
     * @return array|string
370
     */
371
    public function defaultFont($font = null)
372
    {
373
        if (!is_null($font)) {
374
            $font = strtoupper(trim($font));
375
            if ($font == 'ALL') {
376
                //return array
377
                return $this->aFont;
378
            }
379
            //set $this->font
380
            $fonts = array_flip($this->aFont);
381
            $keys = array_keys($fonts);
382
            $reg = array_search($font, $keys, true);
383
            if ($reg !== false) {
384
                $this->font = $font;
385
            }
386
        }
387
        return $this->font;
388
    }
389
    
390
    /**
391
     * Set a printer font 
392
     * If send a valid font name will set the printer otherelse a default font is selected
393
     * @param string $font
394
     */
395
    public function setFont($font = null)
396
    {
397
        $font = $this->defaultFont($font);
398
        $mode = array_keys($this->aFont, $font, true);
399
        $this->buffer->write(self::ESC.'M'.chr($mode));
400
    }
401
402
    /**
403
     * Set emphasys mode on or off.
404
     */
405
    public function setBold()
406
    {
407
        if ($this->boldMode) {
408
            $this->boldMode = false;
409
            $mode = 0;
410
        } else {
411
            $this->boldMode = true;
412
            $mode = 1;
413
        }
414
        $this->buffer->write(self::ESC . 'E' . chr($mode));
415
    }
416
417
    /**
418
     * Set underline mode on or off.
419
     */
420
    public function setUnderlined()
421
    {
422
        if ($this->underlineMode) {
423
            $this->underlineMode = false;
424
            $mode = 0;
425
        } else {
426
            $this->underlineMode = true;
427
            $mode = 1;
428
        }
429
        $this->buffer->write(self::ESC . '-' . chr($mode));
430
    }
431
432
    /**
433
     * Aligns all data in one line to the selected layout in standard mode.
434
     * L - left  C - center  R - rigth
435
     * 
436
     * @param string $align 
437
     */
438
    public function setAlign($align = null)
439
    {
440
        if (is_null($align)) {
441
            $align = 'L';
0 ignored issues
show
Unused Code introduced by
$align is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
442
        }
443
        $value = strtoupper($value);
0 ignored issues
show
Bug introduced by
The variable $value seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
444
        switch ($value) {
445
            case 'C':
446
               $mode = 1;
447
               break;
448
            case 'R':
449
               $mode = 2;
450
               break;
451
            default:
452
                $mode = 0;
453
        }
454
        $this->buffer->write(self::ESC . 'a' . chr($mode));
455
    }
456
    
457
    /**
458
     * Turns white/black reverse print On or Off for characters.
459
     * n = odd: On, n = even: Off.
460
     */
461
    public function setReverseColors()
462
    {
463
        if ($this->reverseColors) {
464
            $this->reverseColors = false;
465
            $mode = 0;
466
        } else {
467
            $this->reverseColors = true;
468
            $mode = 1;
469
        }
470
        $this->buffer->write(self::GS.'B'.chr($mode));
471
    }
472
    
473
    /**
474
     * Set expanded mode.
475
     * @param int $size multiplies normal size 1 - 8 
476
     */
477
    public function setExpanded($size = null)
478
    {
479
        if (is_null($size)) {
480
            $size = 1;
481
        }
482
        $aSize = [
483
            [0, 0],
484
            [16, 1],
485
            [32, 2],
486
            [48, 3],
487
            [64, 4],
488
            [80, 5],
489
            [96, 6],
490
            [112, 7]
491
        ];
492
        self::validateInteger($size, 1, 8, __FUNCTION__);
493
        $mode = $aSize[$size-1][0] + $aSize[$size-1][1];
494
        $this->buffer->write(self::ESC.'!'.chr($mode));
495
    }
496
497
    /**
498
     * Set condensed mode.
499
     */
500
    public function setCondensed()
501
    {
502
        $this->setExpanded(1);
503
        $this->setFont('B');
504
    }
505
    
506
507
    /**
508
     * Set the printer mode.
509
     */
510
    abstract public function setPrintMode($mode = null);
511
512
    /**
513
     * Set rotate 90 degrees.
514
     */
515
    public function setRotate90()
516
    {
517
        if ($this->rotateMode) {
518
            $this->rotateMode = false;
519
            $mode = 0;
520
        } else {
521
            $this->rotateMode = true;
522
            $mode = 1;
523
        }
524
        $this->buffer->write(self::ESC.'V'.chr($mode));
525
    }
526
    
527
    /**
528
     * initialize printer
529
     * Clears the data in the print buffer and resets the printer modes to 
530
     * the modes that were in effect when the power was turned on.
531
     */
532
    public function initialize()
533
    {
534
        $this->buffer->write(self::ESC.'@');
535
        $this->rotateMode = false;
536
        $this->defaultCodePage('CP470');
537
        $this->underlineMode = false;
538
        $this->boldMode = false;
539
        $this->printerMode = 'normal';
540
        $this->font = 'A';
541
    }
542
    
543
    /**
544
     * Send message or command to buffer
545
     * when sending commands is not required to convert characters,
546
     * so the variable may translate by false.
547
     * 
548
     * @param string $text
549
     */
550
    public function text($text = '')
551
    {
552
        self::validateString($text, __FUNCTION__);
553
        $text = $this->zTranslate($text);
0 ignored issues
show
Bug introduced by
The method zTranslate() does not exist on Posprint\Printers\DefaultPrinter. Did you maybe mean translate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
554
        $this->buffer->write($text);
555
    }
556
557
    /**
558
     * Set horizontal and vertical motion units
559
     * $horizontal => character spacing 1/x"
560
     * $vertical => line spacing 1/y".
561
     *
562
     * @param int $horizontal
563
     * @param int $vertical
564
     */
565
    public function setSpacing($horizontal = 30, $vertical = 30)
566
    {
567
        $this->buffer->write(self::GS.'P'.chr($horizontal).chr($vertical));
568
    }
569
570
    /**
571
     * Set right-side character spacing
572
     * 0 ≤ n ≤ 255 => 1/x".
573
     * 
574
     * @param int $value
575
     */
576
    public function setCharSpacing($value = 3)
577
    {
578
        $this->buffer->write(self::ESC.' '.chr($value));
579
    }
580
581
    /**
582
     * Line spacing
583
     * The default is set to zero and 30/180 "
584
     * any different number of zero will generate multiples of.
585
     *
586
     * @param int $paragraph
0 ignored issues
show
Bug introduced by
There is no parameter named $paragraph. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
587
     */
588
    public function setParagraph($value = 0)
589
    {   //n * 1/180-inch vertical motion
590
        //normal paragrafo 30/180" => 4.23 mm
591
        $paragraph = ceil($value);
592
        if ($paragraph == 0) {
593
            $this->buffer->write(self::ESC.'2');
594
            return;
595
        }
596
        if ($paragraph < 25) {
597
            $paragraph = 25;
598
        } elseif ($paragraph > 255) {
599
            $paragraph = 255;
600
        }
601
        $this->buffer->write(self::ESC.'3'.chr($paragraph));
602
    }
603
604
    /**
605
     * Prints data and feeds paper n lines
606
     * ESC d n Prints data and feeds paper n lines.
607
     *
608
     * @param type $lines
609
     */
610
    public function lineFeed($lines = 1)
611
    {
612
        if ($lines <= 1) {
613
            $this->buffer->write(self::LF);
614
        } else {
615
            $this->buffer->write(self::ESC.'d'.chr($lines));
616
        }
617
    }
618
619
    /**
620
     * Prints data and feeds paper n dots
621
     * ESC J n Prints data and feeds paper n dots.
622
     *
623
     * @param int $dots
624
     */
625
    public function dotFeed($dots = 1)
626
    {
627
        self::validateInteger($lines, 1, 80, __FUNCTION__);
0 ignored issues
show
Bug introduced by
The variable $lines does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
628
        $this->buffer->write(self::ESC.'J'.chr($dots));
629
    }
630
631
    /**
632
     * Generate a pulse, for opening a cash drawer if one is connected.
633
     * The default settings should open an Epson drawer.
634
     *
635
     * @param int $pin    0 or 1, for pin 2 or pin 5 kick-out connector respectively.
636
     * @param int $on_ms  pulse ON time, in milliseconds.
637
     * @param int $off_ms pulse OFF time, in milliseconds.
638
     */
639
    public function pulse($pin = 0, $on_ms = 120, $off_ms = 240)
640
    {
641
        self::validateInteger($pin, 0, 1, __FUNCTION__);
642
        self::validateInteger($on_ms, 1, 511, __FUNCTION__);
643
        self::validateInteger($off_ms, 1, 511, __FUNCTION__);
644
        $this->buffer->write(self::ESC.'p'.chr($pin + 48).chr($on_ms / 2).chr($off_ms / 2));
645
    }
646
647
    /**
648
     * Cut the paper.
649
     *
650
     * @param int $mode  FULL or PARTIAL. If not specified, FULL will be used. 
651
     * @param int $lines Number of lines to feed after cut
652
     */
653
    public function cut($mode = 'FULL', $lines = 3)
654
    {
655
        self::validateInteger($lines, 1, 10, __FUNCTION__);
656
        if ($mode == 'FULL') {
657
            $mode = self::CUT_FULL;
658
        } else {
659
            $mode = self::CUT_PARTIAL;
660
        }
661
        $this->buffer->write(self::GS.'V'.chr($mode).chr($lines));
662
    }
663
664
    /**
665
     * Implements barcodes
666
     * GS k m n d1...dn
667
     * Prints bar code. n specifies the data length.
668
     *   m    bar code system             number of d (=k)
669
     *  "A"     UPC-A                       11 or 12
670
     *  "B"     UPC-E                       6, 7, 8, 11 or 12
671
     *  "C"     JAN13 / EAN13               12 or 13
672
     *  "D"     JAN8 / EAN8                 7 or 8
673
     *  "E"     CODE39                      1 or more
674
     *  "F"     ITF                         even
675
     *  "G"     CODABAR (NW-7)              2 or more
676
     *  "H"     CODE93                      1–255
677
     *  "I"     CODE128                     2–255    
678
     *  "J"     GS1-128                     2–255    
679
     *  "K"     GS1 DataBar Omnidirectional 13
680
     *  "L"     GS1 DataBar Truncated       13
681
     *  "M"     GS1 DataBar Limited         13
682
     *  "N"     GS1 DataBar Expanded        2–255.
683
     * 
684
     *  GS h n Sets bar code height to n dots.
685
     *  GS w n Sets bar width of bar code. n = 2–6 (thin–thick)
686
     *  GS H n Selects print position of HRI characters.
687
     *           n = 0, "0": Not printed
688
     *           n = 1, "1": Above the bar code
689
     *           n = 2, "2": Below the bar code
690
     *           n = 3, "3": Both above and below the bar code
691
     *  GS f n Selects font for the HRI characters.
692
     *           n = 0, "0": Font A,
693
     *           n = 1, "1": Font B
694
     *
695
     * @param int    $type        Default CODE128
696
     * @param int    $height
697
     * @param int    $lineWidth
698
     * @param string $txtPosition
699
     * @param string $txtFont
700
     * @param string $data
701
     */
702
    public function barcode(
703
        $type = self::CODE128,
704
        $height = 162,
705
        $lineWidth = 3,
706
        $txtPosition = 'none',
707
        $txtFont = '',
708
        $data = '123456'
709
    ) {
710
        switch ($txtPosition) {
711
            case 'Above':
712
                $tPos = 1;
713
                break;
714
            case 'Below':
715
                $tPos = 2;
716
                break;
717
            case 'Both':
718
                $tPos = 3;
719
                break;
720
            default:
721
                //none
722
                $tPos = 0;
723
        }
724
        $font = 0;
725
        if ($txtFont === 'B') {
726
            $font = 1;
727
        }
728
        self::validateBarcodeData($type, $data, __FUNCTION__);
0 ignored issues
show
Documentation introduced by
$type is of type string|integer, but the function expects a object<Posprint\Printers\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$data is of type string, but the function expects a object<Posprint\Printers\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
__FUNCTION__ is of type string, but the function expects a object<Posprint\Printers\type>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
729
        self::validateInteger($lineWidth, 2, 6, __FUNCTION__);
730
        $nlen = len($data);
731
        //set barcode height
732
        $this->buffer->write(self::GS.'h'.chr($height));
733
        //set barcode bar width
734
        $this->buffer->write(self::GS.'w'.chr($lineWidth));
735
        //Selects print position of HRI characters.
736
        $this->buffer->write(self::GS.'H'.chr($tPos));
737
        //Selects font for the HRI characters.
738
        $this->buffer->write(self::GS.'f'.chr($font));
739
        //Print barcode
740
        $this->buffer->write(self::GS.'k'.chr($type).chr($nlen).$data);
741
    }
742
    
743
    /**
744
     * Imprime o QR Code
745
     * @param string $texto Dados a serem inseridos no QRCode
746
     * @param string $level Nivel de correção L,M,Q ou H
747
     * @param int $modelo modelo de QRCode 1, 2 ou 0 Micro
748
     * @param int $wmod largura da barra 3 ~ 16
749
     */
750
    public function barcodeQRCode($texto = '', $level = 'L', $modelo = 2, $wmod = 4)
751
    {
752
        //set model of QRCode
753
        $n1 = 50;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $n1. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
754
        if ($modelo == 1) {
755
            $n1 = 49;
756
        }
757
        $this->buffer->write(self::GS . "(k" . chr(4) . $chr(0) . chr(49) . chr(65) . chr($n1) .chr(0));
0 ignored issues
show
Bug introduced by
The variable $chr does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
758
        //set module bar width
759
        $this->buffer->write(self::GS . "(k" . chr(3) . $chr(0) . chr(49) . chr(67) . chr($wmod));
760
        //set error correction level
761
        $level = strtoupper($level);
762
        switch ($level) {
763
            case 'L':
764
                $n = 48;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $n. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
765
                break;
766
            case 'M':
767
                $n = 49;
768
                break;
769
            case 'Q':
770
                $n = 50;
771
                break;
772
            case 'H':
773
                $n = 51;
774
                break;
775
            default:
776
                $n = 49;
777
        }
778
        $this->buffer->write(self::GS . "(k" . chr(3) . $chr(0) . chr(49) . chr(69) . chr($n));
779
        //set data for QR Code assuming print only alphanumeric data
780
        $len = strlen($texto) + 3;
781
        $pH = ($len / 256);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $pH. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
782
        $pL = $len % 256;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $pL. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
783
        $this->buffer->write(self::GS . "(k" . chr($pL) . $chr($pH) . chr(49) . chr(80) . chr(48) . $texto);
784
        //Print QR Code
785
        $this->buffer->write(self::GS . "(k" . chr(3) . $chr(0) . chr(49) . chr(81) . chr(48));
786
    }
787
788
    /**
789
     * Close and clean buffer
790
     * All data will be lost.
791
     */
792
    public function close()
793
    {
794
        $this->buffer->close();
795
    }
796
797
    /**
798
     * Return all data buffer.
799
     *
800
     * @param string $type specifies the return format
801
     */
802
    public function getBuffer($type = '')
803
    {
804
        switch ($type) {
805
            case 'binA':
806
                //returns a binary array of buffer
807
                $resp = $this->buffer->getDataBinary(true);
808
                break;
809
            case 'binS':
810
                //returns a binary string of buffer
811
                $resp = $this->buffer->getDataBinary(false);
812
                break;
813
            case 'b64A':
814
                //returns a base64 encoded array of buffer
815
                $resp = $this->buffer->getDataBase64(true);
816
                break;
817
            case 'b64S':
818
                //returns a base64 encoded string of buffer
819
                $resp = $this->buffer->getDataBase64(false);
820
                break;
821
            case 'json':
822
                //returns a json encoded of array buffer
823
                $resp = $this->buffer->getDataJson();
824
                break;
825
            case 'readA':
826
                //returns a human readable format of array buffer
827
                //only for debug reasons
828
                $resp = $this->buffer->getDataReadable(true);
829
                break;
830
            default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
831
                //returns a human readable format of string buffer
832
                //only for debug reasons
833
                $resp = $this->buffer->getDataReadable(false);
834
        }
835
        return $resp;
836
    }
837
838
    /**
839
     * Send commands from buffer to connector printer.
840
     */
841
    public function send(ConnectorInterface $conn = null)
842
    {
843
        if (!is_null($conn)) {
844
            $this->connector = $conn;
0 ignored issues
show
Documentation Bug introduced by
It seems like $conn of type object<Posprint\Connectors\ConnectorInterface> is incompatible with the declared type object<Posprint\Printers\ConnectosInterface> of property $connector.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
845
        }
846
        if (is_null($this->connector)) {
847
            return $this->getBuffer();
848
        }
849
        $aCmds = $this->getBuffer('binA');
850
        foreach ($aCmds as $cmd) {
851
            $this->connector->write($cmd);
852
        }
853
    }
854
855
    /**
856
     * 
857
     * @param type $type
858
     * @param type $data
859
     * @param type $source
860
     * @throws InvalidArgumentException
861
     */
862
    protected static function validateBarcodeData($type, $data, $source)
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
863
    {
864
        $aTypes = [
865
            'A' => ['desc' => 'UPC-A', 'len' => '11;12', 'type' => 'N'],
866
            'B' => ['desc' => 'UPC-E', 'len' => '6;7;8;11;12', 'type' => 'N'],
867
            'C' => ['desc' => 'EAN13', 'len' => '12;13', 'type' => 'N'],
868
            'D' => ['desc' => 'EAN8', 'len' => '7;8', 'type' => 'N'],
869
            'E' => ['desc' => 'CODE39', 'len' => '1-', 'type' => 'C'],
870
            'F' => ['desc' => 'ITF (i25)', 'len' => '1-even ', 'type' => 'N'],
871
            'G' => ['desc' => 'CODABAR', 'len' => '2-more', 'type' => 'C'],
872
            'H' => ['desc' => 'CODE93', 'len' => '1-255', 'type' => 'C'],
873
            'I' => ['desc' => 'CODE128', 'len' => '2-255', 'type' => 'C'],
874
            'J' => ['desc' => 'GS1-128', 'len' => '2-255', 'type' => 'C'],
875
            'K' => ['desc' => 'GS1 DataBar Omnidirectional', 'len' => '13', 'type' => 'N'],
876
            'L' => ['desc' => 'GS1 DataBar Truncated', 'len' => '13', 'type' => 'N'],
877
            'M' => ['desc' => 'GS1 DataBar Limited', 'len' => '13', 'type' => 'N'],
878
            'N' => ['desc' => 'GS1 DataBar Expanded', 'len' => '2-255', 'type' => 'C'],
879
        ];
880
        $barType = $aTypes[$type];
881
        if (empty($barType)) {
882
            throw new InvalidArgumentException("Argument $type to $source is not a valid type of barcode");
883
        }
884
    }
885
886
    /**
887
     * Insert a image.
888
     *
889
     * @param string $filename Path to image file
890
     * @param float  $width
891
     * @param float  $height
892
     * @throws RuntimeException
893
     */
894
    public function putImage($filename = '', $width = null, $height = null)
895
    {
896
        try {
897
            $img = new Graphics($filename, $width, $height);
898
        } catch (RuntimeException $e) {
899
            throw new RuntimeException($e->getMessage());
900
        } catch (InvalidArgumentException $e) {
901
            throw new RuntimeException($e->getMessage());
902
        }
903
        $imgHeader = self::dataHeader(array($img->getWidth(), $img->getHeight()), true);
904
        $tone = '0';
905
        $colors = '1';
906
        $xm = (($size & self::IMG_DOUBLE_WIDTH) == self::IMG_DOUBLE_WIDTH) ? chr(2) : chr(1);
0 ignored issues
show
Bug introduced by
The variable $size does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $xm. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
907
        $ym = (($size & self::IMG_DOUBLE_HEIGHT) == self::IMG_DOUBLE_HEIGHT) ? chr(2) : chr(1);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ym. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
908
        $header = $tone.$xm.$ym.$colors.$imgHeader;
909
        $this->sendGraphicsData('0', 'p', $header.$img->getRasterImage());
910
        $this->sendGraphicsData('0', '2');
911
    }
912
    
913
    /**
914
     * Calculate the size of the word.
915
     *
916
     * @param string $data
917
     */
918
    protected function getWordLength($data = '')
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
919
    {
920
        //k = (pL + pH × 256) – 3
921
        $len = strlen($texto);
0 ignored issues
show
Bug introduced by
The variable $texto does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Unused Code introduced by
$len is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
922
    }
923
924
    /**
925
     * Generate two characters for a number: 
926
     * In lower and higher parts, or more parts as needed.
927
     *
928
     * @param int $int    Input number
0 ignored issues
show
Bug introduced by
There is no parameter named $int. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
929
     * @param int $length The number of bytes to output (1 - 4).
930
     */
931
    protected static function intLowHigh($input, $length)
932
    {
933
        $maxInput = (256 << ($length * 8) - 1);
0 ignored issues
show
Unused Code introduced by
$maxInput is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
934
        $outp = '';
935
        for ($i = 0; $i < $length; ++$i) {
936
            $outp .= chr($input % 256);
937
            $input = (int) ($input / 256);
938
        }
939
        return $outp;
940
    }
941
942
    /**
943
     * Convert widths and heights to characters.
944
     * Used before sending graphics to set the size.
945
     *
946
     * @param array $inputs
947
     * @param bool  $long   True to use 4 bytes, false to use 2
948
     * @return string
949
     */
950
    protected static function dataHeader(array $inputs, $long = true)
951
    {
952
        $outp = array();
953
        foreach ($inputs as $input) {
954
            if ($long) {
955
                $outp[] = self::intLowHigh($input, 2);
956
            } else {
957
                self::validateInteger($input, 0, 255, __FUNCTION__);
958
                $outp[] = chr($input);
959
            }
960
        }
961
        return implode('', $outp);
962
    }
963
964
    /**
965
     * Throw an exception if the argument given is not a boolean.
966
     * 
967
     * @param bool   $test   the input to test
968
     * @param string $source the name of the function calling this
969
     */
970
    protected static function validateBoolean($test, $source)
971
    {
972
        if (!($test === true || $test === false)) {
973
            throw new InvalidArgumentException("Argument to $source must be a boolean");
974
        }
975
    }
976
977
    /**
978
     * Throw an exception if the argument given is not an integer within the specified range.
979
     * 
980
     * @param int    $test   the input to test
981
     * @param int    $min    the minimum allowable value (inclusive)
982
     * @param int    $max    the maximum allowable value (inclusive)
983
     * @param string $source the name of the function calling this
984
     */
985
    protected static function validateInteger($test, $min, $max, $source)
986
    {
987
        if (!is_integer($test) || $test < $min || $test > $max) {
988
            throw new InvalidArgumentException("Argument to $source must be a number between $min and $max, but $test was given.");
989
        }
990
    }
991
992
    /**
993
     * Throw an exception if the argument given can't be cast to a string.
994
     *
995
     * @param string $test   the input to test
996
     * @param string $source the name of the function calling this
997
     */
998
    protected static function validateString($test, $source)
999
    {
1000
        if (is_object($test) && !method_exists($test, '__toString')) {
1001
            throw new InvalidArgumentException("Argument to $source must be a string");
1002
        }
1003
    }
1004
    
1005
    /**
1006
     * Translate the text from UTF-8 for the specified codepage
1007
     * this translation uses "iconv" and admits texts ONLY in UTF-8.
1008
     *
1009
     * @param string $text
1010
     *
1011
     * @return string
1012
     */
1013
    protected function translate($text = '')
1014
    {
1015
        $indCode = $this->getCodePages();
0 ignored issues
show
Bug introduced by
The method getCodePages() does not seem to exist on object<Posprint\Printers\DefaultPrinter>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1016
        if (!empty($indCode)) {
1017
            $codep = $this->aCodePage[$indCode];
1018
            if (!empty($codep)) {
1019
                $text = iconv('UTF-8', $codep['conv'], $text);
1020
            }
1021
        }
1022
        return $text;
1023
    }
1024
    
1025
    /**
1026
     * Wrapper for GS ( L, to calculate and send correct data length.
1027
     *
1028
     * @param string $m    Modifier/variant for function. Usually '0'.
1029
     * @param string $fn   Function number to use, as character.
1030
     * @param string $data Data to send.
1031
     *
1032
     * @throws InvalidArgumentException Where the input lengths are bad.
1033
     */
1034
    private function sendGraphicsData($m, $fn, $data = '')
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $m. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $fn. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
1035
    {
1036
        if (strlen($m) != 1 || strlen($fn) != 1) {
1037
            throw new InvalidArgumentException('wrapperSendGraphicsData: m and fn must be one character each.');
1038
        }
1039
        $header = $this->intLowHigh(strlen($data) + 2, 2);
1040
        $this->buffer->write(self::GS.'(L'.$header.$m.$fn.$data);
1041
    }
1042
}
1043