Test Failed
Push — main ( c8394f...8477f1 )
by Rafael
66:21
created

TCPDFBarcode   F

Complexity

Total Complexity 294

Size/Duplication

Total Lines 2336
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1566
dl 0
loc 2336
rs 0.8
c 0
b 0
f 0
wmc 294

How to fix   Complexity   

Complex Class

Complex classes like TCPDFBarcode often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TCPDFBarcode, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
//============================================================+
4
// File name   : tcpdf_barcodes_1d.php
5
// Version     : 1.0.027
6
// Begin       : 2008-06-09
7
// Last Update : 2014-10-20
8
// Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - [email protected]
9
// License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
10
// -------------------------------------------------------------------
11
// Copyright (C) 2008-2014 Nicola Asuni - Tecnick.com LTD
12
//
13
// This file is part of TCPDF software library.
14
//
15
// TCPDF is free software: you can redistribute it and/or modify it
16
// under the terms of the GNU Lesser General Public License as
17
// published by the Free Software Foundation, either version 3 of the
18
// License, or (at your option) any later version.
19
//
20
// TCPDF is distributed in the hope that it will be useful, but
21
// WITHOUT ANY WARRANTY; without even the implied warranty of
22
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23
// See the GNU Lesser General Public License for more details.
24
//
25
// You should have received a copy of the GNU Lesser General Public License
26
// along with TCPDF.  If not, see <http://www.gnu.org/licenses/>.
27
//
28
// See LICENSE.TXT file for more information.
29
// -------------------------------------------------------------------
30
//
31
// Description : PHP class to creates array representations for
32
//               common 1D barcodes to be used with TCPDF.
33
//
34
//============================================================+
35
36
/**
37
 * @file
38
 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF.
39
 * @package com.tecnick.tcpdf
40
 * @author Nicola Asuni
41
 * @version 1.0.027
42
 */
43
44
/**
45
 * @class TCPDFBarcode
46
 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).<br>
47
 * @package com.tecnick.tcpdf
48
 * @version 1.0.027
49
 * @author Nicola Asuni
50
 */
51
class TCPDFBarcode
52
{
53
    /**
54
     * Array representation of barcode.
55
     * @protected
56
     */
57
    protected $barcode_array = array();
58
59
    /**
60
     * This is the class constructor.
61
     * Return an array representations for common 1D barcodes:<ul>
62
     * <li>$arrcode['code'] code to be printed on text label</li>
63
     * <li>$arrcode['maxh'] max barcode height</li>
64
     * <li>$arrcode['maxw'] max barcode width</li>
65
     * <li>$arrcode['bcode'][$k] single bar or space in $k position</li>
66
     * <li>$arrcode['bcode'][$k]['t'] bar type: true = bar, false = space.</li>
67
     * <li>$arrcode['bcode'][$k]['w'] bar width in units.</li>
68
     * <li>$arrcode['bcode'][$k]['h'] bar height in units.</li>
69
     * <li>$arrcode['bcode'][$k]['p'] bar top position (0 = top, 1 = middle)</li></ul>
70
     * @param string $code code to print
71
     * @param string $type type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128 : CODE 128</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>MSI : MSI (Variation of Plessey code)</li><li>MSI+ : MSI + CHECKSUM (modulo 11)</li><li>POSTNET : POSTNET</li><li>PLANET : PLANET</li><li>RMS4CC : RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)</li><li>KIX : KIX (Klant index - Customer index)</li><li>IMB: Intelligent Mail Barcode - Onecode - USPS-B-3200</li><li>CODABAR : CODABAR</li><li>CODE11 : CODE 11</li><li>PHARMA : PHARMACODE</li><li>PHARMA2T : PHARMACODE TWO-TRACKS</li></ul>
72
     * @public
73
     */
74
    public function __construct($code, $type)
75
    {
76
        $this->setBarcode($code, $type);
77
    }
78
79
    /**
80
     * Return an array representations of barcode.
81
     * @return array
82
     * @public
83
     */
84
    public function getBarcodeArray()
85
    {
86
        return $this->barcode_array;
87
    }
88
89
    /**
90
     * Send barcode as SVG image object to the standard output.
91
     * @param int $w Minimum width of a single bar in user units.
92
     * @param int $h Height of barcode in user units.
93
     * @param string $color Foreground color (in SVG format) for bar elements (background is transparent).
94
     * @public
95
     */
96
    public function getBarcodeSVG($w = 2, $h = 30, $color = 'black')
97
    {
98
        // send headers
99
        $code = $this->getBarcodeSVGcode($w, $h, $color);
100
        header('Content-Type: application/svg+xml');
101
        header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
102
        header('Pragma: public');
103
        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
104
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
105
        header('Content-Disposition: inline; filename="' . md5($code) . '.svg";');
106
        //header('Content-Length: '.strlen($code));
107
        echo $code;
108
    }
109
110
    /**
111
     * Return a SVG string representation of barcode.
112
     * @param int $w Minimum width of a single bar in user units.
113
     * @param int $h Height of barcode in user units.
114
     * @param string $color Foreground color (in SVG format) for bar elements (background is transparent).
115
     * @return string SVG code.
116
     * @public
117
     */
118
    public function getBarcodeSVGcode($w = 2, $h = 30, $color = 'black')
119
    {
120
        // replace table for special characters
121
        $repstr = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
122
        $svg = '<' . '?' . 'xml version="1.0" standalone="no"' . '?' . '>' . "\n";
123
        $svg .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' . "\n";
124
        $svg .= '<svg width="' . round(($this->barcode_array['maxw'] * $w), 3) . '" height="' . $h . '" version="1.1" xmlns="http://www.w3.org/2000/svg">' . "\n";
125
        $svg .= "\t" . '<desc>' . strtr($this->barcode_array['code'], $repstr) . '</desc>' . "\n";
126
        $svg .= "\t" . '<g id="bars" fill="' . $color . '" stroke="none">' . "\n";
127
        // print bars
128
        $x = 0;
129
        foreach ($this->barcode_array['bcode'] as $k => $v) {
130
            $bw = round(($v['w'] * $w), 3);
131
            $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
132
            if ($v['t']) {
133
                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
134
                // draw a vertical bar
135
                $svg .= "\t\t" . '<rect x="' . $x . '" y="' . $y . '" width="' . $bw . '" height="' . $bh . '" />' . "\n";
136
            }
137
            $x += $bw;
138
        }
139
        $svg .= "\t" . '</g>' . "\n";
140
        $svg .= '</svg>' . "\n";
141
        return $svg;
142
    }
143
144
    /**
145
     * Return an HTML representation of barcode.
146
     * @param int $w Width of a single bar element in pixels.
147
     * @param int $h Height of a single bar element in pixels.
148
     * @param string $color Foreground color for bar elements (background is transparent).
149
     * @return string HTML code.
150
     * @public
151
     */
152
    public function getBarcodeHTML($w = 2, $h = 30, $color = 'black')
153
    {
154
        $html = '<div style="font-size:0;position:relative;width:' . ($this->barcode_array['maxw'] * $w) . 'px;height:' . ($h) . 'px;">' . "\n";
155
        // print bars
156
        $x = 0;
157
        foreach ($this->barcode_array['bcode'] as $k => $v) {
158
            $bw = round(($v['w'] * $w), 3);
159
            $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
160
            if ($v['t']) {
161
                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
162
                // draw a vertical bar
163
                $html .= '<div style="background-color:' . $color . ';width:' . $bw . 'px;height:' . $bh . 'px;position:absolute;left:' . $x . 'px;top:' . $y . 'px;">&nbsp;</div>' . "\n";
164
            }
165
            $x += $bw;
166
        }
167
        $html .= '</div>' . "\n";
168
        return $html;
169
    }
170
171
    /**
172
     * Send a PNG image representation of barcode (requires GD or Imagick library).
173
     * @param int $w Width of a single bar element in pixels.
174
     * @param int $h Height of a single bar element in pixels.
175
     * @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
176
     * @public
177
     */
178
    public function getBarcodePNG($w = 2, $h = 30, $color = array(0,0,0))
179
    {
180
        $data = $this->getBarcodePngData($w, $h, $color);
181
        // send headers
182
        header('Content-Type: image/png');
183
        header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
184
        header('Pragma: public');
185
        header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
186
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
187
        //header('Content-Length: '.strlen($data));
188
        echo $data;
189
    }
190
191
    /**
192
     * Return a PNG image representation of barcode (requires GD or Imagick library).
193
     * @param int $w Width of a single bar element in pixels.
194
     * @param int $h Height of a single bar element in pixels.
195
     * @param array $color RGB (0-255) foreground color for bar elements (background is transparent).
196
     * @return string|Imagick|false image or false in case of error.
197
     * @public
198
     */
199
    public function getBarcodePngData($w = 2, $h = 30, $color = array(0,0,0))
200
    {
201
        // calculate image size
202
        $width = ($this->barcode_array['maxw'] * $w);
203
        $height = $h;
204
        if (function_exists('imagecreate')) {
205
            // GD library
206
            $imagick = false;
207
            $png = imagecreate($width, $height);
208
            $bgcol = imagecolorallocate($png, 255, 255, 255);
209
            imagecolortransparent($png, $bgcol);
210
            $fgcol = imagecolorallocate($png, $color[0], $color[1], $color[2]);
211
        } elseif (extension_loaded('imagick')) {
212
            $imagick = true;
213
            $bgcol = new imagickpixel('rgb(255,255,255');
214
            $fgcol = new imagickpixel('rgb(' . $color[0] . ',' . $color[1] . ',' . $color[2] . ')');
215
            $png = new Imagick();
216
            $png->newImage($width, $height, 'none', 'png');
217
            $bar = new imagickdraw();
218
            $bar->setfillcolor($fgcol);
219
        } else {
220
            return false;
221
        }
222
        // print bars
223
        $x = 0;
224
        foreach ($this->barcode_array['bcode'] as $k => $v) {
225
            $bw = round(($v['w'] * $w), 3);
226
            $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
227
            if ($v['t']) {
228
                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
229
                // draw a vertical bar
230
                if ($imagick) {
231
                    $bar->rectangle($x, $y, ($x + $bw - 1), ($y + $bh - 1));
232
                } else {
233
                    imagefilledrectangle($png, $x, $y, ($x + $bw - 1), ($y + $bh - 1), $fgcol);
234
                }
235
            }
236
            $x += $bw;
237
        }
238
        if ($imagick) {
239
            $png->drawimage($bar);
240
            return $png;
241
        } else {
242
            ob_start();
243
            imagepng($png);
244
            $imagedata = ob_get_clean();
245
            imagedestroy($png);
246
            return $imagedata;
247
        }
248
    }
249
250
    /**
251
     * Set the barcode.
252
     * @param string $code code to print
253
     * @param string $type type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128 : CODE 128</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>MSI : MSI (Variation of Plessey code)</li><li>MSI+ : MSI + CHECKSUM (modulo 11)</li><li>POSTNET : POSTNET</li><li>PLANET : PLANET</li><li>RMS4CC : RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)</li><li>KIX : KIX (Klant index - Customer index)</li><li>IMB: Intelligent Mail Barcode - Onecode - USPS-B-3200</li><li>IMBPRE: Pre-processed Intelligent Mail Barcode - Onecode - USPS-B-3200, using only F,A,D,T letters</li><li>CODABAR : CODABAR</li><li>CODE11 : CODE 11</li><li>PHARMA : PHARMACODE</li><li>PHARMA2T : PHARMACODE TWO-TRACKS</li></ul>
254
     * @return void
255
     * @public
256
     */
257
    public function setBarcode($code, $type)
258
    {
259
        switch (strtoupper($type)) {
260
            case 'C39': { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
261
                $arrcode = $this->barcode_code39($code, false, false);
262
                break;
263
            }
264
            case 'C39+': { // CODE 39 with checksum
265
                $arrcode = $this->barcode_code39($code, false, true);
266
                break;
267
            }
268
            case 'C39E': { // CODE 39 EXTENDED
269
                $arrcode = $this->barcode_code39($code, true, false);
270
                break;
271
            }
272
            case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM
273
                $arrcode = $this->barcode_code39($code, true, true);
274
                break;
275
            }
276
            case 'C93': { // CODE 93 - USS-93
277
                $arrcode = $this->barcode_code93($code);
278
                break;
279
            }
280
            case 'S25': { // Standard 2 of 5
281
                $arrcode = $this->barcode_s25($code, false);
282
                break;
283
            }
284
            case 'S25+': { // Standard 2 of 5 + CHECKSUM
285
                $arrcode = $this->barcode_s25($code, true);
286
                break;
287
            }
288
            case 'I25': { // Interleaved 2 of 5
289
                $arrcode = $this->barcode_i25($code, false);
290
                break;
291
            }
292
            case 'I25+': { // Interleaved 2 of 5 + CHECKSUM
293
                $arrcode = $this->barcode_i25($code, true);
294
                break;
295
            }
296
            case 'C128': { // CODE 128
297
                $arrcode = $this->barcode_c128($code, '');
298
                break;
299
            }
300
            case 'C128A': { // CODE 128 A
301
                $arrcode = $this->barcode_c128($code, 'A');
302
                break;
303
            }
304
            case 'C128B': { // CODE 128 B
305
                $arrcode = $this->barcode_c128($code, 'B');
306
                break;
307
            }
308
            case 'C128C': { // CODE 128 C
309
                $arrcode = $this->barcode_c128($code, 'C');
310
                break;
311
            }
312
            case 'EAN2': { // 2-Digits UPC-Based Extension
313
                $arrcode = $this->barcode_eanext($code, 2);
314
                break;
315
            }
316
            case 'EAN5': { // 5-Digits UPC-Based Extension
317
                $arrcode = $this->barcode_eanext($code, 5);
318
                break;
319
            }
320
            case 'EAN8': { // EAN 8
321
                $arrcode = $this->barcode_eanupc($code, 8);
322
                break;
323
            }
324
            case 'EAN13': { // EAN 13
325
                $arrcode = $this->barcode_eanupc($code, 13);
326
                break;
327
            }
328
            case 'UPCA': { // UPC-A
329
                $arrcode = $this->barcode_eanupc($code, 12);
330
                break;
331
            }
332
            case 'UPCE': { // UPC-E
333
                $arrcode = $this->barcode_eanupc($code, 6);
334
                break;
335
            }
336
            case 'MSI': { // MSI (Variation of Plessey code)
337
                $arrcode = $this->barcode_msi($code, false);
338
                break;
339
            }
340
            case 'MSI+': { // MSI + CHECKSUM (modulo 11)
341
                $arrcode = $this->barcode_msi($code, true);
342
                break;
343
            }
344
            case 'POSTNET': { // POSTNET
345
                $arrcode = $this->barcode_postnet($code, false);
346
                break;
347
            }
348
            case 'PLANET': { // PLANET
349
                $arrcode = $this->barcode_postnet($code, true);
350
                break;
351
            }
352
            case 'RMS4CC': { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
353
                $arrcode = $this->barcode_rms4cc($code, false);
354
                break;
355
            }
356
            case 'KIX': { // KIX (Klant index - Customer index)
357
                $arrcode = $this->barcode_rms4cc($code, true);
358
                break;
359
            }
360
            case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
361
                $arrcode = $this->barcode_imb($code);
362
                break;
363
            }
364
            case 'IMBPRE': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200- pre-processed
365
                $arrcode = $this->barcode_imb_pre($code);
366
                break;
367
            }
368
            case 'CODABAR': { // CODABAR
369
                $arrcode = $this->barcode_codabar($code);
370
                break;
371
            }
372
            case 'CODE11': { // CODE 11
373
                $arrcode = $this->barcode_code11($code);
374
                break;
375
            }
376
            case 'PHARMA': { // PHARMACODE
377
                $arrcode = $this->barcode_pharmacode($code);
378
                break;
379
            }
380
            case 'PHARMA2T': { // PHARMACODE TWO-TRACKS
381
                $arrcode = $this->barcode_pharmacode2t($code);
382
                break;
383
            }
384
            default: {
385
                $this->barcode_array = array();
386
                $arrcode = false;
387
                break;
388
            }
389
        }
390
        $this->barcode_array = $arrcode;
391
    }
392
393
    /**
394
     * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
395
     * General-purpose code in very wide use world-wide
396
     * @param string $code code to represent.
397
     * @param boolean $extended if true uses the extended mode.
398
     * @param boolean $checksum if true add a checksum to the code.
399
     * @return array barcode representation.
400
     * @protected
401
     */
402
    protected function barcode_code39($code, $extended = false, $checksum = false)
403
    {
404
        $chr['0'] = '111331311';
405
        $chr['1'] = '311311113';
406
        $chr['2'] = '113311113';
407
        $chr['3'] = '313311111';
408
        $chr['4'] = '111331113';
409
        $chr['5'] = '311331111';
410
        $chr['6'] = '113331111';
411
        $chr['7'] = '111311313';
412
        $chr['8'] = '311311311';
413
        $chr['9'] = '113311311';
414
        $chr['A'] = '311113113';
415
        $chr['B'] = '113113113';
416
        $chr['C'] = '313113111';
417
        $chr['D'] = '111133113';
418
        $chr['E'] = '311133111';
419
        $chr['F'] = '113133111';
420
        $chr['G'] = '111113313';
421
        $chr['H'] = '311113311';
422
        $chr['I'] = '113113311';
423
        $chr['J'] = '111133311';
424
        $chr['K'] = '311111133';
425
        $chr['L'] = '113111133';
426
        $chr['M'] = '313111131';
427
        $chr['N'] = '111131133';
428
        $chr['O'] = '311131131';
429
        $chr['P'] = '113131131';
430
        $chr['Q'] = '111111333';
431
        $chr['R'] = '311111331';
432
        $chr['S'] = '113111331';
433
        $chr['T'] = '111131331';
434
        $chr['U'] = '331111113';
435
        $chr['V'] = '133111113';
436
        $chr['W'] = '333111111';
437
        $chr['X'] = '131131113';
438
        $chr['Y'] = '331131111';
439
        $chr['Z'] = '133131111';
440
        $chr['-'] = '131111313';
441
        $chr['.'] = '331111311';
442
        $chr[' '] = '133111311';
443
        $chr['$'] = '131313111';
444
        $chr['/'] = '131311131';
445
        $chr['+'] = '131113131';
446
        $chr['%'] = '111313131';
447
        $chr['*'] = '131131311';
448
        $code = strtoupper($code);
449
        if ($extended) {
450
            // extended mode
451
            $code = $this->encode_code39_ext($code);
452
        }
453
        if ($code === false) {
454
            return false;
455
        }
456
        if ($checksum) {
457
            // checksum
458
            $code .= $this->checksum_code39($code);
459
        }
460
        // add start and stop codes
461
        $code = '*' . $code . '*';
462
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
463
        $k = 0;
464
        $clen = strlen($code);
465
        for ($i = 0; $i < $clen; ++$i) {
466
            $char = $code[$i];
467
            if(!isset($chr[$char])) {
468
                // invalid character
469
                return false;
470
            }
471
            for ($j = 0; $j < 9; ++$j) {
472
                if (($j % 2) == 0) {
473
                    $t = true; // bar
474
                } else {
475
                    $t = false; // space
476
                }
477
                $w = $chr[$char][$j];
478
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
479
                $bararray['maxw'] += $w;
480
                ++$k;
481
            }
482
            // intercharacter gap
483
            $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
484
            $bararray['maxw'] += 1;
485
            ++$k;
486
        }
487
        return $bararray;
488
    }
489
490
    /**
491
     * Encode a string to be used for CODE 39 Extended mode.
492
     * @param string $code code to represent.
493
     * @return string encoded string.
494
     * @protected
495
     */
496
    protected function encode_code39_ext($code)
497
    {
498
        $encode = array(
499
            chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
500
            chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
501
            chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
502
            chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
503
            chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
504
            chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
505
            chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
506
            chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
507
            chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
508
            chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
509
            chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
510
            chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
511
            chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
512
            chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
513
            chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
514
            chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
515
            chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
516
            chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
517
            chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
518
            chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
519
            chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
520
            chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
521
            chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
522
            chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
523
            chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
524
            chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
525
            chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
526
            chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
527
            chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
528
            chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
529
            chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
530
            chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
531
        $code_ext = '';
532
        $clen = strlen($code);
533
        for ($i = 0 ; $i < $clen; ++$i) {
534
            if (ord($code[$i]) > 127) {
535
                return false;
536
            }
537
            $code_ext .= $encode[$code[$i]];
538
        }
539
        return $code_ext;
540
    }
541
542
    /**
543
     * Calculate CODE 39 checksum (modulo 43).
544
     * @param string $code code to represent.
545
     * @return string char checksum.
546
     * @protected
547
     */
548
    protected function checksum_code39($code)
549
    {
550
        $chars = array(
551
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
552
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
553
            'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
554
            'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
555
        $sum = 0;
556
        $clen = strlen($code);
557
        for ($i = 0 ; $i < $clen; ++$i) {
558
            $k = array_keys($chars, $code[$i]);
559
            $sum += $k[0];
560
        }
561
        $j = ($sum % 43);
562
        return $chars[$j];
563
    }
564
565
    /**
566
     * CODE 93 - USS-93
567
     * Compact code similar to Code 39
568
     * @param string $code code to represent.
569
     * @return array barcode representation.
570
     * @protected
571
     */
572
    protected function barcode_code93($code)
573
    {
574
        $chr[48] = '131112'; // 0
575
        $chr[49] = '111213'; // 1
576
        $chr[50] = '111312'; // 2
577
        $chr[51] = '111411'; // 3
578
        $chr[52] = '121113'; // 4
579
        $chr[53] = '121212'; // 5
580
        $chr[54] = '121311'; // 6
581
        $chr[55] = '111114'; // 7
582
        $chr[56] = '131211'; // 8
583
        $chr[57] = '141111'; // 9
584
        $chr[65] = '211113'; // A
585
        $chr[66] = '211212'; // B
586
        $chr[67] = '211311'; // C
587
        $chr[68] = '221112'; // D
588
        $chr[69] = '221211'; // E
589
        $chr[70] = '231111'; // F
590
        $chr[71] = '112113'; // G
591
        $chr[72] = '112212'; // H
592
        $chr[73] = '112311'; // I
593
        $chr[74] = '122112'; // J
594
        $chr[75] = '132111'; // K
595
        $chr[76] = '111123'; // L
596
        $chr[77] = '111222'; // M
597
        $chr[78] = '111321'; // N
598
        $chr[79] = '121122'; // O
599
        $chr[80] = '131121'; // P
600
        $chr[81] = '212112'; // Q
601
        $chr[82] = '212211'; // R
602
        $chr[83] = '211122'; // S
603
        $chr[84] = '211221'; // T
604
        $chr[85] = '221121'; // U
605
        $chr[86] = '222111'; // V
606
        $chr[87] = '112122'; // W
607
        $chr[88] = '112221'; // X
608
        $chr[89] = '122121'; // Y
609
        $chr[90] = '123111'; // Z
610
        $chr[45] = '121131'; // -
611
        $chr[46] = '311112'; // .
612
        $chr[32] = '311211'; //
613
        $chr[36] = '321111'; // $
614
        $chr[47] = '112131'; // /
615
        $chr[43] = '113121'; // +
616
        $chr[37] = '211131'; // %
617
        $chr[128] = '121221'; // ($)
618
        $chr[129] = '311121'; // (/)
619
        $chr[130] = '122211'; // (+)
620
        $chr[131] = '312111'; // (%)
621
        $chr[42] = '111141'; // start-stop
622
        $code = strtoupper($code);
623
        $encode = array(
624
            chr(0) => chr(131) . 'U', chr(1) => chr(128) . 'A', chr(2) => chr(128) . 'B', chr(3) => chr(128) . 'C',
625
            chr(4) => chr(128) . 'D', chr(5) => chr(128) . 'E', chr(6) => chr(128) . 'F', chr(7) => chr(128) . 'G',
626
            chr(8) => chr(128) . 'H', chr(9) => chr(128) . 'I', chr(10) => chr(128) . 'J', chr(11) => '£K',
627
            chr(12) => chr(128) . 'L', chr(13) => chr(128) . 'M', chr(14) => chr(128) . 'N', chr(15) => chr(128) . 'O',
628
            chr(16) => chr(128) . 'P', chr(17) => chr(128) . 'Q', chr(18) => chr(128) . 'R', chr(19) => chr(128) . 'S',
629
            chr(20) => chr(128) . 'T', chr(21) => chr(128) . 'U', chr(22) => chr(128) . 'V', chr(23) => chr(128) . 'W',
630
            chr(24) => chr(128) . 'X', chr(25) => chr(128) . 'Y', chr(26) => chr(128) . 'Z', chr(27) => chr(131) . 'A',
631
            chr(28) => chr(131) . 'B', chr(29) => chr(131) . 'C', chr(30) => chr(131) . 'D', chr(31) => chr(131) . 'E',
632
            chr(32) => ' ', chr(33) => chr(129) . 'A', chr(34) => chr(129) . 'B', chr(35) => chr(129) . 'C',
633
            chr(36) => chr(129) . 'D', chr(37) => chr(129) . 'E', chr(38) => chr(129) . 'F', chr(39) => chr(129) . 'G',
634
            chr(40) => chr(129) . 'H', chr(41) => chr(129) . 'I', chr(42) => chr(129) . 'J', chr(43) => chr(129) . 'K',
635
            chr(44) => chr(129) . 'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129) . 'O',
636
            chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
637
            chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
638
            chr(56) => '8', chr(57) => '9', chr(58) => chr(129) . 'Z', chr(59) => chr(131) . 'F',
639
            chr(60) => chr(131) . 'G', chr(61) => chr(131) . 'H', chr(62) => chr(131) . 'I', chr(63) => chr(131) . 'J',
640
            chr(64) => chr(131) . 'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
641
            chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
642
            chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
643
            chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
644
            chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
645
            chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
646
            chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131) . 'K',
647
            chr(92) => chr(131) . 'L', chr(93) => chr(131) . 'M', chr(94) => chr(131) . 'N', chr(95) => chr(131) . 'O',
648
            chr(96) => chr(131) . 'W', chr(97) => chr(130) . 'A', chr(98) => chr(130) . 'B', chr(99) => chr(130) . 'C',
649
            chr(100) => chr(130) . 'D', chr(101) => chr(130) . 'E', chr(102) => chr(130) . 'F', chr(103) => chr(130) . 'G',
650
            chr(104) => chr(130) . 'H', chr(105) => chr(130) . 'I', chr(106) => chr(130) . 'J', chr(107) => chr(130) . 'K',
651
            chr(108) => chr(130) . 'L', chr(109) => chr(130) . 'M', chr(110) => chr(130) . 'N', chr(111) => chr(130) . 'O',
652
            chr(112) => chr(130) . 'P', chr(113) => chr(130) . 'Q', chr(114) => chr(130) . 'R', chr(115) => chr(130) . 'S',
653
            chr(116) => chr(130) . 'T', chr(117) => chr(130) . 'U', chr(118) => chr(130) . 'V', chr(119) => chr(130) . 'W',
654
            chr(120) => chr(130) . 'X', chr(121) => chr(130) . 'Y', chr(122) => chr(130) . 'Z', chr(123) => chr(131) . 'P',
655
            chr(124) => chr(131) . 'Q', chr(125) => chr(131) . 'R', chr(126) => chr(131) . 'S', chr(127) => chr(131) . 'T');
656
        $code_ext = '';
657
        $clen = strlen($code);
658
        for ($i = 0 ; $i < $clen; ++$i) {
659
            if (ord($code[$i]) > 127) {
660
                return false;
661
            }
662
            $code_ext .= $encode[$code[$i]];
663
        }
664
        // checksum
665
        $code_ext .= $this->checksum_code93($code_ext);
666
        // add start and stop codes
667
        $code = '*' . $code_ext . '*';
668
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
669
        $k = 0;
670
        $clen = strlen($code);
671
        for ($i = 0; $i < $clen; ++$i) {
672
            $char = ord($code[$i]);
673
            if(!isset($chr[$char])) {
674
                // invalid character
675
                return false;
676
            }
677
            for ($j = 0; $j < 6; ++$j) {
678
                if (($j % 2) == 0) {
679
                    $t = true; // bar
680
                } else {
681
                    $t = false; // space
682
                }
683
                $w = $chr[$char][$j];
684
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
685
                $bararray['maxw'] += $w;
686
                ++$k;
687
            }
688
        }
689
        $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
690
        $bararray['maxw'] += 1;
691
        ++$k;
692
        return $bararray;
693
    }
694
695
    /**
696
     * Calculate CODE 93 checksum (modulo 47).
697
     * @param string $code code to represent.
698
     * @return string checksum code.
699
     * @protected
700
     */
701
    protected function checksum_code93($code)
702
    {
703
        $chars = array(
704
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
705
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
706
            'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
707
            'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%',
708
            '<', '=', '>', '?');
709
        // translate special characters
710
        $code = strtr($code, chr(128) . chr(131) . chr(129) . chr(130), '<=>?');
711
        $len = strlen($code);
712
        // calculate check digit C
713
        $p = 1;
714
        $check = 0;
715
        for ($i = ($len - 1); $i >= 0; --$i) {
716
            $k = array_keys($chars, $code[$i]);
717
            $check += ($k[0] * $p);
718
            ++$p;
719
            if ($p > 20) {
720
                $p = 1;
721
            }
722
        }
723
        $check %= 47;
724
        $c = $chars[$check];
725
        $code .= $c;
726
        // calculate check digit K
727
        $p = 1;
728
        $check = 0;
729
        for ($i = $len; $i >= 0; --$i) {
730
            $k = array_keys($chars, $code[$i]);
731
            $check += ($k[0] * $p);
732
            ++$p;
733
            if ($p > 15) {
734
                $p = 1;
735
            }
736
        }
737
        $check %= 47;
738
        $k = $chars[$check];
739
        $checksum = $c . $k;
740
        // resto respecial characters
741
        $checksum = strtr($checksum, '<=>?', chr(128) . chr(131) . chr(129) . chr(130));
742
        return $checksum;
743
    }
744
745
    /**
746
     * Checksum for standard 2 of 5 barcodes.
747
     * @param string $code code to process.
748
     * @return int checksum.
749
     * @protected
750
     */
751
    protected function checksum_s25($code)
752
    {
753
        $len = strlen($code);
754
        $sum = 0;
755
        for ($i = 0; $i < $len; $i += 2) {
756
            $sum += $code[$i];
757
        }
758
        $sum *= 3;
759
        for ($i = 1; $i < $len; $i += 2) {
760
            $sum += ($code[$i]);
761
        }
762
        $r = $sum % 10;
763
        if($r > 0) {
764
            $r = (10 - $r);
765
        }
766
        return $r;
767
    }
768
769
    /**
770
     * MSI.
771
     * Variation of Plessey code, with similar applications
772
     * Contains digits (0 to 9) and encodes the data only in the width of bars.
773
     * @param string $code code to represent.
774
     * @param boolean $checksum if true add a checksum to the code (modulo 11)
775
     * @return array barcode representation.
776
     * @protected
777
     */
778
    protected function barcode_msi($code, $checksum = false)
779
    {
780
        $chr['0'] = '100100100100';
781
        $chr['1'] = '100100100110';
782
        $chr['2'] = '100100110100';
783
        $chr['3'] = '100100110110';
784
        $chr['4'] = '100110100100';
785
        $chr['5'] = '100110100110';
786
        $chr['6'] = '100110110100';
787
        $chr['7'] = '100110110110';
788
        $chr['8'] = '110100100100';
789
        $chr['9'] = '110100100110';
790
        $chr['A'] = '110100110100';
791
        $chr['B'] = '110100110110';
792
        $chr['C'] = '110110100100';
793
        $chr['D'] = '110110100110';
794
        $chr['E'] = '110110110100';
795
        $chr['F'] = '110110110110';
796
        if ($checksum) {
797
            // add checksum
798
            $clen = strlen($code);
799
            $p = 2;
800
            $check = 0;
801
            for ($i = ($clen - 1); $i >= 0; --$i) {
802
                $check += (hexdec($code[$i]) * $p);
803
                ++$p;
804
                if ($p > 7) {
805
                    $p = 2;
806
                }
807
            }
808
            $check %= 11;
809
            if ($check > 0) {
810
                $check = 11 - $check;
811
            }
812
            $code .= $check;
813
        }
814
        $seq = '110'; // left guard
815
        $clen = strlen($code);
816
        for ($i = 0; $i < $clen; ++$i) {
817
            $digit = $code[$i];
818
            if (!isset($chr[$digit])) {
819
                // invalid character
820
                return false;
821
            }
822
            $seq .= $chr[$digit];
823
        }
824
        $seq .= '1001'; // right guard
825
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
826
        return $this->binseq_to_array($seq, $bararray);
827
    }
828
829
    /**
830
     * Standard 2 of 5 barcodes.
831
     * Used in airline ticket marking, photofinishing
832
     * Contains digits (0 to 9) and encodes the data only in the width of bars.
833
     * @param string $code code to represent.
834
     * @param boolean $checksum if true add a checksum to the code
835
     * @return array barcode representation.
836
     * @protected
837
     */
838
    protected function barcode_s25($code, $checksum = false)
839
    {
840
        $chr['0'] = '10101110111010';
841
        $chr['1'] = '11101010101110';
842
        $chr['2'] = '10111010101110';
843
        $chr['3'] = '11101110101010';
844
        $chr['4'] = '10101110101110';
845
        $chr['5'] = '11101011101010';
846
        $chr['6'] = '10111011101010';
847
        $chr['7'] = '10101011101110';
848
        $chr['8'] = '11101010111010';
849
        $chr['9'] = '10111010111010';
850
        if ($checksum) {
851
            // add checksum
852
            $code .= $this->checksum_s25($code);
853
        }
854
        if((strlen($code) % 2) != 0) {
855
            // add leading zero if code-length is odd
856
            $code = '0' . $code;
857
        }
858
        $seq = '1110111010';
859
        $clen = strlen($code);
860
        for ($i = 0; $i < $clen; ++$i) {
861
            $digit = $code[$i];
862
            if (!isset($chr[$digit])) {
863
                // invalid character
864
                return false;
865
            }
866
            $seq .= $chr[$digit];
867
        }
868
        $seq .= '111010111';
869
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
870
        return $this->binseq_to_array($seq, $bararray);
871
    }
872
873
    /**
874
     * Convert binary barcode sequence to WarnockPDF barcode array.
875
     * @param string $seq barcode as binary sequence.
876
     * @param array $bararray barcode array to fill up
877
     * @return array barcode representation.
878
     * @protected
879
     */
880
    protected function binseq_to_array($seq, $bararray)
881
    {
882
        $len = strlen($seq);
883
        $w = 0;
884
        $k = 0;
885
        for ($i = 0; $i < $len; ++$i) {
886
            $w += 1;
887
            if (($i == ($len - 1)) or (($i < ($len - 1)) and ($seq[$i] != $seq[($i + 1)]))) {
888
                if ($seq[$i] == '1') {
889
                    $t = true; // bar
890
                } else {
891
                    $t = false; // space
892
                }
893
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
894
                $bararray['maxw'] += $w;
895
                ++$k;
896
                $w = 0;
897
            }
898
        }
899
        return $bararray;
900
    }
901
902
    /**
903
     * Interleaved 2 of 5 barcodes.
904
     * Compact numeric code, widely used in industry, air cargo
905
     * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
906
     * @param string $code code to represent.
907
     * @param boolean $checksum if true add a checksum to the code
908
     * @return array barcode representation.
909
     * @protected
910
     */
911
    protected function barcode_i25($code, $checksum = false)
912
    {
913
        $chr['0'] = '11221';
914
        $chr['1'] = '21112';
915
        $chr['2'] = '12112';
916
        $chr['3'] = '22111';
917
        $chr['4'] = '11212';
918
        $chr['5'] = '21211';
919
        $chr['6'] = '12211';
920
        $chr['7'] = '11122';
921
        $chr['8'] = '21121';
922
        $chr['9'] = '12121';
923
        $chr['A'] = '11';
924
        $chr['Z'] = '21';
925
        if ($checksum) {
926
            // add checksum
927
            $code .= $this->checksum_s25($code);
928
        }
929
        if((strlen($code) % 2) != 0) {
930
            // add leading zero if code-length is odd
931
            $code = '0' . $code;
932
        }
933
        // add start and stop codes
934
        $code = 'AA' . strtolower($code) . 'ZA';
935
936
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
937
        $k = 0;
938
        $clen = strlen($code);
939
        for ($i = 0; $i < $clen; $i = ($i + 2)) {
940
            $char_bar = $code[$i];
941
            $char_space = $code[$i + 1];
942
            if((!isset($chr[$char_bar])) or (!isset($chr[$char_space]))) {
943
                // invalid character
944
                return false;
945
            }
946
            // create a bar-space sequence
947
            $seq = '';
948
            $chrlen = strlen($chr[$char_bar]);
949
            for ($s = 0; $s < $chrlen; $s++){
950
                $seq .= $chr[$char_bar][$s] . $chr[$char_space][$s];
951
            }
952
            $seqlen = strlen($seq);
953
            for ($j = 0; $j < $seqlen; ++$j) {
954
                if (($j % 2) == 0) {
955
                    $t = true; // bar
956
                } else {
957
                    $t = false; // space
958
                }
959
                $w = (float)$seq[$j];
960
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
961
                $bararray['maxw'] += $w;
962
                ++$k;
963
            }
964
        }
965
        return $bararray;
966
    }
967
968
    /**
969
     * C128 barcodes.
970
     * Very capable code, excellent density, high reliability; in very wide use world-wide
971
     * @param string $code code to represent.
972
     * @param string $type barcode type: A, B, C or empty for automatic switch (AUTO mode)
973
     * @return array barcode representation.
974
     * @protected
975
     */
976
    protected function barcode_c128($code, $type = '')
977
    {
978
        $chr = array(
979
            '212222', /* 00 */
980
            '222122', /* 01 */
981
            '222221', /* 02 */
982
            '121223', /* 03 */
983
            '121322', /* 04 */
984
            '131222', /* 05 */
985
            '122213', /* 06 */
986
            '122312', /* 07 */
987
            '132212', /* 08 */
988
            '221213', /* 09 */
989
            '221312', /* 10 */
990
            '231212', /* 11 */
991
            '112232', /* 12 */
992
            '122132', /* 13 */
993
            '122231', /* 14 */
994
            '113222', /* 15 */
995
            '123122', /* 16 */
996
            '123221', /* 17 */
997
            '223211', /* 18 */
998
            '221132', /* 19 */
999
            '221231', /* 20 */
1000
            '213212', /* 21 */
1001
            '223112', /* 22 */
1002
            '312131', /* 23 */
1003
            '311222', /* 24 */
1004
            '321122', /* 25 */
1005
            '321221', /* 26 */
1006
            '312212', /* 27 */
1007
            '322112', /* 28 */
1008
            '322211', /* 29 */
1009
            '212123', /* 30 */
1010
            '212321', /* 31 */
1011
            '232121', /* 32 */
1012
            '111323', /* 33 */
1013
            '131123', /* 34 */
1014
            '131321', /* 35 */
1015
            '112313', /* 36 */
1016
            '132113', /* 37 */
1017
            '132311', /* 38 */
1018
            '211313', /* 39 */
1019
            '231113', /* 40 */
1020
            '231311', /* 41 */
1021
            '112133', /* 42 */
1022
            '112331', /* 43 */
1023
            '132131', /* 44 */
1024
            '113123', /* 45 */
1025
            '113321', /* 46 */
1026
            '133121', /* 47 */
1027
            '313121', /* 48 */
1028
            '211331', /* 49 */
1029
            '231131', /* 50 */
1030
            '213113', /* 51 */
1031
            '213311', /* 52 */
1032
            '213131', /* 53 */
1033
            '311123', /* 54 */
1034
            '311321', /* 55 */
1035
            '331121', /* 56 */
1036
            '312113', /* 57 */
1037
            '312311', /* 58 */
1038
            '332111', /* 59 */
1039
            '314111', /* 60 */
1040
            '221411', /* 61 */
1041
            '431111', /* 62 */
1042
            '111224', /* 63 */
1043
            '111422', /* 64 */
1044
            '121124', /* 65 */
1045
            '121421', /* 66 */
1046
            '141122', /* 67 */
1047
            '141221', /* 68 */
1048
            '112214', /* 69 */
1049
            '112412', /* 70 */
1050
            '122114', /* 71 */
1051
            '122411', /* 72 */
1052
            '142112', /* 73 */
1053
            '142211', /* 74 */
1054
            '241211', /* 75 */
1055
            '221114', /* 76 */
1056
            '413111', /* 77 */
1057
            '241112', /* 78 */
1058
            '134111', /* 79 */
1059
            '111242', /* 80 */
1060
            '121142', /* 81 */
1061
            '121241', /* 82 */
1062
            '114212', /* 83 */
1063
            '124112', /* 84 */
1064
            '124211', /* 85 */
1065
            '411212', /* 86 */
1066
            '421112', /* 87 */
1067
            '421211', /* 88 */
1068
            '212141', /* 89 */
1069
            '214121', /* 90 */
1070
            '412121', /* 91 */
1071
            '111143', /* 92 */
1072
            '111341', /* 93 */
1073
            '131141', /* 94 */
1074
            '114113', /* 95 */
1075
            '114311', /* 96 */
1076
            '411113', /* 97 */
1077
            '411311', /* 98 */
1078
            '113141', /* 99 */
1079
            '114131', /* 100 */
1080
            '311141', /* 101 */
1081
            '411131', /* 102 */
1082
            '211412', /* 103 START A */
1083
            '211214', /* 104 START B */
1084
            '211232', /* 105 START C */
1085
            '233111', /* STOP */
1086
            '200000'  /* END */
1087
        );
1088
        // ASCII characters for code A (ASCII 00 - 95)
1089
        $keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
1090
        $keys_a .= chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5) . chr(6) . chr(7) . chr(8) . chr(9);
1091
        $keys_a .= chr(10) . chr(11) . chr(12) . chr(13) . chr(14) . chr(15) . chr(16) . chr(17) . chr(18) . chr(19);
1092
        $keys_a .= chr(20) . chr(21) . chr(22) . chr(23) . chr(24) . chr(25) . chr(26) . chr(27) . chr(28) . chr(29);
1093
        $keys_a .= chr(30) . chr(31);
1094
        // ASCII characters for code B (ASCII 32 - 127)
1095
        $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' . chr(127);
1096
        // special codes
1097
        $fnc_a = array(241 => 102, 242 => 97, 243 => 96, 244 => 101);
1098
        $fnc_b = array(241 => 102, 242 => 97, 243 => 96, 244 => 100);
1099
        // array of symbols
1100
        $code_data = array();
1101
        // length of the code
1102
        $len = strlen($code);
1103
        switch(strtoupper($type)) {
1104
            case 'A': { // MODE A
1105
                $startid = 103;
1106
                for ($i = 0; $i < $len; ++$i) {
1107
                    $char = $code[$i];
1108
                    $char_id = ord($char);
1109
                    if (($char_id >= 241) and ($char_id <= 244)) {
1110
                        $code_data[] = $fnc_a[$char_id];
1111
                    } elseif (($char_id >= 0) and ($char_id <= 95)) {
1112
                        $code_data[] = strpos($keys_a, $char);
1113
                    } else {
1114
                        return false;
1115
                    }
1116
                }
1117
                break;
1118
            }
1119
            case 'B': { // MODE B
1120
                $startid = 104;
1121
                for ($i = 0; $i < $len; ++$i) {
1122
                    $char = $code[$i];
1123
                    $char_id = ord($char);
1124
                    if (($char_id >= 241) and ($char_id <= 244)) {
1125
                        $code_data[] = $fnc_b[$char_id];
1126
                    } elseif (($char_id >= 32) and ($char_id <= 127)) {
1127
                        $code_data[] = strpos($keys_b, $char);
1128
                    } else {
1129
                        return false;
1130
                    }
1131
                }
1132
                break;
1133
            }
1134
            case 'C': { // MODE C
1135
                $startid = 105;
1136
                if (ord($code[0]) == 241) {
1137
                    $code_data[] = 102;
1138
                    $code = substr($code, 1);
1139
                    --$len;
1140
                }
1141
                if (($len % 2) != 0) {
1142
                    // the length must be even
1143
                    return false;
1144
                }
1145
                for ($i = 0; $i < $len; $i += 2) {
1146
                    $chrnum = $code[$i] . $code[$i + 1];
1147
                    if (preg_match('/([0-9]{2})/', $chrnum) > 0) {
1148
                        $code_data[] = intval($chrnum);
1149
                    } else {
1150
                        return false;
1151
                    }
1152
                }
1153
                break;
1154
            }
1155
            default: { // MODE AUTO
1156
                // split code into sequences
1157
                $sequence = array();
1158
                // get numeric sequences (if any)
1159
                $numseq = array();
1160
                preg_match_all('/([0-9]{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE);
1161
                if (isset($numseq[1]) and !empty($numseq[1])) {
1162
                    $end_offset = 0;
1163
                    foreach ($numseq[1] as $val) {
1164
                        $offset = $val[1];
1165
                        if ($offset > $end_offset) {
1166
                            // non numeric sequence
1167
                            $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset))));
1168
                        }
1169
                        // numeric sequence
1170
                        $slen = strlen($val[0]);
1171
                        if (($slen % 2) != 0) {
1172
                            // the length must be even
1173
                            --$slen;
1174
                        }
1175
                        $sequence[] = array('C', substr($code, $offset, $slen), $slen);
1176
                        $end_offset = $offset + $slen;
1177
                    }
1178
                    if ($end_offset < $len) {
1179
                        $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset)));
1180
                    }
1181
                } else {
1182
                    // text code (non C mode)
1183
                    $sequence = array_merge($sequence, $this->get128ABsequence($code));
1184
                }
1185
                // process the sequence
1186
                foreach ($sequence as $key => $seq) {
1187
                    switch($seq[0]) {
1188
                        case 'A': {
1189
                            if ($key == 0) {
1190
                                $startid = 103;
1191
                            } elseif ($sequence[($key - 1)][0] != 'A') {
1192
                                if (($seq[2] == 1) and ($key > 0) and ($sequence[($key - 1)][0] == 'B') and (!isset($sequence[($key - 1)][3]))) {
1193
                                    // single character shift
1194
                                    $code_data[] = 98;
1195
                                    // mark shift
1196
                                    $sequence[$key][3] = true;
1197
                                } elseif (!isset($sequence[($key - 1)][3])) {
1198
                                    $code_data[] = 101;
1199
                                }
1200
                            }
1201
                            for ($i = 0; $i < $seq[2]; ++$i) {
1202
                                $char = $seq[1][$i];
1203
                                $char_id = ord($char);
1204
                                if (($char_id >= 241) and ($char_id <= 244)) {
1205
                                    $code_data[] = $fnc_a[$char_id];
1206
                                } else {
1207
                                    $code_data[] = strpos($keys_a, $char);
1208
                                }
1209
                            }
1210
                            break;
1211
                        }
1212
                        case 'B': {
1213
                            if ($key == 0) {
1214
                                $tmpchr = ord($seq[1][0]);
1215
                                if (($seq[2] == 1) and ($tmpchr >= 241) and ($tmpchr <= 244) and isset($sequence[($key + 1)]) and ($sequence[($key + 1)][0] != 'B')) {
1216
                                    switch ($sequence[($key + 1)][0]) {
1217
                                        case 'A': {
1218
                                            $startid = 103;
1219
                                            $sequence[$key][0] = 'A';
1220
                                            $code_data[] = $fnc_a[$tmpchr];
1221
                                            break;
1222
                                        }
1223
                                        case 'C': {
1224
                                            $startid = 105;
1225
                                            $sequence[$key][0] = 'C';
1226
                                            $code_data[] = $fnc_a[$tmpchr];
1227
                                            break;
1228
                                        }
1229
                                    }
1230
                                    break;
1231
                                } else {
1232
                                    $startid = 104;
1233
                                }
1234
                            } elseif ($sequence[($key - 1)][0] != 'B') {
1235
                                if (($seq[2] == 1) and ($key > 0) and ($sequence[($key - 1)][0] == 'A') and (!isset($sequence[($key - 1)][3]))) {
1236
                                    // single character shift
1237
                                    $code_data[] = 98;
1238
                                    // mark shift
1239
                                    $sequence[$key][3] = true;
1240
                                } elseif (!isset($sequence[($key - 1)][3])) {
1241
                                    $code_data[] = 100;
1242
                                }
1243
                            }
1244
                            for ($i = 0; $i < $seq[2]; ++$i) {
1245
                                $char = $seq[1][$i];
1246
                                $char_id = ord($char);
1247
                                if (($char_id >= 241) and ($char_id <= 244)) {
1248
                                    $code_data[] = $fnc_b[$char_id];
1249
                                } else {
1250
                                    $code_data[] = strpos($keys_b, $char);
1251
                                }
1252
                            }
1253
                            break;
1254
                        }
1255
                        case 'C': {
1256
                            if ($key == 0) {
1257
                                $startid = 105;
1258
                            } elseif ($sequence[($key - 1)][0] != 'C') {
1259
                                $code_data[] = 99;
1260
                            }
1261
                            for ($i = 0; $i < $seq[2]; $i += 2) {
1262
                                $chrnum = $seq[1][$i] . $seq[1][$i + 1];
1263
                                $code_data[] = intval($chrnum);
1264
                            }
1265
                            break;
1266
                        }
1267
                    }
1268
                }
1269
            }
1270
        }
1271
        // calculate check character
1272
        $sum = $startid;
1273
        foreach ($code_data as $key => $val) {
1274
            $sum += ($val * ($key + 1));
1275
        }
1276
        // add check character
1277
        $code_data[] = ($sum % 103);
1278
        // add stop sequence
1279
        $code_data[] = 106;
1280
        $code_data[] = 107;
1281
        // add start code at the beginning
1282
        array_unshift($code_data, $startid);
1283
        // build barcode array
1284
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1285
        foreach ($code_data as $val) {
1286
            $seq = $chr[$val];
1287
            for ($j = 0; $j < 6; ++$j) {
1288
                if (($j % 2) == 0) {
1289
                    $t = true; // bar
1290
                } else {
1291
                    $t = false; // space
1292
                }
1293
                $w = (float)$seq[$j];
1294
                $bararray['bcode'][] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1295
                $bararray['maxw'] += $w;
1296
            }
1297
        }
1298
        return $bararray;
1299
    }
1300
1301
    /**
1302
     * Split text code in A/B sequence for 128 code
1303
     * @param string $code code to split.
1304
     * @return array sequence
1305
     * @protected
1306
     */
1307
    protected function get128ABsequence($code)
1308
    {
1309
        $len = strlen($code);
1310
        $sequence = array();
1311
        // get A sequences (if any)
1312
        $numseq = array();
1313
        preg_match_all('/([\0-\31])/', $code, $numseq, PREG_OFFSET_CAPTURE);
1314
        if (isset($numseq[1]) and !empty($numseq[1])) {
1315
            $end_offset = 0;
1316
            foreach ($numseq[1] as $val) {
1317
                $offset = $val[1];
1318
                if ($offset > $end_offset) {
1319
                    // B sequence
1320
                    $sequence[] = array('B', substr($code, $end_offset, ($offset - $end_offset)), ($offset - $end_offset));
1321
                }
1322
                // A sequence
1323
                $slen = strlen($val[0]);
1324
                $sequence[] = array('A', substr($code, $offset, $slen), $slen);
1325
                $end_offset = $offset + $slen;
1326
            }
1327
            if ($end_offset < $len) {
1328
                $sequence[] = array('B', substr($code, $end_offset), ($len - $end_offset));
1329
            }
1330
        } else {
1331
            // only B sequence
1332
            $sequence[] = array('B', $code, $len);
1333
        }
1334
        return $sequence;
1335
    }
1336
1337
    /**
1338
     * EAN13 and UPC-A barcodes.
1339
     * EAN13: European Article Numbering international retail product code
1340
     * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
1341
     * UPC-E: Short version of UPC symbol
1342
     * @param string $code code to represent.
1343
     * @param string $len barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
1344
     * @return array barcode representation.
1345
     * @protected
1346
     */
1347
    protected function barcode_eanupc($code, $len = 13)
1348
    {
1349
        $upce = false;
1350
        if ($len == 6) {
1351
            $len = 12; // UPC-A
1352
            $upce = true; // UPC-E mode
1353
        }
1354
        $data_len = $len - 1;
1355
        //Padding
1356
        $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
1357
        $code_len = strlen($code);
1358
        // calculate check digit
1359
        $sum_a = 0;
1360
        for ($i = 1; $i < $data_len; $i += 2) {
1361
            $sum_a += $code[$i];
1362
        }
1363
        if ($len > 12) {
1364
            $sum_a *= 3;
1365
        }
1366
        $sum_b = 0;
1367
        for ($i = 0; $i < $data_len; $i += 2) {
1368
            $sum_b += ($code[$i]);
1369
        }
1370
        if ($len < 13) {
1371
            $sum_b *= 3;
1372
        }
1373
        $r = ($sum_a + $sum_b) % 10;
1374
        if($r > 0) {
1375
            $r = (10 - $r);
1376
        }
1377
        if ($code_len == $data_len) {
1378
            // add check digit
1379
            $code .= $r;
1380
        } elseif ($r !== intval($code[$data_len])) {
1381
            // wrong checkdigit
1382
            return false;
1383
        }
1384
        if ($len == 12) {
1385
            // UPC-A
1386
            $code = '0' . $code;
1387
            ++$len;
1388
        }
1389
        if ($upce) {
1390
            // convert UPC-A to UPC-E
1391
            $tmp = substr($code, 4, 3);
1392
            if (($tmp == '000') or ($tmp == '100') or ($tmp == '200')) {
1393
                // manufacturer code ends in 000, 100, or 200
1394
                $upce_code = substr($code, 2, 2) . substr($code, 9, 3) . substr($code, 4, 1);
1395
            } else {
1396
                $tmp = substr($code, 5, 2);
1397
                if ($tmp == '00') {
1398
                    // manufacturer code ends in 00
1399
                    $upce_code = substr($code, 2, 3) . substr($code, 10, 2) . '3';
1400
                } else {
1401
                    $tmp = substr($code, 6, 1);
1402
                    if ($tmp == '0') {
1403
                        // manufacturer code ends in 0
1404
                        $upce_code = substr($code, 2, 4) . substr($code, 11, 1) . '4';
1405
                    } else {
1406
                        // manufacturer code does not end in zero
1407
                        $upce_code = substr($code, 2, 5) . substr($code, 11, 1);
1408
                    }
1409
                }
1410
            }
1411
        }
1412
        //Convert digits to bars
1413
        $codes = array(
1414
            'A' => array( // left odd parity
1415
                '0' => '0001101',
1416
                '1' => '0011001',
1417
                '2' => '0010011',
1418
                '3' => '0111101',
1419
                '4' => '0100011',
1420
                '5' => '0110001',
1421
                '6' => '0101111',
1422
                '7' => '0111011',
1423
                '8' => '0110111',
1424
                '9' => '0001011'),
1425
            'B' => array( // left even parity
1426
                '0' => '0100111',
1427
                '1' => '0110011',
1428
                '2' => '0011011',
1429
                '3' => '0100001',
1430
                '4' => '0011101',
1431
                '5' => '0111001',
1432
                '6' => '0000101',
1433
                '7' => '0010001',
1434
                '8' => '0001001',
1435
                '9' => '0010111'),
1436
            'C' => array( // right
1437
                '0' => '1110010',
1438
                '1' => '1100110',
1439
                '2' => '1101100',
1440
                '3' => '1000010',
1441
                '4' => '1011100',
1442
                '5' => '1001110',
1443
                '6' => '1010000',
1444
                '7' => '1000100',
1445
                '8' => '1001000',
1446
                '9' => '1110100')
1447
        );
1448
        $parities = array(
1449
            '0' => array('A','A','A','A','A','A'),
1450
            '1' => array('A','A','B','A','B','B'),
1451
            '2' => array('A','A','B','B','A','B'),
1452
            '3' => array('A','A','B','B','B','A'),
1453
            '4' => array('A','B','A','A','B','B'),
1454
            '5' => array('A','B','B','A','A','B'),
1455
            '6' => array('A','B','B','B','A','A'),
1456
            '7' => array('A','B','A','B','A','B'),
1457
            '8' => array('A','B','A','B','B','A'),
1458
            '9' => array('A','B','B','A','B','A')
1459
        );
1460
        $upce_parities = array();
1461
        $upce_parities[0] = array(
1462
            '0' => array('B','B','B','A','A','A'),
1463
            '1' => array('B','B','A','B','A','A'),
1464
            '2' => array('B','B','A','A','B','A'),
1465
            '3' => array('B','B','A','A','A','B'),
1466
            '4' => array('B','A','B','B','A','A'),
1467
            '5' => array('B','A','A','B','B','A'),
1468
            '6' => array('B','A','A','A','B','B'),
1469
            '7' => array('B','A','B','A','B','A'),
1470
            '8' => array('B','A','B','A','A','B'),
1471
            '9' => array('B','A','A','B','A','B')
1472
        );
1473
        $upce_parities[1] = array(
1474
            '0' => array('A','A','A','B','B','B'),
1475
            '1' => array('A','A','B','A','B','B'),
1476
            '2' => array('A','A','B','B','A','B'),
1477
            '3' => array('A','A','B','B','B','A'),
1478
            '4' => array('A','B','A','A','B','B'),
1479
            '5' => array('A','B','B','A','A','B'),
1480
            '6' => array('A','B','B','B','A','A'),
1481
            '7' => array('A','B','A','B','A','B'),
1482
            '8' => array('A','B','A','B','B','A'),
1483
            '9' => array('A','B','B','A','B','A')
1484
        );
1485
        $k = 0;
1486
        $seq = '101'; // left guard bar
1487
        if ($upce) {
1488
            $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1489
            $p = $upce_parities[$code[1]][$r];
1490
            for ($i = 0; $i < 6; ++$i) {
1491
                $seq .= $codes[$p[$i]][$upce_code[$i]];
1492
            }
1493
            $seq .= '010101'; // right guard bar
1494
        } else {
1495
            $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1496
            $half_len = intval(ceil($len / 2));
1497
            if ($len == 8) {
1498
                for ($i = 0; $i < $half_len; ++$i) {
1499
                    $seq .= $codes['A'][$code[$i]];
1500
                }
1501
            } else {
1502
                $p = $parities[$code[0]];
1503
                for ($i = 1; $i < $half_len; ++$i) {
1504
                    $seq .= $codes[$p[$i - 1]][$code[$i]];
1505
                }
1506
            }
1507
            $seq .= '01010'; // center guard bar
1508
            for ($i = $half_len; $i < $len; ++$i) {
1509
                $seq .= $codes['C'][$code[$i]];
1510
            }
1511
            $seq .= '101'; // right guard bar
1512
        }
1513
        $clen = strlen($seq);
1514
        $w = 0;
1515
        for ($i = 0; $i < $clen; ++$i) {
1516
            $w += 1;
1517
            if (($i == ($clen - 1)) or (($i < ($clen - 1)) and ($seq[$i] != $seq[$i + 1]))) {
1518
                if ($seq[$i] == '1') {
1519
                    $t = true; // bar
1520
                } else {
1521
                    $t = false; // space
1522
                }
1523
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1524
                $bararray['maxw'] += $w;
1525
                ++$k;
1526
                $w = 0;
1527
            }
1528
        }
1529
        return $bararray;
1530
    }
1531
1532
    /**
1533
     * UPC-Based Extensions
1534
     * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
1535
     * 5-Digit Ext.: Used to mark suggested retail price of books
1536
     * @param string $code code to represent.
1537
     * @param string $len barcode type: 2 = 2-Digit, 5 = 5-Digit
1538
     * @return array barcode representation.
1539
     * @protected
1540
     */
1541
    protected function barcode_eanext($code, $len = 5)
1542
    {
1543
        //Padding
1544
        $code = str_pad($code, $len, '0', STR_PAD_LEFT);
1545
        // calculate check digit
1546
        if ($len == 2) {
1547
            $r = $code % 4;
1548
        } elseif ($len == 5) {
1549
            $r = (3 * ($code[0] + $code[2] + $code[4])) + (9 * ($code[1] + $code[3]));
1550
            $r %= 10;
1551
        } else {
1552
            return false;
1553
        }
1554
        //Convert digits to bars
1555
        $codes = array(
1556
            'A' => array( // left odd parity
1557
                '0' => '0001101',
1558
                '1' => '0011001',
1559
                '2' => '0010011',
1560
                '3' => '0111101',
1561
                '4' => '0100011',
1562
                '5' => '0110001',
1563
                '6' => '0101111',
1564
                '7' => '0111011',
1565
                '8' => '0110111',
1566
                '9' => '0001011'),
1567
            'B' => array( // left even parity
1568
                '0' => '0100111',
1569
                '1' => '0110011',
1570
                '2' => '0011011',
1571
                '3' => '0100001',
1572
                '4' => '0011101',
1573
                '5' => '0111001',
1574
                '6' => '0000101',
1575
                '7' => '0010001',
1576
                '8' => '0001001',
1577
                '9' => '0010111')
1578
        );
1579
        $parities = array();
1580
        $parities[2] = array(
1581
            '0' => array('A','A'),
1582
            '1' => array('A','B'),
1583
            '2' => array('B','A'),
1584
            '3' => array('B','B')
1585
        );
1586
        $parities[5] = array(
1587
            '0' => array('B','B','A','A','A'),
1588
            '1' => array('B','A','B','A','A'),
1589
            '2' => array('B','A','A','B','A'),
1590
            '3' => array('B','A','A','A','B'),
1591
            '4' => array('A','B','B','A','A'),
1592
            '5' => array('A','A','B','B','A'),
1593
            '6' => array('A','A','A','B','B'),
1594
            '7' => array('A','B','A','B','A'),
1595
            '8' => array('A','B','A','A','B'),
1596
            '9' => array('A','A','B','A','B')
1597
        );
1598
        $p = $parities[$len][$r];
1599
        $seq = '1011'; // left guard bar
1600
        $seq .= $codes[$p[0]][$code[0]];
1601
        for ($i = 1; $i < $len; ++$i) {
1602
            $seq .= '01'; // separator
1603
            $seq .= $codes[$p[$i]][$code[$i]];
1604
        }
1605
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1606
        return $this->binseq_to_array($seq, $bararray);
1607
    }
1608
1609
    /**
1610
     * POSTNET and PLANET barcodes.
1611
     * Used by U.S. Postal Service for automated mail sorting
1612
     * @param string $code zip code to represent. Must be a string containing a zip code of the form DDDDD or DDDDD-DDDD.
1613
     * @param boolean $planet if true print the PLANET barcode, otherwise print POSTNET
1614
     * @return array barcode representation.
1615
     * @protected
1616
     */
1617
    protected function barcode_postnet($code, $planet = false)
1618
    {
1619
        // bar length
1620
        if ($planet) {
1621
            $barlen = array(
1622
                0 => array(1,1,2,2,2),
1623
                1 => array(2,2,2,1,1),
1624
                2 => array(2,2,1,2,1),
1625
                3 => array(2,2,1,1,2),
1626
                4 => array(2,1,2,2,1),
1627
                5 => array(2,1,2,1,2),
1628
                6 => array(2,1,1,2,2),
1629
                7 => array(1,2,2,2,1),
1630
                8 => array(1,2,2,1,2),
1631
                9 => array(1,2,1,2,2)
1632
            );
1633
        } else {
1634
            $barlen = array(
1635
                0 => array(2,2,1,1,1),
1636
                1 => array(1,1,1,2,2),
1637
                2 => array(1,1,2,1,2),
1638
                3 => array(1,1,2,2,1),
1639
                4 => array(1,2,1,1,2),
1640
                5 => array(1,2,1,2,1),
1641
                6 => array(1,2,2,1,1),
1642
                7 => array(2,1,1,1,2),
1643
                8 => array(2,1,1,2,1),
1644
                9 => array(2,1,2,1,1)
1645
            );
1646
        }
1647
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
1648
        $k = 0;
1649
        $code = str_replace('-', '', $code);
1650
        $code = str_replace(' ', '', $code);
1651
        $len = strlen($code);
1652
        // calculate checksum
1653
        $sum = 0;
1654
        for ($i = 0; $i < $len; ++$i) {
1655
            $sum += intval($code[$i]);
1656
        }
1657
        $chkd = ($sum % 10);
1658
        if($chkd > 0) {
1659
            $chkd = (10 - $chkd);
1660
        }
1661
        $code .= $chkd;
1662
        $len = strlen($code);
1663
        // start bar
1664
        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1665
        $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1666
        $bararray['maxw'] += 2;
1667
        for ($i = 0; $i < $len; ++$i) {
1668
            for ($j = 0; $j < 5; ++$j) {
1669
                $h = $barlen[$code[$i]][$j];
1670
                $p = floor(1 / $h);
1671
                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1672
                $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1673
                $bararray['maxw'] += 2;
1674
            }
1675
        }
1676
        // end bar
1677
        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1678
        $bararray['maxw'] += 1;
1679
        return $bararray;
1680
    }
1681
1682
    /**
1683
     * RMS4CC - CBC - KIX
1684
     * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
1685
     * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
1686
     * @param string $code code to print
1687
     * @param boolean $kix if true prints the KIX variation (doesn't use the start and end symbols, and the checksum) - in this case the house number must be sufficed with an X and placed at the end of the code.
1688
     * @return array barcode representation.
1689
     * @protected
1690
     */
1691
    protected function barcode_rms4cc($code, $kix = false)
1692
    {
1693
        $notkix = !$kix;
1694
        // bar mode
1695
        // 1 = pos 1, length 2
1696
        // 2 = pos 1, length 3
1697
        // 3 = pos 2, length 1
1698
        // 4 = pos 2, length 2
1699
        $barmode = array(
1700
            '0' => array(3,3,2,2),
1701
            '1' => array(3,4,1,2),
1702
            '2' => array(3,4,2,1),
1703
            '3' => array(4,3,1,2),
1704
            '4' => array(4,3,2,1),
1705
            '5' => array(4,4,1,1),
1706
            '6' => array(3,1,4,2),
1707
            '7' => array(3,2,3,2),
1708
            '8' => array(3,2,4,1),
1709
            '9' => array(4,1,3,2),
1710
            'A' => array(4,1,4,1),
1711
            'B' => array(4,2,3,1),
1712
            'C' => array(3,1,2,4),
1713
            'D' => array(3,2,1,4),
1714
            'E' => array(3,2,2,3),
1715
            'F' => array(4,1,1,4),
1716
            'G' => array(4,1,2,3),
1717
            'H' => array(4,2,1,3),
1718
            'I' => array(1,3,4,2),
1719
            'J' => array(1,4,3,2),
1720
            'K' => array(1,4,4,1),
1721
            'L' => array(2,3,3,2),
1722
            'M' => array(2,3,4,1),
1723
            'N' => array(2,4,3,1),
1724
            'O' => array(1,3,2,4),
1725
            'P' => array(1,4,1,4),
1726
            'Q' => array(1,4,2,3),
1727
            'R' => array(2,3,1,4),
1728
            'S' => array(2,3,2,3),
1729
            'T' => array(2,4,1,3),
1730
            'U' => array(1,1,4,4),
1731
            'V' => array(1,2,3,4),
1732
            'W' => array(1,2,4,3),
1733
            'X' => array(2,1,3,4),
1734
            'Y' => array(2,1,4,3),
1735
            'Z' => array(2,2,3,3)
1736
        );
1737
        $code = strtoupper($code);
1738
        $len = strlen($code);
1739
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
1740
        if ($notkix) {
1741
            // table for checksum calculation (row,col)
1742
            $checktable = array(
1743
                '0' => array(1,1),
1744
                '1' => array(1,2),
1745
                '2' => array(1,3),
1746
                '3' => array(1,4),
1747
                '4' => array(1,5),
1748
                '5' => array(1,0),
1749
                '6' => array(2,1),
1750
                '7' => array(2,2),
1751
                '8' => array(2,3),
1752
                '9' => array(2,4),
1753
                'A' => array(2,5),
1754
                'B' => array(2,0),
1755
                'C' => array(3,1),
1756
                'D' => array(3,2),
1757
                'E' => array(3,3),
1758
                'F' => array(3,4),
1759
                'G' => array(3,5),
1760
                'H' => array(3,0),
1761
                'I' => array(4,1),
1762
                'J' => array(4,2),
1763
                'K' => array(4,3),
1764
                'L' => array(4,4),
1765
                'M' => array(4,5),
1766
                'N' => array(4,0),
1767
                'O' => array(5,1),
1768
                'P' => array(5,2),
1769
                'Q' => array(5,3),
1770
                'R' => array(5,4),
1771
                'S' => array(5,5),
1772
                'T' => array(5,0),
1773
                'U' => array(0,1),
1774
                'V' => array(0,2),
1775
                'W' => array(0,3),
1776
                'X' => array(0,4),
1777
                'Y' => array(0,5),
1778
                'Z' => array(0,0)
1779
            );
1780
            $row = 0;
1781
            $col = 0;
1782
            for ($i = 0; $i < $len; ++$i) {
1783
                $row += $checktable[$code[$i]][0];
1784
                $col += $checktable[$code[$i]][1];
1785
            }
1786
            $row %= 6;
1787
            $col %= 6;
1788
            $chk = array_keys($checktable, array($row,$col));
1789
            $code .= $chk[0];
1790
            ++$len;
1791
        }
1792
        $k = 0;
1793
        if ($notkix) {
1794
            // start bar
1795
            $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1796
            $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1797
            $bararray['maxw'] += 2;
1798
        }
1799
        for ($i = 0; $i < $len; ++$i) {
1800
            for ($j = 0; $j < 4; ++$j) {
1801
                switch ($barmode[$code[$i]][$j]) {
1802
                    case 1: {
1803
                        $p = 0;
1804
                        $h = 2;
1805
                        break;
1806
                    }
1807
                    case 2: {
1808
                        $p = 0;
1809
                        $h = 3;
1810
                        break;
1811
                    }
1812
                    case 3: {
1813
                        $p = 1;
1814
                        $h = 1;
1815
                        break;
1816
                    }
1817
                    case 4: {
1818
                        $p = 1;
1819
                        $h = 2;
1820
                        break;
1821
                    }
1822
                }
1823
                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1824
                $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1825
                $bararray['maxw'] += 2;
1826
            }
1827
        }
1828
        if ($notkix) {
1829
            // stop bar
1830
            $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0);
1831
            $bararray['maxw'] += 1;
1832
        }
1833
        return $bararray;
1834
    }
1835
1836
    /**
1837
     * CODABAR barcodes.
1838
     * Older code often used in library systems, sometimes in blood banks
1839
     * @param string $code code to represent.
1840
     * @return array barcode representation.
1841
     * @protected
1842
     */
1843
    protected function barcode_codabar($code)
1844
    {
1845
        $chr = array(
1846
            '0' => '11111221',
1847
            '1' => '11112211',
1848
            '2' => '11121121',
1849
            '3' => '22111111',
1850
            '4' => '11211211',
1851
            '5' => '21111211',
1852
            '6' => '12111121',
1853
            '7' => '12112111',
1854
            '8' => '12211111',
1855
            '9' => '21121111',
1856
            '-' => '11122111',
1857
            '$' => '11221111',
1858
            ':' => '21112121',
1859
            '/' => '21211121',
1860
            '.' => '21212111',
1861
            '+' => '11222221',
1862
            'A' => '11221211',
1863
            'B' => '12121121',
1864
            'C' => '11121221',
1865
            'D' => '11122211'
1866
        );
1867
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1868
        $k = 0;
1869
        $w = 0;
1870
        $seq = '';
1871
        $code = 'A' . strtoupper($code) . 'A';
1872
        $len = strlen($code);
1873
        for ($i = 0; $i < $len; ++$i) {
1874
            if (!isset($chr[$code[$i]])) {
1875
                return false;
1876
            }
1877
            $seq = $chr[$code[$i]];
1878
            for ($j = 0; $j < 8; ++$j) {
1879
                if (($j % 2) == 0) {
1880
                    $t = true; // bar
1881
                } else {
1882
                    $t = false; // space
1883
                }
1884
                $w = (float)$seq[$j];
1885
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1886
                $bararray['maxw'] += $w;
1887
                ++$k;
1888
            }
1889
        }
1890
        return $bararray;
1891
    }
1892
1893
    /**
1894
     * CODE11 barcodes.
1895
     * Used primarily for labeling telecommunications equipment
1896
     * @param string $code code to represent.
1897
     * @return array barcode representation.
1898
     * @protected
1899
     */
1900
    protected function barcode_code11($code)
1901
    {
1902
        $chr = array(
1903
            '0' => '111121',
1904
            '1' => '211121',
1905
            '2' => '121121',
1906
            '3' => '221111',
1907
            '4' => '112121',
1908
            '5' => '212111',
1909
            '6' => '122111',
1910
            '7' => '111221',
1911
            '8' => '211211',
1912
            '9' => '211111',
1913
            '-' => '112111',
1914
            'S' => '112211'
1915
        );
1916
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1917
        $k = 0;
1918
        $w = 0;
1919
        $seq = '';
1920
        $len = strlen($code);
1921
        // calculate check digit C
1922
        $p = 1;
1923
        $check = 0;
1924
        for ($i = ($len - 1); $i >= 0; --$i) {
1925
            $digit = $code[$i];
1926
            if ($digit == '-') {
1927
                $dval = 10;
1928
            } else {
1929
                $dval = intval($digit);
1930
            }
1931
            $check += ($dval * $p);
1932
            ++$p;
1933
            if ($p > 10) {
1934
                $p = 1;
1935
            }
1936
        }
1937
        $check %= 11;
1938
        if ($check == 10) {
1939
            $check = '-';
1940
        }
1941
        $code .= $check;
1942
        if ($len > 10) {
1943
            // calculate check digit K
1944
            $p = 1;
1945
            $check = 0;
1946
            for ($i = $len; $i >= 0; --$i) {
1947
                $digit = $code[$i];
1948
                if ($digit == '-') {
1949
                    $dval = 10;
1950
                } else {
1951
                    $dval = intval($digit);
1952
                }
1953
                $check += ($dval * $p);
1954
                ++$p;
1955
                if ($p > 9) {
1956
                    $p = 1;
1957
                }
1958
            }
1959
            $check %= 11;
1960
            $code .= $check;
1961
            ++$len;
1962
        }
1963
        $code = 'S' . $code . 'S';
1964
        $len += 3;
1965
        for ($i = 0; $i < $len; ++$i) {
1966
            if (!isset($chr[$code[$i]])) {
1967
                return false;
1968
            }
1969
            $seq = $chr[$code[$i]];
1970
            for ($j = 0; $j < 6; ++$j) {
1971
                if (($j % 2) == 0) {
1972
                    $t = true; // bar
1973
                } else {
1974
                    $t = false; // space
1975
                }
1976
                $w = (float)$seq[$j];
1977
                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1978
                $bararray['maxw'] += $w;
1979
                ++$k;
1980
            }
1981
        }
1982
        return $bararray;
1983
    }
1984
1985
    /**
1986
     * Pharmacode
1987
     * Contains digits (0 to 9)
1988
     * @param string $code code to represent.
1989
     * @return array barcode representation.
1990
     * @protected
1991
     */
1992
    protected function barcode_pharmacode($code)
1993
    {
1994
        $seq = '';
1995
        $code = intval($code);
1996
        while ($code > 0) {
1997
            if (($code % 2) == 0) {
1998
                $seq .= '11100';
1999
                $code -= 2;
2000
            } else {
2001
                $seq .= '100';
2002
                $code -= 1;
2003
            }
2004
            $code /= 2;
2005
        }
2006
        $seq = substr($seq, 0, -2);
2007
        $seq = strrev($seq);
2008
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
2009
        return $this->binseq_to_array($seq, $bararray);
2010
    }
2011
2012
    /**
2013
     * Pharmacode two-track
2014
     * Contains digits (0 to 9)
2015
     * @param string $code code to represent.
2016
     * @return array barcode representation.
2017
     * @protected
2018
     */
2019
    protected function barcode_pharmacode2t($code)
2020
    {
2021
        $seq = '';
2022
        $code = intval($code);
2023
        do {
2024
            switch ($code % 3) {
2025
                case 0: {
2026
                    $seq .= '3';
2027
                    $code = ($code - 3) / 3;
2028
                    break;
2029
                }
2030
                case 1: {
2031
                    $seq .= '1';
2032
                    $code = ($code - 1) / 3;
2033
                    break;
2034
                }
2035
                case 2: {
2036
                    $seq .= '2';
2037
                    $code = ($code - 2) / 3;
2038
                    break;
2039
                }
2040
            }
2041
        } while($code != 0);
2042
        $seq = strrev($seq);
2043
        $k = 0;
2044
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
2045
        $len = strlen($seq);
2046
        for ($i = 0; $i < $len; ++$i) {
2047
            switch ($seq[$i]) {
2048
                case '1': {
2049
                    $p = 1;
2050
                    $h = 1;
2051
                    break;
2052
                }
2053
                case '2': {
2054
                    $p = 0;
2055
                    $h = 1;
2056
                    break;
2057
                }
2058
                case '3': {
2059
                    $p = 0;
2060
                    $h = 2;
2061
                    break;
2062
                }
2063
            }
2064
            $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2065
            $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2066
            $bararray['maxw'] += 2;
2067
        }
2068
        unset($bararray['bcode'][($k - 1)]);
2069
        --$bararray['maxw'];
2070
        return $bararray;
2071
    }
2072
2073
    /**
2074
     * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
2075
     * (requires PHP bcmath extension)
2076
     * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
2077
     * The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the presort identification that is currently printed in human readable form on the optional endorsement line (OEL) as well as for future USPS use. This shall be two digits, with the second digit in the range of 0–4. The allowable encoding ranges shall be 00–04, 10–14, 20–24, 30–34, 40–44, 50–54, 60–64, 70–74, 80–84, and 90–94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php–999. Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The allowable encoding range shall be 000000000–999999999 when used with a 6 digit Mailer ID and 000000-999999 when used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000–99999,  000000000–999999999, and 00000000000–99999999999.</li></ul>
2078
     * @param string $code code to print, separate the ZIP (routing code) from the rest using a minus char '-' (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
2079
     * @return array barcode representation.
2080
     * @protected
2081
     */
2082
    protected function barcode_imb($code)
2083
    {
2084
        $asc_chr = array(4,0,2,6,3,5,1,9,8,7,1,2,0,6,4,8,2,9,5,3,0,1,3,7,4,6,8,9,2,0,5,1,9,4,3,8,6,7,1,2,4,3,9,5,7,8,3,0,2,1,4,0,9,1,7,0,2,4,6,3,7,1,9,5,8);
2085
        $dsc_chr = array(7,1,9,5,8,0,2,4,6,3,5,8,9,7,3,0,6,1,7,4,6,8,9,2,5,1,7,5,4,3,8,7,6,0,2,5,4,9,3,0,1,6,8,2,0,4,5,9,6,7,5,2,6,3,8,5,1,9,8,7,4,0,2,6,3);
2086
        $asc_pos = array(3,0,8,11,1,12,8,11,10,6,4,12,2,7,9,6,7,9,2,8,4,0,12,7,10,9,0,7,10,5,7,9,6,8,2,12,1,4,2,0,1,5,4,6,12,1,0,9,4,7,5,10,2,6,9,11,2,12,6,7,5,11,0,3,2);
2087
        $dsc_pos = array(2,10,12,5,9,1,5,4,3,9,11,5,10,1,6,3,4,1,10,0,2,11,8,6,1,12,3,8,6,4,4,11,0,6,1,9,11,5,3,7,3,10,7,11,8,2,10,3,5,8,0,3,12,11,8,4,5,1,3,0,7,12,9,8,10);
2088
        $code_arr = explode('-', $code);
2089
        $tracking_number = $code_arr[0];
2090
        if (isset($code_arr[1])) {
2091
            $routing_code = $code_arr[1];
2092
        } else {
2093
            $routing_code = '';
2094
        }
2095
        // Conversion of Routing Code
2096
        switch (strlen($routing_code)) {
2097
            case 0: {
2098
                $binary_code = 0;
2099
                break;
2100
            }
2101
            case 5: {
2102
                $binary_code = bcadd($routing_code, '1');
2103
                break;
2104
            }
2105
            case 9: {
2106
                $binary_code = bcadd($routing_code, '100001');
2107
                break;
2108
            }
2109
            case 11: {
2110
                $binary_code = bcadd($routing_code, '1000100001');
2111
                break;
2112
            }
2113
            default: {
2114
                return false;
2115
                break;
2116
            }
2117
        }
2118
        $binary_code = bcmul($binary_code, 10);
2119
        $binary_code = bcadd($binary_code, $tracking_number[0]);
2120
        $binary_code = bcmul($binary_code, 5);
2121
        $binary_code = bcadd($binary_code, $tracking_number[1]);
2122
        $binary_code .= substr($tracking_number, 2, 18);
2123
        // convert to hexadecimal
2124
        $binary_code = $this->dec_to_hex($binary_code);
2125
        // pad to get 13 bytes
2126
        $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
2127
        // convert string to array of bytes
2128
        $binary_code_arr = chunk_split($binary_code, 2, "\r");
2129
        $binary_code_arr = substr($binary_code_arr, 0, -1);
2130
        $binary_code_arr = explode("\r", $binary_code_arr);
2131
        // calculate frame check sequence
2132
        $fcs = $this->imb_crc11fcs($binary_code_arr);
2133
        // exclude first 2 bits from first byte
2134
        $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
2135
        $binary_code_102bit = $first_byte . substr($binary_code, 2);
2136
        // convert binary data to codewords
2137
        $codewords = array();
2138
        $data = $this->hex_to_dec($binary_code_102bit);
2139
        $codewords[0] = bcmod($data, 636) * 2;
2140
        $data = bcdiv($data, 636);
2141
        for ($i = 1; $i < 9; ++$i) {
2142
            $codewords[$i] = bcmod($data, 1365);
2143
            $data = bcdiv($data, 1365);
2144
        }
2145
        $codewords[9] = $data;
2146
        if (($fcs >> 10) == 1) {
2147
            $codewords[9] += 659;
2148
        }
2149
        // generate lookup tables
2150
        $table2of13 = $this->imb_tables(2, 78);
2151
        $table5of13 = $this->imb_tables(5, 1287);
2152
        // convert codewords to characters
2153
        $characters = array();
2154
        $bitmask = 512;
2155
        foreach($codewords as $k => $val) {
2156
            if ($val <= 1286) {
2157
                $chrcode = $table5of13[$val];
2158
            } else {
2159
                $chrcode = $table2of13[($val - 1287)];
2160
            }
2161
            if (($fcs & $bitmask) > 0) {
2162
                // bitwise invert
2163
                $chrcode = ((~$chrcode) & 8191);
2164
            }
2165
            $characters[] = $chrcode;
2166
            $bitmask /= 2;
2167
        }
2168
        $characters = array_reverse($characters);
2169
        // build bars
2170
        $k = 0;
2171
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
2172
        for ($i = 0; $i < 65; ++$i) {
2173
            $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
2174
            $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
2175
            if ($asc and $dsc) {
2176
                // full bar (F)
2177
                $p = 0;
2178
                $h = 3;
2179
            } elseif ($asc) {
2180
                // ascender (A)
2181
                $p = 0;
2182
                $h = 2;
2183
            } elseif ($dsc) {
2184
                // descender (D)
2185
                $p = 1;
2186
                $h = 2;
2187
            } else {
2188
                // tracker (T)
2189
                $p = 1;
2190
                $h = 1;
2191
            }
2192
            $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2193
            $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2194
            $bararray['maxw'] += 2;
2195
        }
2196
        unset($bararray['bcode'][($k - 1)]);
2197
        --$bararray['maxw'];
2198
        return $bararray;
2199
    }
2200
2201
    /**
2202
     * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
2203
     *
2204
     * @param string $code pre-formatted IMB barcode (65 chars "FADT")
2205
     * @return array barcode representation.
2206
     * @protected
2207
     */
2208
    protected function barcode_imb_pre($code)
2209
    {
2210
        if (!preg_match('/^[fadtFADT]{65}$/', $code) == 1) {
2211
            return false;
2212
        }
2213
        $characters = str_split(strtolower($code), 1);
2214
        // build bars
2215
        $k = 0;
2216
        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
2217
        for ($i = 0; $i < 65; ++$i) {
2218
            switch($characters[$i]) {
2219
                case 'f': {
2220
                    // full bar
2221
                    $p = 0;
2222
                    $h = 3;
2223
                    break;
2224
                }
2225
                case 'a': {
2226
                    // ascender
2227
                    $p = 0;
2228
                    $h = 2;
2229
                    break;
2230
                }
2231
                case 'd': {
2232
                    // descender
2233
                    $p = 1;
2234
                    $h = 2;
2235
                    break;
2236
                }
2237
                case 't': {
2238
                    // tracker (short)
2239
                    $p = 1;
2240
                    $h = 1;
2241
                    break;
2242
                }
2243
            }
2244
            $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2245
            $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2246
            $bararray['maxw'] += 2;
2247
        }
2248
        unset($bararray['bcode'][($k - 1)]);
2249
        --$bararray['maxw'];
2250
        return $bararray;
2251
    }
2252
2253
    /**
2254
     * Convert large integer number to hexadecimal representation.
2255
     * (requires PHP bcmath extension)
2256
     * @param string $number number to convert specified as a string
2257
     * @return string hexadecimal representation
2258
     */
2259
    public function dec_to_hex($number)
2260
    {
2261
        $i = 0;
2262
        $hex = array();
2263
        if($number == 0) {
2264
            return '00';
2265
        }
2266
        while($number > 0) {
2267
            if($number == 0) {
2268
                array_push($hex, '0');
2269
            } else {
2270
                array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
2271
                $number = bcdiv($number, '16', 0);
2272
            }
2273
        }
2274
        $hex = array_reverse($hex);
2275
        return implode($hex);
2276
    }
2277
2278
    /**
2279
     * Convert large hexadecimal number to decimal representation (string).
2280
     * (requires PHP bcmath extension)
2281
     * @param string $hex hexadecimal number to convert specified as a string
2282
     * @return string hexadecimal representation
2283
     */
2284
    public function hex_to_dec($hex)
2285
    {
2286
        $dec = 0;
2287
        $bitval = 1;
2288
        $len = strlen($hex);
2289
        for($pos = ($len - 1); $pos >= 0; --$pos) {
2290
            $dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval));
2291
            $bitval = bcmul($bitval, 16);
2292
        }
2293
        return $dec;
2294
    }
2295
2296
    /**
2297
     * Intelligent Mail Barcode calculation of Frame Check Sequence
2298
     * @param string $code_arr array of hexadecimal values (13 bytes holding 102 bits right justified).
2299
     * @return int 11 bit Frame Check Sequence as integer (decimal base)
2300
     * @protected
2301
     */
2302
    protected function imb_crc11fcs($code_arr)
2303
    {
2304
        $genpoly = 0x0F35; // generator polynomial
2305
        $fcs = 0x07FF; // Frame Check Sequence
2306
        // do most significant byte skipping the 2 most significant bits
2307
        $data = hexdec($code_arr[0]) << 5;
2308
        for ($bit = 2; $bit < 8; ++$bit) {
2309
            if (($fcs ^ $data) & 0x400) {
2310
                $fcs = ($fcs << 1) ^ $genpoly;
2311
            } else {
2312
                $fcs = ($fcs << 1);
2313
            }
2314
            $fcs &= 0x7FF;
2315
            $data <<= 1;
2316
        }
2317
        // do rest of bytes
2318
        for ($byte = 1; $byte < 13; ++$byte) {
2319
            $data = hexdec($code_arr[$byte]) << 3;
2320
            for ($bit = 0; $bit < 8; ++$bit) {
2321
                if (($fcs ^ $data) & 0x400) {
2322
                    $fcs = ($fcs << 1) ^ $genpoly;
2323
                } else {
2324
                    $fcs = ($fcs << 1);
2325
                }
2326
                $fcs &= 0x7FF;
2327
                $data <<= 1;
2328
            }
2329
        }
2330
        return $fcs;
2331
    }
2332
2333
    /**
2334
     * Reverse unsigned short value
2335
     * @param int $num value to reversr
2336
     * @return int reversed value
2337
     * @protected
2338
     */
2339
    protected function imb_reverse_us($num)
2340
    {
2341
        $rev = 0;
2342
        for ($i = 0; $i < 16; ++$i) {
2343
            $rev <<= 1;
2344
            $rev |= ($num & 1);
2345
            $num >>= 1;
2346
        }
2347
        return $rev;
2348
    }
2349
2350
    /**
2351
     * generate Nof13 tables used for Intelligent Mail Barcode
2352
     * @param int $n is the type of table: 2 for 2of13 table, 5 for 5of13table
2353
     * @param int $size size of table (78 for n=2 and 1287 for n=5)
2354
     * @return array requested table
2355
     * @protected
2356
     */
2357
    protected function imb_tables($n, $size)
2358
    {
2359
        $table = array();
2360
        $lli = 0; // LUT lower index
2361
        $lui = $size - 1; // LUT upper index
2362
        for ($count = 0; $count < 8192; ++$count) {
2363
            $bit_count = 0;
2364
            for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
2365
                $bit_count += intval(($count & (1 << $bit_index)) != 0);
2366
            }
2367
            // if we don't have the right number of bits on, go on to the next value
2368
            if ($bit_count == $n) {
2369
                $reverse = ($this->imb_reverse_us($count) >> 3);
2370
                // if the reverse is less than count, we have already visited this pair before
2371
                if ($reverse >= $count) {
2372
                    // If count is symmetric, place it at the first free slot from the end of the list.
2373
                    // Otherwise, place it at the first free slot from the beginning of the list AND place $reverse ath the next free slot from the beginning of the list
2374
                    if ($reverse == $count) {
2375
                        $table[$lui] = $count;
2376
                        --$lui;
2377
                    } else {
2378
                        $table[$lli] = $count;
2379
                        ++$lli;
2380
                        $table[$lli] = $reverse;
2381
                        ++$lli;
2382
                    }
2383
                }
2384
            }
2385
        }
2386
        return $table;
2387
    }
2388
} // end of class
2389
//============================================================+
2390
// END OF FILE
2391
//============================================================+
2392