Projet

Général

Profil

Paste
Télécharger (71,7 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / libraries / tcpdf-6.2.9 / tcpdf_barcodes_1d.php @ a1cafe7e

1
<?php
2
//============================================================+
3
// File name   : tcpdf_barcodes_1d.php
4
// Version     : 1.0.027
5
// Begin       : 2008-06-09
6
// Last Update : 2014-10-20
7
// Author      : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com
8
// License     : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html)
9
// -------------------------------------------------------------------
10
// Copyright (C) 2008-2014 Nicola Asuni - Tecnick.com LTD
11
//
12
// This file is part of TCPDF software library.
13
//
14
// TCPDF is free software: you can redistribute it and/or modify it
15
// under the terms of the GNU Lesser General Public License as
16
// published by the Free Software Foundation, either version 3 of the
17
// License, or (at your option) any later version.
18
//
19
// TCPDF is distributed in the hope that it will be useful, but
20
// WITHOUT ANY WARRANTY; without even the implied warranty of
21
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22
// See the GNU Lesser General Public License for more details.
23
//
24
// You should have received a copy of the GNU Lesser General Public License
25
// along with TCPDF.  If not, see <http://www.gnu.org/licenses/>.
26
//
27
// See LICENSE.TXT file for more information.
28
// -------------------------------------------------------------------
29
//
30
// Description : PHP class to creates array representations for
31
//               common 1D barcodes to be used with TCPDF.
32
//
33
//============================================================+
34

    
35
/**
36
 * @file
37
 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF.
38
 * @package com.tecnick.tcpdf
39
 * @author Nicola Asuni
40
 * @version 1.0.027
41
 */
42

    
43
/**
44
 * @class TCPDFBarcode
45
 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).<br>
46
 * @package com.tecnick.tcpdf
47
 * @version 1.0.027
48
 * @author Nicola Asuni
49
 */
50
class TCPDFBarcode {
51

    
52
        /**
53
         * Array representation of barcode.
54
         * @protected
55
         */
56
        protected $barcode_array;
57

    
58
        /**
59
         * This is the class constructor.
60
         * Return an array representations for common 1D barcodes:<ul>
61
         * <li>$arrcode['code'] code to be printed on text label</li>
62
         * <li>$arrcode['maxh'] max barcode height</li>
63
         * <li>$arrcode['maxw'] max barcode width</li>
64
         * <li>$arrcode['bcode'][$k] single bar or space in $k position</li>
65
         * <li>$arrcode['bcode'][$k]['t'] bar type: true = bar, false = space.</li>
66
         * <li>$arrcode['bcode'][$k]['w'] bar width in units.</li>
67
         * <li>$arrcode['bcode'][$k]['h'] bar height in units.</li>
68
         * <li>$arrcode['bcode'][$k]['p'] bar top position (0 = top, 1 = middle)</li></ul>
69
         * @param $code (string) code to print
70
          * @param $type (string) 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>
71
          * @public
72
         */
73
        public function __construct($code, $type) {
74
                $this->setBarcode($code, $type);
75
        }
76

    
77
        /**
78
         * Return an array representations of barcode.
79
          * @return array
80
          * @public
81
         */
82
        public function getBarcodeArray() {
83
                return $this->barcode_array;
84
        }
85

    
86
        /**
87
         * Send barcode as SVG image object to the standard output.
88
         * @param $w (int) Minimum width of a single bar in user units.
89
         * @param $h (int) Height of barcode in user units.
90
         * @param $color (string) Foreground color (in SVG format) for bar elements (background is transparent).
91
          * @public
92
         */
93
        public function getBarcodeSVG($w=2, $h=30, $color='black') {
94
                // send headers
95
                $code = $this->getBarcodeSVGcode($w, $h, $color);
96
                header('Content-Type: application/svg+xml');
97
                header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
98
                header('Pragma: public');
99
                header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
100
                header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
101
                header('Content-Disposition: inline; filename="'.md5($code).'.svg";');
102
                //header('Content-Length: '.strlen($code));
103
                echo $code;
104
        }
105

    
106
        /**
107
         * Return a SVG string representation of barcode.
108
         * @param $w (int) Minimum width of a single bar in user units.
109
         * @param $h (int) Height of barcode in user units.
110
         * @param $color (string) Foreground color (in SVG format) for bar elements (background is transparent).
111
          * @return string SVG code.
112
          * @public
113
         */
114
        public function getBarcodeSVGcode($w=2, $h=30, $color='black') {
115
                // replace table for special characters
116
                $repstr = array("\0" => '', '&' => '&amp;', '<' => '&lt;', '>' => '&gt;');
117
                $svg = '<'.'?'.'xml version="1.0" standalone="no"'.'?'.'>'."\n";
118
                $svg .= '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'."\n";
119
                $svg .= '<svg width="'.round(($this->barcode_array['maxw'] * $w), 3).'" height="'.$h.'" version="1.1" xmlns="http://www.w3.org/2000/svg">'."\n";
120
                $svg .= "\t".'<desc>'.strtr($this->barcode_array['code'], $repstr).'</desc>'."\n";
121
                $svg .= "\t".'<g id="bars" fill="'.$color.'" stroke="none">'."\n";
122
                // print bars
123
                $x = 0;
124
                foreach ($this->barcode_array['bcode'] as $k => $v) {
125
                        $bw = round(($v['w'] * $w), 3);
126
                        $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
127
                        if ($v['t']) {
128
                                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
129
                                // draw a vertical bar
130
                                $svg .= "\t\t".'<rect x="'.$x.'" y="'.$y.'" width="'.$bw.'" height="'.$bh.'" />'."\n";
131
                        }
132
                        $x += $bw;
133
                }
134
                $svg .= "\t".'</g>'."\n";
135
                $svg .= '</svg>'."\n";
136
                return $svg;
137
        }
138

    
139
        /**
140
         * Return an HTML representation of barcode.
141
         * @param $w (int) Width of a single bar element in pixels.
142
         * @param $h (int) Height of a single bar element in pixels.
143
         * @param $color (string) Foreground color for bar elements (background is transparent).
144
          * @return string HTML code.
145
          * @public
146
         */
147
        public function getBarcodeHTML($w=2, $h=30, $color='black') {
148
                $html = '<div style="font-size:0;position:relative;width:'.($this->barcode_array['maxw'] * $w).'px;height:'.($h).'px;">'."\n";
149
                // print bars
150
                $x = 0;
151
                foreach ($this->barcode_array['bcode'] as $k => $v) {
152
                        $bw = round(($v['w'] * $w), 3);
153
                        $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
154
                        if ($v['t']) {
155
                                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
156
                                // draw a vertical bar
157
                                $html .= '<div style="background-color:'.$color.';width:'.$bw.'px;height:'.$bh.'px;position:absolute;left:'.$x.'px;top:'.$y.'px;">&nbsp;</div>'."\n";
158
                        }
159
                        $x += $bw;
160
                }
161
                $html .= '</div>'."\n";
162
                return $html;
163
        }
164

    
165
        /**
166
         * Send a PNG image representation of barcode (requires GD or Imagick library).
167
         * @param $w (int) Width of a single bar element in pixels.
168
         * @param $h (int) Height of a single bar element in pixels.
169
         * @param $color (array) RGB (0-255) foreground color for bar elements (background is transparent).
170
          * @public
171
         */
172
        public function getBarcodePNG($w=2, $h=30, $color=array(0,0,0)) {
173
                $data = $this->getBarcodePngData($w, $h, $color);
174
                // send headers
175
                header('Content-Type: image/png');
176
                header('Cache-Control: public, must-revalidate, max-age=0'); // HTTP/1.1
177
                header('Pragma: public');
178
                header('Expires: Sat, 26 Jul 1997 05:00:00 GMT'); // Date in the past
179
                header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
180
                //header('Content-Length: '.strlen($data));
181
                echo $data;
182
        }
183

    
184
        /**
185
         * Return a PNG image representation of barcode (requires GD or Imagick library).
186
         * @param $w (int) Width of a single bar element in pixels.
187
         * @param $h (int) Height of a single bar element in pixels.
188
         * @param $color (array) RGB (0-255) foreground color for bar elements (background is transparent).
189
          * @return image or false in case of error.
190
          * @public
191
         */
192
        public function getBarcodePngData($w=2, $h=30, $color=array(0,0,0)) {
193
                // calculate image size
194
                $width = ($this->barcode_array['maxw'] * $w);
195
                $height = $h;
196
                if (function_exists('imagecreate')) {
197
                        // GD library
198
                        $imagick = false;
199
                        $png = imagecreate($width, $height);
200
                        $bgcol = imagecolorallocate($png, 255, 255, 255);
201
                        imagecolortransparent($png, $bgcol);
202
                        $fgcol = imagecolorallocate($png, $color[0], $color[1], $color[2]);
203
                } elseif (extension_loaded('imagick')) {
204
                        $imagick = true;
205
                        $bgcol = new imagickpixel('rgb(255,255,255');
206
                        $fgcol = new imagickpixel('rgb('.$color[0].','.$color[1].','.$color[2].')');
207
                        $png = new Imagick();
208
                        $png->newImage($width, $height, 'none', 'png');
209
                        $bar = new imagickdraw();
210
                        $bar->setfillcolor($fgcol);
211
                } else {
212
                        return false;
213
                }
214
                // print bars
215
                $x = 0;
216
                foreach ($this->barcode_array['bcode'] as $k => $v) {
217
                        $bw = round(($v['w'] * $w), 3);
218
                        $bh = round(($v['h'] * $h / $this->barcode_array['maxh']), 3);
219
                        if ($v['t']) {
220
                                $y = round(($v['p'] * $h / $this->barcode_array['maxh']), 3);
221
                                // draw a vertical bar
222
                                if ($imagick) {
223
                                        $bar->rectangle($x, $y, ($x + $bw - 1), ($y + $bh - 1));
224
                                } else {
225
                                        imagefilledrectangle($png, $x, $y, ($x + $bw - 1), ($y + $bh - 1), $fgcol);
226
                                }
227
                        }
228
                        $x += $bw;
229
                }
230
                if ($imagick) {
231
                        $png->drawimage($bar);
232
                        return $png;
233
                } else {
234
                        ob_start();
235
                        imagepng($png);
236
                        $imagedata = ob_get_clean();
237
                        imagedestroy($png);
238
                        return $imagedata;
239
                }
240
        }
241

    
242
        /**
243
         * Set the barcode.
244
         * @param $code (string) code to print
245
          * @param $type (string) 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>
246
          * @return array barcode array
247
          * @public
248
         */
249
        public function setBarcode($code, $type) {
250
                switch (strtoupper($type)) {
251
                        case 'C39': { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
252
                                $arrcode = $this->barcode_code39($code, false, false);
253
                                break;
254
                        }
255
                        case 'C39+': { // CODE 39 with checksum
256
                                $arrcode = $this->barcode_code39($code, false, true);
257
                                break;
258
                        }
259
                        case 'C39E': { // CODE 39 EXTENDED
260
                                $arrcode = $this->barcode_code39($code, true, false);
261
                                break;
262
                        }
263
                        case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM
264
                                $arrcode = $this->barcode_code39($code, true, true);
265
                                break;
266
                        }
267
                        case 'C93': { // CODE 93 - USS-93
268
                                $arrcode = $this->barcode_code93($code);
269
                                break;
270
                        }
271
                        case 'S25': { // Standard 2 of 5
272
                                $arrcode = $this->barcode_s25($code, false);
273
                                break;
274
                        }
275
                        case 'S25+': { // Standard 2 of 5 + CHECKSUM
276
                                $arrcode = $this->barcode_s25($code, true);
277
                                break;
278
                        }
279
                        case 'I25': { // Interleaved 2 of 5
280
                                $arrcode = $this->barcode_i25($code, false);
281
                                break;
282
                        }
283
                        case 'I25+': { // Interleaved 2 of 5 + CHECKSUM
284
                                $arrcode = $this->barcode_i25($code, true);
285
                                break;
286
                        }
287
                        case 'C128': { // CODE 128
288
                                $arrcode = $this->barcode_c128($code, '');
289
                                break;
290
                        }
291
                        case 'C128A': { // CODE 128 A
292
                                $arrcode = $this->barcode_c128($code, 'A');
293
                                break;
294
                        }
295
                        case 'C128B': { // CODE 128 B
296
                                $arrcode = $this->barcode_c128($code, 'B');
297
                                break;
298
                        }
299
                        case 'C128C': { // CODE 128 C
300
                                $arrcode = $this->barcode_c128($code, 'C');
301
                                break;
302
                        }
303
                        case 'EAN2': { // 2-Digits UPC-Based Extension
304
                                $arrcode = $this->barcode_eanext($code, 2);
305
                                break;
306
                        }
307
                        case 'EAN5': { // 5-Digits UPC-Based Extension
308
                                $arrcode = $this->barcode_eanext($code, 5);
309
                                break;
310
                        }
311
                        case 'EAN8': { // EAN 8
312
                                $arrcode = $this->barcode_eanupc($code, 8);
313
                                break;
314
                        }
315
                        case 'EAN13': { // EAN 13
316
                                $arrcode = $this->barcode_eanupc($code, 13);
317
                                break;
318
                        }
319
                        case 'UPCA': { // UPC-A
320
                                $arrcode = $this->barcode_eanupc($code, 12);
321
                                break;
322
                        }
323
                        case 'UPCE': { // UPC-E
324
                                $arrcode = $this->barcode_eanupc($code, 6);
325
                                break;
326
                        }
327
                        case 'MSI': { // MSI (Variation of Plessey code)
328
                                $arrcode = $this->barcode_msi($code, false);
329
                                break;
330
                        }
331
                        case 'MSI+': { // MSI + CHECKSUM (modulo 11)
332
                                $arrcode = $this->barcode_msi($code, true);
333
                                break;
334
                        }
335
                        case 'POSTNET': { // POSTNET
336
                                $arrcode = $this->barcode_postnet($code, false);
337
                                break;
338
                        }
339
                        case 'PLANET': { // PLANET
340
                                $arrcode = $this->barcode_postnet($code, true);
341
                                break;
342
                        }
343
                        case 'RMS4CC': { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
344
                                $arrcode = $this->barcode_rms4cc($code, false);
345
                                break;
346
                        }
347
                        case 'KIX': { // KIX (Klant index - Customer index)
348
                                $arrcode = $this->barcode_rms4cc($code, true);
349
                                break;
350
                        }
351
                        case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
352
                                $arrcode = $this->barcode_imb($code);
353
                                break;
354
                        }
355
                        case 'IMBPRE': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200- pre-processed
356
                                $arrcode = $this->barcode_imb_pre($code);
357
                                break;
358
                        }
359
                        case 'CODABAR': { // CODABAR
360
                                $arrcode = $this->barcode_codabar($code);
361
                                break;
362
                        }
363
                        case 'CODE11': { // CODE 11
364
                                $arrcode = $this->barcode_code11($code);
365
                                break;
366
                        }
367
                        case 'PHARMA': { // PHARMACODE
368
                                $arrcode = $this->barcode_pharmacode($code);
369
                                break;
370
                        }
371
                        case 'PHARMA2T': { // PHARMACODE TWO-TRACKS
372
                                $arrcode = $this->barcode_pharmacode2t($code);
373
                                break;
374
                        }
375
                        default: {
376
                                $this->barcode_array = false;
377
                                $arrcode = false;
378
                                break;
379
                        }
380
                }
381
                $this->barcode_array = $arrcode;
382
        }
383

    
384
        /**
385
         * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
386
         * General-purpose code in very wide use world-wide
387
         * @param $code (string) code to represent.
388
         * @param $extended (boolean) if true uses the extended mode.
389
         * @param $checksum (boolean) if true add a checksum to the code.
390
         * @return array barcode representation.
391
         * @protected
392
         */
393
        protected function barcode_code39($code, $extended=false, $checksum=false) {
394
                $chr['0'] = '111331311';
395
                $chr['1'] = '311311113';
396
                $chr['2'] = '113311113';
397
                $chr['3'] = '313311111';
398
                $chr['4'] = '111331113';
399
                $chr['5'] = '311331111';
400
                $chr['6'] = '113331111';
401
                $chr['7'] = '111311313';
402
                $chr['8'] = '311311311';
403
                $chr['9'] = '113311311';
404
                $chr['A'] = '311113113';
405
                $chr['B'] = '113113113';
406
                $chr['C'] = '313113111';
407
                $chr['D'] = '111133113';
408
                $chr['E'] = '311133111';
409
                $chr['F'] = '113133111';
410
                $chr['G'] = '111113313';
411
                $chr['H'] = '311113311';
412
                $chr['I'] = '113113311';
413
                $chr['J'] = '111133311';
414
                $chr['K'] = '311111133';
415
                $chr['L'] = '113111133';
416
                $chr['M'] = '313111131';
417
                $chr['N'] = '111131133';
418
                $chr['O'] = '311131131';
419
                $chr['P'] = '113131131';
420
                $chr['Q'] = '111111333';
421
                $chr['R'] = '311111331';
422
                $chr['S'] = '113111331';
423
                $chr['T'] = '111131331';
424
                $chr['U'] = '331111113';
425
                $chr['V'] = '133111113';
426
                $chr['W'] = '333111111';
427
                $chr['X'] = '131131113';
428
                $chr['Y'] = '331131111';
429
                $chr['Z'] = '133131111';
430
                $chr['-'] = '131111313';
431
                $chr['.'] = '331111311';
432
                $chr[' '] = '133111311';
433
                $chr['$'] = '131313111';
434
                $chr['/'] = '131311131';
435
                $chr['+'] = '131113131';
436
                $chr['%'] = '111313131';
437
                $chr['*'] = '131131311';
438
                $code = strtoupper($code);
439
                if ($extended) {
440
                        // extended mode
441
                        $code = $this->encode_code39_ext($code);
442
                }
443
                if ($code === false) {
444
                        return false;
445
                }
446
                if ($checksum) {
447
                        // checksum
448
                        $code .= $this->checksum_code39($code);
449
                }
450
                // add start and stop codes
451
                $code = '*'.$code.'*';
452
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
453
                $k = 0;
454
                $clen = strlen($code);
455
                for ($i = 0; $i < $clen; ++$i) {
456
                        $char = $code{$i};
457
                        if(!isset($chr[$char])) {
458
                                // invalid character
459
                                return false;
460
                        }
461
                        for ($j = 0; $j < 9; ++$j) {
462
                                if (($j % 2) == 0) {
463
                                        $t = true; // bar
464
                                } else {
465
                                        $t = false; // space
466
                                }
467
                                $w = $chr[$char]{$j};
468
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
469
                                $bararray['maxw'] += $w;
470
                                ++$k;
471
                        }
472
                        // intercharacter gap
473
                        $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
474
                        $bararray['maxw'] += 1;
475
                        ++$k;
476
                }
477
                return $bararray;
478
        }
479

    
480
        /**
481
         * Encode a string to be used for CODE 39 Extended mode.
482
         * @param $code (string) code to represent.
483
         * @return encoded string.
484
         * @protected
485
         */
486
        protected function encode_code39_ext($code) {
487
                $encode = array(
488
                        chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
489
                        chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
490
                        chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
491
                        chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
492
                        chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
493
                        chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
494
                        chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
495
                        chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
496
                        chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
497
                        chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
498
                        chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
499
                        chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
500
                        chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
501
                        chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
502
                        chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
503
                        chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
504
                        chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
505
                        chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
506
                        chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
507
                        chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
508
                        chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
509
                        chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
510
                        chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
511
                        chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
512
                        chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
513
                        chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
514
                        chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
515
                        chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
516
                        chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
517
                        chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
518
                        chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
519
                        chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
520
                $code_ext = '';
521
                $clen = strlen($code);
522
                for ($i = 0 ; $i < $clen; ++$i) {
523
                        if (ord($code{$i}) > 127) {
524
                                return false;
525
                        }
526
                        $code_ext .= $encode[$code{$i}];
527
                }
528
                return $code_ext;
529
        }
530

    
531
        /**
532
         * Calculate CODE 39 checksum (modulo 43).
533
         * @param $code (string) code to represent.
534
         * @return char checksum.
535
         * @protected
536
         */
537
        protected function checksum_code39($code) {
538
                $chars = array(
539
                        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
540
                        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
541
                        'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
542
                        'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
543
                $sum = 0;
544
                $clen = strlen($code);
545
                for ($i = 0 ; $i < $clen; ++$i) {
546
                        $k = array_keys($chars, $code{$i});
547
                        $sum += $k[0];
548
                }
549
                $j = ($sum % 43);
550
                return $chars[$j];
551
        }
552

    
553
        /**
554
         * CODE 93 - USS-93
555
         * Compact code similar to Code 39
556
         * @param $code (string) code to represent.
557
         * @return array barcode representation.
558
         * @protected
559
         */
560
        protected function barcode_code93($code) {
561
                $chr[48] = '131112'; // 0
562
                $chr[49] = '111213'; // 1
563
                $chr[50] = '111312'; // 2
564
                $chr[51] = '111411'; // 3
565
                $chr[52] = '121113'; // 4
566
                $chr[53] = '121212'; // 5
567
                $chr[54] = '121311'; // 6
568
                $chr[55] = '111114'; // 7
569
                $chr[56] = '131211'; // 8
570
                $chr[57] = '141111'; // 9
571
                $chr[65] = '211113'; // A
572
                $chr[66] = '211212'; // B
573
                $chr[67] = '211311'; // C
574
                $chr[68] = '221112'; // D
575
                $chr[69] = '221211'; // E
576
                $chr[70] = '231111'; // F
577
                $chr[71] = '112113'; // G
578
                $chr[72] = '112212'; // H
579
                $chr[73] = '112311'; // I
580
                $chr[74] = '122112'; // J
581
                $chr[75] = '132111'; // K
582
                $chr[76] = '111123'; // L
583
                $chr[77] = '111222'; // M
584
                $chr[78] = '111321'; // N
585
                $chr[79] = '121122'; // O
586
                $chr[80] = '131121'; // P
587
                $chr[81] = '212112'; // Q
588
                $chr[82] = '212211'; // R
589
                $chr[83] = '211122'; // S
590
                $chr[84] = '211221'; // T
591
                $chr[85] = '221121'; // U
592
                $chr[86] = '222111'; // V
593
                $chr[87] = '112122'; // W
594
                $chr[88] = '112221'; // X
595
                $chr[89] = '122121'; // Y
596
                $chr[90] = '123111'; // Z
597
                $chr[45] = '121131'; // -
598
                $chr[46] = '311112'; // .
599
                $chr[32] = '311211'; //
600
                $chr[36] = '321111'; // $
601
                $chr[47] = '112131'; // /
602
                $chr[43] = '113121'; // +
603
                $chr[37] = '211131'; // %
604
                $chr[128] = '121221'; // ($)
605
                $chr[129] = '311121'; // (/)
606
                $chr[130] = '122211'; // (+)
607
                $chr[131] = '312111'; // (%)
608
                $chr[42] = '111141'; // start-stop
609
                $code = strtoupper($code);
610
                $encode = array(
611
                        chr(0) => chr(131).'U', chr(1) => chr(128).'A', chr(2) => chr(128).'B', chr(3) => chr(128).'C',
612
                        chr(4) => chr(128).'D', chr(5) => chr(128).'E', chr(6) => chr(128).'F', chr(7) => chr(128).'G',
613
                        chr(8) => chr(128).'H', chr(9) => chr(128).'I', chr(10) => chr(128).'J', chr(11) => '£K',
614
                        chr(12) => chr(128).'L', chr(13) => chr(128).'M', chr(14) => chr(128).'N', chr(15) => chr(128).'O',
615
                        chr(16) => chr(128).'P', chr(17) => chr(128).'Q', chr(18) => chr(128).'R', chr(19) => chr(128).'S',
616
                        chr(20) => chr(128).'T', chr(21) => chr(128).'U', chr(22) => chr(128).'V', chr(23) => chr(128).'W',
617
                        chr(24) => chr(128).'X', chr(25) => chr(128).'Y', chr(26) => chr(128).'Z', chr(27) => chr(131).'A',
618
                        chr(28) => chr(131).'B', chr(29) => chr(131).'C', chr(30) => chr(131).'D', chr(31) => chr(131).'E',
619
                        chr(32) => ' ', chr(33) => chr(129).'A', chr(34) => chr(129).'B', chr(35) => chr(129).'C',
620
                        chr(36) => chr(129).'D', chr(37) => chr(129).'E', chr(38) => chr(129).'F', chr(39) => chr(129).'G',
621
                        chr(40) => chr(129).'H', chr(41) => chr(129).'I', chr(42) => chr(129).'J', chr(43) => chr(129).'K',
622
                        chr(44) => chr(129).'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129).'O',
623
                        chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
624
                        chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
625
                        chr(56) => '8', chr(57) => '9', chr(58) => chr(129).'Z', chr(59) => chr(131).'F',
626
                        chr(60) => chr(131).'G', chr(61) => chr(131).'H', chr(62) => chr(131).'I', chr(63) => chr(131).'J',
627
                        chr(64) => chr(131).'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
628
                        chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
629
                        chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
630
                        chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
631
                        chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
632
                        chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
633
                        chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131).'K',
634
                        chr(92) => chr(131).'L', chr(93) => chr(131).'M', chr(94) => chr(131).'N', chr(95) => chr(131).'O',
635
                        chr(96) => chr(131).'W', chr(97) => chr(130).'A', chr(98) => chr(130).'B', chr(99) => chr(130).'C',
636
                        chr(100) => chr(130).'D', chr(101) => chr(130).'E', chr(102) => chr(130).'F', chr(103) => chr(130).'G',
637
                        chr(104) => chr(130).'H', chr(105) => chr(130).'I', chr(106) => chr(130).'J', chr(107) => chr(130).'K',
638
                        chr(108) => chr(130).'L', chr(109) => chr(130).'M', chr(110) => chr(130).'N', chr(111) => chr(130).'O',
639
                        chr(112) => chr(130).'P', chr(113) => chr(130).'Q', chr(114) => chr(130).'R', chr(115) => chr(130).'S',
640
                        chr(116) => chr(130).'T', chr(117) => chr(130).'U', chr(118) => chr(130).'V', chr(119) => chr(130).'W',
641
                        chr(120) => chr(130).'X', chr(121) => chr(130).'Y', chr(122) => chr(130).'Z', chr(123) => chr(131).'P',
642
                        chr(124) => chr(131).'Q', chr(125) => chr(131).'R', chr(126) => chr(131).'S', chr(127) => chr(131).'T');
643
                $code_ext = '';
644
                $clen = strlen($code);
645
                for ($i = 0 ; $i < $clen; ++$i) {
646
                        if (ord($code{$i}) > 127) {
647
                                return false;
648
                        }
649
                        $code_ext .= $encode[$code{$i}];
650
                }
651
                // checksum
652
                $code_ext .= $this->checksum_code93($code_ext);
653
                // add start and stop codes
654
                $code = '*'.$code_ext.'*';
655
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
656
                $k = 0;
657
                $clen = strlen($code);
658
                for ($i = 0; $i < $clen; ++$i) {
659
                        $char = ord($code{$i});
660
                        if(!isset($chr[$char])) {
661
                                // invalid character
662
                                return false;
663
                        }
664
                        for ($j = 0; $j < 6; ++$j) {
665
                                if (($j % 2) == 0) {
666
                                        $t = true; // bar
667
                                } else {
668
                                        $t = false; // space
669
                                }
670
                                $w = $chr[$char]{$j};
671
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
672
                                $bararray['maxw'] += $w;
673
                                ++$k;
674
                        }
675
                }
676
                $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
677
                $bararray['maxw'] += 1;
678
                ++$k;
679
                return $bararray;
680
        }
681

    
682
        /**
683
         * Calculate CODE 93 checksum (modulo 47).
684
         * @param $code (string) code to represent.
685
         * @return string checksum code.
686
         * @protected
687
         */
688
        protected function checksum_code93($code) {
689
                $chars = array(
690
                        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
691
                        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
692
                        'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
693
                        'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%',
694
                        '<', '=', '>', '?');
695
                // translate special characters
696
                $code = strtr($code, chr(128).chr(131).chr(129).chr(130), '<=>?');
697
                $len = strlen($code);
698
                // calculate check digit C
699
                $p = 1;
700
                $check = 0;
701
                for ($i = ($len - 1); $i >= 0; --$i) {
702
                        $k = array_keys($chars, $code{$i});
703
                        $check += ($k[0] * $p);
704
                        ++$p;
705
                        if ($p > 20) {
706
                                $p = 1;
707
                        }
708
                }
709
                $check %= 47;
710
                $c = $chars[$check];
711
                $code .= $c;
712
                // calculate check digit K
713
                $p = 1;
714
                $check = 0;
715
                for ($i = $len; $i >= 0; --$i) {
716
                        $k = array_keys($chars, $code{$i});
717
                        $check += ($k[0] * $p);
718
                        ++$p;
719
                        if ($p > 15) {
720
                                $p = 1;
721
                        }
722
                }
723
                $check %= 47;
724
                $k = $chars[$check];
725
                $checksum = $c.$k;
726
                // resto respecial characters
727
                $checksum = strtr($checksum, '<=>?', chr(128).chr(131).chr(129).chr(130));
728
                return $checksum;
729
        }
730

    
731
        /**
732
         * Checksum for standard 2 of 5 barcodes.
733
         * @param $code (string) code to process.
734
         * @return int checksum.
735
         * @protected
736
         */
737
        protected function checksum_s25($code) {
738
                $len = strlen($code);
739
                $sum = 0;
740
                for ($i = 0; $i < $len; $i+=2) {
741
                        $sum += $code{$i};
742
                }
743
                $sum *= 3;
744
                for ($i = 1; $i < $len; $i+=2) {
745
                        $sum += ($code{$i});
746
                }
747
                $r = $sum % 10;
748
                if($r > 0) {
749
                        $r = (10 - $r);
750
                }
751
                return $r;
752
        }
753

    
754
        /**
755
         * MSI.
756
         * Variation of Plessey code, with similar applications
757
         * Contains digits (0 to 9) and encodes the data only in the width of bars.
758
         * @param $code (string) code to represent.
759
         * @param $checksum (boolean) if true add a checksum to the code (modulo 11)
760
         * @return array barcode representation.
761
         * @protected
762
         */
763
        protected function barcode_msi($code, $checksum=false) {
764
                $chr['0'] = '100100100100';
765
                $chr['1'] = '100100100110';
766
                $chr['2'] = '100100110100';
767
                $chr['3'] = '100100110110';
768
                $chr['4'] = '100110100100';
769
                $chr['5'] = '100110100110';
770
                $chr['6'] = '100110110100';
771
                $chr['7'] = '100110110110';
772
                $chr['8'] = '110100100100';
773
                $chr['9'] = '110100100110';
774
                $chr['A'] = '110100110100';
775
                $chr['B'] = '110100110110';
776
                $chr['C'] = '110110100100';
777
                $chr['D'] = '110110100110';
778
                $chr['E'] = '110110110100';
779
                $chr['F'] = '110110110110';
780
                if ($checksum) {
781
                        // add checksum
782
                        $clen = strlen($code);
783
                        $p = 2;
784
                        $check = 0;
785
                        for ($i = ($clen - 1); $i >= 0; --$i) {
786
                                $check += (hexdec($code{$i}) * $p);
787
                                ++$p;
788
                                if ($p > 7) {
789
                                        $p = 2;
790
                                }
791
                        }
792
                        $check %= 11;
793
                        if ($check > 0) {
794
                                $check = 11 - $check;
795
                        }
796
                        $code .= $check;
797
                }
798
                $seq = '110'; // left guard
799
                $clen = strlen($code);
800
                for ($i = 0; $i < $clen; ++$i) {
801
                        $digit = $code{$i};
802
                        if (!isset($chr[$digit])) {
803
                                // invalid character
804
                                return false;
805
                        }
806
                        $seq .= $chr[$digit];
807
                }
808
                $seq .= '1001'; // right guard
809
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
810
                return $this->binseq_to_array($seq, $bararray);
811
        }
812

    
813
        /**
814
         * Standard 2 of 5 barcodes.
815
         * Used in airline ticket marking, photofinishing
816
         * Contains digits (0 to 9) and encodes the data only in the width of bars.
817
         * @param $code (string) code to represent.
818
         * @param $checksum (boolean) if true add a checksum to the code
819
         * @return array barcode representation.
820
         * @protected
821
         */
822
        protected function barcode_s25($code, $checksum=false) {
823
                $chr['0'] = '10101110111010';
824
                $chr['1'] = '11101010101110';
825
                $chr['2'] = '10111010101110';
826
                $chr['3'] = '11101110101010';
827
                $chr['4'] = '10101110101110';
828
                $chr['5'] = '11101011101010';
829
                $chr['6'] = '10111011101010';
830
                $chr['7'] = '10101011101110';
831
                $chr['8'] = '10101110111010';
832
                $chr['9'] = '10111010111010';
833
                if ($checksum) {
834
                        // add checksum
835
                        $code .= $this->checksum_s25($code);
836
                }
837
                if((strlen($code) % 2) != 0) {
838
                        // add leading zero if code-length is odd
839
                        $code = '0'.$code;
840
                }
841
                $seq = '11011010';
842
                $clen = strlen($code);
843
                for ($i = 0; $i < $clen; ++$i) {
844
                        $digit = $code{$i};
845
                        if (!isset($chr[$digit])) {
846
                                // invalid character
847
                                return false;
848
                        }
849
                        $seq .= $chr[$digit];
850
                }
851
                $seq .= '1101011';
852
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
853
                return $this->binseq_to_array($seq, $bararray);
854
        }
855

    
856
        /**
857
         * Convert binary barcode sequence to TCPDF barcode array.
858
         * @param $seq (string) barcode as binary sequence.
859
         * @param $bararray (array) barcode array.
860
         * òparam array $bararray TCPDF barcode array to fill up
861
         * @return array barcode representation.
862
         * @protected
863
         */
864
        protected function binseq_to_array($seq, $bararray) {
865
                $len = strlen($seq);
866
                $w = 0;
867
                $k = 0;
868
                for ($i = 0; $i < $len; ++$i) {
869
                        $w += 1;
870
                        if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq{$i} != $seq{($i+1)}))) {
871
                                if ($seq{$i} == '1') {
872
                                        $t = true; // bar
873
                                } else {
874
                                        $t = false; // space
875
                                }
876
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
877
                                $bararray['maxw'] += $w;
878
                                ++$k;
879
                                $w = 0;
880
                        }
881
                }
882
                return $bararray;
883
        }
884

    
885
        /**
886
         * Interleaved 2 of 5 barcodes.
887
         * Compact numeric code, widely used in industry, air cargo
888
         * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
889
         * @param $code (string) code to represent.
890
         * @param $checksum (boolean) if true add a checksum to the code
891
         * @return array barcode representation.
892
         * @protected
893
         */
894
        protected function barcode_i25($code, $checksum=false) {
895
                $chr['0'] = '11221';
896
                $chr['1'] = '21112';
897
                $chr['2'] = '12112';
898
                $chr['3'] = '22111';
899
                $chr['4'] = '11212';
900
                $chr['5'] = '21211';
901
                $chr['6'] = '12211';
902
                $chr['7'] = '11122';
903
                $chr['8'] = '21121';
904
                $chr['9'] = '12121';
905
                $chr['A'] = '11';
906
                $chr['Z'] = '21';
907
                if ($checksum) {
908
                        // add checksum
909
                        $code .= $this->checksum_s25($code);
910
                }
911
                if((strlen($code) % 2) != 0) {
912
                        // add leading zero if code-length is odd
913
                        $code = '0'.$code;
914
                }
915
                // add start and stop codes
916
                $code = 'AA'.strtolower($code).'ZA';
917

    
918
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
919
                $k = 0;
920
                $clen = strlen($code);
921
                for ($i = 0; $i < $clen; $i = ($i + 2)) {
922
                        $char_bar = $code{$i};
923
                        $char_space = $code{$i+1};
924
                        if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) {
925
                                // invalid character
926
                                return false;
927
                        }
928
                        // create a bar-space sequence
929
                        $seq = '';
930
                        $chrlen = strlen($chr[$char_bar]);
931
                        for ($s = 0; $s < $chrlen; $s++){
932
                                $seq .= $chr[$char_bar]{$s} . $chr[$char_space]{$s};
933
                        }
934
                        $seqlen = strlen($seq);
935
                        for ($j = 0; $j < $seqlen; ++$j) {
936
                                if (($j % 2) == 0) {
937
                                        $t = true; // bar
938
                                } else {
939
                                        $t = false; // space
940
                                }
941
                                $w = $seq{$j};
942
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
943
                                $bararray['maxw'] += $w;
944
                                ++$k;
945
                        }
946
                }
947
                return $bararray;
948
        }
949

    
950
        /**
951
         * C128 barcodes.
952
         * Very capable code, excellent density, high reliability; in very wide use world-wide
953
         * @param $code (string) code to represent.
954
         * @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode)
955
         * @return array barcode representation.
956
         * @protected
957
         */
958
        protected function barcode_c128($code, $type='') {
959
                $chr = array(
960
                        '212222', /* 00 */
961
                        '222122', /* 01 */
962
                        '222221', /* 02 */
963
                        '121223', /* 03 */
964
                        '121322', /* 04 */
965
                        '131222', /* 05 */
966
                        '122213', /* 06 */
967
                        '122312', /* 07 */
968
                        '132212', /* 08 */
969
                        '221213', /* 09 */
970
                        '221312', /* 10 */
971
                        '231212', /* 11 */
972
                        '112232', /* 12 */
973
                        '122132', /* 13 */
974
                        '122231', /* 14 */
975
                        '113222', /* 15 */
976
                        '123122', /* 16 */
977
                        '123221', /* 17 */
978
                        '223211', /* 18 */
979
                        '221132', /* 19 */
980
                        '221231', /* 20 */
981
                        '213212', /* 21 */
982
                        '223112', /* 22 */
983
                        '312131', /* 23 */
984
                        '311222', /* 24 */
985
                        '321122', /* 25 */
986
                        '321221', /* 26 */
987
                        '312212', /* 27 */
988
                        '322112', /* 28 */
989
                        '322211', /* 29 */
990
                        '212123', /* 30 */
991
                        '212321', /* 31 */
992
                        '232121', /* 32 */
993
                        '111323', /* 33 */
994
                        '131123', /* 34 */
995
                        '131321', /* 35 */
996
                        '112313', /* 36 */
997
                        '132113', /* 37 */
998
                        '132311', /* 38 */
999
                        '211313', /* 39 */
1000
                        '231113', /* 40 */
1001
                        '231311', /* 41 */
1002
                        '112133', /* 42 */
1003
                        '112331', /* 43 */
1004
                        '132131', /* 44 */
1005
                        '113123', /* 45 */
1006
                        '113321', /* 46 */
1007
                        '133121', /* 47 */
1008
                        '313121', /* 48 */
1009
                        '211331', /* 49 */
1010
                        '231131', /* 50 */
1011
                        '213113', /* 51 */
1012
                        '213311', /* 52 */
1013
                        '213131', /* 53 */
1014
                        '311123', /* 54 */
1015
                        '311321', /* 55 */
1016
                        '331121', /* 56 */
1017
                        '312113', /* 57 */
1018
                        '312311', /* 58 */
1019
                        '332111', /* 59 */
1020
                        '314111', /* 60 */
1021
                        '221411', /* 61 */
1022
                        '431111', /* 62 */
1023
                        '111224', /* 63 */
1024
                        '111422', /* 64 */
1025
                        '121124', /* 65 */
1026
                        '121421', /* 66 */
1027
                        '141122', /* 67 */
1028
                        '141221', /* 68 */
1029
                        '112214', /* 69 */
1030
                        '112412', /* 70 */
1031
                        '122114', /* 71 */
1032
                        '122411', /* 72 */
1033
                        '142112', /* 73 */
1034
                        '142211', /* 74 */
1035
                        '241211', /* 75 */
1036
                        '221114', /* 76 */
1037
                        '413111', /* 77 */
1038
                        '241112', /* 78 */
1039
                        '134111', /* 79 */
1040
                        '111242', /* 80 */
1041
                        '121142', /* 81 */
1042
                        '121241', /* 82 */
1043
                        '114212', /* 83 */
1044
                        '124112', /* 84 */
1045
                        '124211', /* 85 */
1046
                        '411212', /* 86 */
1047
                        '421112', /* 87 */
1048
                        '421211', /* 88 */
1049
                        '212141', /* 89 */
1050
                        '214121', /* 90 */
1051
                        '412121', /* 91 */
1052
                        '111143', /* 92 */
1053
                        '111341', /* 93 */
1054
                        '131141', /* 94 */
1055
                        '114113', /* 95 */
1056
                        '114311', /* 96 */
1057
                        '411113', /* 97 */
1058
                        '411311', /* 98 */
1059
                        '113141', /* 99 */
1060
                        '114131', /* 100 */
1061
                        '311141', /* 101 */
1062
                        '411131', /* 102 */
1063
                        '211412', /* 103 START A */
1064
                        '211214', /* 104 START B */
1065
                        '211232', /* 105 START C */
1066
                        '233111', /* STOP */
1067
                        '200000'  /* END */
1068
                );
1069
                // ASCII characters for code A (ASCII 00 - 95)
1070
                $keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
1071
                $keys_a .= chr(0).chr(1).chr(2).chr(3).chr(4).chr(5).chr(6).chr(7).chr(8).chr(9);
1072
                $keys_a .= chr(10).chr(11).chr(12).chr(13).chr(14).chr(15).chr(16).chr(17).chr(18).chr(19);
1073
                $keys_a .= chr(20).chr(21).chr(22).chr(23).chr(24).chr(25).chr(26).chr(27).chr(28).chr(29);
1074
                $keys_a .= chr(30).chr(31);
1075
                // ASCII characters for code B (ASCII 32 - 127)
1076
                $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
1077
                // special codes
1078
                $fnc_a = array(241 => 102, 242 => 97, 243 => 96, 244 => 101);
1079
                $fnc_b = array(241 => 102, 242 => 97, 243 => 96, 244 => 100);
1080
                // array of symbols
1081
                $code_data = array();
1082
                // length of the code
1083
                $len = strlen($code);
1084
                switch(strtoupper($type)) {
1085
                        case 'A': { // MODE A
1086
                                $startid = 103;
1087
                                for ($i = 0; $i < $len; ++$i) {
1088
                                        $char = $code{$i};
1089
                                        $char_id = ord($char);
1090
                                        if (($char_id >= 241) AND ($char_id <= 244)) {
1091
                                                $code_data[] = $fnc_a[$char_id];
1092
                                        } elseif (($char_id >= 0) AND ($char_id <= 95)) {
1093
                                                $code_data[] = strpos($keys_a, $char);
1094
                                        } else {
1095
                                                return false;
1096
                                        }
1097
                                }
1098
                                break;
1099
                        }
1100
                        case 'B': { // MODE B
1101
                                $startid = 104;
1102
                                for ($i = 0; $i < $len; ++$i) {
1103
                                        $char = $code{$i};
1104
                                        $char_id = ord($char);
1105
                                        if (($char_id >= 241) AND ($char_id <= 244)) {
1106
                                                $code_data[] = $fnc_b[$char_id];
1107
                                        } elseif (($char_id >= 32) AND ($char_id <= 127)) {
1108
                                                $code_data[] = strpos($keys_b, $char);
1109
                                        } else {
1110
                                                return false;
1111
                                        }
1112
                                }
1113
                                break;
1114
                        }
1115
                        case 'C': { // MODE C
1116
                                $startid = 105;
1117
                                if (ord($code[0]) == 241) {
1118
                                        $code_data[] = 102;
1119
                                        $code = substr($code, 1);
1120
                                        --$len;
1121
                                }
1122
                                if (($len % 2) != 0) {
1123
                                        // the length must be even
1124
                                        return false;
1125
                                }
1126
                                for ($i = 0; $i < $len; $i+=2) {
1127
                                        $chrnum = $code{$i}.$code{$i+1};
1128
                                        if (preg_match('/([0-9]{2})/', $chrnum) > 0) {
1129
                                                $code_data[] = intval($chrnum);
1130
                                        } else {
1131
                                                return false;
1132
                                        }
1133
                                }
1134
                                break;
1135
                        }
1136
                        default: { // MODE AUTO
1137
                                // split code into sequences
1138
                                $sequence = array();
1139
                                // get numeric sequences (if any)
1140
                                $numseq = array();
1141
                                preg_match_all('/([0-9]{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE);
1142
                                if (isset($numseq[1]) AND !empty($numseq[1])) {
1143
                                        $end_offset = 0;
1144
                                        foreach ($numseq[1] as $val) {
1145
                                                $offset = $val[1];
1146
                                                if ($offset > $end_offset) {
1147
                                                        // non numeric sequence
1148
                                                        $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset))));
1149
                                                }
1150
                                                // numeric sequence
1151
                                                $slen = strlen($val[0]);
1152
                                                if (($slen % 2) != 0) {
1153
                                                        // the length must be even
1154
                                                        --$slen;
1155
                                                }
1156
                                                $sequence[] = array('C', substr($code, $offset, $slen), $slen);
1157
                                                $end_offset = $offset + $slen;
1158
                                        }
1159
                                        if ($end_offset < $len) {
1160
                                                $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset)));
1161
                                        }
1162
                                } else {
1163
                                        // text code (non C mode)
1164
                                        $sequence = array_merge($sequence, $this->get128ABsequence($code));
1165
                                }
1166
                                // process the sequence
1167
                                foreach ($sequence as $key => $seq) {
1168
                                        switch($seq[0]) {
1169
                                                case 'A': {
1170
                                                        if ($key == 0) {
1171
                                                                $startid = 103;
1172
                                                        } elseif ($sequence[($key - 1)][0] != 'A') {
1173
                                                                if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'B') AND (!isset($sequence[($key - 1)][3]))) {
1174
                                                                        // single character shift
1175
                                                                        $code_data[] = 98;
1176
                                                                        // mark shift
1177
                                                                        $sequence[$key][3] = true;
1178
                                                                } elseif (!isset($sequence[($key - 1)][3])) {
1179
                                                                        $code_data[] = 101;
1180
                                                                }
1181
                                                        }
1182
                                                        for ($i = 0; $i < $seq[2]; ++$i) {
1183
                                                                $char = $seq[1]{$i};
1184
                                                                $char_id = ord($char);
1185
                                                                if (($char_id >= 241) AND ($char_id <= 244)) {
1186
                                                                        $code_data[] = $fnc_a[$char_id];
1187
                                                                } else {
1188
                                                                        $code_data[] = strpos($keys_a, $char);
1189
                                                                }
1190
                                                        }
1191
                                                        break;
1192
                                                }
1193
                                                case 'B': {
1194
                                                        if ($key == 0) {
1195
                                                                $tmpchr = ord($seq[1][0]);
1196
                                                                if (($seq[2] == 1) AND ($tmpchr >= 241) AND ($tmpchr <= 244) AND isset($sequence[($key + 1)]) AND ($sequence[($key + 1)][0] != 'B')) {
1197
                                                                        switch ($sequence[($key + 1)][0]) {
1198
                                                                                case 'A': {
1199
                                                                                        $startid = 103;
1200
                                                                                        $sequence[$key][0] = 'A';
1201
                                                                                        $code_data[] = $fnc_a[$tmpchr];
1202
                                                                                        break;
1203
                                                                                }
1204
                                                                                case 'C': {
1205
                                                                                        $startid = 105;
1206
                                                                                        $sequence[$key][0] = 'C';
1207
                                                                                        $code_data[] = $fnc_a[$tmpchr];
1208
                                                                                        break;
1209
                                                                                }
1210
                                                                        }
1211
                                                                        break;
1212
                                                                } else {
1213
                                                                        $startid = 104;
1214
                                                                }
1215
                                                        } elseif ($sequence[($key - 1)][0] != 'B') {
1216
                                                                if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'A') AND (!isset($sequence[($key - 1)][3]))) {
1217
                                                                        // single character shift
1218
                                                                        $code_data[] = 98;
1219
                                                                        // mark shift
1220
                                                                        $sequence[$key][3] = true;
1221
                                                                } elseif (!isset($sequence[($key - 1)][3])) {
1222
                                                                        $code_data[] = 100;
1223
                                                                }
1224
                                                        }
1225
                                                        for ($i = 0; $i < $seq[2]; ++$i) {
1226
                                                                $char = $seq[1]{$i};
1227
                                                                $char_id = ord($char);
1228
                                                                if (($char_id >= 241) AND ($char_id <= 244)) {
1229
                                                                        $code_data[] = $fnc_b[$char_id];
1230
                                                                } else {
1231
                                                                        $code_data[] = strpos($keys_b, $char);
1232
                                                                }
1233
                                                        }
1234
                                                        break;
1235
                                                }
1236
                                                case 'C': {
1237
                                                        if ($key == 0) {
1238
                                                                $startid = 105;
1239
                                                        } elseif ($sequence[($key - 1)][0] != 'C') {
1240
                                                                $code_data[] = 99;
1241
                                                        }
1242
                                                        for ($i = 0; $i < $seq[2]; $i+=2) {
1243
                                                                $chrnum = $seq[1]{$i}.$seq[1]{$i+1};
1244
                                                                $code_data[] = intval($chrnum);
1245
                                                        }
1246
                                                        break;
1247
                                                }
1248
                                        }
1249
                                }
1250
                        }
1251
                }
1252
                // calculate check character
1253
                $sum = $startid;
1254
                foreach ($code_data as $key => $val) {
1255
                        $sum += ($val * ($key + 1));
1256
                }
1257
                // add check character
1258
                $code_data[] = ($sum % 103);
1259
                // add stop sequence
1260
                $code_data[] = 106;
1261
                $code_data[] = 107;
1262
                // add start code at the beginning
1263
                array_unshift($code_data, $startid);
1264
                // build barcode array
1265
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1266
                foreach ($code_data as $val) {
1267
                        $seq = $chr[$val];
1268
                        for ($j = 0; $j < 6; ++$j) {
1269
                                if (($j % 2) == 0) {
1270
                                        $t = true; // bar
1271
                                } else {
1272
                                        $t = false; // space
1273
                                }
1274
                                $w = $seq{$j};
1275
                                $bararray['bcode'][] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1276
                                $bararray['maxw'] += $w;
1277
                        }
1278
                }
1279
                return $bararray;
1280
        }
1281

    
1282
        /**
1283
         * Split text code in A/B sequence for 128 code
1284
         * @param $code (string) code to split.
1285
         * @return array sequence
1286
         * @protected
1287
         */
1288
        protected function get128ABsequence($code) {
1289
                $len = strlen($code);
1290
                $sequence = array();
1291
                // get A sequences (if any)
1292
                $numseq = array();
1293
                preg_match_all('/([\0-\31])/', $code, $numseq, PREG_OFFSET_CAPTURE);
1294
                if (isset($numseq[1]) AND !empty($numseq[1])) {
1295
                        $end_offset = 0;
1296
                        foreach ($numseq[1] as $val) {
1297
                                $offset = $val[1];
1298
                                if ($offset > $end_offset) {
1299
                                        // B sequence
1300
                                        $sequence[] = array('B', substr($code, $end_offset, ($offset - $end_offset)), ($offset - $end_offset));
1301
                                }
1302
                                // A sequence
1303
                                $slen = strlen($val[0]);
1304
                                $sequence[] = array('A', substr($code, $offset, $slen), $slen);
1305
                                $end_offset = $offset + $slen;
1306
                        }
1307
                        if ($end_offset < $len) {
1308
                                $sequence[] = array('B', substr($code, $end_offset), ($len - $end_offset));
1309
                        }
1310
                } else {
1311
                        // only B sequence
1312
                        $sequence[] = array('B', $code, $len);
1313
                }
1314
                return $sequence;
1315
        }
1316

    
1317
        /**
1318
         * EAN13 and UPC-A barcodes.
1319
         * EAN13: European Article Numbering international retail product code
1320
         * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
1321
         * UPC-E: Short version of UPC symbol
1322
         * @param $code (string) code to represent.
1323
         * @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
1324
         * @return array barcode representation.
1325
         * @protected
1326
         */
1327
        protected function barcode_eanupc($code, $len=13) {
1328
                $upce = false;
1329
                if ($len == 6) {
1330
                        $len = 12; // UPC-A
1331
                        $upce = true; // UPC-E mode
1332
                }
1333
                $data_len = $len - 1;
1334
                //Padding
1335
                $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
1336
                $code_len = strlen($code);
1337
                // calculate check digit
1338
                $sum_a = 0;
1339
                for ($i = 1; $i < $data_len; $i+=2) {
1340
                        $sum_a += $code{$i};
1341
                }
1342
                if ($len > 12) {
1343
                        $sum_a *= 3;
1344
                }
1345
                $sum_b = 0;
1346
                for ($i = 0; $i < $data_len; $i+=2) {
1347
                        $sum_b += ($code{$i});
1348
                }
1349
                if ($len < 13) {
1350
                        $sum_b *= 3;
1351
                }
1352
                $r = ($sum_a + $sum_b) % 10;
1353
                if($r > 0) {
1354
                        $r = (10 - $r);
1355
                }
1356
                if ($code_len == $data_len) {
1357
                        // add check digit
1358
                        $code .= $r;
1359
                } elseif ($r !== intval($code{$data_len})) {
1360
                        // wrong checkdigit
1361
                        return false;
1362
                }
1363
                if ($len == 12) {
1364
                        // UPC-A
1365
                        $code = '0'.$code;
1366
                        ++$len;
1367
                }
1368
                if ($upce) {
1369
                        // convert UPC-A to UPC-E
1370
                        $tmp = substr($code, 4, 3);
1371
                        if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
1372
                                // manufacturer code ends in 000, 100, or 200
1373
                                $upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1);
1374
                        } else {
1375
                                $tmp = substr($code, 5, 2);
1376
                                if ($tmp == '00') {
1377
                                        // manufacturer code ends in 00
1378
                                        $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
1379
                                } else {
1380
                                        $tmp = substr($code, 6, 1);
1381
                                        if ($tmp == '0') {
1382
                                                // manufacturer code ends in 0
1383
                                                $upce_code = substr($code, 2, 4).substr($code, 11, 1).'4';
1384
                                        } else {
1385
                                                // manufacturer code does not end in zero
1386
                                                $upce_code = substr($code, 2, 5).substr($code, 11, 1);
1387
                                        }
1388
                                }
1389
                        }
1390
                }
1391
                //Convert digits to bars
1392
                $codes = array(
1393
                        'A'=>array( // left odd parity
1394
                                '0'=>'0001101',
1395
                                '1'=>'0011001',
1396
                                '2'=>'0010011',
1397
                                '3'=>'0111101',
1398
                                '4'=>'0100011',
1399
                                '5'=>'0110001',
1400
                                '6'=>'0101111',
1401
                                '7'=>'0111011',
1402
                                '8'=>'0110111',
1403
                                '9'=>'0001011'),
1404
                        'B'=>array( // left even parity
1405
                                '0'=>'0100111',
1406
                                '1'=>'0110011',
1407
                                '2'=>'0011011',
1408
                                '3'=>'0100001',
1409
                                '4'=>'0011101',
1410
                                '5'=>'0111001',
1411
                                '6'=>'0000101',
1412
                                '7'=>'0010001',
1413
                                '8'=>'0001001',
1414
                                '9'=>'0010111'),
1415
                        'C'=>array( // right
1416
                                '0'=>'1110010',
1417
                                '1'=>'1100110',
1418
                                '2'=>'1101100',
1419
                                '3'=>'1000010',
1420
                                '4'=>'1011100',
1421
                                '5'=>'1001110',
1422
                                '6'=>'1010000',
1423
                                '7'=>'1000100',
1424
                                '8'=>'1001000',
1425
                                '9'=>'1110100')
1426
                );
1427
                $parities = array(
1428
                        '0'=>array('A','A','A','A','A','A'),
1429
                        '1'=>array('A','A','B','A','B','B'),
1430
                        '2'=>array('A','A','B','B','A','B'),
1431
                        '3'=>array('A','A','B','B','B','A'),
1432
                        '4'=>array('A','B','A','A','B','B'),
1433
                        '5'=>array('A','B','B','A','A','B'),
1434
                        '6'=>array('A','B','B','B','A','A'),
1435
                        '7'=>array('A','B','A','B','A','B'),
1436
                        '8'=>array('A','B','A','B','B','A'),
1437
                        '9'=>array('A','B','B','A','B','A')
1438
                );
1439
                $upce_parities = array();
1440
                $upce_parities[0] = array(
1441
                        '0'=>array('B','B','B','A','A','A'),
1442
                        '1'=>array('B','B','A','B','A','A'),
1443
                        '2'=>array('B','B','A','A','B','A'),
1444
                        '3'=>array('B','B','A','A','A','B'),
1445
                        '4'=>array('B','A','B','B','A','A'),
1446
                        '5'=>array('B','A','A','B','B','A'),
1447
                        '6'=>array('B','A','A','A','B','B'),
1448
                        '7'=>array('B','A','B','A','B','A'),
1449
                        '8'=>array('B','A','B','A','A','B'),
1450
                        '9'=>array('B','A','A','B','A','B')
1451
                );
1452
                $upce_parities[1] = array(
1453
                        '0'=>array('A','A','A','B','B','B'),
1454
                        '1'=>array('A','A','B','A','B','B'),
1455
                        '2'=>array('A','A','B','B','A','B'),
1456
                        '3'=>array('A','A','B','B','B','A'),
1457
                        '4'=>array('A','B','A','A','B','B'),
1458
                        '5'=>array('A','B','B','A','A','B'),
1459
                        '6'=>array('A','B','B','B','A','A'),
1460
                        '7'=>array('A','B','A','B','A','B'),
1461
                        '8'=>array('A','B','A','B','B','A'),
1462
                        '9'=>array('A','B','B','A','B','A')
1463
                );
1464
                $k = 0;
1465
                $seq = '101'; // left guard bar
1466
                if ($upce) {
1467
                        $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1468
                        $p = $upce_parities[$code[1]][$r];
1469
                        for ($i = 0; $i < 6; ++$i) {
1470
                                $seq .= $codes[$p[$i]][$upce_code{$i}];
1471
                        }
1472
                        $seq .= '010101'; // right guard bar
1473
                } else {
1474
                        $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1475
                        $half_len = intval(ceil($len / 2));
1476
                        if ($len == 8) {
1477
                                for ($i = 0; $i < $half_len; ++$i) {
1478
                                        $seq .= $codes['A'][$code{$i}];
1479
                                }
1480
                        } else {
1481
                                $p = $parities[$code[0]];
1482
                                for ($i = 1; $i < $half_len; ++$i) {
1483
                                        $seq .= $codes[$p[$i-1]][$code{$i}];
1484
                                }
1485
                        }
1486
                        $seq .= '01010'; // center guard bar
1487
                        for ($i = $half_len; $i < $len; ++$i) {
1488
                                $seq .= $codes['C'][$code{$i}];
1489
                        }
1490
                        $seq .= '101'; // right guard bar
1491
                }
1492
                $clen = strlen($seq);
1493
                $w = 0;
1494
                for ($i = 0; $i < $clen; ++$i) {
1495
                        $w += 1;
1496
                        if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq{$i} != $seq{($i+1)}))) {
1497
                                if ($seq{$i} == '1') {
1498
                                        $t = true; // bar
1499
                                } else {
1500
                                        $t = false; // space
1501
                                }
1502
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1503
                                $bararray['maxw'] += $w;
1504
                                ++$k;
1505
                                $w = 0;
1506
                        }
1507
                }
1508
                return $bararray;
1509
        }
1510

    
1511
        /**
1512
         * UPC-Based Extensions
1513
         * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
1514
         * 5-Digit Ext.: Used to mark suggested retail price of books
1515
         * @param $code (string) code to represent.
1516
         * @param $len (string) barcode type: 2 = 2-Digit, 5 = 5-Digit
1517
         * @return array barcode representation.
1518
         * @protected
1519
         */
1520
        protected function barcode_eanext($code, $len=5) {
1521
                //Padding
1522
                $code = str_pad($code, $len, '0', STR_PAD_LEFT);
1523
                // calculate check digit
1524
                if ($len == 2) {
1525
                        $r = $code % 4;
1526
                } elseif ($len == 5) {
1527
                        $r = (3 * ($code[0] + $code[2] + $code[4])) + (9 * ($code[1] + $code[3]));
1528
                        $r %= 10;
1529
                } else {
1530
                        return false;
1531
                }
1532
                //Convert digits to bars
1533
                $codes = array(
1534
                        'A'=>array( // left odd parity
1535
                                '0'=>'0001101',
1536
                                '1'=>'0011001',
1537
                                '2'=>'0010011',
1538
                                '3'=>'0111101',
1539
                                '4'=>'0100011',
1540
                                '5'=>'0110001',
1541
                                '6'=>'0101111',
1542
                                '7'=>'0111011',
1543
                                '8'=>'0110111',
1544
                                '9'=>'0001011'),
1545
                        'B'=>array( // left even parity
1546
                                '0'=>'0100111',
1547
                                '1'=>'0110011',
1548
                                '2'=>'0011011',
1549
                                '3'=>'0100001',
1550
                                '4'=>'0011101',
1551
                                '5'=>'0111001',
1552
                                '6'=>'0000101',
1553
                                '7'=>'0010001',
1554
                                '8'=>'0001001',
1555
                                '9'=>'0010111')
1556
                );
1557
                $parities = array();
1558
                $parities[2] = array(
1559
                        '0'=>array('A','A'),
1560
                        '1'=>array('A','B'),
1561
                        '2'=>array('B','A'),
1562
                        '3'=>array('B','B')
1563
                );
1564
                $parities[5] = array(
1565
                        '0'=>array('B','B','A','A','A'),
1566
                        '1'=>array('B','A','B','A','A'),
1567
                        '2'=>array('B','A','A','B','A'),
1568
                        '3'=>array('B','A','A','A','B'),
1569
                        '4'=>array('A','B','B','A','A'),
1570
                        '5'=>array('A','A','B','B','A'),
1571
                        '6'=>array('A','A','A','B','B'),
1572
                        '7'=>array('A','B','A','B','A'),
1573
                        '8'=>array('A','B','A','A','B'),
1574
                        '9'=>array('A','A','B','A','B')
1575
                );
1576
                $p = $parities[$len][$r];
1577
                $seq = '1011'; // left guard bar
1578
                $seq .= $codes[$p[0]][$code[0]];
1579
                for ($i = 1; $i < $len; ++$i) {
1580
                        $seq .= '01'; // separator
1581
                        $seq .= $codes[$p[$i]][$code{$i}];
1582
                }
1583
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1584
                return $this->binseq_to_array($seq, $bararray);
1585
        }
1586

    
1587
        /**
1588
         * POSTNET and PLANET barcodes.
1589
         * Used by U.S. Postal Service for automated mail sorting
1590
         * @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or DDDDD-DDDD.
1591
         * @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET
1592
         * @return array barcode representation.
1593
         * @protected
1594
         */
1595
        protected function barcode_postnet($code, $planet=false) {
1596
                // bar length
1597
                if ($planet) {
1598
                        $barlen = Array(
1599
                                0 => Array(1,1,2,2,2),
1600
                                1 => Array(2,2,2,1,1),
1601
                                2 => Array(2,2,1,2,1),
1602
                                3 => Array(2,2,1,1,2),
1603
                                4 => Array(2,1,2,2,1),
1604
                                5 => Array(2,1,2,1,2),
1605
                                6 => Array(2,1,1,2,2),
1606
                                7 => Array(1,2,2,2,1),
1607
                                8 => Array(1,2,2,1,2),
1608
                                9 => Array(1,2,1,2,2)
1609
                        );
1610
                } else {
1611
                        $barlen = Array(
1612
                                0 => Array(2,2,1,1,1),
1613
                                1 => Array(1,1,1,2,2),
1614
                                2 => Array(1,1,2,1,2),
1615
                                3 => Array(1,1,2,2,1),
1616
                                4 => Array(1,2,1,1,2),
1617
                                5 => Array(1,2,1,2,1),
1618
                                6 => Array(1,2,2,1,1),
1619
                                7 => Array(2,1,1,1,2),
1620
                                8 => Array(2,1,1,2,1),
1621
                                9 => Array(2,1,2,1,1)
1622
                        );
1623
                }
1624
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
1625
                $k = 0;
1626
                $code = str_replace('-', '', $code);
1627
                $code = str_replace(' ', '', $code);
1628
                $len = strlen($code);
1629
                // calculate checksum
1630
                $sum = 0;
1631
                for ($i = 0; $i < $len; ++$i) {
1632
                        $sum += intval($code{$i});
1633
                }
1634
                $chkd = ($sum % 10);
1635
                if($chkd > 0) {
1636
                        $chkd = (10 - $chkd);
1637
                }
1638
                $code .= $chkd;
1639
                $len = strlen($code);
1640
                // start bar
1641
                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1642
                $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1643
                $bararray['maxw'] += 2;
1644
                for ($i = 0; $i < $len; ++$i) {
1645
                        for ($j = 0; $j < 5; ++$j) {
1646
                                $h = $barlen[$code{$i}][$j];
1647
                                $p = floor(1 / $h);
1648
                                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1649
                                $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1650
                                $bararray['maxw'] += 2;
1651
                        }
1652
                }
1653
                // end bar
1654
                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1655
                $bararray['maxw'] += 1;
1656
                return $bararray;
1657
        }
1658

    
1659
        /**
1660
         * RMS4CC - CBC - KIX
1661
         * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
1662
         * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
1663
         * @param $code (string) code to print
1664
         * @param $kix (boolean) 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.
1665
         * @return array barcode representation.
1666
         * @protected
1667
         */
1668
        protected function barcode_rms4cc($code, $kix=false) {
1669
                $notkix = !$kix;
1670
                // bar mode
1671
                // 1 = pos 1, length 2
1672
                // 2 = pos 1, length 3
1673
                // 3 = pos 2, length 1
1674
                // 4 = pos 2, length 2
1675
                $barmode = array(
1676
                        '0' => array(3,3,2,2),
1677
                        '1' => array(3,4,1,2),
1678
                        '2' => array(3,4,2,1),
1679
                        '3' => array(4,3,1,2),
1680
                        '4' => array(4,3,2,1),
1681
                        '5' => array(4,4,1,1),
1682
                        '6' => array(3,1,4,2),
1683
                        '7' => array(3,2,3,2),
1684
                        '8' => array(3,2,4,1),
1685
                        '9' => array(4,1,3,2),
1686
                        'A' => array(4,1,4,1),
1687
                        'B' => array(4,2,3,1),
1688
                        'C' => array(3,1,2,4),
1689
                        'D' => array(3,2,1,4),
1690
                        'E' => array(3,2,2,3),
1691
                        'F' => array(4,1,1,4),
1692
                        'G' => array(4,1,2,3),
1693
                        'H' => array(4,2,1,3),
1694
                        'I' => array(1,3,4,2),
1695
                        'J' => array(1,4,3,2),
1696
                        'K' => array(1,4,4,1),
1697
                        'L' => array(2,3,3,2),
1698
                        'M' => array(2,3,4,1),
1699
                        'N' => array(2,4,3,1),
1700
                        'O' => array(1,3,2,4),
1701
                        'P' => array(1,4,1,4),
1702
                        'Q' => array(1,4,2,3),
1703
                        'R' => array(2,3,1,4),
1704
                        'S' => array(2,3,2,3),
1705
                        'T' => array(2,4,1,3),
1706
                        'U' => array(1,1,4,4),
1707
                        'V' => array(1,2,3,4),
1708
                        'W' => array(1,2,4,3),
1709
                        'X' => array(2,1,3,4),
1710
                        'Y' => array(2,1,4,3),
1711
                        'Z' => array(2,2,3,3)
1712
                );
1713
                $code = strtoupper($code);
1714
                $len = strlen($code);
1715
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
1716
                if ($notkix) {
1717
                        // table for checksum calculation (row,col)
1718
                        $checktable = array(
1719
                                '0' => array(1,1),
1720
                                '1' => array(1,2),
1721
                                '2' => array(1,3),
1722
                                '3' => array(1,4),
1723
                                '4' => array(1,5),
1724
                                '5' => array(1,0),
1725
                                '6' => array(2,1),
1726
                                '7' => array(2,2),
1727
                                '8' => array(2,3),
1728
                                '9' => array(2,4),
1729
                                'A' => array(2,5),
1730
                                'B' => array(2,0),
1731
                                'C' => array(3,1),
1732
                                'D' => array(3,2),
1733
                                'E' => array(3,3),
1734
                                'F' => array(3,4),
1735
                                'G' => array(3,5),
1736
                                'H' => array(3,0),
1737
                                'I' => array(4,1),
1738
                                'J' => array(4,2),
1739
                                'K' => array(4,3),
1740
                                'L' => array(4,4),
1741
                                'M' => array(4,5),
1742
                                'N' => array(4,0),
1743
                                'O' => array(5,1),
1744
                                'P' => array(5,2),
1745
                                'Q' => array(5,3),
1746
                                'R' => array(5,4),
1747
                                'S' => array(5,5),
1748
                                'T' => array(5,0),
1749
                                'U' => array(0,1),
1750
                                'V' => array(0,2),
1751
                                'W' => array(0,3),
1752
                                'X' => array(0,4),
1753
                                'Y' => array(0,5),
1754
                                'Z' => array(0,0)
1755
                        );
1756
                        $row = 0;
1757
                        $col = 0;
1758
                        for ($i = 0; $i < $len; ++$i) {
1759
                                $row += $checktable[$code{$i}][0];
1760
                                $col += $checktable[$code{$i}][1];
1761
                        }
1762
                        $row %= 6;
1763
                        $col %= 6;
1764
                        $chk = array_keys($checktable, array($row,$col));
1765
                        $code .= $chk[0];
1766
                        ++$len;
1767
                }
1768
                $k = 0;
1769
                if ($notkix) {
1770
                        // start bar
1771
                        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1772
                        $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1773
                        $bararray['maxw'] += 2;
1774
                }
1775
                for ($i = 0; $i < $len; ++$i) {
1776
                        for ($j = 0; $j < 4; ++$j) {
1777
                                switch ($barmode[$code{$i}][$j]) {
1778
                                        case 1: {
1779
                                                $p = 0;
1780
                                                $h = 2;
1781
                                                break;
1782
                                        }
1783
                                        case 2: {
1784
                                                $p = 0;
1785
                                                $h = 3;
1786
                                                break;
1787
                                        }
1788
                                        case 3: {
1789
                                                $p = 1;
1790
                                                $h = 1;
1791
                                                break;
1792
                                        }
1793
                                        case 4: {
1794
                                                $p = 1;
1795
                                                $h = 2;
1796
                                                break;
1797
                                        }
1798
                                }
1799
                                $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1800
                                $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1801
                                $bararray['maxw'] += 2;
1802
                        }
1803
                }
1804
                if ($notkix) {
1805
                        // stop bar
1806
                        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0);
1807
                        $bararray['maxw'] += 1;
1808
                }
1809
                return $bararray;
1810
        }
1811

    
1812
        /**
1813
         * CODABAR barcodes.
1814
         * Older code often used in library systems, sometimes in blood banks
1815
         * @param $code (string) code to represent.
1816
         * @return array barcode representation.
1817
         * @protected
1818
         */
1819
        protected function barcode_codabar($code) {
1820
                $chr = array(
1821
                        '0' => '11111221',
1822
                        '1' => '11112211',
1823
                        '2' => '11121121',
1824
                        '3' => '22111111',
1825
                        '4' => '11211211',
1826
                        '5' => '21111211',
1827
                        '6' => '12111121',
1828
                        '7' => '12112111',
1829
                        '8' => '12211111',
1830
                        '9' => '21121111',
1831
                        '-' => '11122111',
1832
                        '$' => '11221111',
1833
                        ':' => '21112121',
1834
                        '/' => '21211121',
1835
                        '.' => '21212111',
1836
                        '+' => '11222221',
1837
                        'A' => '11221211',
1838
                        'B' => '12121121',
1839
                        'C' => '11121221',
1840
                        'D' => '11122211'
1841
                );
1842
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1843
                $k = 0;
1844
                $w = 0;
1845
                $seq = '';
1846
                $code = 'A'.strtoupper($code).'A';
1847
                $len = strlen($code);
1848
                for ($i = 0; $i < $len; ++$i) {
1849
                        if (!isset($chr[$code{$i}])) {
1850
                                return false;
1851
                        }
1852
                        $seq = $chr[$code{$i}];
1853
                        for ($j = 0; $j < 8; ++$j) {
1854
                                if (($j % 2) == 0) {
1855
                                        $t = true; // bar
1856
                                } else {
1857
                                        $t = false; // space
1858
                                }
1859
                                $w = $seq{$j};
1860
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1861
                                $bararray['maxw'] += $w;
1862
                                ++$k;
1863
                        }
1864
                }
1865
                return $bararray;
1866
        }
1867

    
1868
        /**
1869
         * CODE11 barcodes.
1870
         * Used primarily for labeling telecommunications equipment
1871
         * @param $code (string) code to represent.
1872
         * @return array barcode representation.
1873
         * @protected
1874
         */
1875
        protected function barcode_code11($code) {
1876
                $chr = array(
1877
                        '0' => '111121',
1878
                        '1' => '211121',
1879
                        '2' => '121121',
1880
                        '3' => '221111',
1881
                        '4' => '112121',
1882
                        '5' => '212111',
1883
                        '6' => '122111',
1884
                        '7' => '111221',
1885
                        '8' => '211211',
1886
                        '9' => '211111',
1887
                        '-' => '112111',
1888
                        'S' => '112211'
1889
                );
1890
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1891
                $k = 0;
1892
                $w = 0;
1893
                $seq = '';
1894
                $len = strlen($code);
1895
                // calculate check digit C
1896
                $p = 1;
1897
                $check = 0;
1898
                for ($i = ($len - 1); $i >= 0; --$i) {
1899
                        $digit = $code{$i};
1900
                        if ($digit == '-') {
1901
                                $dval = 10;
1902
                        } else {
1903
                                $dval = intval($digit);
1904
                        }
1905
                        $check += ($dval * $p);
1906
                        ++$p;
1907
                        if ($p > 10) {
1908
                                $p = 1;
1909
                        }
1910
                }
1911
                $check %= 11;
1912
                if ($check == 10) {
1913
                        $check = '-';
1914
                }
1915
                $code .= $check;
1916
                if ($len > 10) {
1917
                        // calculate check digit K
1918
                        $p = 1;
1919
                        $check = 0;
1920
                        for ($i = $len; $i >= 0; --$i) {
1921
                                $digit = $code{$i};
1922
                                if ($digit == '-') {
1923
                                        $dval = 10;
1924
                                } else {
1925
                                        $dval = intval($digit);
1926
                                }
1927
                                $check += ($dval * $p);
1928
                                ++$p;
1929
                                if ($p > 9) {
1930
                                        $p = 1;
1931
                                }
1932
                        }
1933
                        $check %= 11;
1934
                        $code .= $check;
1935
                        ++$len;
1936
                }
1937
                $code = 'S'.$code.'S';
1938
                $len += 3;
1939
                for ($i = 0; $i < $len; ++$i) {
1940
                        if (!isset($chr[$code{$i}])) {
1941
                                return false;
1942
                        }
1943
                        $seq = $chr[$code{$i}];
1944
                        for ($j = 0; $j < 6; ++$j) {
1945
                                if (($j % 2) == 0) {
1946
                                        $t = true; // bar
1947
                                } else {
1948
                                        $t = false; // space
1949
                                }
1950
                                $w = $seq{$j};
1951
                                $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1952
                                $bararray['maxw'] += $w;
1953
                                ++$k;
1954
                        }
1955
                }
1956
                return $bararray;
1957
        }
1958

    
1959
        /**
1960
         * Pharmacode
1961
         * Contains digits (0 to 9)
1962
         * @param $code (string) code to represent.
1963
         * @return array barcode representation.
1964
         * @protected
1965
         */
1966
        protected function barcode_pharmacode($code) {
1967
                $seq = '';
1968
                $code = intval($code);
1969
                while ($code > 0) {
1970
                        if (($code % 2) == 0) {
1971
                                $seq .= '11100';
1972
                                $code -= 2;
1973
                        } else {
1974
                                $seq .= '100';
1975
                                $code -= 1;
1976
                        }
1977
                        $code /= 2;
1978
                }
1979
                $seq = substr($seq, 0, -2);
1980
                $seq = strrev($seq);
1981
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1982
                return $this->binseq_to_array($seq, $bararray);
1983
        }
1984

    
1985
        /**
1986
         * Pharmacode two-track
1987
         * Contains digits (0 to 9)
1988
         * @param $code (string) code to represent.
1989
         * @return array barcode representation.
1990
         * @protected
1991
         */
1992
        protected function barcode_pharmacode2t($code) {
1993
                $seq = '';
1994
                $code = intval($code);
1995
                do {
1996
                        switch ($code % 3) {
1997
                                case 0: {
1998
                                        $seq .= '3';
1999
                                        $code = ($code - 3) / 3;
2000
                                        break;
2001
                                }
2002
                                case 1: {
2003
                                        $seq .= '1';
2004
                                        $code = ($code - 1) / 3;
2005
                                        break;
2006
                                }
2007
                                case 2: {
2008
                                        $seq .= '2';
2009
                                        $code = ($code - 2) / 3;
2010
                                        break;
2011
                                }
2012
                        }
2013
                } while($code != 0);
2014
                $seq = strrev($seq);
2015
                $k = 0;
2016
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
2017
                $len = strlen($seq);
2018
                for ($i = 0; $i < $len; ++$i) {
2019
                        switch ($seq{$i}) {
2020
                                case '1': {
2021
                                        $p = 1;
2022
                                        $h = 1;
2023
                                        break;
2024
                                }
2025
                                case '2': {
2026
                                        $p = 0;
2027
                                        $h = 1;
2028
                                        break;
2029
                                }
2030
                                case '3': {
2031
                                        $p = 0;
2032
                                        $h = 2;
2033
                                        break;
2034
                                }
2035
                        }
2036
                        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2037
                        $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2038
                        $bararray['maxw'] += 2;
2039
                }
2040
                unset($bararray['bcode'][($k - 1)]);
2041
                --$bararray['maxw'];
2042
                return $bararray;
2043
        }
2044

    
2045
        /**
2046
         * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
2047
         * (requires PHP bcmath extension)
2048
         * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
2049
         * 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>
2050
         * @param $code (string) code to print, separate the ZIP (routing code) from the rest using a minus char '-' (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
2051
         * @return array barcode representation.
2052
         * @protected
2053
         */
2054
        protected function barcode_imb($code) {
2055
                $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);
2056
                $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);
2057
                $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);
2058
                $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);
2059
                $code_arr = explode('-', $code);
2060
                $tracking_number = $code_arr[0];
2061
                if (isset($code_arr[1])) {
2062
                        $routing_code = $code_arr[1];
2063
                } else {
2064
                        $routing_code = '';
2065
                }
2066
                // Conversion of Routing Code
2067
                switch (strlen($routing_code)) {
2068
                        case 0: {
2069
                                $binary_code = 0;
2070
                                break;
2071
                        }
2072
                        case 5: {
2073
                                $binary_code = bcadd($routing_code, '1');
2074
                                break;
2075
                        }
2076
                        case 9: {
2077
                                $binary_code = bcadd($routing_code, '100001');
2078
                                break;
2079
                        }
2080
                        case 11: {
2081
                                $binary_code = bcadd($routing_code, '1000100001');
2082
                                break;
2083
                        }
2084
                        default: {
2085
                                return false;
2086
                                break;
2087
                        }
2088
                }
2089
                $binary_code = bcmul($binary_code, 10);
2090
                $binary_code = bcadd($binary_code, $tracking_number[0]);
2091
                $binary_code = bcmul($binary_code, 5);
2092
                $binary_code = bcadd($binary_code, $tracking_number[1]);
2093
                $binary_code .= substr($tracking_number, 2, 18);
2094
                // convert to hexadecimal
2095
                $binary_code = $this->dec_to_hex($binary_code);
2096
                // pad to get 13 bytes
2097
                $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
2098
                // convert string to array of bytes
2099
                $binary_code_arr = chunk_split($binary_code, 2, "\r");
2100
                $binary_code_arr = substr($binary_code_arr, 0, -1);
2101
                $binary_code_arr = explode("\r", $binary_code_arr);
2102
                // calculate frame check sequence
2103
                $fcs = $this->imb_crc11fcs($binary_code_arr);
2104
                // exclude first 2 bits from first byte
2105
                $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
2106
                $binary_code_102bit = $first_byte.substr($binary_code, 2);
2107
                // convert binary data to codewords
2108
                $codewords = array();
2109
                $data = $this->hex_to_dec($binary_code_102bit);
2110
                $codewords[0] = bcmod($data, 636) * 2;
2111
                $data = bcdiv($data, 636);
2112
                for ($i = 1; $i < 9; ++$i) {
2113
                        $codewords[$i] = bcmod($data, 1365);
2114
                        $data = bcdiv($data, 1365);
2115
                }
2116
                $codewords[9] = $data;
2117
                if (($fcs >> 10) == 1) {
2118
                        $codewords[9] += 659;
2119
                }
2120
                // generate lookup tables
2121
                $table2of13 = $this->imb_tables(2, 78);
2122
                $table5of13 = $this->imb_tables(5, 1287);
2123
                // convert codewords to characters
2124
                $characters = array();
2125
                $bitmask = 512;
2126
                foreach($codewords as $k => $val) {
2127
                        if ($val <= 1286) {
2128
                                $chrcode = $table5of13[$val];
2129
                        } else {
2130
                                $chrcode = $table2of13[($val - 1287)];
2131
                        }
2132
                        if (($fcs & $bitmask) > 0) {
2133
                                // bitwise invert
2134
                                $chrcode = ((~$chrcode) & 8191);
2135
                        }
2136
                        $characters[] = $chrcode;
2137
                        $bitmask /= 2;
2138
                }
2139
                $characters = array_reverse($characters);
2140
                // build bars
2141
                $k = 0;
2142
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
2143
                for ($i = 0; $i < 65; ++$i) {
2144
                        $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
2145
                        $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
2146
                        if ($asc AND $dsc) {
2147
                                // full bar (F)
2148
                                $p = 0;
2149
                                $h = 3;
2150
                        } elseif ($asc) {
2151
                                // ascender (A)
2152
                                $p = 0;
2153
                                $h = 2;
2154
                        } elseif ($dsc) {
2155
                                // descender (D)
2156
                                $p = 1;
2157
                                $h = 2;
2158
                        } else {
2159
                                // tracker (T)
2160
                                $p = 1;
2161
                                $h = 1;
2162
                        }
2163
                        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2164
                        $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2165
                        $bararray['maxw'] += 2;
2166
                }
2167
                unset($bararray['bcode'][($k - 1)]);
2168
                --$bararray['maxw'];
2169
                return $bararray;
2170
        }
2171

    
2172
        /**
2173
         * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
2174
         * 
2175
         * @param $code (string) pre-formatted IMB barcode (65 chars "FADT")
2176
         * @return array barcode representation.
2177
         * @protected
2178
         */
2179
        protected function barcode_imb_pre($code) {
2180
                if (!preg_match('/^[fadtFADT]{65}$/', $code) == 1) {
2181
                        return false;
2182
                }
2183
                $characters = str_split(strtolower($code), 1);
2184
                // build bars
2185
                $k = 0;
2186
                $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
2187
                for ($i = 0; $i < 65; ++$i) {
2188
                        switch($characters[$i]) {
2189
                                case 'f': {
2190
                                        // full bar
2191
                                        $p = 0;
2192
                                        $h = 3;
2193
                                        break;
2194
                                }
2195
                                case 'a': {
2196
                                        // ascender
2197
                                        $p = 0;
2198
                                        $h = 2;
2199
                                        break;
2200
                                }
2201
                                case 'd': {
2202
                                        // descender
2203
                                        $p = 1;
2204
                                        $h = 2;
2205
                                        break;
2206
                                }
2207
                                case 't': {
2208
                                        // tracker (short)
2209
                                        $p = 1;
2210
                                        $h = 1;
2211
                                        break;
2212
                                }
2213
                        }
2214
                        $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
2215
                        $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
2216
                        $bararray['maxw'] += 2;
2217
                }
2218
                unset($bararray['bcode'][($k - 1)]);
2219
                --$bararray['maxw'];
2220
                return $bararray;
2221
        }
2222

    
2223
        /**
2224
         * Convert large integer number to hexadecimal representation.
2225
         * (requires PHP bcmath extension)
2226
         * @param $number (string) number to convert specified as a string
2227
         * @return string hexadecimal representation
2228
         */
2229
        public function dec_to_hex($number) {
2230
                $i = 0;
2231
                $hex = array();
2232
                if($number == 0) {
2233
                        return '00';
2234
                }
2235
                while($number > 0) {
2236
                        if($number == 0) {
2237
                                array_push($hex, '0');
2238
                        } else {
2239
                                array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
2240
                                $number = bcdiv($number, '16', 0);
2241
                        }
2242
                }
2243
                $hex = array_reverse($hex);
2244
                return implode($hex);
2245
        }
2246

    
2247
        /**
2248
         * Convert large hexadecimal number to decimal representation (string).
2249
         * (requires PHP bcmath extension)
2250
         * @param $hex (string) hexadecimal number to convert specified as a string
2251
         * @return string hexadecimal representation
2252
         */
2253
        public function hex_to_dec($hex) {
2254
                $dec = 0;
2255
                $bitval = 1;
2256
                $len = strlen($hex);
2257
                for($pos = ($len - 1); $pos >= 0; --$pos) {
2258
                        $dec = bcadd($dec, bcmul(hexdec($hex{$pos}), $bitval));
2259
                        $bitval = bcmul($bitval, 16);
2260
                }
2261
                return $dec;
2262
        }
2263

    
2264
        /**
2265
         * Intelligent Mail Barcode calculation of Frame Check Sequence
2266
         * @param $code_arr (string) array of hexadecimal values (13 bytes holding 102 bits right justified).
2267
         * @return int 11 bit Frame Check Sequence as integer (decimal base)
2268
         * @protected
2269
         */
2270
        protected function imb_crc11fcs($code_arr) {
2271
                $genpoly = 0x0F35; // generator polynomial
2272
                $fcs = 0x07FF; // Frame Check Sequence
2273
                // do most significant byte skipping the 2 most significant bits
2274
                $data = hexdec($code_arr[0]) << 5;
2275
                for ($bit = 2; $bit < 8; ++$bit) {
2276
                        if (($fcs ^ $data) & 0x400) {
2277
                                $fcs = ($fcs << 1) ^ $genpoly;
2278
                        } else {
2279
                                $fcs = ($fcs << 1);
2280
                        }
2281
                        $fcs &= 0x7FF;
2282
                        $data <<= 1;
2283
                }
2284
                // do rest of bytes
2285
                for ($byte = 1; $byte < 13; ++$byte) {
2286
                        $data = hexdec($code_arr[$byte]) << 3;
2287
                        for ($bit = 0; $bit < 8; ++$bit) {
2288
                                if (($fcs ^ $data) & 0x400) {
2289
                                        $fcs = ($fcs << 1) ^ $genpoly;
2290
                                } else {
2291
                                        $fcs = ($fcs << 1);
2292
                                }
2293
                                $fcs &= 0x7FF;
2294
                                $data <<= 1;
2295
                        }
2296
                }
2297
                return $fcs;
2298
        }
2299

    
2300
        /**
2301
         * Reverse unsigned short value
2302
         * @param $num (int) value to reversr
2303
         * @return int reversed value
2304
         * @protected
2305
         */
2306
        protected function imb_reverse_us($num) {
2307
                $rev = 0;
2308
                for ($i = 0; $i < 16; ++$i) {
2309
                        $rev <<= 1;
2310
                        $rev |= ($num & 1);
2311
                        $num >>= 1;
2312
                }
2313
                return $rev;
2314
        }
2315

    
2316
        /**
2317
         * generate Nof13 tables used for Intelligent Mail Barcode
2318
         * @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table
2319
         * @param $size (int) size of table (78 for n=2 and 1287 for n=5)
2320
         * @return array requested table
2321
         * @protected
2322
         */
2323
        protected function imb_tables($n, $size) {
2324
                $table = array();
2325
                $lli = 0; // LUT lower index
2326
                $lui = $size - 1; // LUT upper index
2327
                for ($count = 0; $count < 8192; ++$count) {
2328
                        $bit_count = 0;
2329
                        for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
2330
                                $bit_count += intval(($count & (1 << $bit_index)) != 0);
2331
                        }
2332
                        // if we don't have the right number of bits on, go on to the next value
2333
                        if ($bit_count == $n) {
2334
                                $reverse = ($this->imb_reverse_us($count) >> 3);
2335
                                // if the reverse is less than count, we have already visited this pair before
2336
                                if ($reverse >= $count) {
2337
                                        // If count is symmetric, place it at the first free slot from the end of the list.
2338
                                        // 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
2339
                                        if ($reverse == $count) {
2340
                                                $table[$lui] = $count;
2341
                                                --$lui;
2342
                                        } else {
2343
                                                $table[$lli] = $count;
2344
                                                ++$lli;
2345
                                                $table[$lli] = $reverse;
2346
                                                ++$lli;
2347
                                        }
2348
                                }
2349
                        }
2350
                }
2351
                return $table;
2352
        }
2353

    
2354
} // end of class
2355
//============================================================+
2356
// END OF FILE
2357
//============================================================+