1<?php 2 3namespace Mpdf\Barcode; 4 5/** 6 * EAN13 and UPC-A barcodes. 7 * EAN13: European Article Numbering international retail product code 8 * UPC-A: Universal product code seen on almost all retail products in the USA and Canada 9 * UPC-E: Short version of UPC symbol 10 */ 11class EanUpc extends \Mpdf\Barcode\AbstractBarcode implements \Mpdf\Barcode\BarcodeInterface 12{ 13 14 /** 15 * @param string $code 16 * @param int $length 17 * @param float $leftMargin 18 * @param float $rightMargin 19 * @param float $xDim 20 * @param float $barHeight 21 */ 22 public function __construct($code, $length, $leftMargin, $rightMargin, $xDim, $barHeight) 23 { 24 $this->init($code, $length); 25 26 $this->data['lightmL'] = $leftMargin; // LEFT light margin = x X-dim (http://www.gs1uk.org) 27 $this->data['lightmR'] = $rightMargin; // RIGHT light margin = x X-dim (http://www.gs1uk.org) 28 $this->data['nom-X'] = $xDim; // Nominal value for X-dim in mm (http://www.gs1uk.org) 29 $this->data['nom-H'] = $barHeight; // Nominal bar height in mm incl. numerals (http://www.gs1uk.org) 30 } 31 32 /** 33 * @param string $code 34 * @param int $length 35 */ 36 private function init($code, $length) 37 { 38 if (preg_match('/[\D]+/', $code)) { 39 throw new \Mpdf\Barcode\BarcodeException('Invalid EAN UPC barcode value'); 40 } 41 42 $upce = false; 43 $checkdigit = false; 44 45 if ($length == 6) { 46 $length = 12; // UPC-A 47 $upce = true; // UPC-E mode 48 } 49 $dataLength = $length - 1; 50 51 // Padding 52 $code = str_pad($code, $dataLength, '0', STR_PAD_LEFT); 53 $codeLength = strlen($code); 54 55 // Calculate check digit 56 $sum_a = 0; 57 for ($i = 1; $i < $dataLength; $i += 2) { 58 $sum_a += $code[$i]; 59 } 60 61 if ($length > 12) { 62 $sum_a *= 3; 63 } 64 $sum_b = 0; 65 for ($i = 0; $i < $dataLength; $i += 2) { 66 $sum_b += ($code[$i]); 67 } 68 69 if ($length < 13) { 70 $sum_b *= 3; 71 } 72 73 $r = ($sum_a + $sum_b) % 10; 74 if ($r > 0) { 75 $r = (10 - $r); 76 } 77 78 if ($codeLength == $dataLength) { 79 // Add check digit 80 $code .= $r; 81 $checkdigit = $r; 82 } elseif ($r !== (int) $code[$dataLength]) { 83 // Wrong checkdigit 84 throw new \Mpdf\Barcode\BarcodeException('Invalid EAN UPC barcode value'); 85 } 86 87 if ($length == 12) { 88 // UPC-A 89 $code = '0' . $code; 90 ++$length; 91 } 92 93 if ($upce) { 94 // Convert UPC-A to UPC-E 95 $tmp = substr($code, 4, 3); 96 $prodCode = (int) substr($code, 7, 5); // product code 97 $invalidUpce = false; 98 if (($tmp == '000') or ($tmp == '100') or ($tmp == '200')) { 99 // Manufacturer code ends in 000, 100, or 200 100 $upceCode = substr($code, 2, 2) . substr($code, 9, 3) . substr($code, 4, 1); 101 if ($prodCode > 999) { 102 $invalidUpce = true; 103 } 104 } else { 105 $tmp = substr($code, 5, 2); 106 if ($tmp == '00') { 107 // Manufacturer code ends in 00 108 $upceCode = substr($code, 2, 3) . substr($code, 10, 2) . '3'; 109 if ($prodCode > 99) { 110 $invalidUpce = true; 111 } 112 } else { 113 $tmp = substr($code, 6, 1); 114 if ($tmp == '0') { 115 // Manufacturer code ends in 0 116 $upceCode = substr($code, 2, 4) . substr($code, 11, 1) . '4'; 117 if ($prodCode > 9) { 118 $invalidUpce = true; 119 } 120 } else { 121 // Manufacturer code does not end in zero 122 $upceCode = substr($code, 2, 5) . substr($code, 11, 1); 123 if ($prodCode > 9) { 124 $invalidUpce = true; 125 } 126 } 127 } 128 } 129 if ($invalidUpce) { 130 throw new \Mpdf\Barcode\BarcodeException('UPC-A cannot produce a valid UPC-E barcode'); 131 } 132 } 133 134 // Convert digits to bars 135 $codes = [ 136 'A' => [// left odd parity 137 '0' => '0001101', 138 '1' => '0011001', 139 '2' => '0010011', 140 '3' => '0111101', 141 '4' => '0100011', 142 '5' => '0110001', 143 '6' => '0101111', 144 '7' => '0111011', 145 '8' => '0110111', 146 '9' => '0001011'], 147 'B' => [// left even parity 148 '0' => '0100111', 149 '1' => '0110011', 150 '2' => '0011011', 151 '3' => '0100001', 152 '4' => '0011101', 153 '5' => '0111001', 154 '6' => '0000101', 155 '7' => '0010001', 156 '8' => '0001001', 157 '9' => '0010111'], 158 'C' => [// right 159 '0' => '1110010', 160 '1' => '1100110', 161 '2' => '1101100', 162 '3' => '1000010', 163 '4' => '1011100', 164 '5' => '1001110', 165 '6' => '1010000', 166 '7' => '1000100', 167 '8' => '1001000', 168 '9' => '1110100'] 169 ]; 170 171 $parities = [ 172 '0' => ['A', 'A', 'A', 'A', 'A', 'A'], 173 '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 174 '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 175 '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 176 '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 177 '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 178 '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 179 '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 180 '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 181 '9' => ['A', 'B', 'B', 'A', 'B', 'A'] 182 ]; 183 184 $upceParities = []; 185 $upceParities[0] = [ 186 '0' => ['B', 'B', 'B', 'A', 'A', 'A'], 187 '1' => ['B', 'B', 'A', 'B', 'A', 'A'], 188 '2' => ['B', 'B', 'A', 'A', 'B', 'A'], 189 '3' => ['B', 'B', 'A', 'A', 'A', 'B'], 190 '4' => ['B', 'A', 'B', 'B', 'A', 'A'], 191 '5' => ['B', 'A', 'A', 'B', 'B', 'A'], 192 '6' => ['B', 'A', 'A', 'A', 'B', 'B'], 193 '7' => ['B', 'A', 'B', 'A', 'B', 'A'], 194 '8' => ['B', 'A', 'B', 'A', 'A', 'B'], 195 '9' => ['B', 'A', 'A', 'B', 'A', 'B'] 196 ]; 197 198 $upceParities[1] = [ 199 '0' => ['A', 'A', 'A', 'B', 'B', 'B'], 200 '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 201 '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 202 '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 203 '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 204 '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 205 '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 206 '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 207 '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 208 '9' => ['A', 'B', 'B', 'A', 'B', 'A'] 209 ]; 210 211 $k = 0; 212 $seq = '101'; // left guard bar 213 214 if ($upce && isset($upceCode)) { 215 $bararray = ['code' => $upceCode, 'maxw' => 0, 'maxh' => 1, 'bcode' => []]; 216 $p = $upceParities[$code[1]][$r]; 217 for ($i = 0; $i < 6; ++$i) { 218 $seq .= $codes[$p[$i]][$upceCode[$i]]; 219 } 220 $seq .= '010101'; // right guard bar 221 } else { 222 $bararray = ['code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => []]; 223 $halfLen = ceil($length / 2); 224 if ($length == 8) { 225 for ($i = 0; $i < $halfLen; ++$i) { 226 $seq .= $codes['A'][$code[$i]]; 227 } 228 } else { 229 $p = $parities[$code[0]]; 230 for ($i = 1; $i < $halfLen; ++$i) { 231 $seq .= $codes[$p[$i - 1]][$code[$i]]; 232 } 233 } 234 $seq .= '01010'; // center guard bar 235 for ($i = $halfLen; $i < $length; ++$i) { 236 $seq .= $codes['C'][$code[(int) $i]]; 237 } 238 $seq .= '101'; // right guard bar 239 } 240 241 $clen = strlen($seq); 242 $w = 0; 243 for ($i = 0; $i < $clen; ++$i) { 244 $w += 1; 245 if (($i == ($clen - 1)) or (($i < ($clen - 1)) and ($seq[$i] != $seq[($i + 1)]))) { 246 if ($seq[$i] == '1') { 247 $t = true; // bar 248 } else { 249 $t = false; // space 250 } 251 $bararray['bcode'][$k] = ['t' => $t, 'w' => $w, 'h' => 1, 'p' => 0]; 252 $bararray['maxw'] += $w; 253 ++$k; 254 $w = 0; 255 } 256 } 257 $bararray['checkdigit'] = $checkdigit; 258 259 $this->data = $bararray; 260 } 261 262 /** 263 * @inheritdoc 264 */ 265 public function getType() 266 { 267 return 'EANUPC'; 268 } 269 270} 271