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)
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'] = 10; // LEFT light margin =  x X-dim (spec.)
26		$this->data['lightmR'] = 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			case 'RAW':
152				$newCode='';
153				$startid = false;
154				foreach (explode(" ", $code) as $v) {
155					if (is_numeric($v) && round($v, 0) == $v) {
156						if ($v>=0 && $v<=105) {
157							if ($startid===false) {
158								$startid=$v;
159							} else {
160								$newCode.=chr($v);
161							}
162						} else {
163							throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128RAW barcode value. 0-105 needed');
164						}
165					} else {
166						//double spaces generates empty $v any other is not allowed
167						if ($v!='') {
168							throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128RAW barcode value. 0-105 needed');
169						}
170					}
171				}
172				if ($startid<103 || $startid>105) {
173					throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128RAW startid value. Must 103,104 or 105 (for A,B or C)');
174				}
175				$keys='';
176				for ($i = 0; $i <= 105; ++$i) {
177					$keys .= chr($i);
178				}
179				$code=$newCode;
180				break;
181			case 'A':
182				$startid = 103;
183				$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_';
184				for ($i = 0; $i < 32; ++$i) {
185					$keys .= chr($i);
186				}
187				break;
188			case 'B':
189				$startid = 104;
190				$keys = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' . chr(127);
191				break;
192			case 'C':
193				$startid = 105;
194				$keys = '';
195				if ((strlen($code) % 2) != 0) {
196					// The length of barcode value must be even ($code). You must pad the number with zeros
197					throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128C barcode value');
198				}
199				for ($i = 0; $i <= 99; ++$i) {
200					$keys .= chr($i);
201				}
202				$newCode = '';
203				$hclen = (strlen($code) / 2);
204				for ($i = 0; $i < $hclen; ++$i) {
205					if ($code[2 * $i]<"0" || $code[2 * $i]>"9" || $code[2 * $i + 1]<"0" || $code[2 * $i + 1]>"9") {
206						throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128C barcode value', $code[$i]));
207					}
208					$newCode .= chr((int) ($code[2 * $i] . $code[2 * $i + 1]));
209				}
210				$code = $newCode;
211				break;
212			default:
213				throw new \Mpdf\Barcode\BarcodeException('Invalid CODE128 barcode type');
214		}
215
216		// calculate check character
217		$sum = $startid;
218
219		// Add FNC 1 - which identifies it as EAN-128
220		if ($ean) {
221			$code = chr(102) . $code;
222		}
223		$clen = strlen($code);
224		for ($i = 0; $i < $clen; ++$i) {
225			if ($ean && $i == 0) {
226				$sum += 102;
227			} else {
228				if (strpos($keys, $code[$i]) === false) {
229					throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128'.$type.' barcode value', $code[$i]));
230				}
231				$sum += (strpos($keys, $code[$i]) * ($i + 1));
232			}
233		}
234		$check = ($sum % 103);
235		$checkdigit = $check;
236
237		// add start, check and stop codes
238		$code = chr($startid) . $code . chr($check) . chr(106) . chr(107);
239		$bararray = ['code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => []];
240		$k = 0;
241		$len = strlen($code);
242
243		for ($i = 0; $i < $len; ++$i) {
244
245			$ck = strpos($keys, $code[$i]);
246			if (($i == 0) || ($ean && $i == 1) | ($i > ($len - 4))) {
247				$char_num = ord($code[$i]);
248				$seq = $chr[$char_num];
249			} elseif (($ck >= 0) && isset($chr[$ck])) {
250				$seq = $chr[$ck];
251			} else {
252				// invalid character
253				throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid character "%s" in CODE128C barcode value', $code[$i]));
254			}
255			for ($j = 0; $j < 6; ++$j) {
256				if (($j % 2) == 0) {
257					$t = true; // bar
258				} else {
259					$t = false; // space
260				}
261				$w = $seq[$j];
262				$bararray['bcode'][$k] = ['t' => $t, 'w' => $w, 'h' => 1, 'p' => 0];
263				$bararray['maxw'] += $w;
264				++$k;
265			}
266		}
267
268		$bararray['checkdigit'] = $checkdigit;
269		$this->data = $bararray;
270	}
271
272	public function getType()
273	{
274		return 'CODE128';
275	}
276
277}
278