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(sprintf('Invalid EAN UPC barcode value "%s"', $code)); 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(sprintf('Invalid EAN UPC barcode value "%s"', $code)); 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 130 if ($invalidUpce) { 131 throw new \Mpdf\Barcode\BarcodeException('UPC-A cannot produce a valid UPC-E barcode'); 132 } 133 } 134 135 // Convert digits to bars 136 $codes = [ 137 'A' => [// left odd parity 138 '0' => '0001101', 139 '1' => '0011001', 140 '2' => '0010011', 141 '3' => '0111101', 142 '4' => '0100011', 143 '5' => '0110001', 144 '6' => '0101111', 145 '7' => '0111011', 146 '8' => '0110111', 147 '9' => '0001011'], 148 'B' => [// left even parity 149 '0' => '0100111', 150 '1' => '0110011', 151 '2' => '0011011', 152 '3' => '0100001', 153 '4' => '0011101', 154 '5' => '0111001', 155 '6' => '0000101', 156 '7' => '0010001', 157 '8' => '0001001', 158 '9' => '0010111'], 159 'C' => [// right 160 '0' => '1110010', 161 '1' => '1100110', 162 '2' => '1101100', 163 '3' => '1000010', 164 '4' => '1011100', 165 '5' => '1001110', 166 '6' => '1010000', 167 '7' => '1000100', 168 '8' => '1001000', 169 '9' => '1110100'] 170 ]; 171 172 $parities = [ 173 '0' => ['A', 'A', 'A', 'A', 'A', 'A'], 174 '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 175 '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 176 '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 177 '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 178 '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 179 '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 180 '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 181 '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 182 '9' => ['A', 'B', 'B', 'A', 'B', 'A'] 183 ]; 184 185 $upceParities = []; 186 $upceParities[0] = [ 187 '0' => ['B', 'B', 'B', 'A', 'A', 'A'], 188 '1' => ['B', 'B', 'A', 'B', 'A', 'A'], 189 '2' => ['B', 'B', 'A', 'A', 'B', 'A'], 190 '3' => ['B', 'B', 'A', 'A', 'A', 'B'], 191 '4' => ['B', 'A', 'B', 'B', 'A', 'A'], 192 '5' => ['B', 'A', 'A', 'B', 'B', 'A'], 193 '6' => ['B', 'A', 'A', 'A', 'B', 'B'], 194 '7' => ['B', 'A', 'B', 'A', 'B', 'A'], 195 '8' => ['B', 'A', 'B', 'A', 'A', 'B'], 196 '9' => ['B', 'A', 'A', 'B', 'A', 'B'] 197 ]; 198 199 $upceParities[1] = [ 200 '0' => ['A', 'A', 'A', 'B', 'B', 'B'], 201 '1' => ['A', 'A', 'B', 'A', 'B', 'B'], 202 '2' => ['A', 'A', 'B', 'B', 'A', 'B'], 203 '3' => ['A', 'A', 'B', 'B', 'B', 'A'], 204 '4' => ['A', 'B', 'A', 'A', 'B', 'B'], 205 '5' => ['A', 'B', 'B', 'A', 'A', 'B'], 206 '6' => ['A', 'B', 'B', 'B', 'A', 'A'], 207 '7' => ['A', 'B', 'A', 'B', 'A', 'B'], 208 '8' => ['A', 'B', 'A', 'B', 'B', 'A'], 209 '9' => ['A', 'B', 'B', 'A', 'B', 'A'] 210 ]; 211 212 $k = 0; 213 $seq = '101'; // left guard bar 214 215 if ($upce && isset($upceCode)) { 216 $bararray = ['code' => $upceCode, 'maxw' => 0, 'maxh' => 1, 'bcode' => []]; 217 $p = $upceParities[$code[1]][$r]; 218 for ($i = 0; $i < 6; ++$i) { 219 $seq .= $codes[$p[$i]][$upceCode[$i]]; 220 } 221 $seq .= '010101'; // right guard bar 222 } else { 223 $bararray = ['code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => []]; 224 $halfLen = ceil($length / 2); 225 if ($length == 8) { 226 for ($i = 0; $i < $halfLen; ++$i) { 227 $seq .= $codes['A'][$code[$i]]; 228 } 229 } else { 230 $p = $parities[$code[0]]; 231 for ($i = 1; $i < $halfLen; ++$i) { 232 $seq .= $codes[$p[$i - 1]][$code[$i]]; 233 } 234 } 235 $seq .= '01010'; // center guard bar 236 for ($i = $halfLen; $i < $length; ++$i) { 237 $seq .= $codes['C'][$code[(int) $i]]; 238 } 239 $seq .= '101'; // right guard bar 240 } 241 242 $clen = strlen($seq); 243 $w = 0; 244 for ($i = 0; $i < $clen; ++$i) { 245 $w += 1; 246 if (($i == ($clen - 1)) or (($i < ($clen - 1)) and ($seq[$i] != $seq[($i + 1)]))) { 247 if ($seq[$i] == '1') { 248 $t = true; // bar 249 } else { 250 $t = false; // space 251 } 252 $bararray['bcode'][$k] = ['t' => $t, 'w' => $w, 'h' => 1, 'p' => 0]; 253 $bararray['maxw'] += $w; 254 ++$k; 255 $w = 0; 256 } 257 } 258 $bararray['checkdigit'] = $checkdigit; 259 260 $this->data = $bararray; 261 } 262 263 /** 264 * @inheritdoc 265 */ 266 public function getType() 267 { 268 return 'EANUPC'; 269 } 270 271} 272