1<?php 2 3namespace jucksearm\barcode\lib; 4 5//============================================================+ 6// File name : tcpdf_barcodes_1d.php 7// Version : 1.0.027 8// Begin : 2008-06-09 9// Last Update : 2014-10-20 10// Author : Nicola Asuni - Tecnick.com LTD - www.tecnick.com - info@tecnick.com 11// License : GNU-LGPL v3 (http://www.gnu.org/copyleft/lesser.html) 12// ------------------------------------------------------------------- 13// Copyright (C) 2008-2014 Nicola Asuni - Tecnick.com LTD 14// 15// This file is part of TCPDF software library. 16// 17// TCPDF is free software: you can redistribute it and/or modify it 18// under the terms of the GNU Lesser General Public License as 19// published by the Free Software Foundation, either version 3 of the 20// License, or (at your option) any later version. 21// 22// TCPDF is distributed in the hope that it will be useful, but 23// WITHOUT ANY WARRANTY; without even the implied warranty of 24// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25// See the GNU Lesser General Public License for more details. 26// 27// You should have received a copy of the GNU Lesser General Public License 28// along with TCPDF. If not, see <http://www.gnu.org/licenses/>. 29// 30// See LICENSE.TXT file for more information. 31// ------------------------------------------------------------------- 32// 33// Description : PHP class to creates array representations for 34// common 1D barcodes to be used with TCPDF. 35// 36//============================================================+ 37 38/** 39 * @file 40 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF. 41 * @package com.tecnick.tcpdf 42 * @author Nicola Asuni 43 * @version 1.0.027 44 */ 45 46/** 47 * @class Barcode1D replace TCPDFBarcode 48 * PHP class to creates array representations for common 1D barcodes to be used with TCPDF (http://www.tcpdf.org).<br> 49 * @package com.tecnick.tcpdf 50 * @version 1.0.027 51 * @author Nicola Asuni 52 */ 53class Barcode1D 54{ 55 56 /** 57 * Array representation of barcode. 58 * @protected 59 */ 60 protected $barcode_array; 61 62 /** 63 * This is the class constructor. 64 * Return an array representations for common 1D barcodes:<ul> 65 * <li>$arrcode['code'] code to be printed on text label</li> 66 * <li>$arrcode['maxh'] max barcode height</li> 67 * <li>$arrcode['maxw'] max barcode width</li> 68 * <li>$arrcode['bcode'][$k] single bar or space in $k position</li> 69 * <li>$arrcode['bcode'][$k]['t'] bar type: true = bar, false = space.</li> 70 * <li>$arrcode['bcode'][$k]['w'] bar width in units.</li> 71 * <li>$arrcode['bcode'][$k]['h'] bar height in units.</li> 72 * <li>$arrcode['bcode'][$k]['p'] bar top position (0 = top, 1 = middle)</li></ul> 73 * @param $code (string) code to print 74 * @param $type (string) type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128 : CODE 128</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>MSI : MSI (Variation of Plessey code)</li><li>MSI+ : MSI + CHECKSUM (modulo 11)</li><li>POSTNET : POSTNET</li><li>PLANET : PLANET</li><li>RMS4CC : RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)</li><li>KIX : KIX (Klant index - Customer index)</li><li>IMB: Intelligent Mail Barcode - Onecode - USPS-B-3200</li><li>CODABAR : CODABAR</li><li>CODE11 : CODE 11</li><li>PHARMA : PHARMACODE</li><li>PHARMA2T : PHARMACODE TWO-TRACKS</li></ul> 75 * @public 76 */ 77 public function __construct($code, $type) { 78 $this->setBarcode($code, $type); 79 } 80 81 /** 82 * Return an array representations of barcode. 83 * @return array 84 * @public 85 */ 86 public function getBarcodeArray() { 87 return $this->barcode_array; 88 } 89 90 /** 91 * Set the barcode. 92 * @param $code (string) code to print 93 * @param $type (string) type of barcode: <ul><li>C39 : CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9.</li><li>C39+ : CODE 39 with checksum</li><li>C39E : CODE 39 EXTENDED</li><li>C39E+ : CODE 39 EXTENDED + CHECKSUM</li><li>C93 : CODE 93 - USS-93</li><li>S25 : Standard 2 of 5</li><li>S25+ : Standard 2 of 5 + CHECKSUM</li><li>I25 : Interleaved 2 of 5</li><li>I25+ : Interleaved 2 of 5 + CHECKSUM</li><li>C128 : CODE 128</li><li>C128A : CODE 128 A</li><li>C128B : CODE 128 B</li><li>C128C : CODE 128 C</li><li>EAN2 : 2-Digits UPC-Based Extension</li><li>EAN5 : 5-Digits UPC-Based Extension</li><li>EAN8 : EAN 8</li><li>EAN13 : EAN 13</li><li>UPCA : UPC-A</li><li>UPCE : UPC-E</li><li>MSI : MSI (Variation of Plessey code)</li><li>MSI+ : MSI + CHECKSUM (modulo 11)</li><li>POSTNET : POSTNET</li><li>PLANET : PLANET</li><li>RMS4CC : RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code)</li><li>KIX : KIX (Klant index - Customer index)</li><li>IMB: Intelligent Mail Barcode - Onecode - USPS-B-3200</li><li>IMBPRE: Pre-processed Intelligent Mail Barcode - Onecode - USPS-B-3200, using only F,A,D,T letters</li><li>CODABAR : CODABAR</li><li>CODE11 : CODE 11</li><li>PHARMA : PHARMACODE</li><li>PHARMA2T : PHARMACODE TWO-TRACKS</li></ul> 94 * @return array barcode array 95 * @public 96 */ 97 public function setBarcode($code, $type) { 98 switch (strtoupper($type)) { 99 case 'C39': { // CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9. 100 $arrcode = $this->barcode_code39($code, false, false); 101 break; 102 } 103 case 'C39+': { // CODE 39 with checksum 104 $arrcode = $this->barcode_code39($code, false, true); 105 break; 106 } 107 case 'C39E': { // CODE 39 EXTENDED 108 $arrcode = $this->barcode_code39($code, true, false); 109 break; 110 } 111 case 'C39E+': { // CODE 39 EXTENDED + CHECKSUM 112 $arrcode = $this->barcode_code39($code, true, true); 113 break; 114 } 115 case 'C93': { // CODE 93 - USS-93 116 $arrcode = $this->barcode_code93($code); 117 break; 118 } 119 case 'S25': { // Standard 2 of 5 120 $arrcode = $this->barcode_s25($code, false); 121 break; 122 } 123 case 'S25+': { // Standard 2 of 5 + CHECKSUM 124 $arrcode = $this->barcode_s25($code, true); 125 break; 126 } 127 case 'I25': { // Interleaved 2 of 5 128 $arrcode = $this->barcode_i25($code, false); 129 break; 130 } 131 case 'I25+': { // Interleaved 2 of 5 + CHECKSUM 132 $arrcode = $this->barcode_i25($code, true); 133 break; 134 } 135 case 'C128': { // CODE 128 136 $arrcode = $this->barcode_c128($code, ''); 137 break; 138 } 139 case 'C128A': { // CODE 128 A 140 $arrcode = $this->barcode_c128($code, 'A'); 141 break; 142 } 143 case 'C128B': { // CODE 128 B 144 $arrcode = $this->barcode_c128($code, 'B'); 145 break; 146 } 147 case 'C128C': { // CODE 128 C 148 $arrcode = $this->barcode_c128($code, 'C'); 149 break; 150 } 151 case 'EAN2': { // 2-Digits UPC-Based Extension 152 $arrcode = $this->barcode_eanext($code, 2); 153 break; 154 } 155 case 'EAN5': { // 5-Digits UPC-Based Extension 156 $arrcode = $this->barcode_eanext($code, 5); 157 break; 158 } 159 case 'EAN8': { // EAN 8 160 $arrcode = $this->barcode_eanupc($code, 8); 161 break; 162 } 163 case 'EAN13': { // EAN 13 164 $arrcode = $this->barcode_eanupc($code, 13); 165 break; 166 } 167 case 'UPCA': { // UPC-A 168 $arrcode = $this->barcode_eanupc($code, 12); 169 break; 170 } 171 case 'UPCE': { // UPC-E 172 $arrcode = $this->barcode_eanupc($code, 6); 173 break; 174 } 175 case 'MSI': { // MSI (Variation of Plessey code) 176 $arrcode = $this->barcode_msi($code, false); 177 break; 178 } 179 case 'MSI+': { // MSI + CHECKSUM (modulo 11) 180 $arrcode = $this->barcode_msi($code, true); 181 break; 182 } 183 case 'POSTNET': { // POSTNET 184 $arrcode = $this->barcode_postnet($code, false); 185 break; 186 } 187 case 'PLANET': { // PLANET 188 $arrcode = $this->barcode_postnet($code, true); 189 break; 190 } 191 case 'RMS4CC': { // RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) 192 $arrcode = $this->barcode_rms4cc($code, false); 193 break; 194 } 195 case 'KIX': { // KIX (Klant index - Customer index) 196 $arrcode = $this->barcode_rms4cc($code, true); 197 break; 198 } 199 case 'IMB': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200 200 $arrcode = $this->barcode_imb($code); 201 break; 202 } 203 case 'IMBPRE': { // IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200- pre-processed 204 $arrcode = $this->barcode_imb_pre($code); 205 break; 206 } 207 case 'CODABAR': { // CODABAR 208 $arrcode = $this->barcode_codabar($code); 209 break; 210 } 211 case 'CODE11': { // CODE 11 212 $arrcode = $this->barcode_code11($code); 213 break; 214 } 215 case 'PHARMA': { // PHARMACODE 216 $arrcode = $this->barcode_pharmacode($code); 217 break; 218 } 219 case 'PHARMA2T': { // PHARMACODE TWO-TRACKS 220 $arrcode = $this->barcode_pharmacode2t($code); 221 break; 222 } 223 default: { 224 $this->barcode_array = false; 225 $arrcode = false; 226 break; 227 } 228 } 229 $this->barcode_array = $arrcode; 230 } 231 232 /** 233 * CODE 39 - ANSI MH10.8M-1983 - USD-3 - 3 of 9. 234 * General-purpose code in very wide use world-wide 235 * @param $code (string) code to represent. 236 * @param $extended (boolean) if true uses the extended mode. 237 * @param $checksum (boolean) if true add a checksum to the code. 238 * @return array barcode representation. 239 * @protected 240 */ 241 protected function barcode_code39($code, $extended=false, $checksum=false) { 242 $chr['0'] = '111331311'; 243 $chr['1'] = '311311113'; 244 $chr['2'] = '113311113'; 245 $chr['3'] = '313311111'; 246 $chr['4'] = '111331113'; 247 $chr['5'] = '311331111'; 248 $chr['6'] = '113331111'; 249 $chr['7'] = '111311313'; 250 $chr['8'] = '311311311'; 251 $chr['9'] = '113311311'; 252 $chr['A'] = '311113113'; 253 $chr['B'] = '113113113'; 254 $chr['C'] = '313113111'; 255 $chr['D'] = '111133113'; 256 $chr['E'] = '311133111'; 257 $chr['F'] = '113133111'; 258 $chr['G'] = '111113313'; 259 $chr['H'] = '311113311'; 260 $chr['I'] = '113113311'; 261 $chr['J'] = '111133311'; 262 $chr['K'] = '311111133'; 263 $chr['L'] = '113111133'; 264 $chr['M'] = '313111131'; 265 $chr['N'] = '111131133'; 266 $chr['O'] = '311131131'; 267 $chr['P'] = '113131131'; 268 $chr['Q'] = '111111333'; 269 $chr['R'] = '311111331'; 270 $chr['S'] = '113111331'; 271 $chr['T'] = '111131331'; 272 $chr['U'] = '331111113'; 273 $chr['V'] = '133111113'; 274 $chr['W'] = '333111111'; 275 $chr['X'] = '131131113'; 276 $chr['Y'] = '331131111'; 277 $chr['Z'] = '133131111'; 278 $chr['-'] = '131111313'; 279 $chr['.'] = '331111311'; 280 $chr[' '] = '133111311'; 281 $chr['$'] = '131313111'; 282 $chr['/'] = '131311131'; 283 $chr['+'] = '131113131'; 284 $chr['%'] = '111313131'; 285 $chr['*'] = '131131311'; 286 $code = strtoupper($code); 287 if ($extended) { 288 // extended mode 289 $code = $this->encode_code39_ext($code); 290 } 291 if ($code === false) { 292 return false; 293 } 294 if ($checksum) { 295 // checksum 296 $code .= $this->checksum_code39($code); 297 } 298 // add start and stop codes 299 $code = '*'.$code.'*'; 300 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 301 $k = 0; 302 $clen = strlen($code); 303 for ($i = 0; $i < $clen; ++$i) { 304 $char = $code[$i]; 305 if(!isset($chr[$char])) { 306 // invalid character 307 return false; 308 } 309 for ($j = 0; $j < 9; ++$j) { 310 if (($j % 2) == 0) { 311 $t = true; // bar 312 } else { 313 $t = false; // space 314 } 315 $w = $chr[$char][$j]; 316 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 317 $bararray['maxw'] += $w; 318 ++$k; 319 } 320 // intercharacter gap 321 $bararray['bcode'][$k] = array('t' => false, 'w' => 1, 'h' => 1, 'p' => 0); 322 $bararray['maxw'] += 1; 323 ++$k; 324 } 325 return $bararray; 326 } 327 328 /** 329 * Encode a string to be used for CODE 39 Extended mode. 330 * @param $code (string) code to represent. 331 * @return encoded string. 332 * @protected 333 */ 334 protected function encode_code39_ext($code) { 335 $encode = array( 336 chr(0) => '%U', chr(1) => '$A', chr(2) => '$B', chr(3) => '$C', 337 chr(4) => '$D', chr(5) => '$E', chr(6) => '$F', chr(7) => '$G', 338 chr(8) => '$H', chr(9) => '$I', chr(10) => '$J', chr(11) => '£K', 339 chr(12) => '$L', chr(13) => '$M', chr(14) => '$N', chr(15) => '$O', 340 chr(16) => '$P', chr(17) => '$Q', chr(18) => '$R', chr(19) => '$S', 341 chr(20) => '$T', chr(21) => '$U', chr(22) => '$V', chr(23) => '$W', 342 chr(24) => '$X', chr(25) => '$Y', chr(26) => '$Z', chr(27) => '%A', 343 chr(28) => '%B', chr(29) => '%C', chr(30) => '%D', chr(31) => '%E', 344 chr(32) => ' ', chr(33) => '/A', chr(34) => '/B', chr(35) => '/C', 345 chr(36) => '/D', chr(37) => '/E', chr(38) => '/F', chr(39) => '/G', 346 chr(40) => '/H', chr(41) => '/I', chr(42) => '/J', chr(43) => '/K', 347 chr(44) => '/L', chr(45) => '-', chr(46) => '.', chr(47) => '/O', 348 chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3', 349 chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7', 350 chr(56) => '8', chr(57) => '9', chr(58) => '/Z', chr(59) => '%F', 351 chr(60) => '%G', chr(61) => '%H', chr(62) => '%I', chr(63) => '%J', 352 chr(64) => '%V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C', 353 chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G', 354 chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K', 355 chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O', 356 chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S', 357 chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W', 358 chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => '%K', 359 chr(92) => '%L', chr(93) => '%M', chr(94) => '%N', chr(95) => '%O', 360 chr(96) => '%W', chr(97) => '+A', chr(98) => '+B', chr(99) => '+C', 361 chr(100) => '+D', chr(101) => '+E', chr(102) => '+F', chr(103) => '+G', 362 chr(104) => '+H', chr(105) => '+I', chr(106) => '+J', chr(107) => '+K', 363 chr(108) => '+L', chr(109) => '+M', chr(110) => '+N', chr(111) => '+O', 364 chr(112) => '+P', chr(113) => '+Q', chr(114) => '+R', chr(115) => '+S', 365 chr(116) => '+T', chr(117) => '+U', chr(118) => '+V', chr(119) => '+W', 366 chr(120) => '+X', chr(121) => '+Y', chr(122) => '+Z', chr(123) => '%P', 367 chr(124) => '%Q', chr(125) => '%R', chr(126) => '%S', chr(127) => '%T'); 368 $code_ext = ''; 369 $clen = strlen($code); 370 for ($i = 0 ; $i < $clen; ++$i) { 371 if (ord($code[$i]) > 127) { 372 return false; 373 } 374 $code_ext .= $encode[$code[$i]]; 375 } 376 return $code_ext; 377 } 378 379 /** 380 * Calculate CODE 39 checksum (modulo 43). 381 * @param $code (string) code to represent. 382 * @return char checksum. 383 * @protected 384 */ 385 protected function checksum_code39($code) { 386 $chars = array( 387 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 388 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 389 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 390 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%'); 391 $sum = 0; 392 $clen = strlen($code); 393 for ($i = 0 ; $i < $clen; ++$i) { 394 $k = array_keys($chars, $code[$i]); 395 $sum += $k[0]; 396 } 397 $j = ($sum % 43); 398 return $chars[$j]; 399 } 400 401 /** 402 * CODE 93 - USS-93 403 * Compact code similar to Code 39 404 * @param $code (string) code to represent. 405 * @return array barcode representation. 406 * @protected 407 */ 408 protected function barcode_code93($code) { 409 $chr[48] = '131112'; // 0 410 $chr[49] = '111213'; // 1 411 $chr[50] = '111312'; // 2 412 $chr[51] = '111411'; // 3 413 $chr[52] = '121113'; // 4 414 $chr[53] = '121212'; // 5 415 $chr[54] = '121311'; // 6 416 $chr[55] = '111114'; // 7 417 $chr[56] = '131211'; // 8 418 $chr[57] = '141111'; // 9 419 $chr[65] = '211113'; // A 420 $chr[66] = '211212'; // B 421 $chr[67] = '211311'; // C 422 $chr[68] = '221112'; // D 423 $chr[69] = '221211'; // E 424 $chr[70] = '231111'; // F 425 $chr[71] = '112113'; // G 426 $chr[72] = '112212'; // H 427 $chr[73] = '112311'; // I 428 $chr[74] = '122112'; // J 429 $chr[75] = '132111'; // K 430 $chr[76] = '111123'; // L 431 $chr[77] = '111222'; // M 432 $chr[78] = '111321'; // N 433 $chr[79] = '121122'; // O 434 $chr[80] = '131121'; // P 435 $chr[81] = '212112'; // Q 436 $chr[82] = '212211'; // R 437 $chr[83] = '211122'; // S 438 $chr[84] = '211221'; // T 439 $chr[85] = '221121'; // U 440 $chr[86] = '222111'; // V 441 $chr[87] = '112122'; // W 442 $chr[88] = '112221'; // X 443 $chr[89] = '122121'; // Y 444 $chr[90] = '123111'; // Z 445 $chr[45] = '121131'; // - 446 $chr[46] = '311112'; // . 447 $chr[32] = '311211'; // 448 $chr[36] = '321111'; // $ 449 $chr[47] = '112131'; // / 450 $chr[43] = '113121'; // + 451 $chr[37] = '211131'; // % 452 $chr[128] = '121221'; // ($) 453 $chr[129] = '311121'; // (/) 454 $chr[130] = '122211'; // (+) 455 $chr[131] = '312111'; // (%) 456 $chr[42] = '111141'; // start-stop 457 $code = strtoupper($code); 458 $encode = array( 459 chr(0) => chr(131).'U', chr(1) => chr(128).'A', chr(2) => chr(128).'B', chr(3) => chr(128).'C', 460 chr(4) => chr(128).'D', chr(5) => chr(128).'E', chr(6) => chr(128).'F', chr(7) => chr(128).'G', 461 chr(8) => chr(128).'H', chr(9) => chr(128).'I', chr(10) => chr(128).'J', chr(11) => '£K', 462 chr(12) => chr(128).'L', chr(13) => chr(128).'M', chr(14) => chr(128).'N', chr(15) => chr(128).'O', 463 chr(16) => chr(128).'P', chr(17) => chr(128).'Q', chr(18) => chr(128).'R', chr(19) => chr(128).'S', 464 chr(20) => chr(128).'T', chr(21) => chr(128).'U', chr(22) => chr(128).'V', chr(23) => chr(128).'W', 465 chr(24) => chr(128).'X', chr(25) => chr(128).'Y', chr(26) => chr(128).'Z', chr(27) => chr(131).'A', 466 chr(28) => chr(131).'B', chr(29) => chr(131).'C', chr(30) => chr(131).'D', chr(31) => chr(131).'E', 467 chr(32) => ' ', chr(33) => chr(129).'A', chr(34) => chr(129).'B', chr(35) => chr(129).'C', 468 chr(36) => chr(129).'D', chr(37) => chr(129).'E', chr(38) => chr(129).'F', chr(39) => chr(129).'G', 469 chr(40) => chr(129).'H', chr(41) => chr(129).'I', chr(42) => chr(129).'J', chr(43) => chr(129).'K', 470 chr(44) => chr(129).'L', chr(45) => '-', chr(46) => '.', chr(47) => chr(129).'O', 471 chr(48) => '0', chr(49) => '1', chr(50) => '2', chr(51) => '3', 472 chr(52) => '4', chr(53) => '5', chr(54) => '6', chr(55) => '7', 473 chr(56) => '8', chr(57) => '9', chr(58) => chr(129).'Z', chr(59) => chr(131).'F', 474 chr(60) => chr(131).'G', chr(61) => chr(131).'H', chr(62) => chr(131).'I', chr(63) => chr(131).'J', 475 chr(64) => chr(131).'V', chr(65) => 'A', chr(66) => 'B', chr(67) => 'C', 476 chr(68) => 'D', chr(69) => 'E', chr(70) => 'F', chr(71) => 'G', 477 chr(72) => 'H', chr(73) => 'I', chr(74) => 'J', chr(75) => 'K', 478 chr(76) => 'L', chr(77) => 'M', chr(78) => 'N', chr(79) => 'O', 479 chr(80) => 'P', chr(81) => 'Q', chr(82) => 'R', chr(83) => 'S', 480 chr(84) => 'T', chr(85) => 'U', chr(86) => 'V', chr(87) => 'W', 481 chr(88) => 'X', chr(89) => 'Y', chr(90) => 'Z', chr(91) => chr(131).'K', 482 chr(92) => chr(131).'L', chr(93) => chr(131).'M', chr(94) => chr(131).'N', chr(95) => chr(131).'O', 483 chr(96) => chr(131).'W', chr(97) => chr(130).'A', chr(98) => chr(130).'B', chr(99) => chr(130).'C', 484 chr(100) => chr(130).'D', chr(101) => chr(130).'E', chr(102) => chr(130).'F', chr(103) => chr(130).'G', 485 chr(104) => chr(130).'H', chr(105) => chr(130).'I', chr(106) => chr(130).'J', chr(107) => chr(130).'K', 486 chr(108) => chr(130).'L', chr(109) => chr(130).'M', chr(110) => chr(130).'N', chr(111) => chr(130).'O', 487 chr(112) => chr(130).'P', chr(113) => chr(130).'Q', chr(114) => chr(130).'R', chr(115) => chr(130).'S', 488 chr(116) => chr(130).'T', chr(117) => chr(130).'U', chr(118) => chr(130).'V', chr(119) => chr(130).'W', 489 chr(120) => chr(130).'X', chr(121) => chr(130).'Y', chr(122) => chr(130).'Z', chr(123) => chr(131).'P', 490 chr(124) => chr(131).'Q', chr(125) => chr(131).'R', chr(126) => chr(131).'S', chr(127) => chr(131).'T'); 491 $code_ext = ''; 492 $clen = strlen($code); 493 for ($i = 0 ; $i < $clen; ++$i) { 494 if (ord($code[$i]) > 127) { 495 return false; 496 } 497 $code_ext .= $encode[$code[$i]]; 498 } 499 // checksum 500 $code_ext .= $this->checksum_code93($code_ext); 501 // add start and stop codes 502 $code = '*'.$code_ext.'*'; 503 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 504 $k = 0; 505 $clen = strlen($code); 506 for ($i = 0; $i < $clen; ++$i) { 507 $char = ord($code[$i]); 508 if(!isset($chr[$char])) { 509 // invalid character 510 return false; 511 } 512 for ($j = 0; $j < 6; ++$j) { 513 if (($j % 2) == 0) { 514 $t = true; // bar 515 } else { 516 $t = false; // space 517 } 518 $w = $chr[$char][$j]; 519 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 520 $bararray['maxw'] += $w; 521 ++$k; 522 } 523 } 524 $bararray['bcode'][$k] = array('t' => true, 'w' => 1, 'h' => 1, 'p' => 0); 525 $bararray['maxw'] += 1; 526 ++$k; 527 return $bararray; 528 } 529 530 /** 531 * Calculate CODE 93 checksum (modulo 47). 532 * @param $code (string) code to represent. 533 * @return string checksum code. 534 * @protected 535 */ 536 protected function checksum_code93($code) { 537 $chars = array( 538 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 539 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 540 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 541 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%', 542 '<', '=', '>', '?'); 543 // translate special characters 544 $code = strtr($code, chr(128).chr(131).chr(129).chr(130), '<=>?'); 545 $len = strlen($code); 546 // calculate check digit C 547 $p = 1; 548 $check = 0; 549 for ($i = ($len - 1); $i >= 0; --$i) { 550 $k = array_keys($chars, $code[$i]); 551 $check += ($k[0] * $p); 552 ++$p; 553 if ($p > 20) { 554 $p = 1; 555 } 556 } 557 $check %= 47; 558 $c = $chars[$check]; 559 $code .= $c; 560 // calculate check digit K 561 $p = 1; 562 $check = 0; 563 for ($i = $len; $i >= 0; --$i) { 564 $k = array_keys($chars, $code[$i]); 565 $check += ($k[0] * $p); 566 ++$p; 567 if ($p > 15) { 568 $p = 1; 569 } 570 } 571 $check %= 47; 572 $k = $chars[$check]; 573 $checksum = $c.$k; 574 // resto respecial characters 575 $checksum = strtr($checksum, '<=>?', chr(128).chr(131).chr(129).chr(130)); 576 return $checksum; 577 } 578 579 /** 580 * Checksum for standard 2 of 5 barcodes. 581 * @param $code (string) code to process. 582 * @return int checksum. 583 * @protected 584 */ 585 protected function checksum_s25($code) { 586 $len = strlen($code); 587 $sum = 0; 588 for ($i = 0; $i < $len; $i+=2) { 589 $sum += $code[$i]; 590 } 591 $sum *= 3; 592 for ($i = 1; $i < $len; $i+=2) { 593 $sum += ($code[$i]); 594 } 595 $r = $sum % 10; 596 if($r > 0) { 597 $r = (10 - $r); 598 } 599 return $r; 600 } 601 602 /** 603 * MSI. 604 * Variation of Plessey code, with similar applications 605 * Contains digits (0 to 9) and encodes the data only in the width of bars. 606 * @param $code (string) code to represent. 607 * @param $checksum (boolean) if true add a checksum to the code (modulo 11) 608 * @return array barcode representation. 609 * @protected 610 */ 611 protected function barcode_msi($code, $checksum=false) { 612 $chr['0'] = '100100100100'; 613 $chr['1'] = '100100100110'; 614 $chr['2'] = '100100110100'; 615 $chr['3'] = '100100110110'; 616 $chr['4'] = '100110100100'; 617 $chr['5'] = '100110100110'; 618 $chr['6'] = '100110110100'; 619 $chr['7'] = '100110110110'; 620 $chr['8'] = '110100100100'; 621 $chr['9'] = '110100100110'; 622 $chr['A'] = '110100110100'; 623 $chr['B'] = '110100110110'; 624 $chr['C'] = '110110100100'; 625 $chr['D'] = '110110100110'; 626 $chr['E'] = '110110110100'; 627 $chr['F'] = '110110110110'; 628 if ($checksum) { 629 // add checksum 630 $clen = strlen($code); 631 $p = 2; 632 $check = 0; 633 for ($i = ($clen - 1); $i >= 0; --$i) { 634 $check += (hexdec($code[$i]) * $p); 635 ++$p; 636 if ($p > 7) { 637 $p = 2; 638 } 639 } 640 $check %= 11; 641 if ($check > 0) { 642 $check = 11 - $check; 643 } 644 $code .= $check; 645 } 646 $seq = '110'; // left guard 647 $clen = strlen($code); 648 for ($i = 0; $i < $clen; ++$i) { 649 $digit = $code[$i]; 650 if (!isset($chr[$digit])) { 651 // invalid character 652 return false; 653 } 654 $seq .= $chr[$digit]; 655 } 656 $seq .= '1001'; // right guard 657 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 658 return $this->binseq_to_array($seq, $bararray); 659 } 660 661 /** 662 * Standard 2 of 5 barcodes. 663 * Used in airline ticket marking, photofinishing 664 * Contains digits (0 to 9) and encodes the data only in the width of bars. 665 * @param $code (string) code to represent. 666 * @param $checksum (boolean) if true add a checksum to the code 667 * @return array barcode representation. 668 * @protected 669 */ 670 protected function barcode_s25($code, $checksum=false) { 671 $chr['0'] = '10101110111010'; 672 $chr['1'] = '11101010101110'; 673 $chr['2'] = '10111010101110'; 674 $chr['3'] = '11101110101010'; 675 $chr['4'] = '10101110101110'; 676 $chr['5'] = '11101011101010'; 677 $chr['6'] = '10111011101010'; 678 $chr['7'] = '10101011101110'; 679 $chr['8'] = '10101110111010'; 680 $chr['9'] = '10111010111010'; 681 if ($checksum) { 682 // add checksum 683 $code .= $this->checksum_s25($code); 684 } 685 if((strlen($code) % 2) != 0) { 686 // add leading zero if code-length is odd 687 $code = '0'.$code; 688 } 689 $seq = '11011010'; 690 $clen = strlen($code); 691 for ($i = 0; $i < $clen; ++$i) { 692 $digit = $code[$i]; 693 if (!isset($chr[$digit])) { 694 // invalid character 695 return false; 696 } 697 $seq .= $chr[$digit]; 698 } 699 $seq .= '1101011'; 700 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 701 return $this->binseq_to_array($seq, $bararray); 702 } 703 704 /** 705 * Convert binary barcode sequence to TCPDF barcode array. 706 * @param $seq (string) barcode as binary sequence. 707 * @param $bararray (array) barcode array. 708 * òparam array $bararray TCPDF barcode array to fill up 709 * @return array barcode representation. 710 * @protected 711 */ 712 protected function binseq_to_array($seq, $bararray) { 713 $len = strlen($seq); 714 $w = 0; 715 $k = 0; 716 for ($i = 0; $i < $len; ++$i) { 717 $w += 1; 718 if (($i == ($len - 1)) OR (($i < ($len - 1)) AND ($seq[$i] != $seq[($i+1)]))) { 719 if ($seq[$i] == '1') { 720 $t = true; // bar 721 } else { 722 $t = false; // space 723 } 724 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 725 $bararray['maxw'] += $w; 726 ++$k; 727 $w = 0; 728 } 729 } 730 return $bararray; 731 } 732 733 /** 734 * Interleaved 2 of 5 barcodes. 735 * Compact numeric code, widely used in industry, air cargo 736 * Contains digits (0 to 9) and encodes the data in the width of both bars and spaces. 737 * @param $code (string) code to represent. 738 * @param $checksum (boolean) if true add a checksum to the code 739 * @return array barcode representation. 740 * @protected 741 */ 742 protected function barcode_i25($code, $checksum=false) { 743 $chr['0'] = '11221'; 744 $chr['1'] = '21112'; 745 $chr['2'] = '12112'; 746 $chr['3'] = '22111'; 747 $chr['4'] = '11212'; 748 $chr['5'] = '21211'; 749 $chr['6'] = '12211'; 750 $chr['7'] = '11122'; 751 $chr['8'] = '21121'; 752 $chr['9'] = '12121'; 753 $chr['A'] = '11'; 754 $chr['Z'] = '21'; 755 if ($checksum) { 756 // add checksum 757 $code .= $this->checksum_s25($code); 758 } 759 if((strlen($code) % 2) != 0) { 760 // add leading zero if code-length is odd 761 $code = '0'.$code; 762 } 763 // add start and stop codes 764 $code = 'AA'.strtolower($code).'ZA'; 765 766 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 767 $k = 0; 768 $clen = strlen($code); 769 for ($i = 0; $i < $clen; $i = ($i + 2)) { 770 $char_bar = $code[$i]; 771 $char_space = $code[$i+1]; 772 if((!isset($chr[$char_bar])) OR (!isset($chr[$char_space]))) { 773 // invalid character 774 return false; 775 } 776 // create a bar-space sequence 777 $seq = ''; 778 $chrlen = strlen($chr[$char_bar]); 779 for ($s = 0; $s < $chrlen; $s++){ 780 $seq .= $chr[$char_bar][$s] . $chr[$char_space][$s]; 781 } 782 $seqlen = strlen($seq); 783 for ($j = 0; $j < $seqlen; ++$j) { 784 if (($j % 2) == 0) { 785 $t = true; // bar 786 } else { 787 $t = false; // space 788 } 789 $w = $seq[$j]; 790 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 791 $bararray['maxw'] += $w; 792 ++$k; 793 } 794 } 795 return $bararray; 796 } 797 798 /** 799 * C128 barcodes. 800 * Very capable code, excellent density, high reliability; in very wide use world-wide 801 * @param $code (string) code to represent. 802 * @param $type (string) barcode type: A, B, C or empty for automatic switch (AUTO mode) 803 * @return array barcode representation. 804 * @protected 805 */ 806 protected function barcode_c128($code, $type='') { 807 $chr = array( 808 '212222', /* 00 */ 809 '222122', /* 01 */ 810 '222221', /* 02 */ 811 '121223', /* 03 */ 812 '121322', /* 04 */ 813 '131222', /* 05 */ 814 '122213', /* 06 */ 815 '122312', /* 07 */ 816 '132212', /* 08 */ 817 '221213', /* 09 */ 818 '221312', /* 10 */ 819 '231212', /* 11 */ 820 '112232', /* 12 */ 821 '122132', /* 13 */ 822 '122231', /* 14 */ 823 '113222', /* 15 */ 824 '123122', /* 16 */ 825 '123221', /* 17 */ 826 '223211', /* 18 */ 827 '221132', /* 19 */ 828 '221231', /* 20 */ 829 '213212', /* 21 */ 830 '223112', /* 22 */ 831 '312131', /* 23 */ 832 '311222', /* 24 */ 833 '321122', /* 25 */ 834 '321221', /* 26 */ 835 '312212', /* 27 */ 836 '322112', /* 28 */ 837 '322211', /* 29 */ 838 '212123', /* 30 */ 839 '212321', /* 31 */ 840 '232121', /* 32 */ 841 '111323', /* 33 */ 842 '131123', /* 34 */ 843 '131321', /* 35 */ 844 '112313', /* 36 */ 845 '132113', /* 37 */ 846 '132311', /* 38 */ 847 '211313', /* 39 */ 848 '231113', /* 40 */ 849 '231311', /* 41 */ 850 '112133', /* 42 */ 851 '112331', /* 43 */ 852 '132131', /* 44 */ 853 '113123', /* 45 */ 854 '113321', /* 46 */ 855 '133121', /* 47 */ 856 '313121', /* 48 */ 857 '211331', /* 49 */ 858 '231131', /* 50 */ 859 '213113', /* 51 */ 860 '213311', /* 52 */ 861 '213131', /* 53 */ 862 '311123', /* 54 */ 863 '311321', /* 55 */ 864 '331121', /* 56 */ 865 '312113', /* 57 */ 866 '312311', /* 58 */ 867 '332111', /* 59 */ 868 '314111', /* 60 */ 869 '221411', /* 61 */ 870 '431111', /* 62 */ 871 '111224', /* 63 */ 872 '111422', /* 64 */ 873 '121124', /* 65 */ 874 '121421', /* 66 */ 875 '141122', /* 67 */ 876 '141221', /* 68 */ 877 '112214', /* 69 */ 878 '112412', /* 70 */ 879 '122114', /* 71 */ 880 '122411', /* 72 */ 881 '142112', /* 73 */ 882 '142211', /* 74 */ 883 '241211', /* 75 */ 884 '221114', /* 76 */ 885 '413111', /* 77 */ 886 '241112', /* 78 */ 887 '134111', /* 79 */ 888 '111242', /* 80 */ 889 '121142', /* 81 */ 890 '121241', /* 82 */ 891 '114212', /* 83 */ 892 '124112', /* 84 */ 893 '124211', /* 85 */ 894 '411212', /* 86 */ 895 '421112', /* 87 */ 896 '421211', /* 88 */ 897 '212141', /* 89 */ 898 '214121', /* 90 */ 899 '412121', /* 91 */ 900 '111143', /* 92 */ 901 '111341', /* 93 */ 902 '131141', /* 94 */ 903 '114113', /* 95 */ 904 '114311', /* 96 */ 905 '411113', /* 97 */ 906 '411311', /* 98 */ 907 '113141', /* 99 */ 908 '114131', /* 100 */ 909 '311141', /* 101 */ 910 '411131', /* 102 */ 911 '211412', /* 103 START A */ 912 '211214', /* 104 START B */ 913 '211232', /* 105 START C */ 914 '233111', /* STOP */ 915 '200000' /* END */ 916 ); 917 // ASCII characters for code A (ASCII 00 - 95) 918 $keys_a = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'; 919 $keys_a .= chr(0).chr(1).chr(2).chr(3).chr(4).chr(5).chr(6).chr(7).chr(8).chr(9); 920 $keys_a .= chr(10).chr(11).chr(12).chr(13).chr(14).chr(15).chr(16).chr(17).chr(18).chr(19); 921 $keys_a .= chr(20).chr(21).chr(22).chr(23).chr(24).chr(25).chr(26).chr(27).chr(28).chr(29); 922 $keys_a .= chr(30).chr(31); 923 // ASCII characters for code B (ASCII 32 - 127) 924 $keys_b = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'.chr(127); 925 // special codes 926 $fnc_a = array(241 => 102, 242 => 97, 243 => 96, 244 => 101); 927 $fnc_b = array(241 => 102, 242 => 97, 243 => 96, 244 => 100); 928 // array of symbols 929 $code_data = array(); 930 // length of the code 931 $len = strlen($code); 932 switch(strtoupper($type)) { 933 case 'A': { // MODE A 934 $startid = 103; 935 for ($i = 0; $i < $len; ++$i) { 936 $char = $code[$i]; 937 $char_id = ord($char); 938 if (($char_id >= 241) AND ($char_id <= 244)) { 939 $code_data[] = $fnc_a[$char_id]; 940 } elseif (($char_id >= 0) AND ($char_id <= 95)) { 941 $code_data[] = strpos($keys_a, $char); 942 } else { 943 return false; 944 } 945 } 946 break; 947 } 948 case 'B': { // MODE B 949 $startid = 104; 950 for ($i = 0; $i < $len; ++$i) { 951 $char = $code[$i]; 952 $char_id = ord($char); 953 if (($char_id >= 241) AND ($char_id <= 244)) { 954 $code_data[] = $fnc_b[$char_id]; 955 } elseif (($char_id >= 32) AND ($char_id <= 127)) { 956 $code_data[] = strpos($keys_b, $char); 957 } else { 958 return false; 959 } 960 } 961 break; 962 } 963 case 'C': { // MODE C 964 $startid = 105; 965 if (ord($code[0]) == 241) { 966 $code_data[] = 102; 967 $code = substr($code, 1); 968 --$len; 969 } 970 if (($len % 2) != 0) { 971 // the length must be even 972 return false; 973 } 974 for ($i = 0; $i < $len; $i+=2) { 975 $chrnum = $code[$i].$code[$i+1]; 976 if (preg_match('/([0-9]{2})/', $chrnum) > 0) { 977 $code_data[] = intval($chrnum); 978 } else { 979 return false; 980 } 981 } 982 break; 983 } 984 default: { // MODE AUTO 985 // split code into sequences 986 $sequence = array(); 987 // get numeric sequences (if any) 988 $numseq = array(); 989 preg_match_all('/([0-9]{4,})/', $code, $numseq, PREG_OFFSET_CAPTURE); 990 if (isset($numseq[1]) AND !empty($numseq[1])) { 991 $end_offset = 0; 992 foreach ($numseq[1] as $val) { 993 $offset = $val[1]; 994 if ($offset > $end_offset) { 995 // non numeric sequence 996 $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset, ($offset - $end_offset)))); 997 } 998 // numeric sequence 999 $slen = strlen($val[0]); 1000 if (($slen % 2) != 0) { 1001 // the length must be even 1002 --$slen; 1003 } 1004 $sequence[] = array('C', substr($code, $offset, $slen), $slen); 1005 $end_offset = $offset + $slen; 1006 } 1007 if ($end_offset < $len) { 1008 $sequence = array_merge($sequence, $this->get128ABsequence(substr($code, $end_offset))); 1009 } 1010 } else { 1011 // text code (non C mode) 1012 $sequence = array_merge($sequence, $this->get128ABsequence($code)); 1013 } 1014 // process the sequence 1015 foreach ($sequence as $key => $seq) { 1016 switch($seq[0]) { 1017 case 'A': { 1018 if ($key == 0) { 1019 $startid = 103; 1020 } elseif ($sequence[($key - 1)][0] != 'A') { 1021 if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'B') AND (!isset($sequence[($key - 1)][3]))) { 1022 // single character shift 1023 $code_data[] = 98; 1024 // mark shift 1025 $sequence[$key][3] = true; 1026 } elseif (!isset($sequence[($key - 1)][3])) { 1027 $code_data[] = 101; 1028 } 1029 } 1030 for ($i = 0; $i < $seq[2]; ++$i) { 1031 $char = $seq[1][$i]; 1032 $char_id = ord($char); 1033 if (($char_id >= 241) AND ($char_id <= 244)) { 1034 $code_data[] = $fnc_a[$char_id]; 1035 } else { 1036 $code_data[] = strpos($keys_a, $char); 1037 } 1038 } 1039 break; 1040 } 1041 case 'B': { 1042 if ($key == 0) { 1043 $tmpchr = ord($seq[1][0]); 1044 if (($seq[2] == 1) AND ($tmpchr >= 241) AND ($tmpchr <= 244) AND isset($sequence[($key + 1)]) AND ($sequence[($key + 1)][0] != 'B')) { 1045 switch ($sequence[($key + 1)][0]) { 1046 case 'A': { 1047 $startid = 103; 1048 $sequence[$key][0] = 'A'; 1049 $code_data[] = $fnc_a[$tmpchr]; 1050 break; 1051 } 1052 case 'C': { 1053 $startid = 105; 1054 $sequence[$key][0] = 'C'; 1055 $code_data[] = $fnc_a[$tmpchr]; 1056 break; 1057 } 1058 } 1059 break; 1060 } else { 1061 $startid = 104; 1062 } 1063 } elseif ($sequence[($key - 1)][0] != 'B') { 1064 if (($seq[2] == 1) AND ($key > 0) AND ($sequence[($key - 1)][0] == 'A') AND (!isset($sequence[($key - 1)][3]))) { 1065 // single character shift 1066 $code_data[] = 98; 1067 // mark shift 1068 $sequence[$key][3] = true; 1069 } elseif (!isset($sequence[($key - 1)][3])) { 1070 $code_data[] = 100; 1071 } 1072 } 1073 for ($i = 0; $i < $seq[2]; ++$i) { 1074 $char = $seq[1][$i]; 1075 $char_id = ord($char); 1076 if (($char_id >= 241) AND ($char_id <= 244)) { 1077 $code_data[] = $fnc_b[$char_id]; 1078 } else { 1079 $code_data[] = strpos($keys_b, $char); 1080 } 1081 } 1082 break; 1083 } 1084 case 'C': { 1085 if ($key == 0) { 1086 $startid = 105; 1087 } elseif ($sequence[($key - 1)][0] != 'C') { 1088 $code_data[] = 99; 1089 } 1090 for ($i = 0; $i < $seq[2]; $i+=2) { 1091 $chrnum = $seq[1][$i].$seq[1][$i+1]; 1092 $code_data[] = intval($chrnum); 1093 } 1094 break; 1095 } 1096 } 1097 } 1098 } 1099 } 1100 // calculate check character 1101 $sum = $startid; 1102 foreach ($code_data as $key => $val) { 1103 $sum += ($val * ($key + 1)); 1104 } 1105 // add check character 1106 $code_data[] = ($sum % 103); 1107 // add stop sequence 1108 $code_data[] = 106; 1109 $code_data[] = 107; 1110 // add start code at the beginning 1111 array_unshift($code_data, $startid); 1112 // build barcode array 1113 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1114 foreach ($code_data as $val) { 1115 $seq = $chr[$val]; 1116 for ($j = 0; $j < 6; ++$j) { 1117 if (($j % 2) == 0) { 1118 $t = true; // bar 1119 } else { 1120 $t = false; // space 1121 } 1122 $w = $seq[$j]; 1123 $bararray['bcode'][] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 1124 $bararray['maxw'] += $w; 1125 } 1126 } 1127 return $bararray; 1128 } 1129 1130 /** 1131 * Split text code in A/B sequence for 128 code 1132 * @param $code (string) code to split. 1133 * @return array sequence 1134 * @protected 1135 */ 1136 protected function get128ABsequence($code) { 1137 $len = strlen($code); 1138 $sequence = array(); 1139 // get A sequences (if any) 1140 $numseq = array(); 1141 preg_match_all('/([\0-\31])/', $code, $numseq, PREG_OFFSET_CAPTURE); 1142 if (isset($numseq[1]) AND !empty($numseq[1])) { 1143 $end_offset = 0; 1144 foreach ($numseq[1] as $val) { 1145 $offset = $val[1]; 1146 if ($offset > $end_offset) { 1147 // B sequence 1148 $sequence[] = array('B', substr($code, $end_offset, ($offset - $end_offset)), ($offset - $end_offset)); 1149 } 1150 // A sequence 1151 $slen = strlen($val[0]); 1152 $sequence[] = array('A', substr($code, $offset, $slen), $slen); 1153 $end_offset = $offset + $slen; 1154 } 1155 if ($end_offset < $len) { 1156 $sequence[] = array('B', substr($code, $end_offset), ($len - $end_offset)); 1157 } 1158 } else { 1159 // only B sequence 1160 $sequence[] = array('B', $code, $len); 1161 } 1162 return $sequence; 1163 } 1164 1165 /** 1166 * EAN13 and UPC-A barcodes. 1167 * EAN13: European Article Numbering international retail product code 1168 * UPC-A: Universal product code seen on almost all retail products in the USA and Canada 1169 * UPC-E: Short version of UPC symbol 1170 * @param $code (string) code to represent. 1171 * @param $len (string) barcode type: 6 = UPC-E, 8 = EAN8, 13 = EAN13, 12 = UPC-A 1172 * @return array barcode representation. 1173 * @protected 1174 */ 1175 protected function barcode_eanupc($code, $len=13) { 1176 $upce = false; 1177 if ($len == 6) { 1178 $len = 12; // UPC-A 1179 $upce = true; // UPC-E mode 1180 } 1181 $data_len = $len - 1; 1182 //Padding 1183 $code = str_pad($code, $data_len, '0', STR_PAD_LEFT); 1184 $code_len = strlen($code); 1185 // calculate check digit 1186 $sum_a = 0; 1187 for ($i = 1; $i < $data_len; $i+=2) { 1188 $sum_a += $code[$i]; 1189 } 1190 if ($len > 12) { 1191 $sum_a *= 3; 1192 } 1193 $sum_b = 0; 1194 for ($i = 0; $i < $data_len; $i+=2) { 1195 $sum_b += ($code[$i]); 1196 } 1197 if ($len < 13) { 1198 $sum_b *= 3; 1199 } 1200 $r = ($sum_a + $sum_b) % 10; 1201 if($r > 0) { 1202 $r = (10 - $r); 1203 } 1204 if ($code_len == $data_len) { 1205 // add check digit 1206 $code .= $r; 1207 } elseif ($r !== intval($code[$data_len])) { 1208 // wrong checkdigit 1209 return false; 1210 } 1211 if ($len == 12) { 1212 // UPC-A 1213 $code = '0'.$code; 1214 ++$len; 1215 } 1216 if ($upce) { 1217 // convert UPC-A to UPC-E 1218 $tmp = substr($code, 4, 3); 1219 if (($tmp == '000') OR ($tmp == '100') OR ($tmp == '200')) { 1220 // manufacturer code ends in 000, 100, or 200 1221 $upce_code = substr($code, 2, 2).substr($code, 9, 3).substr($code, 4, 1); 1222 } else { 1223 $tmp = substr($code, 5, 2); 1224 if ($tmp == '00') { 1225 // manufacturer code ends in 00 1226 $upce_code = substr($code, 2, 3).substr($code, 10, 2).'3'; 1227 } else { 1228 $tmp = substr($code, 6, 1); 1229 if ($tmp == '0') { 1230 // manufacturer code ends in 0 1231 $upce_code = substr($code, 2, 4).substr($code, 11, 1).'4'; 1232 } else { 1233 // manufacturer code does not end in zero 1234 $upce_code = substr($code, 2, 5).substr($code, 11, 1); 1235 } 1236 } 1237 } 1238 } 1239 //Convert digits to bars 1240 $codes = array( 1241 'A'=>array( // left odd parity 1242 '0'=>'0001101', 1243 '1'=>'0011001', 1244 '2'=>'0010011', 1245 '3'=>'0111101', 1246 '4'=>'0100011', 1247 '5'=>'0110001', 1248 '6'=>'0101111', 1249 '7'=>'0111011', 1250 '8'=>'0110111', 1251 '9'=>'0001011'), 1252 'B'=>array( // left even parity 1253 '0'=>'0100111', 1254 '1'=>'0110011', 1255 '2'=>'0011011', 1256 '3'=>'0100001', 1257 '4'=>'0011101', 1258 '5'=>'0111001', 1259 '6'=>'0000101', 1260 '7'=>'0010001', 1261 '8'=>'0001001', 1262 '9'=>'0010111'), 1263 'C'=>array( // right 1264 '0'=>'1110010', 1265 '1'=>'1100110', 1266 '2'=>'1101100', 1267 '3'=>'1000010', 1268 '4'=>'1011100', 1269 '5'=>'1001110', 1270 '6'=>'1010000', 1271 '7'=>'1000100', 1272 '8'=>'1001000', 1273 '9'=>'1110100') 1274 ); 1275 $parities = array( 1276 '0'=>array('A','A','A','A','A','A'), 1277 '1'=>array('A','A','B','A','B','B'), 1278 '2'=>array('A','A','B','B','A','B'), 1279 '3'=>array('A','A','B','B','B','A'), 1280 '4'=>array('A','B','A','A','B','B'), 1281 '5'=>array('A','B','B','A','A','B'), 1282 '6'=>array('A','B','B','B','A','A'), 1283 '7'=>array('A','B','A','B','A','B'), 1284 '8'=>array('A','B','A','B','B','A'), 1285 '9'=>array('A','B','B','A','B','A') 1286 ); 1287 $upce_parities = array(); 1288 $upce_parities[0] = array( 1289 '0'=>array('B','B','B','A','A','A'), 1290 '1'=>array('B','B','A','B','A','A'), 1291 '2'=>array('B','B','A','A','B','A'), 1292 '3'=>array('B','B','A','A','A','B'), 1293 '4'=>array('B','A','B','B','A','A'), 1294 '5'=>array('B','A','A','B','B','A'), 1295 '6'=>array('B','A','A','A','B','B'), 1296 '7'=>array('B','A','B','A','B','A'), 1297 '8'=>array('B','A','B','A','A','B'), 1298 '9'=>array('B','A','A','B','A','B') 1299 ); 1300 $upce_parities[1] = array( 1301 '0'=>array('A','A','A','B','B','B'), 1302 '1'=>array('A','A','B','A','B','B'), 1303 '2'=>array('A','A','B','B','A','B'), 1304 '3'=>array('A','A','B','B','B','A'), 1305 '4'=>array('A','B','A','A','B','B'), 1306 '5'=>array('A','B','B','A','A','B'), 1307 '6'=>array('A','B','B','B','A','A'), 1308 '7'=>array('A','B','A','B','A','B'), 1309 '8'=>array('A','B','A','B','B','A'), 1310 '9'=>array('A','B','B','A','B','A') 1311 ); 1312 $k = 0; 1313 $seq = '101'; // left guard bar 1314 if ($upce) { 1315 $bararray = array('code' => $upce_code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1316 $p = $upce_parities[$code[1]][$r]; 1317 for ($i = 0; $i < 6; ++$i) { 1318 $seq .= $codes[$p[$i]][$upce_code[$i]]; 1319 } 1320 $seq .= '010101'; // right guard bar 1321 } else { 1322 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1323 $half_len = intval(ceil($len / 2)); 1324 if ($len == 8) { 1325 for ($i = 0; $i < $half_len; ++$i) { 1326 $seq .= $codes['A'][$code[$i]]; 1327 } 1328 } else { 1329 $p = $parities[$code[0]]; 1330 for ($i = 1; $i < $half_len; ++$i) { 1331 $seq .= $codes[$p[$i-1]][$code[$i]]; 1332 } 1333 } 1334 $seq .= '01010'; // center guard bar 1335 for ($i = $half_len; $i < $len; ++$i) { 1336 $seq .= $codes['C'][$code[$i]]; 1337 } 1338 $seq .= '101'; // right guard bar 1339 } 1340 $clen = strlen($seq); 1341 $w = 0; 1342 for ($i = 0; $i < $clen; ++$i) { 1343 $w += 1; 1344 if (($i == ($clen - 1)) OR (($i < ($clen - 1)) AND ($seq[$i] != $seq[($i+1)]))) { 1345 if ($seq[$i] == '1') { 1346 $t = true; // bar 1347 } else { 1348 $t = false; // space 1349 } 1350 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 1351 $bararray['maxw'] += $w; 1352 ++$k; 1353 $w = 0; 1354 } 1355 } 1356 return $bararray; 1357 } 1358 1359 /** 1360 * UPC-Based Extensions 1361 * 2-Digit Ext.: Used to indicate magazines and newspaper issue numbers 1362 * 5-Digit Ext.: Used to mark suggested retail price of books 1363 * @param $code (string) code to represent. 1364 * @param $len (string) barcode type: 2 = 2-Digit, 5 = 5-Digit 1365 * @return array barcode representation. 1366 * @protected 1367 */ 1368 protected function barcode_eanext($code, $len=5) { 1369 //Padding 1370 $code = str_pad($code, $len, '0', STR_PAD_LEFT); 1371 // calculate check digit 1372 if ($len == 2) { 1373 $r = $code % 4; 1374 } elseif ($len == 5) { 1375 $r = (3 * ($code[0] + $code[2] + $code[4])) + (9 * ($code[1] + $code[3])); 1376 $r %= 10; 1377 } else { 1378 return false; 1379 } 1380 //Convert digits to bars 1381 $codes = array( 1382 'A'=>array( // left odd parity 1383 '0'=>'0001101', 1384 '1'=>'0011001', 1385 '2'=>'0010011', 1386 '3'=>'0111101', 1387 '4'=>'0100011', 1388 '5'=>'0110001', 1389 '6'=>'0101111', 1390 '7'=>'0111011', 1391 '8'=>'0110111', 1392 '9'=>'0001011'), 1393 'B'=>array( // left even parity 1394 '0'=>'0100111', 1395 '1'=>'0110011', 1396 '2'=>'0011011', 1397 '3'=>'0100001', 1398 '4'=>'0011101', 1399 '5'=>'0111001', 1400 '6'=>'0000101', 1401 '7'=>'0010001', 1402 '8'=>'0001001', 1403 '9'=>'0010111') 1404 ); 1405 $parities = array(); 1406 $parities[2] = array( 1407 '0'=>array('A','A'), 1408 '1'=>array('A','B'), 1409 '2'=>array('B','A'), 1410 '3'=>array('B','B') 1411 ); 1412 $parities[5] = array( 1413 '0'=>array('B','B','A','A','A'), 1414 '1'=>array('B','A','B','A','A'), 1415 '2'=>array('B','A','A','B','A'), 1416 '3'=>array('B','A','A','A','B'), 1417 '4'=>array('A','B','B','A','A'), 1418 '5'=>array('A','A','B','B','A'), 1419 '6'=>array('A','A','A','B','B'), 1420 '7'=>array('A','B','A','B','A'), 1421 '8'=>array('A','B','A','A','B'), 1422 '9'=>array('A','A','B','A','B') 1423 ); 1424 $p = $parities[$len][$r]; 1425 $seq = '1011'; // left guard bar 1426 $seq .= $codes[$p[0]][$code[0]]; 1427 for ($i = 1; $i < $len; ++$i) { 1428 $seq .= '01'; // separator 1429 $seq .= $codes[$p[$i]][$code[$i]]; 1430 } 1431 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1432 return $this->binseq_to_array($seq, $bararray); 1433 } 1434 1435 /** 1436 * POSTNET and PLANET barcodes. 1437 * Used by U.S. Postal Service for automated mail sorting 1438 * @param $code (string) zip code to represent. Must be a string containing a zip code of the form DDDDD or DDDDD-DDDD. 1439 * @param $planet (boolean) if true print the PLANET barcode, otherwise print POSTNET 1440 * @return array barcode representation. 1441 * @protected 1442 */ 1443 protected function barcode_postnet($code, $planet=false) { 1444 // bar length 1445 if ($planet) { 1446 $barlen = Array( 1447 0 => Array(1,1,2,2,2), 1448 1 => Array(2,2,2,1,1), 1449 2 => Array(2,2,1,2,1), 1450 3 => Array(2,2,1,1,2), 1451 4 => Array(2,1,2,2,1), 1452 5 => Array(2,1,2,1,2), 1453 6 => Array(2,1,1,2,2), 1454 7 => Array(1,2,2,2,1), 1455 8 => Array(1,2,2,1,2), 1456 9 => Array(1,2,1,2,2) 1457 ); 1458 } else { 1459 $barlen = Array( 1460 0 => Array(2,2,1,1,1), 1461 1 => Array(1,1,1,2,2), 1462 2 => Array(1,1,2,1,2), 1463 3 => Array(1,1,2,2,1), 1464 4 => Array(1,2,1,1,2), 1465 5 => Array(1,2,1,2,1), 1466 6 => Array(1,2,2,1,1), 1467 7 => Array(2,1,1,1,2), 1468 8 => Array(2,1,1,2,1), 1469 9 => Array(2,1,2,1,1) 1470 ); 1471 } 1472 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array()); 1473 $k = 0; 1474 $code = str_replace('-', '', $code); 1475 $code = str_replace(' ', '', $code); 1476 $len = strlen($code); 1477 // calculate checksum 1478 $sum = 0; 1479 for ($i = 0; $i < $len; ++$i) { 1480 $sum += intval($code[$i]); 1481 } 1482 $chkd = ($sum % 10); 1483 if($chkd > 0) { 1484 $chkd = (10 - $chkd); 1485 } 1486 $code .= $chkd; 1487 $len = strlen($code); 1488 // start bar 1489 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0); 1490 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 1491 $bararray['maxw'] += 2; 1492 for ($i = 0; $i < $len; ++$i) { 1493 for ($j = 0; $j < 5; ++$j) { 1494 $h = $barlen[$code[$i]][$j]; 1495 $p = floor(1 / $h); 1496 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p); 1497 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 1498 $bararray['maxw'] += 2; 1499 } 1500 } 1501 // end bar 1502 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0); 1503 $bararray['maxw'] += 1; 1504 return $bararray; 1505 } 1506 1507 /** 1508 * RMS4CC - CBC - KIX 1509 * RMS4CC (Royal Mail 4-state Customer Code) - CBC (Customer Bar Code) - KIX (Klant index - Customer index) 1510 * RM4SCC is the name of the barcode symbology used by the Royal Mail for its Cleanmail service. 1511 * @param $code (string) code to print 1512 * @param $kix (boolean) if true prints the KIX variation (doesn't use the start and end symbols, and the checksum) - in this case the house number must be sufficed with an X and placed at the end of the code. 1513 * @return array barcode representation. 1514 * @protected 1515 */ 1516 protected function barcode_rms4cc($code, $kix=false) { 1517 $notkix = !$kix; 1518 // bar mode 1519 // 1 = pos 1, length 2 1520 // 2 = pos 1, length 3 1521 // 3 = pos 2, length 1 1522 // 4 = pos 2, length 2 1523 $barmode = array( 1524 '0' => array(3,3,2,2), 1525 '1' => array(3,4,1,2), 1526 '2' => array(3,4,2,1), 1527 '3' => array(4,3,1,2), 1528 '4' => array(4,3,2,1), 1529 '5' => array(4,4,1,1), 1530 '6' => array(3,1,4,2), 1531 '7' => array(3,2,3,2), 1532 '8' => array(3,2,4,1), 1533 '9' => array(4,1,3,2), 1534 'A' => array(4,1,4,1), 1535 'B' => array(4,2,3,1), 1536 'C' => array(3,1,2,4), 1537 'D' => array(3,2,1,4), 1538 'E' => array(3,2,2,3), 1539 'F' => array(4,1,1,4), 1540 'G' => array(4,1,2,3), 1541 'H' => array(4,2,1,3), 1542 'I' => array(1,3,4,2), 1543 'J' => array(1,4,3,2), 1544 'K' => array(1,4,4,1), 1545 'L' => array(2,3,3,2), 1546 'M' => array(2,3,4,1), 1547 'N' => array(2,4,3,1), 1548 'O' => array(1,3,2,4), 1549 'P' => array(1,4,1,4), 1550 'Q' => array(1,4,2,3), 1551 'R' => array(2,3,1,4), 1552 'S' => array(2,3,2,3), 1553 'T' => array(2,4,1,3), 1554 'U' => array(1,1,4,4), 1555 'V' => array(1,2,3,4), 1556 'W' => array(1,2,4,3), 1557 'X' => array(2,1,3,4), 1558 'Y' => array(2,1,4,3), 1559 'Z' => array(2,2,3,3) 1560 ); 1561 $code = strtoupper($code); 1562 $len = strlen($code); 1563 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array()); 1564 if ($notkix) { 1565 // table for checksum calculation (row,col) 1566 $checktable = array( 1567 '0' => array(1,1), 1568 '1' => array(1,2), 1569 '2' => array(1,3), 1570 '3' => array(1,4), 1571 '4' => array(1,5), 1572 '5' => array(1,0), 1573 '6' => array(2,1), 1574 '7' => array(2,2), 1575 '8' => array(2,3), 1576 '9' => array(2,4), 1577 'A' => array(2,5), 1578 'B' => array(2,0), 1579 'C' => array(3,1), 1580 'D' => array(3,2), 1581 'E' => array(3,3), 1582 'F' => array(3,4), 1583 'G' => array(3,5), 1584 'H' => array(3,0), 1585 'I' => array(4,1), 1586 'J' => array(4,2), 1587 'K' => array(4,3), 1588 'L' => array(4,4), 1589 'M' => array(4,5), 1590 'N' => array(4,0), 1591 'O' => array(5,1), 1592 'P' => array(5,2), 1593 'Q' => array(5,3), 1594 'R' => array(5,4), 1595 'S' => array(5,5), 1596 'T' => array(5,0), 1597 'U' => array(0,1), 1598 'V' => array(0,2), 1599 'W' => array(0,3), 1600 'X' => array(0,4), 1601 'Y' => array(0,5), 1602 'Z' => array(0,0) 1603 ); 1604 $row = 0; 1605 $col = 0; 1606 for ($i = 0; $i < $len; ++$i) { 1607 $row += $checktable[$code[$i]][0]; 1608 $col += $checktable[$code[$i]][1]; 1609 } 1610 $row %= 6; 1611 $col %= 6; 1612 $chk = array_keys($checktable, array($row,$col)); 1613 $code .= $chk[0]; 1614 ++$len; 1615 } 1616 $k = 0; 1617 if ($notkix) { 1618 // start bar 1619 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 2, 'p' => 0); 1620 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 1621 $bararray['maxw'] += 2; 1622 } 1623 for ($i = 0; $i < $len; ++$i) { 1624 for ($j = 0; $j < 4; ++$j) { 1625 switch ($barmode[$code[$i]][$j]) { 1626 case 1: { 1627 $p = 0; 1628 $h = 2; 1629 break; 1630 } 1631 case 2: { 1632 $p = 0; 1633 $h = 3; 1634 break; 1635 } 1636 case 3: { 1637 $p = 1; 1638 $h = 1; 1639 break; 1640 } 1641 case 4: { 1642 $p = 1; 1643 $h = 2; 1644 break; 1645 } 1646 } 1647 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p); 1648 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 1649 $bararray['maxw'] += 2; 1650 } 1651 } 1652 if ($notkix) { 1653 // stop bar 1654 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => 3, 'p' => 0); 1655 $bararray['maxw'] += 1; 1656 } 1657 return $bararray; 1658 } 1659 1660 /** 1661 * CODABAR barcodes. 1662 * Older code often used in library systems, sometimes in blood banks 1663 * @param $code (string) code to represent. 1664 * @return array barcode representation. 1665 * @protected 1666 */ 1667 protected function barcode_codabar($code) { 1668 $chr = array( 1669 '0' => '11111221', 1670 '1' => '11112211', 1671 '2' => '11121121', 1672 '3' => '22111111', 1673 '4' => '11211211', 1674 '5' => '21111211', 1675 '6' => '12111121', 1676 '7' => '12112111', 1677 '8' => '12211111', 1678 '9' => '21121111', 1679 '-' => '11122111', 1680 '$' => '11221111', 1681 ':' => '21112121', 1682 '/' => '21211121', 1683 '.' => '21212111', 1684 '+' => '11222221', 1685 'A' => '11221211', 1686 'B' => '12121121', 1687 'C' => '11121221', 1688 'D' => '11122211' 1689 ); 1690 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1691 $k = 0; 1692 $w = 0; 1693 $seq = ''; 1694 $code = 'A'.strtoupper($code).'A'; 1695 $len = strlen($code); 1696 for ($i = 0; $i < $len; ++$i) { 1697 if (!isset($chr[$code[$i]])) { 1698 return false; 1699 } 1700 $seq = $chr[$code[$i]]; 1701 for ($j = 0; $j < 8; ++$j) { 1702 if (($j % 2) == 0) { 1703 $t = true; // bar 1704 } else { 1705 $t = false; // space 1706 } 1707 $w = $seq[$j]; 1708 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 1709 $bararray['maxw'] += $w; 1710 ++$k; 1711 } 1712 } 1713 return $bararray; 1714 } 1715 1716 /** 1717 * CODE11 barcodes. 1718 * Used primarily for labeling telecommunications equipment 1719 * @param $code (string) code to represent. 1720 * @return array barcode representation. 1721 * @protected 1722 */ 1723 protected function barcode_code11($code) { 1724 $chr = array( 1725 '0' => '111121', 1726 '1' => '211121', 1727 '2' => '121121', 1728 '3' => '221111', 1729 '4' => '112121', 1730 '5' => '212111', 1731 '6' => '122111', 1732 '7' => '111221', 1733 '8' => '211211', 1734 '9' => '211111', 1735 '-' => '112111', 1736 'S' => '112211' 1737 ); 1738 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1739 $k = 0; 1740 $w = 0; 1741 $seq = ''; 1742 $len = strlen($code); 1743 // calculate check digit C 1744 $p = 1; 1745 $check = 0; 1746 for ($i = ($len - 1); $i >= 0; --$i) { 1747 $digit = $code[$i]; 1748 if ($digit == '-') { 1749 $dval = 10; 1750 } else { 1751 $dval = intval($digit); 1752 } 1753 $check += ($dval * $p); 1754 ++$p; 1755 if ($p > 10) { 1756 $p = 1; 1757 } 1758 } 1759 $check %= 11; 1760 if ($check == 10) { 1761 $check = '-'; 1762 } 1763 $code .= $check; 1764 if ($len > 10) { 1765 // calculate check digit K 1766 $p = 1; 1767 $check = 0; 1768 for ($i = $len; $i >= 0; --$i) { 1769 $digit = $code[$i]; 1770 if ($digit == '-') { 1771 $dval = 10; 1772 } else { 1773 $dval = intval($digit); 1774 } 1775 $check += ($dval * $p); 1776 ++$p; 1777 if ($p > 9) { 1778 $p = 1; 1779 } 1780 } 1781 $check %= 11; 1782 $code .= $check; 1783 ++$len; 1784 } 1785 $code = 'S'.$code.'S'; 1786 $len += 3; 1787 for ($i = 0; $i < $len; ++$i) { 1788 if (!isset($chr[$code[$i]])) { 1789 return false; 1790 } 1791 $seq = $chr[$code[$i]]; 1792 for ($j = 0; $j < 6; ++$j) { 1793 if (($j % 2) == 0) { 1794 $t = true; // bar 1795 } else { 1796 $t = false; // space 1797 } 1798 $w = $seq[$j]; 1799 $bararray['bcode'][$k] = array('t' => $t, 'w' => $w, 'h' => 1, 'p' => 0); 1800 $bararray['maxw'] += $w; 1801 ++$k; 1802 } 1803 } 1804 return $bararray; 1805 } 1806 1807 /** 1808 * Pharmacode 1809 * Contains digits (0 to 9) 1810 * @param $code (string) code to represent. 1811 * @return array barcode representation. 1812 * @protected 1813 */ 1814 protected function barcode_pharmacode($code) { 1815 $seq = ''; 1816 $code = intval($code); 1817 while ($code > 0) { 1818 if (($code % 2) == 0) { 1819 $seq .= '11100'; 1820 $code -= 2; 1821 } else { 1822 $seq .= '100'; 1823 $code -= 1; 1824 } 1825 $code /= 2; 1826 } 1827 $seq = substr($seq, 0, -2); 1828 $seq = strrev($seq); 1829 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 1, 'bcode' => array()); 1830 return $this->binseq_to_array($seq, $bararray); 1831 } 1832 1833 /** 1834 * Pharmacode two-track 1835 * Contains digits (0 to 9) 1836 * @param $code (string) code to represent. 1837 * @return array barcode representation. 1838 * @protected 1839 */ 1840 protected function barcode_pharmacode2t($code) { 1841 $seq = ''; 1842 $code = intval($code); 1843 do { 1844 switch ($code % 3) { 1845 case 0: { 1846 $seq .= '3'; 1847 $code = ($code - 3) / 3; 1848 break; 1849 } 1850 case 1: { 1851 $seq .= '1'; 1852 $code = ($code - 1) / 3; 1853 break; 1854 } 1855 case 2: { 1856 $seq .= '2'; 1857 $code = ($code - 2) / 3; 1858 break; 1859 } 1860 } 1861 } while($code != 0); 1862 $seq = strrev($seq); 1863 $k = 0; 1864 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 2, 'bcode' => array()); 1865 $len = strlen($seq); 1866 for ($i = 0; $i < $len; ++$i) { 1867 switch ($seq[$i]) { 1868 case '1': { 1869 $p = 1; 1870 $h = 1; 1871 break; 1872 } 1873 case '2': { 1874 $p = 0; 1875 $h = 1; 1876 break; 1877 } 1878 case '3': { 1879 $p = 0; 1880 $h = 2; 1881 break; 1882 } 1883 } 1884 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p); 1885 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 1886 $bararray['maxw'] += 2; 1887 } 1888 unset($bararray['bcode'][($k - 1)]); 1889 --$bararray['maxw']; 1890 return $bararray; 1891 } 1892 1893 /** 1894 * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200 1895 * (requires PHP bcmath extension) 1896 * Intelligent Mail barcode is a 65-bar code for use on mail in the United States. 1897 * The fields are described as follows:<ul><li>The Barcode Identifier shall be assigned by USPS to encode the presort identification that is currently printed in human readable form on the optional endorsement line (OEL) as well as for future USPS use. This shall be two digits, with the second digit in the range of 0–4. The allowable encoding ranges shall be 00–04, 10–14, 20–24, 30–34, 40–44, 50–54, 60–64, 70–74, 80–84, and 90–94.</li><li>The Service Type Identifier shall be assigned by USPS for any combination of services requested on the mailpiece. The allowable encoding range shall be 000http://it2.php.net/manual/en/function.dechex.php–999. Each 3-digit value shall correspond to a particular mail class with a particular combination of service(s). Each service program, such as OneCode Confirm and OneCode ACS, shall provide the list of Service Type Identifier values.</li><li>The Mailer or Customer Identifier shall be assigned by USPS as a unique, 6 or 9 digit number that identifies a business entity. The allowable encoding range for the 6 digit Mailer ID shall be 000000- 899999, while the allowable encoding range for the 9 digit Mailer ID shall be 900000000-999999999.</li><li>The Serial or Sequence Number shall be assigned by the mailer for uniquely identifying and tracking mailpieces. The allowable encoding range shall be 000000000–999999999 when used with a 6 digit Mailer ID and 000000-999999 when used with a 9 digit Mailer ID. e. The Delivery Point ZIP Code shall be assigned by the mailer for routing the mailpiece. This shall replace POSTNET for routing the mailpiece to its final delivery point. The length may be 0, 5, 9, or 11 digits. The allowable encoding ranges shall be no ZIP Code, 00000–99999, 000000000–999999999, and 00000000000–99999999999.</li></ul> 1898 * @param $code (string) code to print, separate the ZIP (routing code) from the rest using a minus char '-' (BarcodeID_ServiceTypeID_MailerID_SerialNumber-RoutingCode) 1899 * @return array barcode representation. 1900 * @protected 1901 */ 1902 protected function barcode_imb($code) { 1903 $asc_chr = array(4,0,2,6,3,5,1,9,8,7,1,2,0,6,4,8,2,9,5,3,0,1,3,7,4,6,8,9,2,0,5,1,9,4,3,8,6,7,1,2,4,3,9,5,7,8,3,0,2,1,4,0,9,1,7,0,2,4,6,3,7,1,9,5,8); 1904 $dsc_chr = array(7,1,9,5,8,0,2,4,6,3,5,8,9,7,3,0,6,1,7,4,6,8,9,2,5,1,7,5,4,3,8,7,6,0,2,5,4,9,3,0,1,6,8,2,0,4,5,9,6,7,5,2,6,3,8,5,1,9,8,7,4,0,2,6,3); 1905 $asc_pos = array(3,0,8,11,1,12,8,11,10,6,4,12,2,7,9,6,7,9,2,8,4,0,12,7,10,9,0,7,10,5,7,9,6,8,2,12,1,4,2,0,1,5,4,6,12,1,0,9,4,7,5,10,2,6,9,11,2,12,6,7,5,11,0,3,2); 1906 $dsc_pos = array(2,10,12,5,9,1,5,4,3,9,11,5,10,1,6,3,4,1,10,0,2,11,8,6,1,12,3,8,6,4,4,11,0,6,1,9,11,5,3,7,3,10,7,11,8,2,10,3,5,8,0,3,12,11,8,4,5,1,3,0,7,12,9,8,10); 1907 $code_arr = explode('-', $code); 1908 $tracking_number = $code_arr[0]; 1909 if (isset($code_arr[1])) { 1910 $routing_code = $code_arr[1]; 1911 } else { 1912 $routing_code = ''; 1913 } 1914 // Conversion of Routing Code 1915 switch (strlen($routing_code)) { 1916 case 0: { 1917 $binary_code = 0; 1918 break; 1919 } 1920 case 5: { 1921 $binary_code = bcadd($routing_code, '1'); 1922 break; 1923 } 1924 case 9: { 1925 $binary_code = bcadd($routing_code, '100001'); 1926 break; 1927 } 1928 case 11: { 1929 $binary_code = bcadd($routing_code, '1000100001'); 1930 break; 1931 } 1932 default: { 1933 return false; 1934 break; 1935 } 1936 } 1937 $binary_code = bcmul($binary_code, 10); 1938 $binary_code = bcadd($binary_code, $tracking_number[0]); 1939 $binary_code = bcmul($binary_code, 5); 1940 $binary_code = bcadd($binary_code, $tracking_number[1]); 1941 $binary_code .= substr($tracking_number, 2, 18); 1942 // convert to hexadecimal 1943 $binary_code = $this->dec_to_hex($binary_code); 1944 // pad to get 13 bytes 1945 $binary_code = str_pad($binary_code, 26, '0', STR_PAD_LEFT); 1946 // convert string to array of bytes 1947 $binary_code_arr = chunk_split($binary_code, 2, "\r"); 1948 $binary_code_arr = substr($binary_code_arr, 0, -1); 1949 $binary_code_arr = explode("\r", $binary_code_arr); 1950 // calculate frame check sequence 1951 $fcs = $this->imb_crc11fcs($binary_code_arr); 1952 // exclude first 2 bits from first byte 1953 $first_byte = sprintf('%2s', dechex((hexdec($binary_code_arr[0]) << 2) >> 2)); 1954 $binary_code_102bit = $first_byte.substr($binary_code, 2); 1955 // convert binary data to codewords 1956 $codewords = array(); 1957 $data = $this->hex_to_dec($binary_code_102bit); 1958 $codewords[0] = bcmod($data, 636) * 2; 1959 $data = bcdiv($data, 636); 1960 for ($i = 1; $i < 9; ++$i) { 1961 $codewords[$i] = bcmod($data, 1365); 1962 $data = bcdiv($data, 1365); 1963 } 1964 $codewords[9] = $data; 1965 if (($fcs >> 10) == 1) { 1966 $codewords[9] += 659; 1967 } 1968 // generate lookup tables 1969 $table2of13 = $this->imb_tables(2, 78); 1970 $table5of13 = $this->imb_tables(5, 1287); 1971 // convert codewords to characters 1972 $characters = array(); 1973 $bitmask = 512; 1974 foreach($codewords as $k => $val) { 1975 if ($val <= 1286) { 1976 $chrcode = $table5of13[$val]; 1977 } else { 1978 $chrcode = $table2of13[($val - 1287)]; 1979 } 1980 if (($fcs & $bitmask) > 0) { 1981 // bitwise invert 1982 $chrcode = ((~$chrcode) & 8191); 1983 } 1984 $characters[] = $chrcode; 1985 $bitmask /= 2; 1986 } 1987 $characters = array_reverse($characters); 1988 // build bars 1989 $k = 0; 1990 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array()); 1991 for ($i = 0; $i < 65; ++$i) { 1992 $asc = (($characters[$asc_chr[$i]] & pow(2, $asc_pos[$i])) > 0); 1993 $dsc = (($characters[$dsc_chr[$i]] & pow(2, $dsc_pos[$i])) > 0); 1994 if ($asc AND $dsc) { 1995 // full bar (F) 1996 $p = 0; 1997 $h = 3; 1998 } elseif ($asc) { 1999 // ascender (A) 2000 $p = 0; 2001 $h = 2; 2002 } elseif ($dsc) { 2003 // descender (D) 2004 $p = 1; 2005 $h = 2; 2006 } else { 2007 // tracker (T) 2008 $p = 1; 2009 $h = 1; 2010 } 2011 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p); 2012 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 2013 $bararray['maxw'] += 2; 2014 } 2015 unset($bararray['bcode'][($k - 1)]); 2016 --$bararray['maxw']; 2017 return $bararray; 2018 } 2019 2020 /** 2021 * IMB - Intelligent Mail Barcode - Onecode - USPS-B-3200 2022 * 2023 * @param $code (string) pre-formatted IMB barcode (65 chars "FADT") 2024 * @return array barcode representation. 2025 * @protected 2026 */ 2027 protected function barcode_imb_pre($code) { 2028 if (!preg_match('/^[fadtFADT]{65}$/', $code) == 1) { 2029 return false; 2030 } 2031 $characters = str_split(strtolower($code), 1); 2032 // build bars 2033 $k = 0; 2034 $bararray = array('code' => $code, 'maxw' => 0, 'maxh' => 3, 'bcode' => array()); 2035 for ($i = 0; $i < 65; ++$i) { 2036 switch($characters[$i]) { 2037 case 'f': { 2038 // full bar 2039 $p = 0; 2040 $h = 3; 2041 break; 2042 } 2043 case 'a': { 2044 // ascender 2045 $p = 0; 2046 $h = 2; 2047 break; 2048 } 2049 case 'd': { 2050 // descender 2051 $p = 1; 2052 $h = 2; 2053 break; 2054 } 2055 case 't': { 2056 // tracker (short) 2057 $p = 1; 2058 $h = 1; 2059 break; 2060 } 2061 } 2062 $bararray['bcode'][$k++] = array('t' => 1, 'w' => 1, 'h' => $h, 'p' => $p); 2063 $bararray['bcode'][$k++] = array('t' => 0, 'w' => 1, 'h' => 2, 'p' => 0); 2064 $bararray['maxw'] += 2; 2065 } 2066 unset($bararray['bcode'][($k - 1)]); 2067 --$bararray['maxw']; 2068 return $bararray; 2069 } 2070 2071 /** 2072 * Convert large integer number to hexadecimal representation. 2073 * (requires PHP bcmath extension) 2074 * @param $number (string) number to convert specified as a string 2075 * @return string hexadecimal representation 2076 */ 2077 public function dec_to_hex($number) { 2078 $i = 0; 2079 $hex = array(); 2080 if($number == 0) { 2081 return '00'; 2082 } 2083 while($number > 0) { 2084 if($number == 0) { 2085 array_push($hex, '0'); 2086 } else { 2087 array_push($hex, strtoupper(dechex(bcmod($number, '16')))); 2088 $number = bcdiv($number, '16', 0); 2089 } 2090 } 2091 $hex = array_reverse($hex); 2092 return implode($hex); 2093 } 2094 2095 /** 2096 * Convert large hexadecimal number to decimal representation (string). 2097 * (requires PHP bcmath extension) 2098 * @param $hex (string) hexadecimal number to convert specified as a string 2099 * @return string hexadecimal representation 2100 */ 2101 public function hex_to_dec($hex) { 2102 $dec = 0; 2103 $bitval = 1; 2104 $len = strlen($hex); 2105 for($pos = ($len - 1); $pos >= 0; --$pos) { 2106 $dec = bcadd($dec, bcmul(hexdec($hex[$pos]), $bitval)); 2107 $bitval = bcmul($bitval, 16); 2108 } 2109 return $dec; 2110 } 2111 2112 /** 2113 * Intelligent Mail Barcode calculation of Frame Check Sequence 2114 * @param $code_arr (string) array of hexadecimal values (13 bytes holding 102 bits right justified). 2115 * @return int 11 bit Frame Check Sequence as integer (decimal base) 2116 * @protected 2117 */ 2118 protected function imb_crc11fcs($code_arr) { 2119 $genpoly = 0x0F35; // generator polynomial 2120 $fcs = 0x07FF; // Frame Check Sequence 2121 // do most significant byte skipping the 2 most significant bits 2122 $data = hexdec($code_arr[0]) << 5; 2123 for ($bit = 2; $bit < 8; ++$bit) { 2124 if (($fcs ^ $data) & 0x400) { 2125 $fcs = ($fcs << 1) ^ $genpoly; 2126 } else { 2127 $fcs = ($fcs << 1); 2128 } 2129 $fcs &= 0x7FF; 2130 $data <<= 1; 2131 } 2132 // do rest of bytes 2133 for ($byte = 1; $byte < 13; ++$byte) { 2134 $data = hexdec($code_arr[$byte]) << 3; 2135 for ($bit = 0; $bit < 8; ++$bit) { 2136 if (($fcs ^ $data) & 0x400) { 2137 $fcs = ($fcs << 1) ^ $genpoly; 2138 } else { 2139 $fcs = ($fcs << 1); 2140 } 2141 $fcs &= 0x7FF; 2142 $data <<= 1; 2143 } 2144 } 2145 return $fcs; 2146 } 2147 2148 /** 2149 * Reverse unsigned short value 2150 * @param $num (int) value to reversr 2151 * @return int reversed value 2152 * @protected 2153 */ 2154 protected function imb_reverse_us($num) { 2155 $rev = 0; 2156 for ($i = 0; $i < 16; ++$i) { 2157 $rev <<= 1; 2158 $rev |= ($num & 1); 2159 $num >>= 1; 2160 } 2161 return $rev; 2162 } 2163 2164 /** 2165 * generate Nof13 tables used for Intelligent Mail Barcode 2166 * @param $n (int) is the type of table: 2 for 2of13 table, 5 for 5of13table 2167 * @param $size (int) size of table (78 for n=2 and 1287 for n=5) 2168 * @return array requested table 2169 * @protected 2170 */ 2171 protected function imb_tables($n, $size) { 2172 $table = array(); 2173 $lli = 0; // LUT lower index 2174 $lui = $size - 1; // LUT upper index 2175 for ($count = 0; $count < 8192; ++$count) { 2176 $bit_count = 0; 2177 for ($bit_index = 0; $bit_index < 13; ++$bit_index) { 2178 $bit_count += intval(($count & (1 << $bit_index)) != 0); 2179 } 2180 // if we don't have the right number of bits on, go on to the next value 2181 if ($bit_count == $n) { 2182 $reverse = ($this->imb_reverse_us($count) >> 3); 2183 // if the reverse is less than count, we have already visited this pair before 2184 if ($reverse >= $count) { 2185 // If count is symmetric, place it at the first free slot from the end of the list. 2186 // Otherwise, place it at the first free slot from the beginning of the list AND place $reverse ath the next free slot from the beginning of the list 2187 if ($reverse == $count) { 2188 $table[$lui] = $count; 2189 --$lui; 2190 } else { 2191 $table[$lli] = $count; 2192 ++$lli; 2193 $table[$lli] = $reverse; 2194 ++$lli; 2195 } 2196 } 2197 } 2198 } 2199 return $table; 2200 } 2201 2202} // end of class 2203//============================================================+ 2204// END OF FILE 2205//============================================================+ 2206