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