Subversion Repositories Applications.annuaire

Rev

Details | Last modification | View Log | RSS feed

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