1<?php 2 3namespace jucksearm\barcode\lib; 4 5//============================================================+ 6// File name : datamatrix.php 7// Version : 1.0.008 8// Begin : 2010-06-07 9// Last Update : 2014-05-06 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) 2010-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 : 34// 35// Class to create DataMatrix ECC 200 barcode arrays for TCPDF class. 36// DataMatrix (ISO/IEC 16022:2006) is a 2-dimensional bar code. 37//============================================================+ 38 39/** 40* @file 41* Class to create DataMatrix ECC 200 barcode arrays for TCPDF class. 42* DataMatrix (ISO/IEC 16022:2006) is a 2-dimensional bar code. 43* 44* @package com.tecnick.tcpdf 45* @author Nicola Asuni 46* @version 1.0.008 47*/ 48 49/** 50* @class Datamatrix 51* Class to create DataMatrix ECC 200 barcode arrays for TCPDF class. 52* DataMatrix (ISO/IEC 16022:2006) is a 2-dimensional bar code. 53* 54* @package com.tecnick.tcpdf 55* @author Nicola Asuni 56* @version 1.0.004 57*/ 58class Datamatrix 59{ 60 /** 61 * ASCII encoding: ASCII character 0 to 127 (1 byte per CW) 62 */ 63 const ENC_ASCII = 0; 64 65 /** 66 * C40 encoding: Upper-case alphanumeric (3/2 bytes per CW) 67 */ 68 const ENC_C40 = 1; 69 70 /** 71 * TEXT encoding: Lower-case alphanumeric (3/2 bytes per CW) 72 */ 73 const ENC_TXT = 2; 74 75 /** 76 * X12 encoding: ANSI X12 (3/2 byte per CW) 77 */ 78 const ENC_X12 = 3; 79 80 /** 81 * EDIFACT encoding: ASCII character 32 to 94 (4/3 bytes per CW) 82 */ 83 const ENC_EDF = 4; 84 85 /** 86 * BASE 256 encoding: ASCII character 0 to 255 (1 byte per CW) 87 */ 88 const ENC_BASE256 = 5; 89 90 /** 91 * ASCII extended encoding: ASCII character 128 to 255 (1/2 byte per CW) 92 */ 93 const ENC_ASCII_EXT = 6; 94 95 /** 96 * ASCII number encoding: ASCII digits (2 bytes per CW) 97 */ 98 const ENC_ASCII_NUM = 7; 99 100 /** 101 * Barcode array to be returned which is readable by TCPDF. 102 * @protected 103 */ 104 protected $barcode_array = array(); 105 106 /** 107 * Store last used encoding for data codewords. 108 * @protected 109 */ 110 protected $last_enc = self::ENC_ASCII; 111 112 /** 113 * Table of Data Matrix ECC 200 Symbol Attributes:<ul> 114 * <li>total matrix rows (including finder pattern)</li> 115 * <li>total matrix cols (including finder pattern)</li> 116 * <li>total matrix rows (without finder pattern)</li> 117 * <li>total matrix cols (without finder pattern)</li> 118 * <li>region data rows (with finder pattern)</li> 119 * <li>region data col (with finder pattern)</li> 120 * <li>region data rows (without finder pattern)</li> 121 * <li>region data col (without finder pattern)</li> 122 * <li>horizontal regions</li> 123 * <li>vertical regions</li> 124 * <li>regions</li> 125 * <li>data codewords</li> 126 * <li>error codewords</li> 127 * <li>blocks</li> 128 * <li>data codewords per block</li> 129 * <li>error codewords per block</li> 130 * </ul> 131 * @protected 132 */ 133 protected $symbattr = array( 134 // square form --------------------------------------------------------------------------------------- 135 array(0x00a,0x00a,0x008,0x008,0x00a,0x00a,0x008,0x008,0x001,0x001,0x001,0x003,0x005,0x001,0x003,0x005), // 10x10 136 array(0x00c,0x00c,0x00a,0x00a,0x00c,0x00c,0x00a,0x00a,0x001,0x001,0x001,0x005,0x007,0x001,0x005,0x007), // 12x12 137 array(0x00e,0x00e,0x00c,0x00c,0x00e,0x00e,0x00c,0x00c,0x001,0x001,0x001,0x008,0x00a,0x001,0x008,0x00a), // 14x14 138 array(0x010,0x010,0x00e,0x00e,0x010,0x010,0x00e,0x00e,0x001,0x001,0x001,0x00c,0x00c,0x001,0x00c,0x00c), // 16x16 139 array(0x012,0x012,0x010,0x010,0x012,0x012,0x010,0x010,0x001,0x001,0x001,0x012,0x00e,0x001,0x012,0x00e), // 18x18 140 array(0x014,0x014,0x012,0x012,0x014,0x014,0x012,0x012,0x001,0x001,0x001,0x016,0x012,0x001,0x016,0x012), // 20x20 141 array(0x016,0x016,0x014,0x014,0x016,0x016,0x014,0x014,0x001,0x001,0x001,0x01e,0x014,0x001,0x01e,0x014), // 22x22 142 array(0x018,0x018,0x016,0x016,0x018,0x018,0x016,0x016,0x001,0x001,0x001,0x024,0x018,0x001,0x024,0x018), // 24x24 143 array(0x01a,0x01a,0x018,0x018,0x01a,0x01a,0x018,0x018,0x001,0x001,0x001,0x02c,0x01c,0x001,0x02c,0x01c), // 26x26 144 array(0x020,0x020,0x01c,0x01c,0x010,0x010,0x00e,0x00e,0x002,0x002,0x004,0x03e,0x024,0x001,0x03e,0x024), // 32x32 145 array(0x024,0x024,0x020,0x020,0x012,0x012,0x010,0x010,0x002,0x002,0x004,0x056,0x02a,0x001,0x056,0x02a), // 36x36 146 array(0x028,0x028,0x024,0x024,0x014,0x014,0x012,0x012,0x002,0x002,0x004,0x072,0x030,0x001,0x072,0x030), // 40x40 147 array(0x02c,0x02c,0x028,0x028,0x016,0x016,0x014,0x014,0x002,0x002,0x004,0x090,0x038,0x001,0x090,0x038), // 44x44 148 array(0x030,0x030,0x02c,0x02c,0x018,0x018,0x016,0x016,0x002,0x002,0x004,0x0ae,0x044,0x001,0x0ae,0x044), // 48x48 149 array(0x034,0x034,0x030,0x030,0x01a,0x01a,0x018,0x018,0x002,0x002,0x004,0x0cc,0x054,0x002,0x066,0x02a), // 52x52 150 array(0x040,0x040,0x038,0x038,0x010,0x010,0x00e,0x00e,0x004,0x004,0x010,0x118,0x070,0x002,0x08c,0x038), // 64x64 151 array(0x048,0x048,0x040,0x040,0x012,0x012,0x010,0x010,0x004,0x004,0x010,0x170,0x090,0x004,0x05c,0x024), // 72x72 152 array(0x050,0x050,0x048,0x048,0x014,0x014,0x012,0x012,0x004,0x004,0x010,0x1c8,0x0c0,0x004,0x072,0x030), // 80x80 153 array(0x058,0x058,0x050,0x050,0x016,0x016,0x014,0x014,0x004,0x004,0x010,0x240,0x0e0,0x004,0x090,0x038), // 88x88 154 array(0x060,0x060,0x058,0x058,0x018,0x018,0x016,0x016,0x004,0x004,0x010,0x2b8,0x110,0x004,0x0ae,0x044), // 96x96 155 array(0x068,0x068,0x060,0x060,0x01a,0x01a,0x018,0x018,0x004,0x004,0x010,0x330,0x150,0x006,0x088,0x038), // 104x104 156 array(0x078,0x078,0x06c,0x06c,0x014,0x014,0x012,0x012,0x006,0x006,0x024,0x41a,0x198,0x006,0x0af,0x044), // 120x120 157 array(0x084,0x084,0x078,0x078,0x016,0x016,0x014,0x014,0x006,0x006,0x024,0x518,0x1f0,0x008,0x0a3,0x03e), // 132x132 158 array(0x090,0x090,0x084,0x084,0x018,0x018,0x016,0x016,0x006,0x006,0x024,0x616,0x26c,0x00a,0x09c,0x03e), // 144x144 159 // rectangular form (currently unused) --------------------------------------------------------------------------- 160 array(0x008,0x012,0x006,0x010,0x008,0x012,0x006,0x010,0x001,0x001,0x001,0x005,0x007,0x001,0x005,0x007), // 8x18 161 array(0x008,0x020,0x006,0x01c,0x008,0x010,0x006,0x00e,0x001,0x002,0x002,0x00a,0x00b,0x001,0x00a,0x00b), // 8x32 162 array(0x00c,0x01a,0x00a,0x018,0x00c,0x01a,0x00a,0x018,0x001,0x001,0x001,0x010,0x00e,0x001,0x010,0x00e), // 12x26 163 array(0x00c,0x024,0x00a,0x020,0x00c,0x012,0x00a,0x010,0x001,0x002,0x002,0x00c,0x012,0x001,0x00c,0x012), // 12x36 164 array(0x010,0x024,0x00e,0x020,0x010,0x012,0x00e,0x010,0x001,0x002,0x002,0x020,0x018,0x001,0x020,0x018), // 16x36 165 array(0x010,0x030,0x00e,0x02c,0x010,0x018,0x00e,0x016,0x001,0x002,0x002,0x031,0x01c,0x001,0x031,0x01c) // 16x48 166 ); 167 168 /** 169 * Map encodation modes whit character sets. 170 * @protected 171 */ 172 protected $chset_id = array(self::ENC_C40 => 'C40', self::ENC_TXT => 'TXT', self::ENC_X12 =>'X12'); 173 174 /** 175 * Basic set of characters for each encodation mode. 176 * @protected 177 */ 178 protected $chset = array( 179 'C40' => array( // Basic set for C40 ---------------------------------------------------------------------------- 180 'S1'=>0x00,'S2'=>0x01,'S3'=>0x02,0x20=>0x03,0x30=>0x04,0x31=>0x05,0x32=>0x06,0x33=>0x07,0x34=>0x08,0x35=>0x09, // 181 0x36=>0x0a,0x37=>0x0b,0x38=>0x0c,0x39=>0x0d,0x41=>0x0e,0x42=>0x0f,0x43=>0x10,0x44=>0x11,0x45=>0x12,0x46=>0x13, // 182 0x47=>0x14,0x48=>0x15,0x49=>0x16,0x4a=>0x17,0x4b=>0x18,0x4c=>0x19,0x4d=>0x1a,0x4e=>0x1b,0x4f=>0x1c,0x50=>0x1d, // 183 0x51=>0x1e,0x52=>0x1f,0x53=>0x20,0x54=>0x21,0x55=>0x22,0x56=>0x23,0x57=>0x24,0x58=>0x25,0x59=>0x26,0x5a=>0x27),// 184 'TXT' => array( // Basic set for TEXT --------------------------------------------------------------------------- 185 'S1'=>0x00,'S2'=>0x01,'S3'=>0x02,0x20=>0x03,0x30=>0x04,0x31=>0x05,0x32=>0x06,0x33=>0x07,0x34=>0x08,0x35=>0x09, // 186 0x36=>0x0a,0x37=>0x0b,0x38=>0x0c,0x39=>0x0d,0x61=>0x0e,0x62=>0x0f,0x63=>0x10,0x64=>0x11,0x65=>0x12,0x66=>0x13, // 187 0x67=>0x14,0x68=>0x15,0x69=>0x16,0x6a=>0x17,0x6b=>0x18,0x6c=>0x19,0x6d=>0x1a,0x6e=>0x1b,0x6f=>0x1c,0x70=>0x1d, // 188 0x71=>0x1e,0x72=>0x1f,0x73=>0x20,0x74=>0x21,0x75=>0x22,0x76=>0x23,0x77=>0x24,0x78=>0x25,0x79=>0x26,0x7a=>0x27),// 189 'SH1' => array( // Shift 1 set ---------------------------------------------------------------------------------- 190 0x00=>0x00,0x01=>0x01,0x02=>0x02,0x03=>0x03,0x04=>0x04,0x05=>0x05,0x06=>0x06,0x07=>0x07,0x08=>0x08,0x09=>0x09, // 191 0x0a=>0x0a,0x0b=>0x0b,0x0c=>0x0c,0x0d=>0x0d,0x0e=>0x0e,0x0f=>0x0f,0x10=>0x10,0x11=>0x11,0x12=>0x12,0x13=>0x13, // 192 0x14=>0x14,0x15=>0x15,0x16=>0x16,0x17=>0x17,0x18=>0x18,0x19=>0x19,0x1a=>0x1a,0x1b=>0x1b,0x1c=>0x1c,0x1d=>0x1d, // 193 0x1e=>0x1e,0x1f=>0x1f), // 194 'SH2' => array( // Shift 2 set ---------------------------------------------------------------------------------- 195 0x21=>0x00,0x22=>0x01,0x23=>0x02,0x24=>0x03,0x25=>0x04,0x26=>0x05,0x27=>0x06,0x28=>0x07,0x29=>0x08,0x2a=>0x09, // 196 0x2b=>0x0a,0x2c=>0x0b,0x2d=>0x0c,0x2e=>0x0d,0x2f=>0x0e,0x3a=>0x0f,0x3b=>0x10,0x3c=>0x11,0x3d=>0x12,0x3e=>0x13, // 197 0x3f=>0x14,0x40=>0x15,0x5b=>0x16,0x5c=>0x17,0x5d=>0x18,0x5e=>0x19,0x5f=>0x1a,'F1'=>0x1b,'US'=>0x1e), // 198 'S3C' => array( // Shift 3 set for C40 -------------------------------------------------------------------------- 199 0x60=>0x00,0x61=>0x01,0x62=>0x02,0x63=>0x03,0x64=>0x04,0x65=>0x05,0x66=>0x06,0x67=>0x07,0x68=>0x08,0x69=>0x09, // 200 0x6a=>0x0a,0x6b=>0x0b,0x6c=>0x0c,0x6d=>0x0d,0x6e=>0x0e,0x6f=>0x0f,0x70=>0x10,0x71=>0x11,0x72=>0x12,0x73=>0x13, // 201 0x74=>0x14,0x75=>0x15,0x76=>0x16,0x77=>0x17,0x78=>0x18,0x79=>0x19,0x7a=>0x1a,0x7b=>0x1b,0x7c=>0x1c,0x7d=>0x1d, // 202 0x7e=>0x1e,0x7f=>0x1f), 203 'S3T' => array( // Shift 3 set for TEXT ------------------------------------------------------------------------- 204 0x60=>0x00,0x41=>0x01,0x42=>0x02,0x43=>0x03,0x44=>0x04,0x45=>0x05,0x46=>0x06,0x47=>0x07,0x48=>0x08,0x49=>0x09, // 205 0x4a=>0x0a,0x4b=>0x0b,0x4c=>0x0c,0x4d=>0x0d,0x4e=>0x0e,0x4f=>0x0f,0x50=>0x10,0x51=>0x11,0x52=>0x12,0x53=>0x13, // 206 0x54=>0x14,0x55=>0x15,0x56=>0x16,0x57=>0x17,0x58=>0x18,0x59=>0x19,0x5a=>0x1a,0x7b=>0x1b,0x7c=>0x1c,0x7d=>0x1d, // 207 0x7e=>0x1e,0x7f=>0x1f), // 208 'X12' => array( // Set for X12 ---------------------------------------------------------------------------------- 209 0x0d=>0x00,0x2a=>0x01,0x3e=>0x02,0x20=>0x03,0x30=>0x04,0x31=>0x05,0x32=>0x06,0x33=>0x07,0x34=>0x08,0x35=>0x09, // 210 0x36=>0x0a,0x37=>0x0b,0x38=>0x0c,0x39=>0x0d,0x41=>0x0e,0x42=>0x0f,0x43=>0x10,0x44=>0x11,0x45=>0x12,0x46=>0x13, // 211 0x47=>0x14,0x48=>0x15,0x49=>0x16,0x4a=>0x17,0x4b=>0x18,0x4c=>0x19,0x4d=>0x1a,0x4e=>0x1b,0x4f=>0x1c,0x50=>0x1d, // 212 0x51=>0x1e,0x52=>0x1f,0x53=>0x20,0x54=>0x21,0x55=>0x22,0x56=>0x23,0x57=>0x24,0x58=>0x25,0x59=>0x26,0x5a=>0x27) // 213 ); 214 215// ----------------------------------------------------------------------------- 216 217 /** 218 * This is the class constructor. 219 * Creates a datamatrix object 220 * @param $code (string) Code to represent using Datamatrix. 221 * @public 222 */ 223 public function __construct($code) { 224 $barcode_array = array(); 225 if ((is_null($code)) OR ($code == '\0') OR ($code == '')) { 226 return false; 227 } 228 // get data codewords 229 $cw = $this->getHighLevelEncoding($code); 230 // number of data codewords 231 $nd = count($cw); 232 // check size 233 if ($nd > 1558) { 234 return false; 235 } 236 // get minimum required matrix size. 237 foreach ($this->symbattr as $params) { 238 if ($params[11] >= $nd) { 239 break; 240 } 241 } 242 if ($params[11] < $nd) { 243 // too much data 244 return false; 245 } elseif ($params[11] > $nd) { 246 // add padding 247 if ((($params[11] - $nd) > 1) AND ($cw[($nd - 1)] != 254)) { 248 if ($this->last_enc == self::ENC_EDF) { 249 // switch to ASCII encoding 250 $cw[] = 124; 251 ++$nd; 252 } elseif (($this->last_enc != self::ENC_ASCII) AND ($this->last_enc != self::ENC_BASE256)) { 253 // switch to ASCII encoding 254 $cw[] = 254; 255 ++$nd; 256 } 257 } 258 if ($params[11] > $nd) { 259 // add first pad 260 $cw[] = 129; 261 ++$nd; 262 // add remaining pads 263 for ($i = $nd; $i < $params[11]; ++$i) { 264 $cw[] = $this->get253StateCodeword(129, $i); 265 } 266 } 267 } 268 // add error correction codewords 269 $cw = $this->getErrorCorrection($cw, $params[13], $params[14], $params[15]); 270 // initialize empty arrays 271 $grid = array_fill(0, ($params[2] * $params[3]), 0); 272 // get placement map 273 $places = $this->getPlacementMap($params[2], $params[3]); 274 // fill the grid with data 275 $grid = array(); 276 $i = 0; 277 // region data row max index 278 $rdri = ($params[4] - 1); 279 // region data column max index 280 $rdci = ($params[5] - 1); 281 // for each vertical region 282 for ($vr = 0; $vr < $params[9]; ++$vr) { 283 // for each row on region 284 for ($r = 0; $r < $params[4]; ++$r) { 285 // get row 286 $row = (($vr * $params[4]) + $r); 287 // for each horizontal region 288 for ($hr = 0; $hr < $params[8]; ++$hr) { 289 // for each column on region 290 for ($c = 0; $c < $params[5]; ++$c) { 291 // get column 292 $col = (($hr * $params[5]) + $c); 293 // braw bits by case 294 if ($r == 0) { 295 // top finder pattern 296 if ($c % 2) { 297 $grid[$row][$col] = 0; 298 } else { 299 $grid[$row][$col] = 1; 300 } 301 } elseif ($r == $rdri) { 302 // bottom finder pattern 303 $grid[$row][$col] = 1; 304 } elseif ($c == 0) { 305 // left finder pattern 306 $grid[$row][$col] = 1; 307 } elseif ($c == $rdci) { 308 // right finder pattern 309 if ($r % 2) { 310 $grid[$row][$col] = 1; 311 } else { 312 $grid[$row][$col] = 0; 313 } 314 } else { // data bit 315 if ($places[$i] < 2) { 316 $grid[$row][$col] = $places[$i]; 317 } else { 318 // codeword ID 319 $cw_id = (floor($places[$i] / 10) - 1); 320 // codeword BIT mask 321 $cw_bit = pow(2, (8 - ($places[$i] % 10))); 322 $grid[$row][$col] = (($cw[$cw_id] & $cw_bit) == 0) ? 0 : 1; 323 } 324 ++$i; 325 } 326 } 327 } 328 } 329 } 330 $this->barcode_array['num_rows'] = $params[0]; 331 $this->barcode_array['num_cols'] = $params[1]; 332 $this->barcode_array['bcode'] = $grid; 333 } 334 335 /** 336 * Returns a barcode array which is readable by TCPDF 337 * @return array barcode array readable by TCPDF; 338 * @public 339 */ 340 public function getBarcodeArray() { 341 return $this->barcode_array; 342 } 343 344 /** 345 * Product of two numbers in a Power-of-Two Galois Field 346 * @param $a (int) first number to multiply. 347 * @param $b (int) second number to multiply. 348 * @param $log (array) Log table. 349 * @param $alog (array) Anti-Log table. 350 * @param $gf (array) Number of Factors of the Reed-Solomon polynomial. 351 * @return int product 352 * @protected 353 */ 354 protected function getGFProduct($a, $b, $log, $alog, $gf) { 355 if (($a == 0) OR ($b == 0)) { 356 return 0; 357 } 358 return ($alog[($log[$a] + $log[$b]) % ($gf - 1)]); 359 } 360 361 /** 362 * Add error correction codewords to data codewords array (ANNEX E). 363 * @param $wd (array) Array of datacodewords. 364 * @param $nb (int) Number of blocks. 365 * @param $nd (int) Number of data codewords per block. 366 * @param $nc (int) Number of correction codewords per block. 367 * @param $gf (int) numner of fields on log/antilog table (power of 2). 368 * @param $pp (int) The value of its prime modulus polynomial (301 for ECC200). 369 * @return array data codewords + error codewords 370 * @protected 371 */ 372 protected function getErrorCorrection($wd, $nb, $nd, $nc, $gf=256, $pp=301) { 373 // generate the log ($log) and antilog ($alog) tables 374 $log[0] = 0; 375 $alog[0] = 1; 376 for ($i = 1; $i < $gf; ++$i) { 377 $alog[$i] = ($alog[($i - 1)] * 2); 378 if ($alog[$i] >= $gf) { 379 $alog[$i] ^= $pp; 380 } 381 $log[$alog[$i]] = $i; 382 } 383 ksort($log); 384 // generate the polynomial coefficients (c) 385 $c = array_fill(0, ($nc + 1), 0); 386 $c[0] = 1; 387 for ($i = 1; $i <= $nc; ++$i) { 388 $c[$i] = $c[($i-1)]; 389 for ($j = ($i - 1); $j >= 1; --$j) { 390 $c[$j] = $c[($j - 1)] ^ $this->getGFProduct($c[$j], $alog[$i], $log, $alog, $gf); 391 } 392 $c[0] = $this->getGFProduct($c[0], $alog[$i], $log, $alog, $gf); 393 } 394 ksort($c); 395 // total number of data codewords 396 $num_wd = ($nb * $nd); 397 // total number of error codewords 398 $num_we = ($nb * $nc); 399 // for each block 400 for ($b = 0; $b < $nb; ++$b) { 401 // create interleaved data block 402 $block = array(); 403 for ($n = $b; $n < $num_wd; $n += $nb) { 404 $block[] = $wd[$n]; 405 } 406 // initialize error codewords 407 $we = array_fill(0, ($nc + 1), 0); 408 // calculate error correction codewords for this block 409 for ($i = 0; $i < $nd; ++$i) { 410 $k = ($we[0] ^ $block[$i]); 411 for ($j = 0; $j < $nc; ++$j) { 412 $we[$j] = ($we[($j + 1)] ^ $this->getGFProduct($k, $c[($nc - $j - 1)], $log, $alog, $gf)); 413 } 414 } 415 // add error codewords at the end of data codewords 416 $j = 0; 417 for ($i = $b; $i < $num_we; $i += $nb) { 418 $wd[($num_wd + $i)] = $we[$j]; 419 ++$j; 420 } 421 } 422 // reorder codewords 423 ksort($wd); 424 return $wd; 425 } 426 427 /** 428 * Return the 253-state codeword 429 * @param $cwpad (int) Pad codeword. 430 * @param $cwpos (int) Number of data codewords from the beginning of encoded data. 431 * @return pad codeword 432 * @protected 433 */ 434 protected function get253StateCodeword($cwpad, $cwpos) { 435 $pad = ($cwpad + (((149 * $cwpos) % 253) + 1)); 436 if ($pad > 254) { 437 $pad -= 254; 438 } 439 return $pad; 440 } 441 442 /** 443 * Return the 255-state codeword 444 * @param $cwpad (int) Pad codeword. 445 * @param $cwpos (int) Number of data codewords from the beginning of encoded data. 446 * @return pad codeword 447 * @protected 448 */ 449 protected function get255StateCodeword($cwpad, $cwpos) { 450 $pad = ($cwpad + (((149 * $cwpos) % 255) + 1)); 451 if ($pad > 255) { 452 $pad -= 256; 453 } 454 return $pad; 455 } 456 457 /** 458 * Returns true if the char belongs to the selected mode 459 * @param $chr (int) Character (byte) to check. 460 * @param $mode (int) Current encoding mode. 461 * @return boolean true if the char is of the selected mode. 462 * @protected 463 */ 464 protected function isCharMode($chr, $mode) { 465 $status = false; 466 switch ($mode) { 467 case self::ENC_ASCII: { // ASCII character 0 to 127 468 $status = (($chr >= 0) AND ($chr <= 127)); 469 break; 470 } 471 case self::ENC_C40: { // Upper-case alphanumeric 472 $status = (($chr == 32) OR (($chr >= 48) AND ($chr <= 57)) OR (($chr >= 65) AND ($chr <= 90))); 473 break; 474 } 475 case self::ENC_TXT: { // Lower-case alphanumeric 476 $status = (($chr == 32) OR (($chr >= 48) AND ($chr <= 57)) OR (($chr >= 97) AND ($chr <= 122))); 477 break; 478 } 479 case self::ENC_X12: { // ANSI X12 480 $status = (($chr == 13) OR ($chr == 42) OR ($chr == 62)); 481 break; 482 } 483 case self::ENC_EDF: { // ASCII character 32 to 94 484 $status = (($chr >= 32) AND ($chr <= 94)); 485 break; 486 } 487 case self::ENC_BASE256: { // Function character (FNC1, Structured Append, Reader Program, or Code Page) 488 $status = (($chr == 232) OR ($chr == 233) OR ($chr == 234) OR ($chr == 241)); 489 break; 490 } 491 case self::ENC_ASCII_EXT: { // ASCII character 128 to 255 492 $status = (($chr >= 128) AND ($chr <= 255)); 493 break; 494 } 495 case self::ENC_ASCII_NUM: { // ASCII digits 496 $status = (($chr >= 48) AND ($chr <= 57)); 497 break; 498 } 499 } 500 return $status; 501 } 502 503 /** 504 * The look-ahead test scans the data to be encoded to find the best mode (Annex P - steps from J to S). 505 * @param $data (string) data to encode 506 * @param $pos (int) current position 507 * @param $mode (int) current encoding mode 508 * @return int encoding mode 509 * @protected 510 */ 511 protected function lookAheadTest($data, $pos, $mode) { 512 $data_length = strlen($data); 513 if ($pos >= $data_length) { 514 return $mode; 515 } 516 $charscount = 0; // count processed chars 517 // STEP J 518 if ($mode == self::ENC_ASCII) { 519 $numch = array(0, 1, 1, 1, 1, 1.25); 520 } else { 521 $numch = array(1, 2, 2, 2, 2, 2.25); 522 $numch[$mode] = 0; 523 } 524 while (true) { 525 // STEP K 526 if (($pos + $charscount) == $data_length) { 527 if ($numch[self::ENC_ASCII] <= ceil(min($numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_EDF], $numch[self::ENC_BASE256]))) { 528 return self::ENC_ASCII; 529 } 530 if ($numch[self::ENC_BASE256] < ceil(min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_EDF]))) { 531 return self::ENC_BASE256; 532 } 533 if ($numch[self::ENC_EDF] < ceil(min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_BASE256]))) { 534 return self::ENC_EDF; 535 } 536 if ($numch[self::ENC_TXT] < ceil(min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_X12], $numch[self::ENC_EDF], $numch[self::ENC_BASE256]))) { 537 return self::ENC_TXT; 538 } 539 if ($numch[self::ENC_X12] < ceil(min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_EDF], $numch[self::ENC_BASE256]))) { 540 return self::ENC_X12; 541 } 542 return self::ENC_C40; 543 } 544 // get char 545 $chr = ord($data[$pos + $charscount]); 546 $charscount++; 547 // STEP L 548 if ($this->isCharMode($chr, self::ENC_ASCII_NUM)) { 549 $numch[self::ENC_ASCII] += (1 / 2); 550 } elseif ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 551 $numch[self::ENC_ASCII] = ceil($numch[self::ENC_ASCII]); 552 $numch[self::ENC_ASCII] += 2; 553 } else { 554 $numch[self::ENC_ASCII] = ceil($numch[self::ENC_ASCII]); 555 $numch[self::ENC_ASCII] += 1; 556 } 557 // STEP M 558 if ($this->isCharMode($chr, self::ENC_C40)) { 559 $numch[self::ENC_C40] += (2 / 3); 560 } elseif ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 561 $numch[self::ENC_C40] += (8 / 3); 562 } else { 563 $numch[self::ENC_C40] += (4 / 3); 564 } 565 // STEP N 566 if ($this->isCharMode($chr, self::ENC_TXT)) { 567 $numch[self::ENC_TXT] += (2 / 3); 568 } elseif ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 569 $numch[self::ENC_TXT] += (8 / 3); 570 } else { 571 $numch[self::ENC_TXT] += (4 / 3); 572 } 573 // STEP O 574 if ($this->isCharMode($chr, self::ENC_X12) OR $this->isCharMode($chr, self::ENC_C40)) { 575 $numch[self::ENC_X12] += (2 / 3); 576 } elseif ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 577 $numch[self::ENC_X12] += (13 / 3); 578 } else { 579 $numch[self::ENC_X12] += (10 / 3); 580 } 581 // STEP P 582 if ($this->isCharMode($chr, self::ENC_EDF)) { 583 $numch[self::ENC_EDF] += (3 / 4); 584 } elseif ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 585 $numch[self::ENC_EDF] += (17 / 4); 586 } else { 587 $numch[self::ENC_EDF] += (13 / 4); 588 } 589 // STEP Q 590 if ($this->isCharMode($chr, self::ENC_BASE256)) { 591 $numch[self::ENC_BASE256] += 4; 592 } else { 593 $numch[self::ENC_BASE256] += 1; 594 } 595 // STEP R 596 if ($charscount >= 4) { 597 if (($numch[self::ENC_ASCII] + 1) <= min($numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_EDF], $numch[self::ENC_BASE256])) { 598 return self::ENC_ASCII; 599 } 600 if ((($numch[self::ENC_BASE256] + 1) <= $numch[self::ENC_ASCII]) 601 OR (($numch[self::ENC_BASE256] + 1) < min($numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_EDF]))) { 602 return self::ENC_BASE256; 603 } 604 if (($numch[self::ENC_EDF] + 1) < min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_X12], $numch[self::ENC_BASE256])) { 605 return self::ENC_EDF; 606 } 607 if (($numch[self::ENC_TXT] + 1) < min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_X12], $numch[self::ENC_EDF], $numch[self::ENC_BASE256])) { 608 return self::ENC_TXT; 609 } 610 if (($numch[self::ENC_X12] + 1) < min($numch[self::ENC_ASCII], $numch[self::ENC_C40], $numch[self::ENC_TXT], $numch[self::ENC_EDF], $numch[self::ENC_BASE256])) { 611 return self::ENC_X12; 612 } 613 if (($numch[self::ENC_C40] + 1) < min($numch[self::ENC_ASCII], $numch[self::ENC_TXT], $numch[self::ENC_EDF], $numch[self::ENC_BASE256])) { 614 if ($numch[self::ENC_C40] < $numch[self::ENC_X12]) { 615 return self::ENC_C40; 616 } 617 if ($numch[self::ENC_C40] == $numch[self::ENC_X12]) { 618 $k = ($pos + $charscount + 1); 619 while ($k < $data_length) { 620 $tmpchr = ord($data[$k]); 621 if ($this->isCharMode($tmpchr, self::ENC_X12)) { 622 return self::ENC_X12; 623 } elseif (!($this->isCharMode($tmpchr, self::ENC_X12) OR $this->isCharMode($tmpchr, self::ENC_C40))) { 624 break; 625 } 626 ++$k; 627 } 628 return self::ENC_C40; 629 } 630 } 631 } 632 } // end of while 633 } 634 635 /** 636 * Get the switching codeword to a new encoding mode (latch codeword) 637 * @param $mode (int) New encoding mode. 638 * @return (int) Switch codeword. 639 * @protected 640 */ 641 protected function getSwitchEncodingCodeword($mode) { 642 switch ($mode) { 643 case self::ENC_ASCII: { // ASCII character 0 to 127 644 $cw = 254; 645 if ($this->last_enc == self::ENC_EDF) { 646 $cw = 124; 647 } 648 break; 649 } 650 case self::ENC_C40: { // Upper-case alphanumeric 651 $cw = 230; 652 break; 653 } 654 case self::ENC_TXT: { // Lower-case alphanumeric 655 $cw = 239; 656 break; 657 } 658 case self::ENC_X12: { // ANSI X12 659 $cw = 238; 660 break; 661 } 662 case self::ENC_EDF: { // ASCII character 32 to 94 663 $cw = 240; 664 break; 665 } 666 case self::ENC_BASE256: { // Function character (FNC1, Structured Append, Reader Program, or Code Page) 667 $cw = 231; 668 break; 669 } 670 } 671 return $cw; 672 } 673 674 /** 675 * Choose the minimum matrix size and return the max number of data codewords. 676 * @param $numcw (int) Number of current codewords. 677 * @return number of data codewords in matrix 678 * @protected 679 */ 680 protected function getMaxDataCodewords($numcw) { 681 foreach ($this->symbattr as $key => $matrix) { 682 if ($matrix[11] >= $numcw) { 683 return $matrix[11]; 684 } 685 } 686 return 0; 687 } 688 689 /** 690 * Get high level encoding using the minimum symbol data characters for ECC 200 691 * @param $data (string) data to encode 692 * @return array of codewords 693 * @protected 694 */ 695 protected function getHighLevelEncoding($data) { 696 // STEP A. Start in ASCII encodation. 697 $enc = self::ENC_ASCII; // current encoding mode 698 $pos = 0; // current position 699 $cw = array(); // array of codewords to be returned 700 $cw_num = 0; // number of data codewords 701 $data_length = strlen($data); // number of chars 702 while ($pos < $data_length) { 703 // set last used encoding 704 $this->last_enc = $enc; 705 switch ($enc) { 706 case self::ENC_ASCII: { // STEP B. While in ASCII encodation 707 if (($data_length > 1) AND ($pos < ($data_length - 1)) AND ($this->isCharMode(ord($data[$pos]), self::ENC_ASCII_NUM) AND $this->isCharMode(ord($data[$pos + 1]), self::ENC_ASCII_NUM))) { 708 // 1. If the next data sequence is at least 2 consecutive digits, encode the next two digits as a double digit in ASCII mode. 709 $cw[] = (intval(substr($data, $pos, 2)) + 130); 710 ++$cw_num; 711 $pos += 2; 712 } else { 713 // 2. If the look-ahead test (starting at step J) indicates another mode, switch to that mode. 714 $newenc = $this->lookAheadTest($data, $pos, $enc); 715 if ($newenc != $enc) { 716 // switch to new encoding 717 $enc = $newenc; 718 $cw[] = $this->getSwitchEncodingCodeword($enc); 719 ++$cw_num; 720 } else { 721 // get new byte 722 $chr = ord($data[$pos]); 723 ++$pos; 724 if ($this->isCharMode($chr, self::ENC_ASCII_EXT)) { 725 // 3. If the next data character is extended ASCII (greater than 127) encode it in ASCII mode first using the Upper Shift (value 235) character. 726 $cw[] = 235; 727 $cw[] = ($chr - 127); 728 $cw_num += 2; 729 } else { 730 // 4. Otherwise process the next data character in ASCII encodation. 731 $cw[] = ($chr + 1); 732 ++$cw_num; 733 } 734 } 735 } 736 break; 737 } 738 case self::ENC_C40 : // Upper-case alphanumeric 739 case self::ENC_TXT : // Lower-case alphanumeric 740 case self::ENC_X12 : { // ANSI X12 741 $temp_cw = array(); 742 $p = 0; 743 $epos = $pos; 744 // get charset ID 745 $set_id = $this->chset_id[$enc]; 746 // get basic charset for current encoding 747 $charset = $this->chset[$set_id]; 748 do { 749 // 2. process the next character in C40 encodation. 750 $chr = ord($data[$epos]); 751 ++$epos; 752 // check for extended character 753 if ($chr & 0x80) { 754 if ($enc == self::ENC_X12) { 755 return false; 756 } 757 $chr = ($chr & 0x7f); 758 $temp_cw[] = 1; // shift 2 759 $temp_cw[] = 30; // upper shift 760 $p += 2; 761 } 762 if (isset($charset[$chr])) { 763 $temp_cw[] = $charset[$chr]; 764 ++$p; 765 } else { 766 if (isset($this->chset['SH1'][$chr])) { 767 $temp_cw[] = 0; // shift 1 768 $shiftset = $this->chset['SH1']; 769 } elseif (isset($chr, $this->chset['SH2'][$chr])) { 770 $temp_cw[] = 1; // shift 2 771 $shiftset = $this->chset['SH2']; 772 } elseif (($enc == self::ENC_C40) AND isset($this->chset['S3C'][$chr])) { 773 $temp_cw[] = 2; // shift 3 774 $shiftset = $this->chset['S3C']; 775 } elseif (($enc == self::ENC_TXT) AND isset($this->chset['S3T'][$chr])) { 776 $temp_cw[] = 2; // shift 3 777 $shiftset = $this->chset['S3T']; 778 } else { 779 return false; 780 } 781 $temp_cw[] = $shiftset[$chr]; 782 $p += 2; 783 } 784 if ($p >= 3) { 785 $c1 = array_shift($temp_cw); 786 $c2 = array_shift($temp_cw); 787 $c3 = array_shift($temp_cw); 788 $p -= 3; 789 $tmp = ((1600 * $c1) + (40 * $c2) + $c3 + 1); 790 $cw[] = ($tmp >> 8); 791 $cw[] = ($tmp % 256); 792 $cw_num += 2; 793 $pos = $epos; 794 // 1. If the C40 encoding is at the point of starting a new double symbol character and if the look-ahead test (starting at step J) indicates another mode, switch to that mode. 795 $newenc = $this->lookAheadTest($data, $pos, $enc); 796 if ($newenc != $enc) { 797 // switch to new encoding 798 $enc = $newenc; 799 if ($enc != self::ENC_ASCII) { 800 // set unlatch character 801 $cw[] = $this->getSwitchEncodingCodeword(self::ENC_ASCII); 802 ++$cw_num; 803 } 804 $cw[] = $this->getSwitchEncodingCodeword($enc); 805 ++$cw_num; 806 $pos -= $p; 807 $p = 0; 808 break; 809 } 810 } 811 } while (($p > 0) AND ($epos < $data_length)); 812 // process last data (if any) 813 if ($p > 0) { 814 // get remaining number of data symbols 815 $cwr = ($this->getMaxDataCodewords($cw_num) - $cw_num); 816 if (($cwr == 1) AND ($p == 1)) { 817 // d. If one symbol character remains and one C40 value (data character) remains to be encoded 818 $c1 = array_shift($temp_cw); 819 --$p; 820 $cw[] = ($chr + 1); 821 ++$cw_num; 822 $pos = $epos; 823 $enc = self::ENC_ASCII; 824 $this->last_enc = $enc; 825 } elseif (($cwr == 2) AND ($p == 1)) { 826 // c. If two symbol characters remain and only one C40 value (data character) remains to be encoded 827 $c1 = array_shift($temp_cw); 828 --$p; 829 $cw[] = 254; 830 $cw[] = ($chr + 1); 831 $cw_num += 2; 832 $pos = $epos; 833 $enc = self::ENC_ASCII; 834 $this->last_enc = $enc; 835 } elseif (($cwr == 2) AND ($p == 2)) { 836 // b. If two symbol characters remain and two C40 values remain to be encoded 837 $c1 = array_shift($temp_cw); 838 $c2 = array_shift($temp_cw); 839 $p -= 2; 840 $tmp = ((1600 * $c1) + (40 * $c2) + 1); 841 $cw[] = ($tmp >> 8); 842 $cw[] = ($tmp % 256); 843 $cw_num += 2; 844 $pos = $epos; 845 $enc = self::ENC_ASCII; 846 $this->last_enc = $enc; 847 } else { 848 // switch to ASCII encoding 849 if ($enc != self::ENC_ASCII) { 850 $enc = self::ENC_ASCII; 851 $this->last_enc = $enc; 852 $cw[] = $this->getSwitchEncodingCodeword($enc); 853 ++$cw_num; 854 $pos = ($epos - $p); 855 } 856 } 857 } 858 break; 859 } 860 case self::ENC_EDF: { // F. While in EDIFACT (EDF) encodation 861 // initialize temporary array with 0 length 862 $temp_cw = array(); 863 $epos = $pos; 864 $field_length = 0; 865 $newenc = $enc; 866 do { 867 // 2. process the next character in EDIFACT encodation. 868 $chr = ord($data[$epos]); 869 if ($this->isCharMode($chr, self::ENC_EDF)) { 870 ++$epos; 871 $temp_cw[] = $chr; 872 ++$field_length; 873 } 874 if (($field_length == 4) OR ($epos == $data_length) OR !$this->isCharMode($chr, self::ENC_EDF)) { 875 if (($epos == $data_length) AND ($field_length < 3)) { 876 $enc = self::ENC_ASCII; 877 $cw[] = $this->getSwitchEncodingCodeword($enc); 878 ++$cw_num; 879 break; 880 } 881 if ($field_length < 4) { 882 // set unlatch character 883 $temp_cw[] = 0x1f; 884 ++$field_length; 885 // fill empty characters 886 for ($i = $field_length; $i < 4; ++$i) { 887 $temp_cw[] = 0; 888 } 889 $enc = self::ENC_ASCII; 890 $this->last_enc = $enc; 891 } 892 // encodes four data characters in three codewords 893 $tcw = (($temp_cw[0] & 0x3F) << 2) + (($temp_cw[1] & 0x30) >> 4); 894 if ($tcw > 0) { 895 $cw[] = $tcw; 896 $cw_num++; 897 } 898 $tcw= (($temp_cw[1] & 0x0F) << 4) + (($temp_cw[2] & 0x3C) >> 2); 899 if ($tcw > 0) { 900 $cw[] = $tcw; 901 $cw_num++; 902 } 903 $tcw = (($temp_cw[2] & 0x03) << 6) + ($temp_cw[3] & 0x3F); 904 if ($tcw > 0) { 905 $cw[] = $tcw; 906 $cw_num++; 907 } 908 $temp_cw = array(); 909 $pos = $epos; 910 $field_length = 0; 911 if ($enc == self::ENC_ASCII) { 912 break; // exit from EDIFACT mode 913 } 914 } 915 } while ($epos < $data_length); 916 break; 917 } 918 case self::ENC_BASE256: { // G. While in Base 256 (B256) encodation 919 // initialize temporary array with 0 length 920 $temp_cw = array(); 921 $field_length = 0; 922 while (($pos < $data_length) AND ($field_length <= 1555)) { 923 $newenc = $this->lookAheadTest($data, $pos, $enc); 924 if ($newenc != $enc) { 925 // 1. If the look-ahead test (starting at step J) indicates another mode, switch to that mode. 926 $enc = $newenc; 927 break; // exit from B256 mode 928 } else { 929 // 2. Otherwise, process the next character in Base 256 encodation. 930 $chr = ord($data[$pos]); 931 ++$pos; 932 $temp_cw[] = $chr; 933 ++$field_length; 934 } 935 } 936 // set field length 937 if ($field_length <= 249) { 938 $cw[] = $this->get255StateCodeword($field_length, ($cw_num + 1)); 939 ++$cw_num; 940 } else { 941 $cw[] = $this->get255StateCodeword((floor($field_length / 250) + 249), ($cw_num + 1)); 942 $cw[] = $this->get255StateCodeword(($field_length % 250), ($cw_num + 2)); 943 $cw_num += 2; 944 } 945 if (!empty($temp_cw)) { 946 // add B256 field 947 foreach ($temp_cw as $p => $cht) { 948 $cw[] = $this->get255StateCodeword($cht, ($cw_num + $p + 1)); 949 } 950 } 951 break; 952 } 953 } // end of switch enc 954 } // end of while 955 return $cw; 956 } 957 958 /** 959 * Places "chr+bit" with appropriate wrapping within array[]. 960 * (Annex F - ECC 200 symbol character placement) 961 * @param $marr (array) Array of symbols. 962 * @param $nrow (int) Number of rows. 963 * @param $ncol (int) Number of columns. 964 * @param $row (int) Row number. 965 * @param $col (int) Column number. 966 * @param $chr (int) Char byte. 967 * @param $bit (int) Bit. 968 * @return array 969 * @protected 970 */ 971 protected function placeModule($marr, $nrow, $ncol, $row, $col, $chr, $bit) { 972 if ($row < 0) { 973 $row += $nrow; 974 $col += (4 - (($nrow + 4) % 8)); 975 } 976 if ($col < 0) { 977 $col += $ncol; 978 $row += (4 - (($ncol + 4) % 8)); 979 } 980 $marr[(($row * $ncol) + $col)] = ((10 * $chr) + $bit); 981 return $marr; 982 } 983 984 /** 985 * Places the 8 bits of a utah-shaped symbol character. 986 * (Annex F - ECC 200 symbol character placement) 987 * @param $marr (array) Array of symbols. 988 * @param $nrow (int) Number of rows. 989 * @param $ncol (int) Number of columns. 990 * @param $row (int) Row number. 991 * @param $col (int) Column number. 992 * @param $chr (int) Char byte. 993 * @return array 994 * @protected 995 */ 996 protected function placeUtah($marr, $nrow, $ncol, $row, $col, $chr) { 997 $marr = $this->placeModule($marr, $nrow, $ncol, $row-2, $col-2, $chr, 1); 998 $marr = $this->placeModule($marr, $nrow, $ncol, $row-2, $col-1, $chr, 2); 999 $marr = $this->placeModule($marr, $nrow, $ncol, $row-1, $col-2, $chr, 3); 1000 $marr = $this->placeModule($marr, $nrow, $ncol, $row-1, $col-1, $chr, 4); 1001 $marr = $this->placeModule($marr, $nrow, $ncol, $row-1, $col, $chr, 5); 1002 $marr = $this->placeModule($marr, $nrow, $ncol, $row, $col-2, $chr, 6); 1003 $marr = $this->placeModule($marr, $nrow, $ncol, $row, $col-1, $chr, 7); 1004 $marr = $this->placeModule($marr, $nrow, $ncol, $row, $col, $chr, 8); 1005 return $marr; 1006 } 1007 1008 /** 1009 * Places the 8 bits of the first special corner case. 1010 * (Annex F - ECC 200 symbol character placement) 1011 * @param $marr (array) Array of symbols. 1012 * @param $nrow (int) Number of rows. 1013 * @param $ncol (int) Number of columns. 1014 * @param $chr (int) Char byte. 1015 * @return array 1016 * @protected 1017 */ 1018 protected function placeCornerA($marr, $nrow, $ncol, $chr) { 1019 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 0, $chr, 1); 1020 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 1, $chr, 2); 1021 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 2, $chr, 3); 1022 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-2, $chr, 4); 1023 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-1, $chr, 5); 1024 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-1, $chr, 6); 1025 $marr = $this->placeModule($marr, $nrow, $ncol, 2, $ncol-1, $chr, 7); 1026 $marr = $this->placeModule($marr, $nrow, $ncol, 3, $ncol-1, $chr, 8); 1027 return $marr; 1028 } 1029 1030 /** 1031 * Places the 8 bits of the second special corner case. 1032 * (Annex F - ECC 200 symbol character placement) 1033 * @param $marr (array) Array of symbols. 1034 * @param $nrow (int) Number of rows. 1035 * @param $ncol (int) Number of columns. 1036 * @param $chr (int) Char byte. 1037 * @return array 1038 * @protected 1039 */ 1040 protected function placeCornerB($marr, $nrow, $ncol, $chr) { 1041 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-3, 0, $chr, 1); 1042 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-2, 0, $chr, 2); 1043 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 0, $chr, 3); 1044 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-4, $chr, 4); 1045 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-3, $chr, 5); 1046 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-2, $chr, 6); 1047 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-1, $chr, 7); 1048 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-1, $chr, 8); 1049 return $marr; 1050 } 1051 1052 /** 1053 * Places the 8 bits of the third special corner case. 1054 * (Annex F - ECC 200 symbol character placement) 1055 * @param $marr (array) Array of symbols. 1056 * @param $nrow (int) Number of rows. 1057 * @param $ncol (int) Number of columns. 1058 * @param $chr (int) Char byte. 1059 * @return array 1060 * @protected 1061 */ 1062 protected function placeCornerC($marr, $nrow, $ncol, $chr) { 1063 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-3, 0, $chr, 1); 1064 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-2, 0, $chr, 2); 1065 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 0, $chr, 3); 1066 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-2, $chr, 4); 1067 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-1, $chr, 5); 1068 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-1, $chr, 6); 1069 $marr = $this->placeModule($marr, $nrow, $ncol, 2, $ncol-1, $chr, 7); 1070 $marr = $this->placeModule($marr, $nrow, $ncol, 3, $ncol-1, $chr, 8); 1071 return $marr; 1072 } 1073 1074 /** 1075 * Places the 8 bits of the fourth special corner case. 1076 * (Annex F - ECC 200 symbol character placement) 1077 * @param $marr (array) Array of symbols. 1078 * @param $nrow (int) Number of rows. 1079 * @param $ncol (int) Number of columns. 1080 * @param $chr (int) Char byte. 1081 * @return array 1082 * @protected 1083 */ 1084 protected function placeCornerD($marr, $nrow, $ncol, $chr) { 1085 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, 0, $chr, 1); 1086 $marr = $this->placeModule($marr, $nrow, $ncol, $nrow-1, $ncol-1, $chr, 2); 1087 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-3, $chr, 3); 1088 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-2, $chr, 4); 1089 $marr = $this->placeModule($marr, $nrow, $ncol, 0, $ncol-1, $chr, 5); 1090 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-3, $chr, 6); 1091 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-2, $chr, 7); 1092 $marr = $this->placeModule($marr, $nrow, $ncol, 1, $ncol-1, $chr, 8); 1093 return $marr; 1094 } 1095 1096 /** 1097 * Build a placement map. 1098 * (Annex F - ECC 200 symbol character placement) 1099 * @param $nrow (int) Number of rows. 1100 * @param $ncol (int) Number of columns. 1101 * @return array 1102 * @protected 1103 */ 1104 protected function getPlacementMap($nrow, $ncol) { 1105 // initialize array with zeros 1106 $marr = array_fill(0, ($nrow * $ncol), 0); 1107 // set starting values 1108 $chr = 1; 1109 $row = 4; 1110 $col = 0; 1111 do { 1112 // repeatedly first check for one of the special corner cases, then 1113 if (($row == $nrow) AND ($col == 0)) { 1114 $marr = $this->placeCornerA($marr, $nrow, $ncol, $chr); 1115 ++$chr; 1116 } 1117 if (($row == ($nrow - 2)) AND ($col == 0) AND ($ncol % 4)) { 1118 $marr = $this->placeCornerB($marr, $nrow, $ncol, $chr); 1119 ++$chr; 1120 } 1121 if (($row == ($nrow - 2)) AND ($col == 0) AND (($ncol % 8) == 4)) { 1122 $marr = $this->placeCornerC($marr, $nrow, $ncol, $chr); 1123 ++$chr; 1124 } 1125 if (($row == ($nrow + 4)) AND ($col == 2) AND (!($ncol % 8))) { 1126 $marr = $this->placeCornerD($marr, $nrow, $ncol, $chr); 1127 ++$chr; 1128 } 1129 // sweep upward diagonally, inserting successive characters, 1130 do { 1131 if (($row < $nrow) AND ($col >= 0) AND (!$marr[(($row * $ncol) + $col)])) { 1132 $marr = $this->placeUtah($marr, $nrow, $ncol, $row, $col, $chr); 1133 ++$chr; 1134 } 1135 $row -= 2; 1136 $col += 2; 1137 } while (($row >= 0) AND ($col < $ncol)); 1138 ++$row; 1139 $col += 3; 1140 // & then sweep downward diagonally, inserting successive characters,... 1141 do { 1142 if (($row >= 0) AND ($col < $ncol) AND (!$marr[(($row * $ncol) + $col)])) { 1143 $marr = $this->placeUtah($marr, $nrow, $ncol, $row, $col, $chr); 1144 ++$chr; 1145 } 1146 $row += 2; 1147 $col -= 2; 1148 } while (($row < $nrow) AND ($col >= 0)); 1149 $row += 3; 1150 ++$col; 1151 // ... until the entire array is scanned 1152 } while (($row < $nrow) OR ($col < $ncol)); 1153 // lastly, if the lower righthand corner is untouched, fill in fixed pattern 1154 if (!$marr[(($nrow * $ncol) - 1)]) { 1155 $marr[(($nrow * $ncol) - 1)] = 1; 1156 $marr[(($nrow * $ncol) - $ncol - 2)] = 1; 1157 } 1158 return $marr; 1159 } 1160 1161} // end DataMatrix class 1162//============================================================+ 1163// END OF FILE 1164//============================================================+ 1165