1<?php
2
3namespace Mpdf\Barcode;
4
5/**
6 * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.
7 */
8class Code39 extends \Mpdf\Barcode\AbstractBarcode implements \Mpdf\Barcode\BarcodeInterface
9{
10
11	/**
12	 * @param string $code
13	 * @param float $printRatio
14	 * @param bool $extended
15	 * @param bool $checksum
16	 */
17	public function __construct($code, $printRatio, $extended = false, $checksum = false, $quiet_zone_left = null, $quiet_zone_right = null)
18	{
19		$this->init($code, $printRatio, $extended, $checksum);
20
21		$this->data['nom-X'] = 0.381; // Nominal value for X-dim (bar width) in mm (2 X min. spec.)
22		$this->data['nom-H'] = 10;  // Nominal value for Height of Full bar in mm (non-spec.)
23		$this->data['lightmL'] = ($quiet_zone_left !== null ? $quiet_zone_left : 10); // LEFT light margin =  x X-dim (spec.)
24		$this->data['lightmR'] = ($quiet_zone_right !== null ? $quiet_zone_right : 10); // RIGHT light margin =  x X-dim (spec.)
25		$this->data['lightTB'] = 0; // TOP/BOTTOM light margin =  x X-dim (non-spec.)
26	}
27
28	/**
29	 * @param string $code
30	 * @param float $printRatio
31	 * @param bool $extended
32	 * @param bool $checksum
33	 *
34	 * @return mixed[]
35	 */
36	private function init($code, $printRatio, $extended, $checksum)
37	{
38		$chr = [
39			'0' => '111221211',
40			'1' => '211211112',
41			'2' => '112211112',
42			'3' => '212211111',
43			'4' => '111221112',
44			'5' => '211221111',
45			'6' => '112221111',
46			'7' => '111211212',
47			'8' => '211211211',
48			'9' => '112211211',
49			'A' => '211112112',
50			'B' => '112112112',
51			'C' => '212112111',
52			'D' => '111122112',
53			'E' => '211122111',
54			'F' => '112122111',
55			'G' => '111112212',
56			'H' => '211112211',
57			'I' => '112112211',
58			'J' => '111122211',
59			'K' => '211111122',
60			'L' => '112111122',
61			'M' => '212111121',
62			'N' => '111121122',
63			'O' => '211121121',
64			'P' => '112121121',
65			'Q' => '111111222',
66			'R' => '211111221',
67			'S' => '112111221',
68			'T' => '111121221',
69			'U' => '221111112',
70			'V' => '122111112',
71			'W' => '222111111',
72			'X' => '121121112',
73			'Y' => '221121111',
74			'Z' => '122121111',
75			'-' => '121111212',
76			'.' => '221111211',
77			' ' => '122111211',
78			'$' => '121212111',
79			'/' => '121211121',
80			'+' => '121112121',
81			'%' => '111212121',
82			'*' => '121121211',
83		];
84
85		$code = strtoupper($code);
86		$checkdigit = '';
87
88		if ($extended) {
89			// extended mode
90			$code = $this->encodeExt($code);
91		}
92
93		if ($code === false) {
94			throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE39 barcode value "%s"', $code));
95		}
96
97		if ($checksum) {
98			// checksum
99			$checkdigit = $this->checksum($code);
100			$code .= $checkdigit;
101		}
102		// add star$this->>datat and stop codes
103		$code = '*' . $code . '*';
104
105		$bararray = ['code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => []];
106		$k = 0;
107		$clen = strlen($code);
108		for ($i = 0; $i < $clen; ++$i) {
109			$char = $code[$i];
110			if (!isset($chr[$char])) {
111				// invalid character
112				throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE39 barcode value "%s"', $code));
113			}
114			for ($j = 0; $j < 9; ++$j) {
115				if (($j % 2) == 0) {
116					$t = true; // bar
117				} else {
118					$t = false; // space
119				}
120				$x = $chr[$char][$j];
121				if ($x == 2) {
122					$w = $printRatio;
123				} else {
124					$w = 1;
125				}
126
127				$bararray['bcode'][$k] = ['t' => $t, 'w' => $w, 'h' => 1, 'p' => 0];
128				$bararray['maxw'] += $w;
129				++$k;
130			}
131			$bararray['bcode'][$k] = ['t' => false, 'w' => 1, 'h' => 1, 'p' => 0];
132			$bararray['maxw'] += 1;
133			++$k;
134		}
135
136		$bararray['checkdigit'] = $checkdigit;
137
138		$this->data = $bararray;
139	}
140
141	/**
142	 * Encode a string to be used for CODE 39 Extended mode.
143	 *
144	 * @param string $code
145	 * @return string
146	 */
147	protected function encodeExt($code)
148	{
149		$encode = [
150			chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C',
151			chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G',
152			chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K',
153			chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O',
154			chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S',
155			chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W',
156			chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A',
157			chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E',
158			chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C',
159			chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G',
160			chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K',
161			chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O',
162			chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3',
163			chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7',
164			chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F',
165			chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J',
166			chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C',
167			chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G',
168			chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K',
169			chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O',
170			chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S',
171			chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W',
172			chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K',
173			chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O',
174			chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C',
175			chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G',
176			chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K',
177			chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O',
178			chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S',
179			chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W',
180			chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P',
181			chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T'
182		];
183
184		$code_ext = '';
185		$clen = strlen($code);
186
187		for ($i = 0; $i < $clen; ++$i) {
188
189			if (ord($code[$i]) > 127) {
190				throw new \Mpdf\Barcode\BarcodeException(sprintf('Invalid CODE39 barcode value "%s"', $code));
191			}
192
193			$code_ext .= $encode[$code[$i]];
194		}
195
196		return $code_ext;
197	}
198
199	/**
200	 * Calculate CODE 39 checksum (modulo 43).
201	 *
202	 * @param string $code
203	 * @return string mixed
204	 */
205	protected function checksum($code)
206	{
207		$chars = [
208			'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
209			'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
210			'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
211			'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%'
212		];
213
214		$sum = 0;
215		$clen = strlen($code);
216
217		for ($i = 0; $i < $clen; ++$i) {
218			$k = array_keys($chars, $code[$i]);
219			$sum += $k[0];
220		}
221
222		$j = ($sum % 43);
223
224		return $chars[$j];
225	}
226
227	/**
228	 * @inheritdoc
229	 */
230	public function getType()
231	{
232		return 'CODE39';
233	}
234
235}
236