Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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