getECCodewordsPerBlock(); $ecbArray = $ecBlocks[0]->getECBlocks(); } else { $ecCodewords = $ecBlocks->getECCodewordsPerBlock(); $ecbArray = $ecBlocks->getECBlocks(); } foreach ($ecbArray as $ecBlock) { $total += $ecBlock->getCount() * ($ecBlock->getDataCodewords() + $ecCodewords); } $this->totalCodewords = $total; } public function getVersionNumber() { return $this->versionNumber; } public function getAlignmentPatternCenters() { return $this->alignmentPatternCenters; } public function getTotalCodewords() { return $this->totalCodewords; } public function getDimensionForVersion() { return 17 + 4 * $this->versionNumber; } public function getECBlocksForLevel($ecLevel) { return $this->ecBlocks[$ecLevel->getOrdinal()]; } /** *

Deduces version information purely from QR Code dimensions.

* * @param dimension $dimension in modules * @return Version for a QR Code of that dimension * @throws FormatException if dimension is not 1 mod 4 */ public static function getProvisionalVersionForDimension($dimension) { if ($dimension % 4 != 1) { throw FormatException::getFormatInstance(); } try { return self::getVersionForNumber(($dimension - 17) / 4); } catch (\InvalidArgumentException) { throw FormatException::getFormatInstance(); } } public static function getVersionForNumber($versionNumber) { if ($versionNumber < 1 || $versionNumber > 40) { throw new \InvalidArgumentException(); } if (!self::$VERSIONS) { self::$VERSIONS = self::buildVersions(); } return self::$VERSIONS[$versionNumber - 1]; } public static function decodeVersionInformation($versionBits) { $bestDifference = PHP_INT_MAX; $bestVersion = 0; for ($i = 0; $i < count(self::$VERSION_DECODE_INFO); $i++) { $targetVersion = self::$VERSION_DECODE_INFO[$i]; // Do the version info bits match exactly? done. if ($targetVersion == $versionBits) { return self::getVersionForNumber($i + 7); } // Otherwise see if this is the closest to a real version info bit string // we have seen so far $bitsDifference = FormatInformation::numBitsDiffering($versionBits, $targetVersion); if ($bitsDifference < $bestDifference) { $bestVersion = $i + 7; $bestDifference = $bitsDifference; } } // We can tolerate up to 3 bits of error since no two version info codewords will // differ in less than 8 bits. if ($bestDifference <= 3) { return self::getVersionForNumber($bestVersion); } // If we didn't find a close enough match, fail return null; } /** * See ISO 18004:2006 Annex E */ public function buildFunctionPattern() { $dimension = self::getDimensionForVersion(); $bitMatrix = new BitMatrix($dimension); // Top left finder pattern + separator + format $bitMatrix->setRegion(0, 0, 9, 9); // Top right finder pattern + separator + format $bitMatrix->setRegion($dimension - 8, 0, 8, 9); // Bottom left finder pattern + separator + format $bitMatrix->setRegion(0, $dimension - 8, 9, 8); // Alignment patterns $max = is_countable($this->alignmentPatternCenters) ? count($this->alignmentPatternCenters) : 0; for ($x = 0; $x < $max; $x++) { $i = $this->alignmentPatternCenters[$x] - 2; for ($y = 0; $y < $max; $y++) { if (($x == 0 && ($y == 0 || $y == $max - 1)) || ($x == $max - 1 && $y == 0)) { // No alignment patterns near the three finder paterns continue; } $bitMatrix->setRegion($this->alignmentPatternCenters[$y] - 2, $i, 5, 5); } } // Vertical timing pattern $bitMatrix->setRegion(6, 9, 1, $dimension - 17); // Horizontal timing pattern $bitMatrix->setRegion(9, 6, $dimension - 17, 1); if ($this->versionNumber > 6) { // Version info, top right $bitMatrix->setRegion($dimension - 11, 0, 3, 6); // Version info, bottom left $bitMatrix->setRegion(0, $dimension - 11, 6, 3); } return $bitMatrix; } /** * See ISO 18004:2006 6.5.1 Table 9 */ private static function buildVersions() { return [ new Version( 1, [], [new ECBlocks(7, [new ECB(1, 19)]), new ECBlocks(10, [new ECB(1, 16)]), new ECBlocks(13, [new ECB(1, 13)]), new ECBlocks(17, [new ECB(1, 9)])] ), new Version( 2, [6, 18], [new ECBlocks(10, [new ECB(1, 34)]), new ECBlocks(16, [new ECB(1, 28)]), new ECBlocks(22, [new ECB(1, 22)]), new ECBlocks(28, [new ECB(1, 16)])] ), new Version( 3, [6, 22], [ new ECBlocks(15, [new ECB(1, 55)]), new ECBlocks(26, [new ECB(1, 44)]), new ECBlocks(18, [new ECB(2, 17)]), new ECBlocks(22, [new ECB(2, 13)])] ), new Version( 4, [6, 26], [new ECBlocks(20, [new ECB(1, 80)]), new ECBlocks(18, [new ECB(2, 32)]), new ECBlocks(26, [new ECB(2, 24)]), new ECBlocks(16, [new ECB(4, 9)])] ), new Version( 5, [6, 30], [new ECBlocks(26, [new ECB(1, 108)]), new ECBlocks(24, [new ECB(2, 43)]), new ECBlocks(18, [new ECB(2, 15), new ECB(2, 16)]), new ECBlocks(22, [new ECB(2, 11), new ECB(2, 12)])] ), new Version( 6, [6, 34], [new ECBlocks(18, [new ECB(2, 68)]), new ECBlocks(16, [new ECB(4, 27)]), new ECBlocks(24, [new ECB(4, 19)]), new ECBlocks(28, [new ECB(4, 15)])] ), new Version( 7, [6, 22, 38], [new ECBlocks(20, [new ECB(2, 78)]), new ECBlocks(18, [new ECB(4, 31)]), new ECBlocks(18, [new ECB(2, 14), new ECB(4, 15)]), new ECBlocks(26, [new ECB(4, 13), new ECB(1, 14)])] ), new Version( 8, [6, 24, 42], [new ECBlocks(24, [new ECB(2, 97)]), new ECBlocks(22, [new ECB(2, 38), new ECB(2, 39)]), new ECBlocks(22, [new ECB(4, 18), new ECB(2, 19)]), new ECBlocks(26, [new ECB(4, 14), new ECB(2, 15)])] ), new Version( 9, [6, 26, 46], [new ECBlocks(30, [new ECB(2, 116)]), new ECBlocks(22, [new ECB(3, 36), new ECB(2, 37)]), new ECBlocks(20, [new ECB(4, 16), new ECB(4, 17)]), new ECBlocks(24, [new ECB(4, 12), new ECB(4, 13)])] ), new Version( 10, [6, 28, 50], [new ECBlocks(18, [new ECB(2, 68), new ECB(2, 69)]), new ECBlocks(26, [new ECB(4, 43), new ECB(1, 44)]), new ECBlocks(24, [new ECB(6, 19), new ECB(2, 20)]), new ECBlocks(28, [new ECB(6, 15), new ECB(2, 16)])] ), new Version( 11, [6, 30, 54], [new ECBlocks(20, [new ECB(4, 81)]), new ECBlocks(30, [new ECB(1, 50), new ECB(4, 51)]), new ECBlocks(28, [new ECB(4, 22), new ECB(4, 23)]), new ECBlocks(24, [new ECB(3, 12), new ECB(8, 13)])] ), new Version( 12, [6, 32, 58], [new ECBlocks(24, [new ECB(2, 92), new ECB(2, 93)]), new ECBlocks(22, [new ECB(6, 36), new ECB(2, 37)]), new ECBlocks(26, [new ECB(4, 20), new ECB(6, 21)]), new ECBlocks(28, [new ECB(7, 14), new ECB(4, 15)])] ), new Version( 13, [6, 34, 62], [new ECBlocks(26, [new ECB(4, 107)]), new ECBlocks(22, [new ECB(8, 37), new ECB(1, 38)]), new ECBlocks(24, [new ECB(8, 20), new ECB(4, 21)]), new ECBlocks(22, [new ECB(12, 11), new ECB(4, 12)])] ), new Version( 14, [6, 26, 46, 66], [new ECBlocks(30, [new ECB(3, 115), new ECB(1, 116)]), new ECBlocks(24, [new ECB(4, 40), new ECB(5, 41)]), new ECBlocks(20, [new ECB(11, 16), new ECB(5, 17)]), new ECBlocks(24, [new ECB(11, 12), new ECB(5, 13)])] ), new Version( 15, [6, 26, 48, 70], [new ECBlocks(22, [new ECB(5, 87), new ECB(1, 88)]), new ECBlocks(24, [new ECB(5, 41), new ECB(5, 42)]), new ECBlocks(30, [new ECB(5, 24), new ECB(7, 25)]), new ECBlocks(24, [new ECB(11, 12), new ECB(7, 13)])] ), new Version( 16, [6, 26, 50, 74], [new ECBlocks(24, [new ECB(5, 98), new ECB(1, 99)]), new ECBlocks(28, [new ECB(7, 45), new ECB(3, 46)]), new ECBlocks(24, [new ECB(15, 19), new ECB(2, 20)]), new ECBlocks(30, [new ECB(3, 15), new ECB(13, 16)])] ), new Version( 17, [6, 30, 54, 78], [new ECBlocks(28, [new ECB(1, 107), new ECB(5, 108)]), new ECBlocks(28, [new ECB(10, 46), new ECB(1, 47)]), new ECBlocks(28, [new ECB(1, 22), new ECB(15, 23)]), new ECBlocks(28, [new ECB(2, 14), new ECB(17, 15)])] ), new Version( 18, [6, 30, 56, 82], [new ECBlocks(30, [new ECB(5, 120), new ECB(1, 121)]), new ECBlocks(26, [new ECB(9, 43), new ECB(4, 44)]), new ECBlocks(28, [new ECB(17, 22), new ECB(1, 23)]), new ECBlocks(28, [new ECB(2, 14), new ECB(19, 15)])] ), new Version( 19, [6, 30, 58, 86], [new ECBlocks(28, [new ECB(3, 113), new ECB(4, 114)]), new ECBlocks(26, [new ECB(3, 44), new ECB(11, 45)]), new ECBlocks(26, [new ECB(17, 21), new ECB(4, 22)]), new ECBlocks(26, [new ECB(9, 13), new ECB(16, 14)])] ), new Version( 20, [6, 34, 62, 90], [new ECBlocks(28, [new ECB(3, 107), new ECB(5, 108)]), new ECBlocks(26, [new ECB(3, 41), new ECB(13, 42)]), new ECBlocks(30, [new ECB(15, 24), new ECB(5, 25)]), new ECBlocks(28, [new ECB(15, 15), new ECB(10, 16)])] ), new Version( 21, [6, 28, 50, 72, 94], [ new ECBlocks(28, [new ECB(4, 116), new ECB(4, 117)]), new ECBlocks(26, [new ECB(17, 42)]), new ECBlocks(28, [new ECB(17, 22), new ECB(6, 23)]), new ECBlocks(30, [new ECB(19, 16), new ECB(6, 17)])] ), new Version( 22, [6, 26, 50, 74, 98], [new ECBlocks(28, [new ECB(2, 111), new ECB(7, 112)]), new ECBlocks(28, [new ECB(17, 46)]), new ECBlocks(30, [new ECB(7, 24), new ECB(16, 25)]), new ECBlocks(24, [new ECB(34, 13)])] ), new Version( 23, [6, 30, 54, 78, 102], new ECBlocks(30, [new ECB(4, 121), new ECB(5, 122)]), new ECBlocks(28, [new ECB(4, 47), new ECB(14, 48)]), new ECBlocks(30, [new ECB(11, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(16, 15), new ECB(14, 16)]) ), new Version( 24, [6, 28, 54, 80, 106], [new ECBlocks(30, [new ECB(6, 117), new ECB(4, 118)]), new ECBlocks(28, [new ECB(6, 45), new ECB(14, 46)]), new ECBlocks(30, [new ECB(11, 24), new ECB(16, 25)]), new ECBlocks(30, [new ECB(30, 16), new ECB(2, 17)])] ), new Version( 25, [6, 32, 58, 84, 110], [new ECBlocks(26, [new ECB(8, 106), new ECB(4, 107)]), new ECBlocks(28, [new ECB(8, 47), new ECB(13, 48)]), new ECBlocks(30, [new ECB(7, 24), new ECB(22, 25)]), new ECBlocks(30, [new ECB(22, 15), new ECB(13, 16)])] ), new Version( 26, [6, 30, 58, 86, 114], [new ECBlocks(28, [new ECB(10, 114), new ECB(2, 115)]), new ECBlocks(28, [new ECB(19, 46), new ECB(4, 47)]), new ECBlocks(28, [new ECB(28, 22), new ECB(6, 23)]), new ECBlocks(30, [new ECB(33, 16), new ECB(4, 17)])] ), new Version( 27, [6, 34, 62, 90, 118], [new ECBlocks(30, [new ECB(8, 122), new ECB(4, 123)]), new ECBlocks(28, [new ECB(22, 45), new ECB(3, 46)]), new ECBlocks(30, [new ECB(8, 23), new ECB(26, 24)]), new ECBlocks(30, [new ECB(12, 15), new ECB(28, 16)])] ), new Version( 28, [6, 26, 50, 74, 98, 122], [new ECBlocks(30, [new ECB(3, 117), new ECB(10, 118)]), new ECBlocks(28, [new ECB(3, 45), new ECB(23, 46)]), new ECBlocks(30, [new ECB(4, 24), new ECB(31, 25)]), new ECBlocks(30, [new ECB(11, 15), new ECB(31, 16)])] ), new Version( 29, [6, 30, 54, 78, 102, 126], [new ECBlocks(30, [new ECB(7, 116), new ECB(7, 117)]), new ECBlocks(28, [new ECB(21, 45), new ECB(7, 46)]), new ECBlocks(30, [new ECB(1, 23), new ECB(37, 24)]), new ECBlocks(30, [new ECB(19, 15), new ECB(26, 16)])] ), new Version( 30, [6, 26, 52, 78, 104, 130], [new ECBlocks(30, [new ECB(5, 115), new ECB(10, 116)]), new ECBlocks(28, [new ECB(19, 47), new ECB(10, 48)]), new ECBlocks(30, [new ECB(15, 24), new ECB(25, 25)]), new ECBlocks(30, [new ECB(23, 15), new ECB(25, 16)])] ), new Version( 31, [6, 30, 56, 82, 108, 134], [new ECBlocks(30, [new ECB(13, 115), new ECB(3, 116)]), new ECBlocks(28, [new ECB(2, 46), new ECB(29, 47)]), new ECBlocks(30, [new ECB(42, 24), new ECB(1, 25)]), new ECBlocks(30, [new ECB(23, 15), new ECB(28, 16)])] ), new Version( 32, [6, 34, 60, 86, 112, 138], [new ECBlocks(30, [new ECB(17, 115)]), new ECBlocks(28, [new ECB(10, 46), new ECB(23, 47)]), new ECBlocks(30, [new ECB(10, 24), new ECB(35, 25)]), new ECBlocks(30, [new ECB(19, 15), new ECB(35, 16)])] ), new Version( 33, [6, 30, 58, 86, 114, 142], [new ECBlocks(30, [new ECB(17, 115), new ECB(1, 116)]), new ECBlocks(28, [new ECB(14, 46), new ECB(21, 47)]), new ECBlocks(30, [new ECB(29, 24), new ECB(19, 25)]), new ECBlocks(30, [new ECB(11, 15), new ECB(46, 16)])] ), new Version( 34, [6, 34, 62, 90, 118, 146], [new ECBlocks(30, [new ECB(13, 115), new ECB(6, 116)]), new ECBlocks(28, [new ECB(14, 46), new ECB(23, 47)]), new ECBlocks(30, [new ECB(44, 24), new ECB(7, 25)]), new ECBlocks(30, [new ECB(59, 16), new ECB(1, 17)])] ), new Version( 35, [6, 30, 54, 78, 102, 126, 150], [new ECBlocks(30, [new ECB(12, 121), new ECB(7, 122)]), new ECBlocks(28, [new ECB(12, 47), new ECB(26, 48)]), new ECBlocks(30, [new ECB(39, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(22, 15), new ECB(41, 16)])] ), new Version( 36, [6, 24, 50, 76, 102, 128, 154], [new ECBlocks(30, [new ECB(6, 121), new ECB(14, 122)]), new ECBlocks(28, [new ECB(6, 47), new ECB(34, 48)]), new ECBlocks(30, [new ECB(46, 24), new ECB(10, 25)]), new ECBlocks(30, [new ECB(2, 15), new ECB(64, 16)])] ), new Version( 37, [6, 28, 54, 80, 106, 132, 158], [new ECBlocks(30, [new ECB(17, 122), new ECB(4, 123)]), new ECBlocks(28, [new ECB(29, 46), new ECB(14, 47)]), new ECBlocks(30, [new ECB(49, 24), new ECB(10, 25)]), new ECBlocks(30, [new ECB(24, 15), new ECB(46, 16)])] ), new Version( 38, [6, 32, 58, 84, 110, 136, 162], [new ECBlocks(30, [new ECB(4, 122), new ECB(18, 123)]), new ECBlocks(28, [new ECB(13, 46), new ECB(32, 47)]), new ECBlocks(30, [new ECB(48, 24), new ECB(14, 25)]), new ECBlocks(30, [new ECB(42, 15), new ECB(32, 16)])] ), new Version( 39, [6, 26, 54, 82, 110, 138, 166], [new ECBlocks(30, [new ECB(20, 117), new ECB(4, 118)]), new ECBlocks(28, [new ECB(40, 47), new ECB(7, 48)]), new ECBlocks(30, [new ECB(43, 24), new ECB(22, 25)]), new ECBlocks(30, [new ECB(10, 15), new ECB(67, 16)])] ), new Version( 40, [6, 30, 58, 86, 114, 142, 170], [new ECBlocks(30, [new ECB(19, 118), new ECB(6, 119)]), new ECBlocks(28, [new ECB(18, 47), new ECB(31, 48)]), new ECBlocks(30, [new ECB(34, 24), new ECB(34, 25)]), new ECBlocks(30, [new ECB(20, 15), new ECB(61, 16)])] ) ]; } } /** *

Encapsulates a set of error-correction blocks in one symbol version. Most versions will * use blocks of differing sizes within one version, so, this encapsulates the parameters for * each set of blocks. It also holds the number of error-correction codewords per block since it * will be the same across all blocks within one version.

*/ final class ECBlocks { public function __construct(private $ecCodewordsPerBlock, private $ecBlocks) { } public function getECCodewordsPerBlock() { return $this->ecCodewordsPerBlock; } public function getNumBlocks() { $total = 0; foreach ($this->ecBlocks as $ecBlock) { $total += $ecBlock->getCount(); } return $total; } public function getTotalECCodewords() { return $this->ecCodewordsPerBlock * $this->getNumBlocks(); } public function getECBlocks() { return $this->ecBlocks; } } /** *

Encapsualtes the parameters for one error-correction block in one symbol version. * This includes the number of data codewords, and the number of times a block with these * parameters is used consecutively in the QR code version's format.

*/ final class ECB { public function __construct(private $count, private $dataCodewords) { } public function getCount() { return $this->count; } public function getDataCodewords() { return $this->dataCodewords; } //@Override public function toString(): never { die('Version ECB toString()'); // return parent::$versionNumber; } }