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