Subversion Repositories cheapmusic

Rev

Details | Last modification | View Log | RSS feed

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