OSDN Git Service

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