1<?php
2
3namespace Mpdf\Barcode;
4
5use Mpdf\Utils\UtfString;
6
7/**
8 * C128 barcodes.
9 * Very capable code, excellent density, high reliability; in very wide use world-wide
10 */
11class Code128 extends \Mpdf\Barcode\AbstractBarcode implements \Mpdf\Barcode\BarcodeInterface
12{
13
14	/**
15	 * @param string $code
16	 * @param string $type
17	 * @param bool $ean
18	 */
19	public function __construct($code, $type = 'B', $ean = false, $quiet_zone_left = null, $quiet_zone_right = null)
20	{
21		$this->init($code, $type, $ean);
22
23		$this->data['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
24		$this->data['nom-H'] = 10;  // Nominal value for Height of Full bar in mm (non-spec.)
25		$this->data['lightmL'] = ($quiet_zone_left !== null ? $quiet_zone_left : 10); // LEFT light margin =  x X-dim (spec.)
26		$this->data['lightmR'] = ($quiet_zone_right !== null ? $quiet_zone_right : 10); // RIGHT light margin =  x X-dim (spec.)
27		$this->data['lightTB'] = 0; // TOP/BOTTOM light margin =  x X-dim (non-spec.)
28	}
29
30	/**
31	 * @param string $code
32	 * @param string $type
33	 * @param bool $ean
34	 */
35	protected function init($code, $type, $ean)
36	{
37		$code = UtfString::strcode2utf($code); // mPDF 5.7.1 Allows e.g. <barcode code="5432&#013;1068" type="C128A" />
38
39		$chr = [
40			'212222', /* 00 */
41			'222122', /* 01 */
42			'222221', /* 02 */
43			'121223', /* 03 */
44			'121322', /* 04 */
45			'131222', /* 05 */
46			'122213', /* 06 */
47			'122312', /* 07 */
48			'132212', /* 08 */
49			'221213', /* 09 */
50			'221312', /* 10 */
51			'231212', /* 11 */
52			'112232', /* 12 */
53			'122132', /* 13 */
54			'122231', /* 14 */
55			'113222', /* 15 */
56			'123122', /* 16 */
57			'123221', /* 17 */
58			'223211', /* 18 */
59			'221132', /* 19 */
60			'221231', /* 20 */
61			'213212', /* 21 */
62			'223112', /* 22 */
63			'312131', /* 23 */
64			'311222', /* 24 */
65			'321122', /* 25 */
66			'321221', /* 26 */
67			'312212', /* 27 */
68			'322112', /* 28 */
69			'322211', /* 29 */
70			'212123', /* 30 */
71			'212321', /* 31 */
72			'232121', /* 32 */
73			'111323', /* 33 */
74			'131123', /* 34 */
75			'131321', /* 35 */
76			'112313', /* 36 */
77			'132113', /* 37 */
78			'132311', /* 38 */
79			'211313', /* 39 */
80			'231113', /* 40 */
81			'231311', /* 41 */
82			'112133', /* 42 */
83			'112331', /* 43 */
84			'132131', /* 44 */
85			'113123', /* 45 */
86			'113321', /* 46 */
87			'133121', /* 47 */
88			'313121', /* 48 */
89			'211331', /* 49 */
90			'231131', /* 50 */
91			'213113', /* 51 */
92			'213311', /* 52 */
93			'213131', /* 53 */
94			'311123', /* 54 */
95			'311321', /* 55 */
96			'331121', /* 56 */
97			'312113', /* 57 */
98			'312311', /* 58 */
99			'332111', /* 59 */
100			'314111', /* 60 */
101			'221411', /* 61 */
102			'431111', /* 62 */
103			'111224', /* 63 */
104			'111422', /* 64 */
105			'121124', /* 65 */
106			'121421', /* 66 */
107			'141122', /* 67 */
108			'141221', /* 68 */
109			'112214', /* 69 */
110			'112412', /* 70 */
111			'122114', /* 71 */
112			'122411', /* 72 */
113			'142112', /* 73 */
114			'142211', /* 74 */
115			'241211', /* 75 */
116			'221114', /* 76 */
117			'413111', /* 77 */
118			'241112', /* 78 */
119			'134111', /* 79 */
120			'111242', /* 80 */
121			'121142', /* 81 */
122			'121241', /* 82 */
123			'114212', /* 83 */
124			'124112', /* 84 */
125			'124211', /* 85 */
126			'411212', /* 86 */
127			'421112', /* 87 */
128			'421211', /* 88 */
129			'212141', /* 89 */
130			'214121', /* 90 */
131			'412121', /* 91 */
132			'111143', /* 92 */
133			'111341', /* 93 */
134			'131141', /* 94 */
135			'114113', /* 95 */
136			'114311', /* 96 */
137			'411113', /* 97 */
138			'411311', /* 98 */
139			'113141', /* 99 */
140			'114131', /* 100 */
141			'311141', /* 101 */
142			'411131', /* 102 */
143			'211412', /* 103 START A */
144			'211214', /* 104 START B  */
145			'211232', /* 105 START C  */
146			'233111', /* STOP */
147			'200000' /* END */
148		];
149
150		switch (strtoupper($type)) {
151
152			case 'RAW':
153
154				$newCode='';
155				$startid = false;
156
157				foreach (explode(" ", $code) as $v) {
158
159					if (is_numeric($v) && round($v, 0) == $v) {
160
161						if ($v>=0 && $v<=105) {
162							if ($startid===false) {
163								$startid=$v;
164							} else {
165								$newCode.=chr($v);
166							}
167						} else {
168							throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE128RAW barcode value "%s". 0-105 needed', $code));
169						}
170
171					} else {
172						//double spaces generates empty $v any other is not allowed
173						if ($v!='') {
174							throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE128RAW barcode value "%s". 0-105 needed', $code));
175						}
176					}
177				}
178
179				if ($startid < 103 || $startid > 105) {
180					throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE128RAW startid value "%s". Must be 103, 104 or 105 (for A, B or C)', $startid));
181				}
182
183				$keys='';
184
185				for ($i = 0; $i <= 105; ++$i) {
186					$keys .= chr($i);
187				}
188
189				$code=$newCode;
190
191				break;
192
193			case 'A':
194
195				$startid = 103;
196				$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
197
198				for ($i = 0; $i < 32; ++$i) {
199					$keys .= chr($i);
200				}
201
202				break;
203
204			case 'B':
205
206				$startid = 104;
207
208				$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' . chr(127);
209
210				break;
211
212			case 'C':
213
214				$startid = 105;
215				$keys = '';
216
217				if ((strlen($code) % 2) != 0) {
218					// The length of barcode value must be even ($code). You must pad the number with zeros
219					throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128C barcode value');
220				}
221
222				for ($i = 0; $i <= 99; ++$i) {
223					$keys .= chr($i);
224				}
225
226				$newCode = '';
227				$hclen = (strlen($code) / 2);
228
229				for ($i = 0; $i < $hclen; ++$i) {
230					if ($code[2 * $i]<"0" || $code[2 * $i]>"9" || $code[2 * $i + 1]<"0" || $code[2 * $i + 1]>"9") {
231						throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128C barcode value "%s"', $code[$i], $code));
232					}
233					$newCode .= chr((int) ($code[2 * $i] . $code[2 * $i + 1]));
234				}
235
236				$code = $newCode;
237
238				break;
239
240			default:
241				throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE128 barcode type "%s"', $type));
242		}
243
244		// calculate check character
245		$sum = $startid;
246
247		// Add FNC 1 - which identifies it as EAN-128
248		if ($ean) {
249			$code = chr(102) . $code;
250		}
251
252		$clen = strlen($code);
253		for ($i = 0; $i < $clen; ++$i) {
254			if ($ean && $i == 0) {
255				$sum += 102;
256			} else {
257				if (strpos($keys, $code[$i]) === false) {
258					throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128%s barcode value "%s"', $code[$i], $type, $code));
259				}
260				$sum += (strpos($keys, $code[$i]) * ($i + 1));
261			}
262		}
263
264		$check = ($sum % 103);
265		$checkdigit = $check;
266
267		// add start, check and stop codes
268		$code = chr($startid) . $code . chr($check) . chr(106) . chr(107);
269		$bararray = ['code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => []];
270		$k = 0;
271		$len = strlen($code);
272
273		for ($i = 0; $i < $len; ++$i) {
274
275			$ck = strpos($keys, $code[$i]);
276			if (($i == 0) || ($ean && $i == 1) | ($i > ($len - 4))) {
277				$char_num = ord($code[$i]);
278				$seq = $chr[$char_num];
279			} elseif (($ck >= 0) && isset($chr[$ck])) {
280				$seq = $chr[$ck];
281			} else {
282				// invalid character
283				throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128C barcode value "%s"', $code[$i], $code));
284			}
285
286			for ($j = 0; $j < 6; ++$j) {
287				if (($j % 2) == 0) {
288					$t = true; // bar
289				} else {
290					$t = false; // space
291				}
292				$w = $seq[$j];
293				$bararray['bcode'][$k] = ['t' => $t, 'w' => $w, 'h' => 1, 'p' => 0];
294				$bararray['maxw'] += $w;
295				++$k;
296			}
297		}
298
299		$bararray['checkdigit'] = $checkdigit;
300		$this->data = $bararray;
301	}
302
303	public function getType()
304	{
305		return 'CODE128';
306	}
307
308}
309