]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/tcpdf/barcodes.php
Release 6.5.0
[Github/sugarcrm.git] / include / tcpdf / barcodes.php
1 <?php
2
3 /*
4
5 Modification information for LGPL compliance
6
7 r56990 - 2010-06-16 13:05:36 -0700 (Wed, 16 Jun 2010) - kjing - snapshot "Mango" svn branch to a new one for GitHub sync
8
9 r56989 - 2010-06-16 13:01:33 -0700 (Wed, 16 Jun 2010) - kjing - defunt "Mango" svn dev branch before github cutover
10
11 r55980 - 2010-04-19 13:31:28 -0700 (Mon, 19 Apr 2010) - kjing - create Mango (6.1) based on windex
12
13 r51719 - 2009-10-22 10:18:00 -0700 (Thu, 22 Oct 2009) - mitani - Converted to Build 3  tags and updated the build system 
14
15 r51634 - 2009-10-19 13:32:22 -0700 (Mon, 19 Oct 2009) - mitani - Windex is the branch for Sugar Sales 1.0 development
16
17 r50375 - 2009-08-24 18:07:43 -0700 (Mon, 24 Aug 2009) - dwong - branch kobe2 from tokyo r50372
18
19 r46451 - 2009-04-23 16:57:40 -0700 (Thu, 23 Apr 2009) - jenny - tcpdf initial checkin.
20
21
22 */
23
24
25 //============================================================+
26 // File name   : barcodes.php
27 // Begin       : 2008-06-09
28 // Last Update : 2009-04-15
29 // Version     : 1.0.008
30 // License     : GNU LGPL (http://www.gnu.org/copyleft/lesser.html)
31 //      ----------------------------------------------------------------------------
32 //  Copyright (C) 2008-2009 Nicola Asuni - Tecnick.com S.r.l.
33 //      
34 //      This program is free software: you can redistribute it and/or modify
35 //      it under the terms of the GNU Lesser General Public License as published by
36 //      the Free Software Foundation, either version 2.1 of the License, or
37 //      (at your option) any later version.
38 //      
39 //      This program is distributed in the hope that it will be useful,
40 //      but WITHOUT ANY WARRANTY; without even the implied warranty of
41 //      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
42 //      GNU Lesser General Public License for more details.
43 //      
44 //      You should have received a copy of the GNU Lesser General Public License
45 //      along with this program.  If not, see <http://www.gnu.org/licenses/>.
46 //      
47 //      See LICENSE.TXT file for more information.
48 //  ----------------------------------------------------------------------------
49 //
50 // Description : PHP class to creates array representations for 
51 //               common 1D barcodes to be used with TCPDF.
52 //
53 // Author: Nicola Asuni
54 //
55 // (c) Copyright:
56 //               Nicola Asuni
57 //               Tecnick.com S.r.l.
58 //               Via della Pace, 11
59 //               09044 Quartucciu (CA)
60 //               ITALY
61 //               www.tecnick.com
62 //               info@tecnick.com
63 //============================================================+
64
65 /**
66  * PHP class to creates array representations for common 1D barcodes to be used with TCPDF.
67  * @package com.tecnick.tcpdf
68  * @abstract Functions for generating string representation of common 1D barcodes.
69  * @author Nicola Asuni
70  * @copyright 2008-2009 Nicola Asuni - Tecnick.com S.r.l (www.tecnick.com) Via Della Pace, 11 - 09044 - Quartucciu (CA) - ITALY - www.tecnick.com - info@tecnick.com
71  * @link http://www.tcpdf.org
72  * @license http://www.gnu.org/copyleft/lesser.html LGPL
73  * @version 1.0.008
74  */
75
76         /**
77         * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).<br>
78         * @name TCPDFBarcode
79         * @package com.tecnick.tcpdf
80         * @version 1.0.008
81         * @author Nicola Asuni
82         * @link http://www.tcpdf.org
83         * @license http://www.gnu.org/copyleft/lesser.html LGPL
84         */
85 class TCPDFBarcode {
86         
87         /**
88          * @var array representation of barcode.
89          * @access protected
90          */
91         protected $barcode_array;
92                 
93         /**
94          * This is the class constructor. 
95          * Return an array representations for common 1D barcodes:<ul>
96          * <li>$arrcode['code'] code to be printed on text label</li>
97          * <li>$arrcode['maxh'] max bar height</li>
98          * <li>$arrcode['maxw'] max bar width</li>
99          * <li>$arrcode['bcode'][$k] single bar or space in $k position</li>
100          * <li>$arrcode['bcode'][$k]['t'] bar type: true = bar, false = space.</li>
101          * <li>$arrcode['bcode'][$k]['w'] bar width in units.</li>
102          * <li>$arrcode['bcode'][$k]['h'] bar height in units.</li>
103          * <li>$arrcode['bcode'][$k]['p'] bar top position (0 = top, 1 = middle)</li></ul>
104          * @param string $code code to print
105          * @param string $type type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extention</li><li>EAN5 : 5-Digits UPC-Based Extention</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>
106          */
107         public function __construct($code, $type) {
108                 $this->setBarcode($code, $type);
109         }
110         
111         /** 
112          * Return an array representations of barcode.
113          * @return array
114          */
115         public function getBarcodeArray() {
116                 return $this->barcode_array;
117         }
118         
119         /** 
120          * Set the barcode.
121          * @param string $code code to print
122          * @param string $type type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extention</li><li>EAN5 : 5-Digits UPC-Based Extention</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>
123          * @return array
124          */
125         public function setBarcode($code, $type) {
126                 switch (strtoupper($type)) {
127                         case 'C39': { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
128                                 $arrcode = $this->barcode_code39($code, false, false);
129                                 break;
130                         }
131                         case 'C39+': { // CODE 39 with checksum
132                                 $arrcode = $this->barcode_code39($code, false, true);
133                                 break;
134                         }
135                         case 'C39E': { // CODE 39 EXTENDED
136                                 $arrcode = $this->barcode_code39($code, true, false);
137                                 break;
138                         }
139                         case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM
140                                 $arrcode = $this->barcode_code39($code, true, true);
141                                 break;
142                         }
143                         case 'C93': { // CODE 93 - USS-93
144                                 $arrcode = $this->barcode_code93($code);
145                                 break;
146                         }
147                         case 'S25': { // Standard 2 of 5
148                                 $arrcode = $this->barcode_s25($code, false);
149                                 break;
150                         }
151                         case 'S25+': { // Standard 2 of 5 + CHECKSUM
152                                 $arrcode = $this->barcode_s25($code, true);
153                                 break;
154                         }
155                         case 'I25': { // Interleaved 2 of 5
156                                 $arrcode = $this->barcode_i25($code, false);
157                                 break;
158                         }
159                         case 'I25+': { // Interleaved 2 of 5 + CHECKSUM
160                                 $arrcode = $this->barcode_i25($code, true);
161                                 break;
162                         }
163                         case 'C128A': { // CODE 128 A
164                                 $arrcode = $this->barcode_c128($code, 'A');
165                                 break;
166                         }
167                         case 'C128B': { // CODE 128 B
168                                 $arrcode = $this->barcode_c128($code, 'B');
169                                 break;
170                         }
171                         case 'C128C': { // CODE 128 C
172                                 $arrcode = $this->barcode_c128($code, 'C');
173                                 break;
174                         }
175                         case 'EAN2': { // 2-Digits UPC-Based Extention
176                                 $arrcode = $this->barcode_eanext($code, 2);
177                                 break;
178                         }
179                         case 'EAN5': { // 5-Digits UPC-Based Extention
180                                 $arrcode = $this->barcode_eanext($code, 5);
181                                 break;
182                         }
183                         case 'EAN8': { // EAN 8
184                                 $arrcode = $this->barcode_eanupc($code, 8);
185                                 break;
186                         }
187                         case 'EAN13': { // EAN 13
188                                 $arrcode = $this->barcode_eanupc($code, 13);
189                                 break;
190                         }
191                         case 'UPCA': { // UPC-A
192                                 $arrcode = $this->barcode_eanupc($code, 12);
193                                 break;
194                         }
195                         case 'UPCE': { // UPC-E
196                                 $arrcode = $this->barcode_eanupc($code, 6);
197                                 break;
198                         }
199                         case 'MSI': { // MSI (Variation of Plessey code)
200                                 $arrcode = $this->barcode_msi($code, false);
201                                 break;
202                         }
203                         case 'MSI+': { // MSI + CHECKSUM (modulo 11)
204                                 $arrcode = $this->barcode_msi($code, true);
205                                 break;
206                         }
207                         case 'POSTNET': { // POSTNET
208                                 $arrcode = $this->barcode_postnet($code, false);
209                                 break;
210                         }
211                         case 'PLANET': { // PLANET
212                                 $arrcode = $this->barcode_postnet($code, true);
213                                 break;
214                         }
215                         case 'RMS4CC': { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)
216                                 $arrcode = $this->barcode_rms4cc($code, false);
217                                 break;
218                         }
219                         case 'KIX': { // KIX (Klant index - Customer index)
220                                 $arrcode = $this->barcode_rms4cc($code, true);
221                                 break;
222                         }
223                         case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
224                                 $arrcode = $this->barcode_imb($code);
225                                 break;
226                         }
227                         case 'CODABAR': { // CODABAR
228                                 $arrcode = $this->barcode_codabar($code);
229                                 break;
230                         }
231                         case 'CODE11': { // CODE 11
232                                 $arrcode = $this->barcode_code11($code);
233                                 break;
234                         }
235                         case 'PHARMA': { // PHARMACODE
236                                 $arrcode = $this->barcode_pharmacode($code);
237                                 break;
238                         }
239                         case 'PHARMA2T': { // PHARMACODE TWO-TRACKS
240                                 $arrcode = $this->barcode_pharmacode2t($code);
241                                 break;
242                         }
243                         default: {
244                                 $this->barcode_array = false;
245                         }
246                 }
247                 $this->barcode_array = $arrcode;
248         }
249         
250         /**
251          * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
252          * General-purpose code in very wide use world-wide
253          * @param string $code code to represent.
254          * @param boolean $checksum if true add a checksum to the code
255          * @return array barcode representation.
256          * @access protected
257          */
258         protected function barcode_code39($code, $extended=false, $checksum=false) {
259                 $chr['0'] = '111221211';
260                 $chr['1'] = '211211112';
261                 $chr['2'] = '112211112';
262                 $chr['3'] = '212211111';
263                 $chr['4'] = '111221112';
264                 $chr['5'] = '211221111';
265                 $chr['6'] = '112221111';
266                 $chr['7'] = '111211212';
267                 $chr['8'] = '211211211';
268                 $chr['9'] = '112211211';
269                 $chr['A'] = '211112112';
270                 $chr['B'] = '112112112';
271                 $chr['C'] = '212112111';
272                 $chr['D'] = '111122112';
273                 $chr['E'] = '211122111';
274                 $chr['F'] = '112122111';
275                 $chr['G'] = '111112212';
276                 $chr['H'] = '211112211';
277                 $chr['I'] = '112112211';
278                 $chr['J'] = '111122211';
279                 $chr['K'] = '211111122';
280                 $chr['L'] = '112111122';
281                 $chr['M'] = '212111121';
282                 $chr['N'] = '111121122';
283                 $chr['O'] = '211121121';
284                 $chr['P'] = '112121121';
285                 $chr['Q'] = '111111222';
286                 $chr['R'] = '211111221';
287                 $chr['S'] = '112111221';
288                 $chr['T'] = '111121221';
289                 $chr['U'] = '221111112';
290                 $chr['V'] = '122111112';
291                 $chr['W'] = '222111111';
292                 $chr['X'] = '121121112';
293                 $chr['Y'] = '221121111';
294                 $chr['Z'] = '122121111';
295                 $chr['-'] = '121111212';
296                 $chr['.'] = '221111211';
297                 $chr[' '] = '122111211';
298                 $chr['$'] = '121212111';
299                 $chr['/'] = '121211121';
300                 $chr['+'] = '121112121';
301                 $chr['%'] = '111212121';
302                 $chr['*'] = '121121211';
303                 
304                 $code = strtoupper($code);
305                 if ($extended) {
306                         // extended mode
307                         $code = $this->encode_code39_ext($code);
308                 }
309                 if ($code === false) {
310                         return false;
311                 }
312                 if ($checksum) {
313                         // checksum
314                         $code .= $this->checksum_code39($code);
315                 }
316                 // add start and stop codes
317                 $code = '*'.$code.'*';
318                 
319                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
320                 $k = 0;
321                 $clen = strlen($code);
322                 for ($i = 0; $i < $clen; ++$i) {
323                         $char = $code{$i};
324                         if(!isset($chr[$char])) {
325                                 // invalid character
326                                 return false;
327                         }
328                         for ($j = 0; $j < 9; ++$j) {
329                                 if (($j % 2) == 0) {
330                                         $t = true; // bar
331                                 } else {
332                                         $t = false; // space
333                                 }
334                                 $w = $chr[$char]{$j};
335                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
336                                 $bararray['maxw'] += $w;
337                                 ++$k;
338                         }
339                         $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0);
340                         $bararray['maxw'] += 1;
341                         ++$k;
342                 }
343                 return $bararray;
344         }
345         
346         /**
347          * Encode a string to be used for CODE 39 Extended mode.
348          * @param string $code code to represent.
349          * @return encoded string.
350          * @access protected
351          */
352         protected function encode_code39_ext($code) {
353                 $encode = array(
354                         chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
355                         chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
356                         chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
357                         chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
358                         chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
359                         chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
360                         chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
361                         chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
362                         chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
363                         chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
364                         chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
365                         chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
366                         chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
367                         chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
368                         chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
369                         chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
370                         chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
371                         chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
372                         chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
373                         chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
374                         chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
375                         chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
376                         chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
377                         chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
378                         chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
379                         chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
380                         chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
381                         chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
382                         chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
383                         chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
384                         chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
385                         chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T');
386                 $code_ext = '';
387                 $clen = strlen($code);
388                 for ($i = 0 ; $i < $clen; ++$i) {
389                         if (ord($code{$i}) > 127) {
390                                 return false;
391                         }
392                         $code_ext .= $encode[$code{$i}];
393                 }
394                 return $code_ext;
395         }
396         
397         /**
398          * Calculate CODE 39 checksum (modulo 43).
399          * @param string $code code to represent.
400          * @return char checksum.
401          * @access protected
402          */
403         protected function checksum_code39($code) {
404                 $chars = array(
405                         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
406                         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
407                         'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
408                         'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
409                 $sum = 0;
410                 $clen = strlen($code);
411                 for ($i = 0 ; $i < $clen; ++$i) {
412                         $k = array_keys($chars, $code{$i});
413                         $sum += $k[0];
414                 }
415                 $j = ($sum % 43);
416                 return $chars[$j];
417         }
418         
419         /**
420          * CODE 93 - USS-93
421          * Compact code similar to Code 39
422          * @param string $code code to represent.
423          * @param boolean $checksum if true add a checksum to the code
424          * @return array barcode representation.
425          * @access protected
426          */
427         protected function barcode_code93($code) {
428                 $chr['0'] = '131112';
429                 $chr['1'] = '111213';
430                 $chr['2'] = '111312';
431                 $chr['3'] = '111411';
432                 $chr['4'] = '121113';
433                 $chr['5'] = '121212';
434                 $chr['6'] = '121311';
435                 $chr['7'] = '111114';
436                 $chr['8'] = '131211';
437                 $chr['9'] = '141111';
438                 $chr['A'] = '211113';
439                 $chr['B'] = '211212';
440                 $chr['C'] = '211311';
441                 $chr['D'] = '221112';
442                 $chr['E'] = '221211';
443                 $chr['F'] = '231111';
444                 $chr['G'] = '112113';
445                 $chr['H'] = '112212';
446                 $chr['I'] = '112311';
447                 $chr['J'] = '122112';
448                 $chr['K'] = '132111';
449                 $chr['L'] = '111123';
450                 $chr['M'] = '111222';
451                 $chr['N'] = '111321';
452                 $chr['O'] = '121122';
453                 $chr['P'] = '131121';
454                 $chr['Q'] = '212112';
455                 $chr['R'] = '212211';
456                 $chr['S'] = '211122';
457                 $chr['T'] = '211221';
458                 $chr['U'] = '221121';
459                 $chr['V'] = '222111';
460                 $chr['W'] = '112122';
461                 $chr['X'] = '112221';
462                 $chr['Y'] = '122121';
463                 $chr['Z'] = '123111';
464                 $chr['-'] = '121131';
465                 $chr['.'] = '311112';
466                 $chr[' '] = '311211';
467                 $chr['$'] = '321111';
468                 $chr['/'] = '112131';
469                 $chr['+'] = '113121';
470                 $chr['%'] = '211131';
471                 $chr[128] = '121221'; // ($)
472                 $chr[129] = '311121'; // (/)
473                 $chr[130] = '122211'; // (+)
474                 $chr[131] = '312111'; // (%)
475                 $chr['*'] = '111141';
476                 $code = strtoupper($code);
477                 $encode = array(
478                         chr(0) => chr(131).'U', chr(1) => chr(128).'A', chr(2) => chr(128).'B', chr(3) => chr(128).'C',
479                         chr(4) => chr(128).'D', chr(5) => chr(128).'E', chr(6) => chr(128).'F', chr(7) => chr(128).'G',
480                         chr(8) => chr(128).'H', chr(9) => chr(128).'I', chr(10) => chr(128).'J', chr(11) => '£K',
481                         chr(12) => chr(128).'L', chr(13) => chr(128).'M', chr(14) => chr(128).'N', chr(15) => chr(128).'O',
482                         chr(16) => chr(128).'P', chr(17) => chr(128).'Q', chr(18) => chr(128).'R', chr(19) => chr(128).'S',
483                         chr(20) => chr(128).'T', chr(21) => chr(128).'U', chr(22) => chr(128).'V', chr(23) => chr(128).'W',
484                         chr(24) => chr(128).'X', chr(25) => chr(128).'Y', chr(26) => chr(128).'Z', chr(27) => chr(131).'A',
485                         chr(28) => chr(131).'B', chr(29) => chr(131).'C', chr(30) => chr(131).'D', chr(31) => chr(131).'E',
486                         chr(32) => ' ', chr(33) => chr(129).'A', chr(34) => chr(129).'B', chr(35) => chr(129).'C',
487                         chr(36) => chr(129).'D', chr(37) => chr(129).'E', chr(38) => chr(129).'F', chr(39) => chr(129).'G',
488                         chr(40) => chr(129).'H', chr(41) => chr(129).'I', chr(42) => chr(129).'J', chr(43) => chr(129).'K',
489                         chr(44) => chr(129).'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129).'O',
490                         chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
491                         chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
492                         chr(56) => '8', chr(57) => '9', chr(58) => chr(129).'Z', chr(59) => chr(131).'F',
493                         chr(60) => chr(131).'G', chr(61) => chr(131).'H', chr(62) => chr(131).'I', chr(63) => chr(131).'J',
494                         chr(64) => chr(131).'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
495                         chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
496                         chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
497                         chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
498                         chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
499                         chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
500                         chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131).'K',
501                         chr(92) => chr(131).'L', chr(93) => chr(131).'M', chr(94) => chr(131).'N', chr(95) => chr(131).'O',
502                         chr(96) => chr(131).'W', chr(97) => chr(130).'A', chr(98) => chr(130).'B', chr(99) => chr(130).'C',
503                         chr(100) => chr(130).'D', chr(101) => chr(130).'E', chr(102) => chr(130).'F', chr(103) => chr(130).'G',
504                         chr(104) => chr(130).'H', chr(105) => chr(130).'I', chr(106) => chr(130).'J', chr(107) => chr(130).'K',
505                         chr(108) => chr(130).'L', chr(109) => chr(130).'M', chr(110) => chr(130).'N', chr(111) => chr(130).'O',
506                         chr(112) => chr(130).'P', chr(113) => chr(130).'Q', chr(114) => chr(130).'R', chr(115) => chr(130).'S',
507                         chr(116) => chr(130).'T', chr(117) => chr(130).'U', chr(118) => chr(130).'V', chr(119) => chr(130).'W',
508                         chr(120) => chr(130).'X', chr(121) => chr(130).'Y', chr(122) => chr(130).'Z', chr(123) => chr(131).'P',
509                         chr(124) => chr(131).'Q', chr(125) => chr(131).'R', chr(126) => chr(131).'S', chr(127) => chr(131).'T');
510                 $code_ext = '';
511                 $clen = strlen($code);
512                 for ($i = 0 ; $i < $clen; ++$i) {
513                         if (ord($code{$i}) > 127) {
514                                 return false;
515                         }
516                         $code_ext .= $encode[$code{$i}];
517                 }
518                 // checksum
519                 $code .= $this->checksum_code93($code);
520                 // add start and stop codes
521                 $code = '*'.$code.'*';
522                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
523                 $k = 0;
524                 $clen = strlen($code);
525                 for ($i = 0; $i < $clen; ++$i) {
526                         $char = $code{$i};
527                         if(!isset($chr[$char])) {
528                                 // invalid character
529                                 return false;
530                         }
531                         for ($j = 0; $j < 6; ++$j) {
532                                 if (($j % 2) == 0) {
533                                         $t = true; // bar
534                                 } else {
535                                         $t = false; // space
536                                 }
537                                 $w = $chr[$char]{$j};
538                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
539                                 $bararray['maxw'] += $w;
540                                 ++$k;
541                         }
542                 }
543                 $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0);
544                 $bararray['maxw'] += 1;
545                 ++$k;           
546                 return $bararray;
547         }
548         
549         /**
550          * Calculate CODE 93 checksum (modulo 47).
551          * @param string $code code to represent.
552          * @return string checksum code.
553          * @access protected
554          */
555         protected function checksum_code93($code) {
556                 $chars = array(
557                         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
558                         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
559                         'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
560                         'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%');
561                 // translate special characters
562                 $code = strtr($code, chr(128).chr(129).chr(130).chr(131), '$/+%');      
563                 $len = strlen($code);
564                 // calculate check digit C
565                 $p = 1;
566                 $check = 0;
567                 for ($i = ($len - 1); $i >= 0; --$i) {
568                         $k = array_keys($chars, $code{$i});
569                         $check += ($k[0] * $p);
570                         ++$p;
571                         if ($p > 20) {
572                                 $p = 1;
573                         }
574                 }
575                 $check %= 47;
576                 $c = $chars[$check];
577                 $code .= $c;
578                 // calculate check digit K
579                 $p = 1;
580                 $check = 0;
581                 for ($i = $len; $i >= 0; --$i) {
582                         $k = array_keys($chars, $code{$i});
583                         $check += ($k[0] * $p);
584                         ++$p;
585                         if ($p > 15) {
586                                 $p = 1;
587                         }
588                 }
589                 $check %= 47;
590                 $k = $chars[$check];
591                 return $c.$k;
592         }
593         
594         /**
595          * Checksum for standard 2 of 5 barcodes.
596          * @param string $code code to process.
597          * @return int checksum.
598          * @access protected
599          */
600         protected function checksum_s25($code) {
601                 $len = strlen($code);
602                 $sum = 0;
603                 for ($i = 0; $i < $len; $i+=2) {
604                         $sum += $code{$i};
605                 }
606                 $sum *= 3;
607                 for ($i = 1; $i < $len; $i+=2) {
608                         $sum += ($code{$i});
609                 }
610                 $r = $sum % 10;
611                 if($r > 0) {
612                         $r = (10 - $r);
613                 }
614                 return $r;
615         }
616         
617         /**
618          * MSI.
619          * Variation of Plessey code, with similar applications 
620          * Contains digits (0 to 9) and encodes the data only in the width of bars.
621          * @param string $code code to represent.
622          * @param boolean $checksum if true add a checksum to the code (modulo 11)
623          * @return array barcode representation.
624          * @access protected
625          */
626         protected function barcode_msi($code, $checksum=false) {
627                 $chr['0'] = '100100100100';
628                 $chr['1'] = '100100100110';
629                 $chr['2'] = '100100110100';
630                 $chr['3'] = '100100110110';
631                 $chr['4'] = '100110100100';
632                 $chr['5'] = '100110100110';
633                 $chr['6'] = '100110110100';
634                 $chr['7'] = '100110110110';
635                 $chr['8'] = '110100100100';
636                 $chr['9'] = '110100100110';
637                 $chr['A'] = '110100110100';
638                 $chr['B'] = '110100110110';
639                 $chr['C'] = '110110100100';
640                 $chr['D'] = '110110100110';
641                 $chr['E'] = '110110110100';
642                 $chr['F'] = '110110110110';
643                 if ($checksum) {
644                         // add checksum
645                         $clen = strlen($code);
646                         $p = 2;
647                         $check = 0;
648                         for ($i = ($clen - 1); $i >= 0; --$i) {
649                                 $check += (hexdec($code{$i}) * $p);
650                                 ++$p;
651                                 if ($p > 7) {
652                                         $p = 2;
653                                 }
654                         }
655                         $check %= 11;
656                         if ($check > 0) {
657                                 $check = 11 - $check;
658                         }
659                         $code .= $check;
660                 }
661                 $seq = '110'; // left guard
662                 $clen = strlen($code);
663                 for ($i = 0; $i < $clen; ++$i) {
664                         $digit = $code{$i};
665                         if (!isset($chr[$digit])) {
666                                 // invalid character
667                                 return false;
668                         }
669                         $seq .= $chr[$digit];
670                 }               
671                 $seq .= '1001'; // right guard
672                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
673                 return $this->binseq_to_array($seq, $bararray);
674         }
675         
676         /**
677          * Standard 2 of 5 barcodes.
678          * Used in airline ticket marking, photofinishing
679          * Contains digits (0 to 9) and encodes the data only in the width of bars.
680          * @param string $code code to represent.
681          * @param boolean $checksum if true add a checksum to the code
682          * @return array barcode representation.
683          * @access protected
684          */
685         protected function barcode_s25($code, $checksum=false) {
686                 $chr['0'] = '10101110111010';
687                 $chr['1'] = '11101010101110';
688                 $chr['2'] = '10111010101110';
689                 $chr['3'] = '11101110101010';
690                 $chr['4'] = '10101110101110';
691                 $chr['5'] = '11101011101010';
692                 $chr['6'] = '10111011101010';
693                 $chr['7'] = '10101011101110';
694                 $chr['8'] = '10101110111010';
695                 $chr['9'] = '10111010111010';
696                 if ($checksum) {
697                         // add checksum
698                         $code .= $this->checksum_s25($code);
699                 }
700                 if((strlen($code) % 2) != 0) {
701                         // add leading zero if code-length is odd
702                         $code = '0'.$code;
703                 }
704                 $seq = '11011010';
705                 $clen = strlen($code);
706                 for ($i = 0; $i < $clen; ++$i) {
707                         $digit = $code{$i};
708                         if (!isset($chr[$digit])) {
709                                 // invalid character
710                                 return false;
711                         }
712                         $seq .= $chr[$digit];
713                 }               
714                 $seq .= '1101011';
715                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
716                 return $this->binseq_to_array($seq, $bararray);
717         }
718         
719         /**
720          * Convert binary barcode sequence to TCPDF barcode array
721          * @param string $seq barcode as binary sequence
722          * Ã²param array $bararray TCPDF barcode array to fill up
723          * @return array barcode representation.
724          * @access protected
725          */
726         protected function binseq_to_array($seq, $bararray) {
727                 $len = strlen($seq);
728                 $w = 0;
729                 $k = 0;
730                 for ($i = 0; $i < $len; ++$i) {
731                         $w += 1;
732                         if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq{$i} != $seq{($i+1)}))) {
733                                 if ($seq{$i} == '1') {
734                                         $t = true; // bar
735                                 } else {
736                                         $t = false; // space
737                                 }
738                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
739                                 $bararray['maxw'] += $w;
740                                 ++$k;
741                                 $w = 0;
742                         }
743                 }
744                 return $bararray;
745         }
746         
747         /**
748          * Interleaved 2 of 5 barcodes.
749          * Compact numeric code, widely used in industry, air cargo
750          * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces.
751          * @param string $code code to represent.
752          * @param boolean $checksum if true add a checksum to the code
753          * @return array barcode representation.
754          * @access protected
755          */
756         protected function barcode_i25($code, $checksum=false) {
757                 $chr['0'] = '11221';
758                 $chr['1'] = '21112';
759                 $chr['2'] = '12112';
760                 $chr['3'] = '22111';
761                 $chr['4'] = '11212';
762                 $chr['5'] = '21211';
763                 $chr['6'] = '12211';
764                 $chr['7'] = '11122';
765                 $chr['8'] = '21121';
766                 $chr['9'] = '12121';
767                 $chr['A'] = '11';
768                 $chr['Z'] = '21';
769                 if ($checksum) {
770                         // add checksum
771                         $code .= $this->checksum_s25($code);
772                 }
773                 if((strlen($code) % 2) != 0) {
774                         // add leading zero if code-length is odd
775                         $code = '0'.$code;
776                 }
777                 // add start and stop codes
778                 $code = 'AA'.strtolower($code).'ZA';
779                         
780                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
781                 $k = 0;
782                 $clen = strlen($code);
783                 for ($i = 0; $i < $clen; $i = ($i + 2)) {
784                         $char_bar = $code{$i};
785                         $char_space = $code{$i+1};
786                         if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) {
787                                 // invalid character
788                                 return false;
789                         }
790                         // create a bar-space sequence
791                         $seq = '';
792                         $chrlen = strlen($chr[$char_bar]);
793                         for ($s = 0; $s < $chrlen; $s++){
794                                 $seq .= $chr[$char_bar]{$s} . $chr[$char_space]{$s};
795                         }
796                         $seqlen = strlen($seq);
797                         for ($j = 0; $j < $seqlen; ++$j) {
798                                 if (($j % 2) == 0) {
799                                         $t = true; // bar
800                                 } else {
801                                         $t = false; // space
802                                 }
803                                 $w = $seq{$j};
804                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
805                                 $bararray['maxw'] += $w;
806                                 ++$k;
807                         }
808                 }
809                 return $bararray;
810         }
811         
812         /**
813          * C128 barcodes. 
814          * Very capable code, excellent density, high reliability; in very wide use world-wide
815          * @param string $code code to represent.
816          * @param string $type barcode type: A, B or C
817          * @return array barcode representation.
818          * @access protected
819          */
820         protected function barcode_c128($code, $type='B') {
821                 $chr = array(
822                         '212222', /* 00 */
823                         '222122', /* 01 */
824                         '222221', /* 02 */
825                         '121223', /* 03 */
826                         '121322', /* 04 */
827                         '131222', /* 05 */
828                         '122213', /* 06 */
829                         '122312', /* 07 */
830                         '132212', /* 08 */
831                         '221213', /* 09 */
832                         '221312', /* 10 */
833                         '231212', /* 11 */
834                         '112232', /* 12 */
835                         '122132', /* 13 */
836                         '122231', /* 14 */
837                         '113222', /* 15 */
838                         '123122', /* 16 */
839                         '123221', /* 17 */
840                         '223211', /* 18 */
841                         '221132', /* 19 */
842                         '221231', /* 20 */
843                         '213212', /* 21 */
844                         '223112', /* 22 */
845                         '312131', /* 23 */
846                         '311222', /* 24 */
847                         '321122', /* 25 */
848                         '321221', /* 26 */
849                         '312212', /* 27 */
850                         '322112', /* 28 */
851                         '322211', /* 29 */
852                         '212123', /* 30 */
853                         '212321', /* 31 */
854                         '232121', /* 32 */
855                         '111323', /* 33 */
856                         '131123', /* 34 */
857                         '131321', /* 35 */
858                         '112313', /* 36 */
859                         '132113', /* 37 */
860                         '132311', /* 38 */
861                         '211313', /* 39 */
862                         '231113', /* 40 */
863                         '231311', /* 41 */
864                         '112133', /* 42 */
865                         '112331', /* 43 */
866                         '132131', /* 44 */
867                         '113123', /* 45 */
868                         '113321', /* 46 */
869                         '133121', /* 47 */
870                         '313121', /* 48 */
871                         '211331', /* 49 */
872                         '231131', /* 50 */
873                         '213113', /* 51 */
874                         '213311', /* 52 */
875                         '213131', /* 53 */
876                         '311123', /* 54 */
877                         '311321', /* 55 */
878                         '331121', /* 56 */
879                         '312113', /* 57 */
880                         '312311', /* 58 */
881                         '332111', /* 59 */
882                         '314111', /* 60 */
883                         '221411', /* 61 */
884                         '431111', /* 62 */
885                         '111224', /* 63 */
886                         '111422', /* 64 */
887                         '121124', /* 65 */
888                         '121421', /* 66 */
889                         '141122', /* 67 */
890                         '141221', /* 68 */
891                         '112214', /* 69 */
892                         '112412', /* 70 */
893                         '122114', /* 71 */
894                         '122411', /* 72 */
895                         '142112', /* 73 */
896                         '142211', /* 74 */
897                         '241211', /* 75 */
898                         '221114', /* 76 */
899                         '413111', /* 77 */
900                         '241112', /* 78 */
901                         '134111', /* 79 */
902                         '111242', /* 80 */
903                         '121142', /* 81 */
904                         '121241', /* 82 */
905                         '114212', /* 83 */
906                         '124112', /* 84 */
907                         '124211', /* 85 */
908                         '411212', /* 86 */
909                         '421112', /* 87 */
910                         '421211', /* 88 */
911                         '212141', /* 89 */
912                         '214121', /* 90 */
913                         '412121', /* 91 */
914                         '111143', /* 92 */
915                         '111341', /* 93 */
916                         '131141', /* 94 */
917                         '114113', /* 95 */
918                         '114311', /* 96 */
919                         '411113', /* 97 */
920                         '411311', /* 98 */
921                         '113141', /* 99 */
922                         '114131', /* 100 */
923                         '311141', /* 101 */
924                         '411131', /* 102 */
925                         '211412', /* 103 START A */
926                         '211214', /* 104 START B  */
927                         '211232', /* 105 START C  */
928                         '233111', /* STOP */
929                         '200000'  /* END */
930                 );
931                 $keys = '';
932                 switch(strtoupper($type)) {
933                         case 'A': {
934                                 $startid = 103;
935                                 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
936                                 for ($i = 0; $i < 32; ++$i) {
937                                         $keys .= chr($i);
938                                 }
939                                 break;
940                         }
941                         case 'B': {
942                                 $startid = 104;
943                                 $keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127);
944                                 break;
945                         }
946                         case 'C': {
947                                 $startid = 105;
948                                 $keys = '';
949                                 if ((strlen($code) % 2) != 0) {
950                                         // The length of barcode value must be even ($code). You must pad the number with zeros
951                                         return false;
952                                 }
953                                 for ($i = 0; $i <= 99; ++$i) {
954                                         $keys .= chr($i);
955                                 }
956                                 $new_code = '';
957                                 $hclen = (strlen($code) / 2);
958                                 for ($i = 0; $i < $hclen; ++$i) {
959                                         $new_code .= chr(intval($code{(2 * $i)}.$code{(2 * $i + 1)}));
960                                 }
961                                 $code = $new_code;
962                                 break;
963                         }
964                         default: {
965                                 return false;
966                         }
967                 }
968                 // calculate check character
969                 $sum = $startid;
970                 $clen = strlen($code);
971                 for ($i = 0; $i < $clen; ++$i) {
972                         $sum +=  (strpos($keys, $code{$i}) * ($i+1));
973                 }
974                 $check = ($sum % 103);
975                 // add start, check and stop codes
976                 $code = chr($startid).$code.chr($check).chr(106).chr(107);
977                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
978                 $k = 0;
979                 $len = strlen($code);
980                 for ($i = 0; $i < $len; ++$i) {
981                         $ck = strpos($keys, $code{$i});
982                         if (($i == 0) OR ($i > ($len-4))) {
983                                 $char_num = ord($code{$i});
984                                 $seq = $chr[$char_num];
985                         } elseif(($ck >= 0) AND isset($chr[$ck])) {
986                                         $seq = $chr[$ck];
987                         } else {
988                                 // invalid character
989                                 return false;
990                         }
991                         for ($j = 0; $j < 6; ++$j) {
992                                 if (($j % 2) == 0) {
993                                         $t = true; // bar
994                                 } else {
995                                         $t = false; // space
996                                 }
997                                 $w = $seq{$j};
998                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
999                                 $bararray['maxw'] += $w;
1000                                 ++$k;
1001                         }
1002                 }
1003                 return $bararray;               
1004         }
1005         
1006         /**
1007          * EAN13 and UPC-A barcodes.
1008          * EAN13: European Article Numbering international retail product code
1009          * UPC-A: Universal product code seen on almost all retail products in the USA and Canada
1010          * UPC-E: Short version of UPC symbol
1011          * @param string $code code to represent.
1012          * @param string $len barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A
1013          * @return array barcode representation.
1014          * @access protected
1015          */
1016         protected function barcode_eanupc($code, $len=13) {
1017                 $upce = false;
1018                 if ($len == 6) {
1019                         $len = 12; // UPC-A
1020                         $upce = true; // UPC-E mode
1021                 }
1022                 $data_len = $len - 1;
1023                 //Padding
1024                 $code = str_pad($code, $data_len, '0', STR_PAD_LEFT);
1025                 $code_len = strlen($code);
1026                 // calculate check digit
1027                 $sum_a = 0;
1028                 for ($i = 1; $i < $data_len; $i+=2) {
1029                         $sum_a += $code{$i};
1030                 }
1031                 if ($len > 12) {
1032                         $sum_a *= 3;
1033                 }
1034                 $sum_b = 0;
1035                 for ($i = 0; $i < $data_len; $i+=2) {
1036                         $sum_b += ($code{$i});
1037                 }
1038                 if ($len < 13) {
1039                         $sum_b *= 3;
1040                 }
1041                 $r = ($sum_a + $sum_b) % 10;
1042                 if($r > 0) {
1043                         $r = (10 - $r);
1044                 }
1045                 if ($code_len == $data_len) {
1046                         // add check digit
1047                         $code .= $r;
1048                 } elseif ($r !== intval($code{$data_len})) {
1049                         // wrong checkdigit
1050                         return false;
1051                 }
1052                 if ($len == 12) {
1053                         // UPC-A
1054                         $code = '0'.$code;
1055                         ++$len;
1056                 }
1057                 if ($upce) {
1058                         // convert UPC-A to UPC-E
1059                         $tmp = substr($code, 4, 3);
1060                         if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) {
1061                                 // manufacturer code ends in 000, 100, or 200
1062                                 $upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1);
1063                         } else {
1064                                 $tmp = substr($code, 5, 2);
1065                                 if ($tmp == '00') {
1066                                         // manufacturer code ends in 00
1067                                         $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3';
1068                                 } else {
1069                                         $tmp = substr($code, 6, 1);
1070                                         if ($tmp == '0') {
1071                                                 // manufacturer code ends in 0
1072                                                 $upce_code = substr($code, 2, 4).substr($code, 11, 1).'4';
1073                                         } else {
1074                                                 // manufacturer code does not end in zero
1075                                                 $upce_code = substr($code, 2, 5).substr($code, 11, 1);
1076                                         }
1077                                 }
1078                         }
1079                 }
1080                 //Convert digits to bars
1081                 $codes = array(
1082                         'A'=>array( // left odd parity
1083                                 '0'=>'0001101',
1084                                 '1'=>'0011001',
1085                                 '2'=>'0010011',
1086                                 '3'=>'0111101',
1087                                 '4'=>'0100011',
1088                                 '5'=>'0110001',
1089                                 '6'=>'0101111',
1090                                 '7'=>'0111011',
1091                                 '8'=>'0110111',
1092                                 '9'=>'0001011'),
1093                         'B'=>array( // left even parity
1094                                 '0'=>'0100111',
1095                                 '1'=>'0110011',
1096                                 '2'=>'0011011',
1097                                 '3'=>'0100001',
1098                                 '4'=>'0011101',
1099                                 '5'=>'0111001',
1100                                 '6'=>'0000101',
1101                                 '7'=>'0010001',
1102                                 '8'=>'0001001',
1103                                 '9'=>'0010111'),
1104                         'C'=>array( // right
1105                                 '0'=>'1110010',
1106                                 '1'=>'1100110',
1107                                 '2'=>'1101100',
1108                                 '3'=>'1000010',
1109                                 '4'=>'1011100',
1110                                 '5'=>'1001110',
1111                                 '6'=>'1010000',
1112                                 '7'=>'1000100',
1113                                 '8'=>'1001000',
1114                                 '9'=>'1110100')
1115                 );
1116                 $parities = array(
1117                         '0'=>array('A','A','A','A','A','A'),
1118                         '1'=>array('A','A','B','A','B','B'),
1119                         '2'=>array('A','A','B','B','A','B'),
1120                         '3'=>array('A','A','B','B','B','A'),
1121                         '4'=>array('A','B','A','A','B','B'),
1122                         '5'=>array('A','B','B','A','A','B'),
1123                         '6'=>array('A','B','B','B','A','A'),
1124                         '7'=>array('A','B','A','B','A','B'),
1125                         '8'=>array('A','B','A','B','B','A'),
1126                         '9'=>array('A','B','B','A','B','A')
1127                 );
1128                 $upce_parities = array();
1129                 $upce_parities[0] = array(
1130                         '0'=>array('B','B','B','A','A','A'),
1131                         '1'=>array('B','B','A','B','A','A'),
1132                         '2'=>array('B','B','A','A','B','A'),
1133                         '3'=>array('B','B','A','A','A','B'),
1134                         '4'=>array('B','A','B','B','A','A'),
1135                         '5'=>array('B','A','A','B','B','A'),
1136                         '6'=>array('B','A','A','A','B','B'),
1137                         '7'=>array('B','A','B','A','B','A'),
1138                         '8'=>array('B','A','B','A','A','B'),
1139                         '9'=>array('B','A','A','B','A','B')
1140                 );
1141                 $upce_parities[1] = array(
1142                         '0'=>array('A','A','A','B','B','B'),
1143                         '1'=>array('A','A','B','A','B','B'),
1144                         '2'=>array('A','A','B','B','A','B'),
1145                         '3'=>array('A','A','B','B','B','A'),
1146                         '4'=>array('A','B','A','A','B','B'),
1147                         '5'=>array('A','B','B','A','A','B'),
1148                         '6'=>array('A','B','B','B','A','A'),
1149                         '7'=>array('A','B','A','B','A','B'),
1150                         '8'=>array('A','B','A','B','B','A'),
1151                         '9'=>array('A','B','B','A','B','A')
1152                 );
1153                 $k = 0;
1154                 $seq = '101'; // left guard bar
1155                 if ($upce) {
1156                         $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1157                         $p = $upce_parities[$code{1}][$r];
1158                         for ($i = 0; $i < 6; ++$i) {
1159                                 $seq .= $codes[$p[$i]][$upce_code{$i}];
1160                         }
1161                         $seq .= '010101'; // right guard bar
1162                 } else {
1163                         $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1164                         $half_len = ceil($len / 2);
1165                         if ($len == 8) {
1166                                 for ($i = 0; $i < $half_len; ++$i) {
1167                                         $seq .= $codes['A'][$code{$i}];
1168                                 }
1169                         } else {
1170                                 $p = $parities[$code{0}];
1171                                 for ($i = 1; $i < $half_len; ++$i) {
1172                                         $seq .= $codes[$p[$i-1]][$code{$i}];
1173                                 }
1174                         }
1175                         $seq .= '01010'; // center guard bar
1176                         for ($i = $half_len; $i < $len; ++$i) {
1177                                 $seq .= $codes['C'][$code{$i}];
1178                         }
1179                         $seq .= '101'; // right guard bar
1180                 }
1181                 $clen = strlen($seq);
1182                 $w = 0;
1183                 for ($i = 0; $i < $clen; ++$i) {
1184                         $w += 1;
1185                         if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq{$i} != $seq{($i+1)}))) {
1186                                 if ($seq{$i} == '1') {
1187                                         $t = true; // bar
1188                                 } else {
1189                                         $t = false; // space
1190                                 }
1191                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1192                                 $bararray['maxw'] += $w;
1193                                 ++$k;
1194                                 $w = 0;
1195                         }
1196                 }
1197                 return $bararray;
1198         }
1199         
1200         /**
1201          * UPC-Based Extentions
1202          * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers
1203          * 5-Digit Ext.: Used to mark suggested retail price of books
1204          * @param string $code code to represent.
1205          * @param string $len barcode type: 2 = 2-Digit, 5 = 5-Digit
1206          * @return array barcode representation.
1207          * @access protected
1208          */
1209         protected function barcode_eanext($code, $len=5) {
1210                 //Padding
1211                 $code = str_pad($code, $len, '0', STR_PAD_LEFT);
1212                 // calculate check digit
1213                 if ($len == 2) {
1214                         $r = $code % 4;
1215                 } elseif ($len == 5) {
1216                         $r = (3 * ($code{0} + $code{2} + $code{4})) + (9 * ($code{1} + $code{3}));
1217                         $r %= 10;
1218                 } else {
1219                         return false;
1220                 }
1221                 //Convert digits to bars
1222                 $codes = array(
1223                         'A'=>array( // left odd parity
1224                                 '0'=>'0001101',
1225                                 '1'=>'0011001',
1226                                 '2'=>'0010011',
1227                                 '3'=>'0111101',
1228                                 '4'=>'0100011',
1229                                 '5'=>'0110001',
1230                                 '6'=>'0101111',
1231                                 '7'=>'0111011',
1232                                 '8'=>'0110111',
1233                                 '9'=>'0001011'),
1234                         'B'=>array( // left even parity
1235                                 '0'=>'0100111',
1236                                 '1'=>'0110011',
1237                                 '2'=>'0011011',
1238                                 '3'=>'0100001',
1239                                 '4'=>'0011101',
1240                                 '5'=>'0111001',
1241                                 '6'=>'0000101',
1242                                 '7'=>'0010001',
1243                                 '8'=>'0001001',
1244                                 '9'=>'0010111')
1245                 );
1246                 $parities = array();
1247                 $parities[2] = array(
1248                         '0'=>array('A','A'),
1249                         '1'=>array('A','B'),
1250                         '2'=>array('B','A'),
1251                         '3'=>array('B','B')
1252                 );
1253                 $parities[5] = array(
1254                         '0'=>array('B','B','A','A','A'),
1255                         '1'=>array('B','A','B','A','A'),
1256                         '2'=>array('B','A','A','B','A'),
1257                         '3'=>array('B','A','A','A','B'),
1258                         '4'=>array('A','B','B','A','A'),
1259                         '5'=>array('A','A','B','B','A'),
1260                         '6'=>array('A','A','A','B','B'),
1261                         '7'=>array('A','B','A','B','A'),
1262                         '8'=>array('A','B','A','A','B'),
1263                         '9'=>array('A','A','B','A','B')
1264                 );      
1265                 $p = $parities[$len][$r];
1266                 $seq = '1011'; // left guard bar
1267                 $seq .= $codes[$p[0]][$code{0}];
1268                 for ($i = 1; $i < $len; ++$i) {
1269                         $seq .= '01'; // separator
1270                         $seq .= $codes[$p[$i]][$code{$i}];
1271                 }
1272                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1273                 return $this->binseq_to_array($seq, $bararray);
1274         }
1275         
1276         /**
1277          * POSTNET and PLANET barcodes.
1278          * Used by U.S. Postal Service for automated mail sorting
1279          * @param string $code zip code to represent. Must be a string containing a zip code of the form DDDDD or DDDDD-DDDD.
1280          * @param boolean $planet if true print the PLANET barcode, otherwise print POSTNET
1281          * @return array barcode representation.
1282          * @access protected
1283          */
1284         protected function barcode_postnet($code, $planet=false) {
1285                 // bar lenght
1286                 if ($planet) {
1287                         $barlen = Array(
1288                                 0 => Array(1,1,2,2,2),
1289                                 1 => Array(2,2,2,1,1),
1290                                 2 => Array(2,2,1,2,1),
1291                                 3 => Array(2,2,1,1,2),
1292                                 4 => Array(2,1,2,2,1),
1293                                 5 => Array(2,1,2,1,2),
1294                                 6 => Array(2,1,1,2,2),
1295                                 7 => Array(1,2,2,2,1),
1296                                 8 => Array(1,2,2,1,2),
1297                                 9 => Array(1,2,1,2,2)
1298                         );
1299                 } else {
1300                         $barlen = Array(
1301                                 0 => Array(2,2,1,1,1),
1302                                 1 => Array(1,1,1,2,2),
1303                                 2 => Array(1,1,2,1,2),
1304                                 3 => Array(1,1,2,2,1),
1305                                 4 => Array(1,2,1,1,2),
1306                                 5 => Array(1,2,1,2,1),
1307                                 6 => Array(1,2,2,1,1),
1308                                 7 => Array(2,1,1,1,2),
1309                                 8 => Array(2,1,1,2,1),
1310                                 9 => Array(2,1,2,1,1)
1311                         );
1312                 }
1313                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
1314                 $k = 0;
1315                 $code = str_replace('-', '', $code);
1316                 $code = str_replace(' ', '', $code);
1317                 $len = strlen($code);
1318                 // calculate checksum
1319                 $sum = 0;
1320                 for ($i = 0; $i < $len; ++$i) {
1321                         $sum += intval($code{$i});
1322                 }
1323                 $chkd = ($sum % 10);
1324                 if($chkd > 0) {
1325                         $chkd = (10 - $chkd);
1326                 }
1327                 $code .= $chkd;
1328                 $len = strlen($code);
1329                 // start bar
1330                 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1331                 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1332                 $bararray['maxw'] += 2;
1333                 for ($i = 0; $i < $len; ++$i) {
1334                         for ($j = 0; $j < 5; ++$j) {
1335                                 $h = $barlen[$code{$i}][$j];
1336                                 $p = floor(1 / $h);
1337                                 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1338                                 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1339                                 $bararray['maxw'] += 2;
1340                         }
1341                 }
1342                 // end bar
1343                 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1344                 $bararray['maxw'] += 1;
1345                 return $bararray;
1346         }
1347         
1348         /**
1349          * RMS4CC - CBC - KIX
1350          * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index)
1351          * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service.
1352          * @param string $code code to print
1353          * @param boolean $kix if true prints the KIX variation (doesn't use the start and end symbols, and the checksum) - in this case the house number must be sufficed with an X and placed at the end of the code.
1354          * @return array barcode representation.
1355          * @access protected
1356          */
1357         protected function barcode_rms4cc($code, $kix=false) {
1358                 $notkix = !$kix;
1359                 // bar mode
1360                 // 1 = pos 1, length 2
1361                 // 2 = pos 1, length 3
1362                 // 3 = pos 2, length 1
1363                 // 4 = pos 2, length 2
1364                 $barmode = array(
1365                         '0' => array(3,3,2,2),
1366                         '1' => array(3,4,1,2),
1367                         '2' => array(3,4,2,1),
1368                         '3' => array(4,3,1,2),
1369                         '4' => array(4,3,2,1),
1370                         '5' => array(4,4,1,1),
1371                         '6' => array(3,1,4,2),
1372                         '7' => array(3,2,3,2),
1373                         '8' => array(3,2,4,1),
1374                         '9' => array(4,1,3,2),
1375                         'A' => array(4,1,4,1),
1376                         'B' => array(4,2,3,1),
1377                         'C' => array(3,1,2,4),
1378                         'D' => array(3,2,1,4),
1379                         'E' => array(3,2,2,3),
1380                         'F' => array(4,1,1,4),
1381                         'G' => array(4,1,2,3),
1382                         'H' => array(4,2,1,3),
1383                         'I' => array(1,3,4,2),
1384                         'J' => array(1,4,3,2),
1385                         'K' => array(1,4,4,1),
1386                         'L' => array(2,3,3,2),
1387                         'M' => array(2,3,4,1),
1388                         'N' => array(2,4,3,1),
1389                         'O' => array(1,3,2,4),
1390                         'P' => array(1,4,1,4),
1391                         'Q' => array(1,4,2,3),
1392                         'R' => array(2,3,1,4),
1393                         'S' => array(2,3,2,3),
1394                         'T' => array(2,4,1,3),
1395                         'U' => array(1,1,4,4),
1396                         'V' => array(1,2,3,4),
1397                         'W' => array(1,2,4,3),
1398                         'X' => array(2,1,3,4),
1399                         'Y' => array(2,1,4,3),
1400                         'Z' => array(2,2,3,3)           
1401                 );
1402                 $code = strtoupper($code);
1403                 $len = strlen($code);
1404                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
1405                 if ($notkix) {
1406                         // table for checksum calculation (row,col)
1407                         $checktable = array(
1408                                 '0' => array(1,1),
1409                                 '1' => array(1,2),
1410                                 '2' => array(1,3),
1411                                 '3' => array(1,4),
1412                                 '4' => array(1,5),
1413                                 '5' => array(1,0),
1414                                 '6' => array(2,1),
1415                                 '7' => array(2,2),
1416                                 '8' => array(2,3),
1417                                 '9' => array(2,4),
1418                                 'A' => array(2,5),
1419                                 'B' => array(2,0),
1420                                 'C' => array(3,1),
1421                                 'D' => array(3,2),
1422                                 'E' => array(3,3),
1423                                 'F' => array(3,4),
1424                                 'G' => array(3,5),
1425                                 'H' => array(3,0),
1426                                 'I' => array(4,1),
1427                                 'J' => array(4,2),
1428                                 'K' => array(4,3),
1429                                 'L' => array(4,4),
1430                                 'M' => array(4,5),
1431                                 'N' => array(4,0),
1432                                 'O' => array(5,1),
1433                                 'P' => array(5,2),
1434                                 'Q' => array(5,3),
1435                                 'R' => array(5,4),
1436                                 'S' => array(5,5),
1437                                 'T' => array(5,0),
1438                                 'U' => array(0,1),
1439                                 'V' => array(0,2),
1440                                 'W' => array(0,3),
1441                                 'X' => array(0,4),
1442                                 'Y' => array(0,5),
1443                                 'Z' => array(0,0)
1444                         );
1445                         $row = 0;
1446                         $col = 0;
1447                         for ($i = 0; $i < $len; ++$i) {
1448                                 $row += $checktable[$code{$i}][0];
1449                                 $col += $checktable[$code{$i}][1];
1450                         }
1451                         $row %= 6;
1452                         $col %= 6;
1453                         $chk = array_keys($checktable, array($row,$col));
1454                         $code .= $chk[0];
1455                         ++$len;
1456                 }
1457                 $k = 0;
1458                 if ($notkix) {
1459                         // start bar
1460                         $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0);
1461                         $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1462                         $bararray['maxw'] += 2;
1463                 }
1464                 for ($i = 0; $i < $len; ++$i) {
1465                         for ($j = 0; $j < 4; ++$j) {
1466                                 switch ($barmode[$code{$i}][$j]) {
1467                                         case 1: {
1468                                                 $p = 0;
1469                                                 $h = 2;
1470                                                 break;
1471                                         }
1472                                         case 2: {
1473                                                 $p = 0;
1474                                                 $h = 3;
1475                                                 break;
1476                                         }
1477                                         case 3: {
1478                                                 $p = 1;
1479                                                 $h = 1;
1480                                                 break;
1481                                         }
1482                                         case 4: {
1483                                                 $p = 1;
1484                                                 $h = 2;
1485                                                 break;
1486                                         }
1487                                 }
1488                                 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1489                                 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1490                                 $bararray['maxw'] += 2;
1491                         }
1492                 }
1493                 if ($notkix) {
1494                         // stop bar
1495                         $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0);
1496                         $bararray['maxw'] += 1;
1497                 }
1498                 return $bararray;
1499         }
1500         
1501         /**
1502          * CODABAR barcodes.
1503          * Older code often used in library systems, sometimes in blood banks
1504          * @param string $code code to represent.
1505          * @return array barcode representation.
1506          * @access protected
1507          */
1508         protected function barcode_codabar($code) {
1509                 $chr = array(
1510                         '0' => '11111221',
1511                         '1' => '11112211',
1512                         '2' => '11121121',
1513                         '3' => '22111111',
1514                         '4' => '11211211',
1515                         '5' => '21111211',
1516                         '6' => '12111121',
1517                         '7' => '12112111',
1518                         '8' => '12211111',
1519                         '9' => '21121111',
1520                         '-' => '11122111',
1521                         '$' => '11221111',
1522                         ':' => '21112121',
1523                         '/' => '21211121',
1524                         '.' => '21212111',
1525                         '+' => '11222221',
1526                         'A' => '11221211',
1527                         'B' => '12121121',
1528                         'C' => '11121221',
1529                         'D' => '11122211'
1530                 );
1531                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1532                 $k = 0;
1533                 $w = 0;
1534                 $seq = '';
1535                 $code = 'A'.strtoupper($code).'A';
1536                 $len = strlen($code);
1537                 for ($i = 0; $i < $len; ++$i) {
1538                         if (!isset($chr[$code{$i}])) {
1539                                 return false;
1540                         }
1541                         $seq = $chr[$code{$i}];
1542                         for ($j = 0; $j < 8; ++$j) {
1543                                 if (($j % 2) == 0) {
1544                                         $t = true; // bar
1545                                 } else {
1546                                         $t = false; // space
1547                                 }
1548                                 $w = $seq{$j};
1549                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1550                                 $bararray['maxw'] += $w;
1551                                 ++$k;
1552                         }
1553                 }
1554                 return $bararray;
1555         }
1556         
1557         /**
1558          * CODE11 barcodes.
1559          * Used primarily for labeling telecommunications equipment
1560          * @param string $code code to represent.
1561          * @return array barcode representation.
1562          * @access protected
1563          */
1564         protected function barcode_code11($code) {
1565                 $chr = array(
1566                         '0' => '111121',
1567                         '1' => '211121',
1568                         '2' => '121121',
1569                         '3' => '221111',
1570                         '4' => '112121',
1571                         '5' => '212111',
1572                         '6' => '122111',
1573                         '7' => '111221',
1574                         '8' => '211211',
1575                         '9' => '211111',
1576                         '-' => '112111',
1577                         'S' => '112211'
1578                 );
1579                 
1580                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1581                 $k = 0;
1582                 $w = 0;
1583                 $seq = '';
1584                 $len = strlen($code);
1585                 // calculate check digit C
1586                 $p = 1;
1587                 $check = 0;
1588                 for ($i = ($len - 1); $i >= 0; --$i) {
1589                         $digit = $code{$i};
1590                         if ($digit == '-') {
1591                                 $dval = 10;
1592                         } else {
1593                                 $dval = intval($digit);
1594                         }
1595                         $check += ($dval * $p);
1596                         ++$p;
1597                         if ($p > 10) {
1598                                 $p = 1;
1599                         }
1600                 }
1601                 $check %= 11;
1602                 if ($check == 10) {
1603                         $check = '-';
1604                 } 
1605                 $code .= $check;
1606                 if ($len > 10) {
1607                         // calculate check digit K
1608                         $p = 1;
1609                         $check = 0;
1610                         for ($i = $len; $i >= 0; --$i) {
1611                                 $digit = $code{$i};
1612                                 if ($digit == '-') {
1613                                         $dval = 10;
1614                                 } else {
1615                                         $dval = intval($digit);
1616                                 }
1617                                 $check += ($dval * $p);
1618                                 ++$p;
1619                                 if ($p > 9) {
1620                                         $p = 1;
1621                                 }
1622                         }
1623                         $check %= 11;
1624                         $code .= $check;
1625                         ++$len;
1626                 }
1627                 $code = 'S'.$code.'S';
1628                 $len += 3;
1629                 for ($i = 0; $i < $len; ++$i) {
1630                         if (!isset($chr[$code{$i}])) {
1631                                 return false;
1632                         }
1633                         $seq = $chr[$code{$i}];
1634                         for ($j = 0; $j < 6; ++$j) {
1635                                 if (($j % 2) == 0) {
1636                                         $t = true; // bar
1637                                 } else {
1638                                         $t = false; // space
1639                                 }
1640                                 $w = $seq{$j};
1641                                 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0);
1642                                 $bararray['maxw'] += $w;
1643                                 ++$k;
1644                         }
1645                 }
1646                 return $bararray;
1647         }
1648         
1649         /**
1650          * Pharmacode
1651          * Contains digits (0 to 9)
1652          * @param string $code code to represent.
1653          * @return array barcode representation.
1654          * @access protected
1655          */
1656         protected function barcode_pharmacode($code) {
1657                 $seq = '';
1658                 $code = intval($code);
1659                 while ($code > 0) {
1660                         if (($code % 2) == 0) {
1661                                 $seq .= '11100';
1662                                 $code -= 2;
1663                         } else {
1664                                 $seq .= '100';
1665                                 $code -= 1;
1666                         }
1667                         $code /= 2;
1668                 }
1669                 $seq = substr($seq, 0, -2);
1670                 $seq = strrev($seq);
1671                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array());
1672                 return $this->binseq_to_array($seq, $bararray);
1673         }
1674         
1675         /**
1676          * Pharmacode two-track
1677          * Contains digits (0 to 9)
1678          * @param string $code code to represent.
1679          * @return array barcode representation.
1680          * @access protected
1681          */
1682         protected function barcode_pharmacode2t($code) {
1683                 $seq = '';
1684                 $code = intval($code);
1685                 do {
1686                         switch ($code % 3) {
1687                                 case 0: {
1688                                         $seq .= '3';
1689                                         $code = ($code - 3) / 3;
1690                                         break;
1691                                 }
1692                                 case 1: {
1693                                         $seq .= '1';
1694                                         $code = ($code - 1) / 3;
1695                                         break;
1696                                 }
1697                                 case 2: {
1698                                         $seq .= '2';
1699                                         $code = ($code - 2) / 3;
1700                                         break;
1701                                 }
1702                         }
1703                 } while($code != 0);
1704                 $seq = strrev($seq);
1705                 $k = 0;
1706                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array());
1707                 $len = strlen($seq);
1708                 for ($i = 0; $i < $len; ++$i) {
1709                         switch ($seq{$i}) {
1710                                 case '1': {
1711                                         $p = 1;
1712                                         $h = 1;
1713                                         break;
1714                                 }
1715                                 case '2': {
1716                                         $p = 0;
1717                                         $h = 1;
1718                                         break;
1719                                 }
1720                                 case '3': {
1721                                         $p = 0;
1722                                         $h = 2;
1723                                         break;
1724                                 }
1725                         }
1726                         $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1727                         $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1728                         $bararray['maxw'] += 2;
1729                 }
1730                 unset($bararray['bcode'][($k - 1)]);
1731                 --$bararray['maxw'];
1732                 return $bararray;
1733         }
1734         
1735         
1736         /**
1737          * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200
1738          * (requires PHP bcmath extension) 
1739          * Intelligent Mail barcode is a 65-bar code for use on mail in the United States.
1740          * 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>
1741          * @param string $code code to print, separate the ZIP (routing code) from the rest using a minus char '-' (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode)
1742          * @return array barcode representation.
1743          * @access protected
1744          */
1745         protected function barcode_imb($code) {
1746                 $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);
1747                 $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);
1748                 $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);
1749                 $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);
1750                 $code_arr = explode('-', $code);
1751                 $tracking_number = $code_arr[0];
1752                 if (isset($code_arr[1])) {
1753                         $routing_code = $code_arr[1];
1754                 } else {
1755                         $routing_code = '';
1756                 }
1757                 // Conversion of Routing Code
1758                 switch (strlen($routing_code)) {
1759                         case 0: {
1760                                 $binary_code = 0;
1761                                 break;
1762                         }
1763                         case 5: {
1764                                 $binary_code = bcadd($routing_code, '1');
1765                                 break;
1766                         }
1767                         case 9: {
1768                                 $binary_code = bcadd($routing_code, '100001');
1769                                 break;
1770                         }
1771                         case 11: {
1772                                 $binary_code = bcadd($routing_code, '1000100001');
1773                                 break;
1774                         }
1775                         default: {
1776                                 return false;
1777                                 break;
1778                         }
1779                 }
1780                 $binary_code = bcmul($binary_code, 10);
1781                 $binary_code = bcadd($binary_code, $tracking_number{0});
1782                 $binary_code = bcmul($binary_code, 5);
1783                 $binary_code = bcadd($binary_code, $tracking_number{1});
1784                 $binary_code .= substr($tracking_number, 2, 18);
1785                 // convert to hexadecimal
1786                 $binary_code = $this->dec_to_hex($binary_code);
1787                 // pad to get 13 bytes
1788                 $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT);
1789                 // convert string to array of bytes
1790                 $binary_code_arr = chunk_split($binary_code, 2, "\r");
1791                 $binary_code_arr = substr($binary_code_arr, 0, -1);
1792                 $binary_code_arr = explode("\r", $binary_code_arr);
1793                 // calculate frame check sequence
1794                 $fcs = $this->imb_crc11fcs($binary_code_arr);
1795                 // exclude first 2 bits from first byte
1796                 $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2));
1797                 $binary_code_102bit = $first_byte.substr($binary_code, 2);
1798                 // convert binary data to codewords
1799                 $codewords = array();
1800                 $data = $this->hex_to_dec($binary_code_102bit);
1801                 $codewords[0] = bcmod($data, 636) * 2;
1802                 $data = bcdiv($data, 636);
1803                 for ($i = 1; $i < 9; ++$i) {
1804                         $codewords[$i] = bcmod($data, 1365);
1805                         $data = bcdiv($data, 1365);
1806                 }
1807                 $codewords[9] = $data;
1808                 if (($fcs >> 10) == 1) {
1809                         $codewords[9] += 659;
1810                 }
1811                 // generate lookup tables
1812                 $table2of13 = $this->imb_tables(2, 78);
1813                 $table5of13 = $this->imb_tables(5, 1287);
1814                 // convert codewords to characters
1815                 $characters = array();
1816                 $bitmask = 512;
1817                 foreach($codewords as $k => $val) {
1818                         if ($val <= 1286) {
1819                                 $chrcode = $table5of13[$val];
1820                         } else {
1821                                 $chrcode = $table2of13[($val - 1287)];
1822                         }
1823                         if (($fcs & $bitmask) > 0) {
1824                                 // bitwise invert
1825                                 $chrcode = ((~$chrcode) & 8191);
1826                         }
1827                         $characters[] = $chrcode;
1828                         $bitmask /= 2;
1829                 }
1830                 $characters = array_reverse($characters);
1831                 // build bars
1832                 $k = 0;
1833                 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array());
1834                 for ($i = 0; $i < 65; ++$i) {
1835                         $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0);
1836                         $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0);
1837                         if ($asc AND $dsc) {
1838                                 // full bar (F)
1839                                 $p = 0;
1840                                 $h = 3;
1841                         } elseif ($asc) {
1842                                 // ascender (A)
1843                                 $p = 0;
1844                                 $h = 2;
1845                         } elseif ($dsc) {
1846                                 // descender (D)
1847                                 $p = 1;
1848                                 $h = 2;
1849                         } else {
1850                                 // tracker (T)
1851                                 $p = 1;
1852                                 $h = 1;
1853                         }
1854                         $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p);
1855                         $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0);
1856                         $bararray['maxw'] += 2;
1857                 }
1858                 unset($bararray['bcode'][($k - 1)]);
1859                 --$bararray['maxw'];
1860                 return $bararray;
1861         }
1862         
1863         /**
1864          * Convert large integer number to hexadecimal representation.
1865          * (requires PHP bcmath extension) 
1866          * @param string $number number to convert specified as a string
1867          * @return string hexadecimal representation
1868          */
1869         public function dec_to_hex($number) {
1870                 $i = 0;
1871                 $hex = array();
1872                 if($number == 0) {
1873                         return '00';
1874                 }
1875                 while($number > 0) {
1876                         if($number == 0) {
1877                                 array_push($hex, '0');
1878                         } else {
1879                                 array_push($hex, strtoupper(dechex(bcmod($number, '16'))));
1880                                 $number = bcdiv($number, '16', 0);
1881                         }
1882                 }
1883                 $hex = array_reverse($hex);
1884                 return implode($hex);
1885         }
1886         
1887         /**
1888          * Convert large hexadecimal number to decimal representation (string).
1889          * (requires PHP bcmath extension) 
1890          * @param string $hex hexadecimal number to convert specified as a string
1891          * @return string hexadecimal representation
1892          */
1893         public function hex_to_dec($hex) {
1894                 $dec = 0;
1895                 $bitval = 1;
1896                 $len = strlen($hex);
1897                 for($pos = ($len - 1); $pos >= 0; --$pos) {
1898                         $dec = bcadd($dec, bcmul(hexdec($hex{$pos}), $bitval));
1899                         $bitval = bcmul($bitval, 16);
1900                 }
1901                 return $dec;
1902         }       
1903         
1904         /**
1905          * Intelligent Mail Barcode calculation of Frame Check Sequence
1906          * @param string $code_arr array of hexadecimal values (13 bytes holding 102 bits right justified).
1907          * @return int 11 bit Frame Check Sequence as integer (decimal base)
1908          * @access protected
1909          */
1910         protected function imb_crc11fcs($code_arr) {
1911                 $genpoly = 0x0F35; // generator polynomial
1912                 $fcs = 0x07FF; // Frame Check Sequence
1913                 // do most significant byte skipping the 2 most significant bits
1914                 $data = hexdec($code_arr[0]) << 5;
1915                 for ($bit = 2; $bit < 8; ++$bit) {
1916                         if (($fcs ^ $data) & 0x400) {
1917                                 $fcs = ($fcs << 1) ^ $genpoly;
1918                         } else {
1919                                 $fcs = ($fcs << 1);
1920                         }
1921                         $fcs &= 0x7FF;
1922                         $data <<= 1;
1923                 }
1924                 // do rest of bytes
1925                 for ($byte = 1; $byte < 13; ++$byte) {
1926                         $data = hexdec($code_arr[$byte]) << 3;
1927                         for ($bit = 0; $bit < 8; ++$bit) {
1928                                 if (($fcs ^ $data) & 0x400) {
1929                                         $fcs = ($fcs << 1) ^ $genpoly;
1930                                 } else {
1931                                         $fcs = ($fcs << 1);
1932                                 }
1933                                 $fcs &= 0x7FF;
1934                                 $data <<= 1;
1935                         }
1936                 }
1937                 return $fcs;            
1938         }
1939         
1940         /**
1941          * Reverse unsigned short value
1942          * @param int $num value to reversr
1943          * @return int reversed value
1944          * @access protected
1945          */
1946         protected function imb_reverse_us($num) {
1947                 $rev = 0;
1948                 for ($i = 0; $i < 16; ++$i) {
1949                         $rev <<= 1;
1950                         $rev |= ($num & 1);
1951                         $num >>= 1;
1952                 }
1953                 return $rev;
1954         }
1955         
1956         /**
1957          * generate Nof13 tables used for Intelligent Mail Barcode
1958          * @param int $n is the type of table: 2 for 2of13 table, 5 for 5of13table
1959          * @param int $size size of table (78 for n=2 and 1287 for n=5)
1960          * @return array requested table
1961          * @access protected
1962          */
1963         protected function imb_tables($n, $size) {
1964                 $table = array();
1965                 $lli = 0; // LUT lower index
1966                 $lui = $size - 1; // LUT upper index
1967                 for ($count = 0; $count < 8192; ++$count) {
1968                         $bit_count = 0;
1969                         for ($bit_index = 0; $bit_index < 13; ++$bit_index) {
1970                                 $bit_count += intval(($count & (1 << $bit_index)) != 0);
1971                         }
1972                         // if we don't have the right number of bits on, go on to the next value
1973                         if ($bit_count == $n) {
1974                                 $reverse = ($this->imb_reverse_us($count) >> 3);
1975                                 // if the reverse is less than count, we have already visited this pair before
1976                                 if ($reverse >= $count) {
1977                                         // If count is symmetric, place it at the first free slot from the end of the list.
1978                                         // 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
1979                                         if ($reverse == $count) {
1980                                                 $table[$lui] = $count;
1981                                                 --$lui;
1982                                         } else {
1983                                                 $table[$lli] = $count;
1984                                                 ++$lli;
1985                                                 $table[$lli] = $reverse;
1986                                                 ++$lli;
1987                                         }
1988                                 }
1989                         }
1990                 }
1991                 return $table;
1992         }
1993         
1994 } // end of class
1995
1996 //============================================================+
1997 // END OF FILE                                                 
1998 //============================================================+
1999 ?>