diff --git a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs index 8eecd56..90b89de 100644 --- a/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs +++ b/src/common/cpp_essentials/base_extentions/qr_formatinformation.rs @@ -64,7 +64,7 @@ impl FormatInformation { // Use bits 3/4 for error correction, and 0-2 for mask. fi.error_correction_level = - ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, true); + ErrorCorrectionLevel::ECLevelFromBits((fi.index >> 3) & 0x03, false); fi.data_mask = fi.index & 0x07; fi.isMirrored = fi.bitsIndex > 1; diff --git a/src/qrcode/cpp_port/mod.rs b/src/qrcode/cpp_port/mod.rs index a604bc6..f18f038 100644 --- a/src/qrcode/cpp_port/mod.rs +++ b/src/qrcode/cpp_port/mod.rs @@ -6,3 +6,6 @@ mod qr_cpp_reader; pub use qr_cpp_reader::QrReader; mod bitmatrix_parser; + +#[cfg(test)] +mod test; diff --git a/src/qrcode/cpp_port/test/MQRDecoderTest.cpp b/src/qrcode/cpp_port/test/MQRDecoderTest.cpp new file mode 100644 index 0000000..25e7f70 --- /dev/null +++ b/src/qrcode/cpp_port/test/MQRDecoderTest.cpp @@ -0,0 +1,124 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "qrcode/QRDecoder.h" + +#include "BitMatrix.h" +#include "BitMatrixIO.h" +#include "DecoderResult.h" + +#include "gtest/gtest.h" + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(MQRDecoderTest, MQRCodeM3L) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X X X \n" + "X XXX X XXXXXXX\n" + "X XXX X X X XX\n" + "X XXX X X XX\n" + "X X X X X X\n" + "XXXXXXX X XX \n" + " X X X\n" + "XXXXXX X X X\n" + " X XX XXX\n" + "XXX XX XXXX XXX\n" + " X X XXX X \n" + "X XXXXX XXX X X\n" + " X X X XXX \n" + "XXX XX X X XXXX\n", + 88, false); + + const auto result = Decode(bitMatrix); + EXPECT_TRUE(result.isValid()); +} + +TEST(MQRDecoderTest, MQRCodeM3M) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X XX\n" + "X XXX X X XX XX\n" + "X XXX X X X \n" + "X XXX X XX XXXX\n" + "X X XX \n" + "XXXXXXX X XXXX\n" + " X XXX \n" + "X XX XX X X\n" + " X X XX \n" + "XX XX XXXXXXX\n" + " X X X\n" + "XX X X X \n" + " X X X \n" + "X X XXXX XXX\n", + 88, false); + + const auto result = Decode(bitMatrix); + EXPECT_TRUE(result.isValid()); +} + +TEST(MQRDecoderTest, MQRCodeM1) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X\n" + "X X \n" + "X XXX X XXX\n" + "X XXX X XX\n" + "X XXX X X\n" + "X X XX \n" + "XXXXXXX X \n" + " X \n" + "XX X \n" + " X XXXXX X\n" + "X XXXXXX X\n", + 88, false); + const auto result = Decode(bitMatrix); + EXPECT_TRUE(result.isValid()); + EXPECT_EQ(L"123", result.text()); +} + +TEST(MQRDecoderTest, MQRCodeM1Error4Bits) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X\n" + "X X XX\n" + "X XXX X X \n" + "X XXX X XX\n" + "X XXX X X\n" + "X X XX \n" + "XXXXXXX X \n" + " X \n" + "XX X \n" + " X XXXXXX \n" + "X XXXXXXX \n", + 88, false); + const auto result = Decode(bitMatrix); + EXPECT_EQ(Error::Checksum, result.error()); + EXPECT_TRUE(result.text().empty()); +} + +TEST(MQRDecoderTest, MQRCodeM4) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X X\n" + "X X XX X XX\n" + "X XXX X X X XX\n" + "X XXX X XX XX XX\n" + "X XXX X X XXXXX\n" + "X X XX X\n" + "XXXXXXX XX X XX\n" + " X XX XX\n" + "X X XXX X XXX\n" + " XX X XX XX X \n" + "XX XXXX X XX XX\n" + " XX XX X XX XX\n" + "XXX XXX XXX XX XX\n" + " X X X XX X\n" + "X X XX XXXXX \n" + " X X X X X \n" + "X XXXXXXX X X X\n", + 88, false); + const auto result = Decode(bitMatrix); + EXPECT_TRUE(result.isValid()); +} diff --git a/src/qrcode/cpp_port/test/QRBitMatrixParserTest.cpp b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.cpp new file mode 100644 index 0000000..f8c5a93 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRBitMatrixParserTest.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Huy Cuong Nguyen + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "BitMatrix.h" +#include "BitMatrixIO.h" +#include "ByteArray.h" +#include "qrcode/QRBitMatrixParser.h" +#include "qrcode/QRFormatInformation.h" +#include "qrcode/QRVersion.h" + +#include "gtest/gtest.h" + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(QRBitMatrixParserTest, MQRCodeM3L) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X X X \n" + "X XXX X XXXXXXX\n" + "X XXX X X X XX\n" + "X XXX X X XX\n" + "X X X X X X\n" + "XXXXXXX X XX \n" + " X X X\n" + "XXXXXX X X X\n" + " X XX XXX\n" + "XXX XX XXXX XXX\n" + " X X XXX X \n" + "X XXXXX XXX X X\n" + " X X X XXX \n" + "XXX XX X X XXXX\n", + 88, false); + + const auto version = ReadVersion(bitMatrix); + EXPECT_EQ(3, version->versionNumber()); + const auto format = ReadFormatInformation(bitMatrix, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format); + EXPECT_EQ(17, codewords.size()); + EXPECT_EQ(0x0, codewords[10]); + EXPECT_EQ(0xd1, codewords[11]); +} + +TEST(QRBitMatrixParserTest, MQRCodeM3M) +{ + const auto bitMatrix = ParseBitMatrix("XXXXXXX X X X X\n" + "X X XX\n" + "X XXX X X XX XX\n" + "X XXX X X X \n" + "X XXX X XX XXXX\n" + "X X XX \n" + "XXXXXXX X XXXX\n" + " X XXX \n" + "X XX XX X X\n" + " X X XX \n" + "XX XX XXXXXXX\n" + " X X X\n" + "XX X X X \n" + " X X X \n" + "X X XXXX XXX\n", + 88, false); + + const auto version = ReadVersion(bitMatrix); + EXPECT_EQ(3, version->versionNumber()); + const auto format = ReadFormatInformation(bitMatrix, true); + const auto codewords = ReadCodewords(bitMatrix, *version, format); + EXPECT_EQ(17, codewords.size()); + EXPECT_EQ(0x0, codewords[8]); + EXPECT_EQ(0x89, codewords[9]); +} diff --git a/src/qrcode/cpp_port/test/QRDataMaskTest.cpp b/src/qrcode/cpp_port/test/QRDataMaskTest.cpp new file mode 100644 index 0000000..24baa4a --- /dev/null +++ b/src/qrcode/cpp_port/test/QRDataMaskTest.cpp @@ -0,0 +1,100 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2007 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "qrcode/QRDataMask.h" +#include "BitMatrix.h" + +#include "gtest/gtest.h" +#include + +using namespace ZXing; +using namespace ZXing::QRCode; + +namespace { + + void TestMaskAcrossDimensionsImpl(int maskIndex, bool isMicro, const int versionMax, const int dimensionStart, const int dimensionStep, std::function condition) + { + for (int version = 1; version <= versionMax; version++) { + int dimension = dimensionStart + dimensionStep * version; + BitMatrix bits(dimension); + + for (int i = 0; i < dimension; i++) + for (int j = 0; j < dimension; j++) + EXPECT_EQ(GetMaskedBit(bits, j, i, maskIndex, isMicro), condition(i, j)) << "(" << i << ',' << j << ')'; + } + } + + void TestMaskAcrossDimensions(int maskIndex, std::function condition) + { + TestMaskAcrossDimensionsImpl(maskIndex, false, 40, 17, 4, condition); + } + + void TestMicroMaskAcrossDimensions(int maskIndex, std::function condition) + { + TestMaskAcrossDimensionsImpl(maskIndex, true, 4, 9, 2, condition); + } + +} + +TEST(QRDataMaskTest, Mask0) +{ + TestMaskAcrossDimensions(0, [](int i, int j) { return (i + j) % 2 == 0; }); +} + +TEST(QRDataMaskTest, Mask1) +{ + TestMaskAcrossDimensions(1, [](int i, int) { return i % 2 == 0; }); +} + +TEST(QRDataMaskTest, Mask2) +{ + TestMaskAcrossDimensions(2, [](int, int j) { return j % 3 == 0; }); +} + +TEST(QRDataMaskTest, Mask3) +{ + TestMaskAcrossDimensions(3, [](int i, int j) { return (i + j) % 3 == 0; }); +} + +TEST(QRDataMaskTest, Mask4) +{ + TestMaskAcrossDimensions(4, [](int i, int j) { return (i / 2 + j / 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, Mask5) +{ + TestMaskAcrossDimensions(5, [](int i, int j) { return (i * j) % 2 + (i * j) % 3 == 0; }); +} + +TEST(QRDataMaskTest, Mask6) +{ + TestMaskAcrossDimensions(6, [](int i, int j) { return ((i * j) % 2 + (i * j) % 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, Mask7) +{ + TestMaskAcrossDimensions(7, [](int i, int j) { return ((i + j) % 2 + (i * j) % 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask0) +{ + TestMicroMaskAcrossDimensions(0, [](int i, int) { return i % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask1) +{ + TestMicroMaskAcrossDimensions(1, [](int i, int j) { return (i / 2 + j / 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask2) +{ + TestMicroMaskAcrossDimensions(2, [](int i, int j) { return ((i * j) % 2 + (i * j) % 3) % 2 == 0; }); +} + +TEST(QRDataMaskTest, MicroMask3) +{ + TestMicroMaskAcrossDimensions(3, [](int i, int j) { return ((i + j) % 2 + (i * j) % 3) % 2 == 0; }); +} diff --git a/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.cpp b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.cpp new file mode 100644 index 0000000..66d4f89 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRDecodedBitStreamParserTest.cpp @@ -0,0 +1,134 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "BitArray.h" +#include "ByteArray.h" +#include "DecoderResult.h" +#include "qrcode/QRDataMask.h" +#include "qrcode/QRDecoder.h" +#include "qrcode/QRErrorCorrectionLevel.h" +#include "qrcode/QRVersion.h" + +#include "gtest/gtest.h" + +namespace ZXing { + namespace QRCode { + DecoderResult DecodeBitStream(ByteArray&& bytes, const Version& version, ErrorCorrectionLevel ecLevel); + } +} + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(QRDecodedBitStreamParserTest, SimpleByteMode) +{ + BitArray ba; + ba.appendBits(0x04, 4); // Byte mode + ba.appendBits(0x03, 8); // 3 bytes + ba.appendBits(0xF1, 8); + ba.appendBits(0xF2, 8); + ba.appendBits(0xF3, 8); + auto result = DecodeBitStream(ba.toBytes(), *Version::FromNumber(1), ErrorCorrectionLevel::Medium).text(); + EXPECT_EQ(L"\xF1\xF2\xF3", result); +} + +TEST(QRDecodedBitStreamParserTest, SimpleSJIS) +{ + BitArray ba; + ba.appendBits(0x04, 4); // Byte mode + ba.appendBits(0x04, 8); // 4 bytes + ba.appendBits(0xA1, 8); + ba.appendBits(0xA2, 8); + ba.appendBits(0xA3, 8); + ba.appendBits(0xD0, 8); + auto result = DecodeBitStream(ba.toBytes(), *Version::FromNumber(1), ErrorCorrectionLevel::Medium).text(); + EXPECT_EQ(L"\uff61\uff62\uff63\uff90", result); +} + +TEST(QRDecodedBitStreamParserTest, ECI) +{ + BitArray ba; + ba.appendBits(0x07, 4); // ECI mode + ba.appendBits(0x02, 8); // ECI 2 = CP437 encoding + ba.appendBits(0x04, 4); // Byte mode + ba.appendBits(0x03, 8); // 3 bytes + ba.appendBits(0xA1, 8); + ba.appendBits(0xA2, 8); + ba.appendBits(0xA3, 8); + auto result = DecodeBitStream(ba.toBytes(), *Version::FromNumber(1), ErrorCorrectionLevel::Medium).text(); + EXPECT_EQ(L"\xED\xF3\xFA", result); +} + +TEST(QRDecodedBitStreamParserTest, Hanzi) +{ + BitArray ba; + ba.appendBits(0x0D, 4); // Hanzi mode + ba.appendBits(0x01, 4); // Subset 1 = GB2312 encoding + ba.appendBits(0x01, 8); // 1 characters + ba.appendBits(0x03C1, 13); + auto result = DecodeBitStream(ba.toBytes(), *Version::FromNumber(1), ErrorCorrectionLevel::Medium).text(); + EXPECT_EQ(L"\u963f", result); +} + +TEST(QRDecodedBitStreamParserTest, HanziLevel1) +{ + BitArray ba; + ba.appendBits(0x0D, 4); // Hanzi mode + ba.appendBits(0x01, 4); // Subset 1 = GB2312 encoding + ba.appendBits(0x01, 8); // 1 characters + // A5A2 (U+30A2) => A5A2 - A1A1 = 401, 4*60 + 01 = 0181 + ba.appendBits(0x0181, 13); + + auto result = DecodeBitStream(ba.toBytes(), *Version::FromNumber(1), ErrorCorrectionLevel::Medium).text(); + EXPECT_EQ(L"\u30a2", result); +} + +TEST(QRDecodedBitStreamParserTest, SymbologyIdentifier) +{ + const Version& version = *Version::FromNumber(1); + const ErrorCorrectionLevel ecLevel = ErrorCorrectionLevel::Medium; + DecoderResult result; + + // Plain "ANUM(1) A" + result = DecodeBitStream({0x20, 0x09, 0x40}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q1"); + EXPECT_EQ(result.text(), L"A"); + + // GS1 "FNC1(1st) NUM(4) 2001" + result = DecodeBitStream({0x51, 0x01, 0x0C, 0x81, 0x00}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); + EXPECT_EQ(result.text(), L"2001"); // "(20)01" + + // GS1 "NUM(4) 2001 FNC1(1st) 301" - FNC1(1st) can occur anywhere (this actually violates the specification) + result = DecodeBitStream({0x10, 0x10, 0xC8, 0x15, 0x10, 0x0D, 0x2D, 0x00}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q3"); + EXPECT_EQ(result.text(), L"2001301"); // "(20)01(30)1" + + // AIM "FNC1(2nd) 99 (0x63) ANUM(1) A" + result = DecodeBitStream({0x96, 0x32, 0x00, 0x94, 0x00}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); + EXPECT_EQ(result.text(), L"99A"); + + // AIM "BYTE(1) A FNC1(2nd) 99 (0x63) BYTE(1) B" - FNC1(2nd) can occur anywhere + // Disabled this test, since this violates the specification and the code does support it anymore +// result = DecodeBitStream({0x40, 0x14, 0x19, 0x63, 0x40, 0x14, 0x20, 0x00}, version, ecLevel, ""); +// EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); +// EXPECT_EQ(result.text(), L"99AB"); // Application Indicator prefixed to data + + // AIM "FNC1(2nd) A (100 + 61 = 0xA5) ANUM(1) B" + result = DecodeBitStream({0x9A, 0x52, 0x00, 0x96, 0x00}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); + EXPECT_EQ(result.text(), L"AB"); + + // AIM "FNC1(2nd) a (100 + 97 = 0xC5) ANUM(1) B" + result = DecodeBitStream({0x9C, 0x52, 0x00, 0x96, 0x00}, version, ecLevel); + EXPECT_EQ(result.symbologyIdentifier(), "]Q5"); + EXPECT_EQ(result.text(), L"aB"); + + // Bad AIM Application Indicator "FNC1(2nd) @ (0xA4) ANUM(1) B" + result = DecodeBitStream({0x9A, 0x42, 0x00, 0x96, 0x00}, version, ecLevel); + EXPECT_FALSE(result.isValid()); +} diff --git a/src/qrcode/cpp_port/test/QREncoderTest.cpp b/src/qrcode/cpp_port/test/QREncoderTest.cpp new file mode 100644 index 0000000..8e28abc --- /dev/null +++ b/src/qrcode/cpp_port/test/QREncoderTest.cpp @@ -0,0 +1,657 @@ +/* + * Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "BitArray.h" +#include "BitArrayUtility.h" +#include "BitMatrixIO.h" +#include "CharacterSet.h" +#include "TextDecoder.h" +#include "Utf.h" +#include "qrcode/QREncoder.h" +#include "qrcode/QRCodecMode.h" +#include "qrcode/QREncodeResult.h" +#include "qrcode/QRErrorCorrectionLevel.h" + +#include "gtest/gtest.h" + +namespace ZXing { + namespace QRCode { + int GetAlphanumericCode(int code); + CodecMode ChooseMode(const std::wstring& content, CharacterSet encoding); + void AppendModeInfo(CodecMode mode, BitArray& bits); + void AppendLengthInfo(int numLetters, const Version& version, CodecMode mode, BitArray& bits); + void AppendNumericBytes(const std::wstring& content, BitArray& bits); + void AppendAlphanumericBytes(const std::wstring& content, BitArray& bits); + void Append8BitBytes(const std::wstring& content, CharacterSet encoding, BitArray& bits); + void AppendKanjiBytes(const std::wstring& content, BitArray& bits); + void AppendBytes(const std::wstring& content, CodecMode mode, CharacterSet encoding, BitArray& bits); + void TerminateBits(int numDataBytes, BitArray& bits); + void GetNumDataBytesAndNumECBytesForBlockID(int numTotalBytes, int numDataBytes, int numRSBlocks, int blockID, int& numDataBytesInBlock, int& numECBytesInBlock); + void GenerateECBytes(const ByteArray& dataBytes, int numEcBytesInBlock, ByteArray& ecBytes); + BitArray InterleaveWithECBytes(const BitArray& bits, int numTotalBytes, int numDataBytes, int numRSBlocks); + } +} + +using namespace ZXing; +using namespace ZXing::QRCode; +using namespace ZXing::Utility; + +namespace { + std::wstring ShiftJISString(const std::vector& bytes) + { + std::string str; + TextDecoder::Append(str, bytes.data(), bytes.size(), CharacterSet::Shift_JIS); + return FromUtf8(str); + } + + std::string RemoveSpace(std::string s) + { + s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); + return s; + } +} + +TEST(QREncoderTest, GetAlphanumericCode) +{ + // The first ten code points are numbers. + for (int i = 0; i < 10; ++i) { + EXPECT_EQ(i, GetAlphanumericCode('0' + i)); + } + + // The next 26 code points are capital alphabet letters. + for (int i = 10; i < 36; ++i) { + EXPECT_EQ(i, GetAlphanumericCode('A' + i - 10)); + } + + // Others are symbol letters + EXPECT_EQ(36, GetAlphanumericCode(' ')); + EXPECT_EQ(37, GetAlphanumericCode('$')); + EXPECT_EQ(38, GetAlphanumericCode('%')); + EXPECT_EQ(39, GetAlphanumericCode('*')); + EXPECT_EQ(40, GetAlphanumericCode('+')); + EXPECT_EQ(41, GetAlphanumericCode('-')); + EXPECT_EQ(42, GetAlphanumericCode('.')); + EXPECT_EQ(43, GetAlphanumericCode('/')); + EXPECT_EQ(44, GetAlphanumericCode(':')); + + // Should return -1 for other letters; + EXPECT_EQ(-1, GetAlphanumericCode('a')); + EXPECT_EQ(-1, GetAlphanumericCode('#')); + EXPECT_EQ(-1, GetAlphanumericCode('\0')); +} + +TEST(QREncoderTest, ChooseMode) +{ + // Numeric mode. + EXPECT_EQ(CodecMode::NUMERIC, ChooseMode(L"0", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::NUMERIC, ChooseMode(L"0123456789", CharacterSet::Unknown)); + // Alphanumeric mode. + EXPECT_EQ(CodecMode::ALPHANUMERIC, ChooseMode(L"A", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::ALPHANUMERIC, + ChooseMode(L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", CharacterSet::Unknown)); + // 8-bit byte mode. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"a", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"#", CharacterSet::Unknown)); + EXPECT_EQ(CodecMode::BYTE, ChooseMode(L"", CharacterSet::Unknown)); + // Kanji mode. We used to use MODE_KANJI for these, but we stopped + // doing that as we cannot distinguish Shift_JIS from other encodings + // from data bytes alone. See also comments in qrcode_encoder.h. + + // AIUE in Hiragana in Shift_JIS + EXPECT_EQ(CodecMode::BYTE, + ChooseMode(ShiftJISString({0x8, 0xa, 0x8, 0xa, 0x8, 0xa, 0x8, 0xa6}), CharacterSet::Unknown)); + + // Nihon in Kanji in Shift_JIS. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(ShiftJISString({0x9, 0xf, 0x9, 0x7b}), CharacterSet::Unknown)); + + // Sou-Utsu-Byou in Kanji in Shift_JIS. + EXPECT_EQ(CodecMode::BYTE, ChooseMode(ShiftJISString({0xe, 0x4, 0x9, 0x5, 0x9, 0x61}), CharacterSet::Unknown)); +} + +TEST(QREncoderTest, Encode) +{ + auto qrCode = Encode(L"ABCDEF", ErrorCorrectionLevel::High, CharacterSet::Unknown, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::ALPHANUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 4); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X \n" + " X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X \n" + " X X X X X X X X X X X X X \n" + " X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeWithVersion) +{ + auto qrCode = Encode(L"ABCDEF", ErrorCorrectionLevel::High, CharacterSet::Unknown, 7, false, -1); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 7); +} + +TEST(QREncoderTest, EncodeWithVersionTooSmall) +{ + EXPECT_THROW( + Encode(L"THISMESSAGEISTOOLONGFORAQRCODEVERSION3", ErrorCorrectionLevel::High, CharacterSet::Unknown, 3, false, -1) + , std::invalid_argument); +} + +TEST(QREncoderTest, SimpleUTF8ECI) +{ + auto qrCode = Encode(L"hello", ErrorCorrectionLevel::High, CharacterSet::UTF8, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 6); + EXPECT_EQ(ToString(qrCode.matrix), // break the line comment + "X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X \n" + " X X X X X X X \n" + " X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X \n" + " X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, SimpleBINARYECI) +{ + auto qrCode = Encode(L"\u00E9", ErrorCorrectionLevel::High, CharacterSet::BINARY, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 6); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X X \n" + "X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X \n" + " X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + " X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeKanjiMode) +{ + auto qrCode = Encode(L"\u65e5\u672c", ErrorCorrectionLevel::Medium, CharacterSet::Shift_JIS, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::KANJI); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::Medium); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 0); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X \n" + "X X X X X X X X \n" + "X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + " X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeShiftjisNumeric) +{ + auto qrCode = Encode(L"0123", ErrorCorrectionLevel::Medium, CharacterSet::Shift_JIS, 0, false, -1); + EXPECT_EQ(qrCode.mode, CodecMode::NUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::Medium); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 2); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + "X X X X X X X X X \n" + " X X X X X X X X \n" + " X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeGS1) +{ + auto qrCode = Encode(L"100001%11171218", ErrorCorrectionLevel::High, CharacterSet::Unknown, 0, true, -1); + EXPECT_EQ(qrCode.mode, CodecMode::ALPHANUMERIC); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 2); + EXPECT_EQ(qrCode.maskPattern, 4); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X \n" + " X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, EncodeGS1ModeHeaderWithECI) +{ + auto qrCode = Encode(L"hello", ErrorCorrectionLevel::High, CharacterSet::UTF8, 0, true, -1); + EXPECT_EQ(qrCode.mode, CodecMode::BYTE); + EXPECT_EQ(qrCode.ecLevel, ErrorCorrectionLevel::High); + ASSERT_NE(qrCode.version, nullptr); + EXPECT_EQ(qrCode.version->versionNumber(), 1); + EXPECT_EQ(qrCode.maskPattern, 5); + EXPECT_EQ(ToString(qrCode.matrix), + "X X X X X X X X X X X X X X X X X \n" + "X X X X X X \n" + "X X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X X X X X X X X \n" + " X X X X \n" + " X X X X X X X X \n" + " X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X \n" + " X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X \n" + "X X X X X X X X X X \n" + "X X X X X X X X X X X X \n" + "X X X X X X X X X X X \n" + "X X X X X X X X X \n" + "X X X X X X X X X X X X X X \n"); +} + +TEST(QREncoderTest, AppendModeInfo) +{ + BitArray bits; + AppendModeInfo(CodecMode::NUMERIC, bits); + EXPECT_EQ(ToString(bits), "...X"); +} + +TEST(QREncoderTest, AppendLengthInfo) +{ + BitArray bits; + AppendLengthInfo(1, // 1 letter (1/1). + *Version::FromNumber(1), CodecMode::NUMERIC, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ .X")); // 10 bits. + + bits = BitArray(); + AppendLengthInfo(2, // 2 letters (2/1). + *Version::FromNumber(10), CodecMode::ALPHANUMERIC, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ .X.")); // 11 bits. + + bits = BitArray(); + AppendLengthInfo(255, // 255 letter (255/1). + *Version::FromNumber(27), CodecMode::BYTE, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("........ XXXXXXXX")); // 16 bits. + + bits = BitArray(); + AppendLengthInfo(512, // 512 letters (1024/2). + *Version::FromNumber(40), CodecMode::KANJI, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X..... ....")); // 12 bits. +} + +TEST(QREncoderTest, AppendNumericBytes) +{ + // 1 = 01 = 0001 in 4 bits. + BitArray bits; + AppendNumericBytes(L"1", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...X")); + + // 12 = 0xc = 0001100 in 7 bits. + bits = BitArray(); + AppendNumericBytes(L"12", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XX..")); + + // 123 = 0x7b = 0001111011 in 10 bits. + bits = BitArray(); + AppendNumericBytes(L"123", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XXXX. XX")); + + // 1234 = "123" + "4" = 0001111011 + 0100 + bits = BitArray(); + AppendNumericBytes(L"1234", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...XXXX. XX.X..")); + + // Empty. + bits = BitArray(); + AppendNumericBytes(L"", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); +} + +TEST(QREncoderTest, AppendAlphanumericBytes) +{ + // A = 10 = 0xa = 001010 in 6 bits + BitArray bits; + AppendAlphanumericBytes(L"A", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X.X.")); + + // AB = 10 * 45 + 11 = 461 = 0x1cd = 00111001101 in 11 bits + bits = BitArray(); + AppendAlphanumericBytes(L"AB", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..XXX..X X.X")); + + // ABC = "AB" + "C" = 00111001101 + 001100 + bits = BitArray(); + AppendAlphanumericBytes(L"ABC", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..XXX..X X.X..XX. .")); + + // Empty. + bits = BitArray(); + AppendAlphanumericBytes(L"", bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); + + // Invalid data. + EXPECT_THROW(AppendAlphanumericBytes(L"abc", bits), std::invalid_argument); +} + +TEST(QREncoderTest, Append8BitBytes) +{ + // 0x61, 0x62, 0x63 + BitArray bits; + Append8BitBytes(L"abc", CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX....X .XX...X. .XX...XX")); + + // Empty. + bits = BitArray(); + Append8BitBytes(L"", CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("")); +} + +// Numbers are from page 21 of JISX0510:2004 +TEST(QREncoderTest, AppendKanjiBytes) +{ + BitArray bits; + AppendKanjiBytes(ShiftJISString({ 0x93, 0x5f }), bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXX")); + + AppendKanjiBytes(ShiftJISString({ 0xe4, 0xaa }), bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXXXX. X.X.X.X. X.")); +} + +TEST(QREncoderTest, AppendBytes) +{ + // Should use appendNumericBytes. + // 1 = 01 = 0001 in 4 bits. + BitArray bits; + AppendBytes(L"1", CodecMode::NUMERIC, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("...X")); + + // Should use appendAlphanumericBytes. + // A = 10 = 0xa = 001010 in 6 bits + bits = BitArray(); + AppendBytes(L"A", CodecMode::ALPHANUMERIC, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace("..X.X.")); + + // Lower letters such as 'a' cannot be encoded in MODE_ALPHANUMERIC. + bits = BitArray(); + EXPECT_THROW(AppendBytes(L"a", CodecMode::ALPHANUMERIC, CharacterSet::Unknown, bits), std::invalid_argument); + + // Should use append8BitBytes. + // 0x61, 0x62, 0x63 + bits = BitArray(); + AppendBytes(L"abc", CodecMode::BYTE, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX....X .XX...X. .XX...XX")); + + // Anything can be encoded in QRCode.MODE_8BIT_BYTE. + AppendBytes(L"\0", CodecMode::BYTE, CharacterSet::Unknown, bits); + + // Should use appendKanjiBytes. + // 0x93, 0x5f + bits = BitArray(); + AppendBytes(ShiftJISString({0x93, 0x5f}), CodecMode::KANJI, CharacterSet::Unknown, bits); + EXPECT_EQ(ToString(bits), RemoveSpace(".XX.XX.. XXXXX")); +} + +TEST(QREncoderTest, TerminateBits) +{ + BitArray v; + TerminateBits(0, v); + EXPECT_EQ(ToString(v), RemoveSpace("")); + + v = BitArray(); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 3); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 5); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + v.appendBits(0, 8); + TerminateBits(1, v); + EXPECT_EQ(ToString(v), RemoveSpace("........")); + + v = BitArray(); + TerminateBits(2, v); + EXPECT_EQ(ToString(v), RemoveSpace("........ XXX.XX..")); + + v = BitArray(); + v.appendBits(0, 1); + TerminateBits(3, v); + EXPECT_EQ(ToString(v), RemoveSpace("........ XXX.XX.. ...X...X")); +} + +TEST(QREncoderTest, GetNumDataBytesAndNumECBytesForBlockID) +{ + int numDataBytes; + int numEcBytes; + // Version 1-H. + GetNumDataBytesAndNumECBytesForBlockID(26, 9, 1, 0, numDataBytes, numEcBytes); + EXPECT_EQ(9, numDataBytes); + EXPECT_EQ(17, numEcBytes); + + // Version 3-H. 2 blocks. + GetNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 0, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(22, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(70, 26, 2, 1, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(22, numEcBytes); + + // Version 7-H. (4 + 1) blocks. + GetNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 0, numDataBytes, numEcBytes); + EXPECT_EQ(13, numDataBytes); + EXPECT_EQ(26, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(196, 66, 5, 4, numDataBytes, numEcBytes); + EXPECT_EQ(14, numDataBytes); + EXPECT_EQ(26, numEcBytes); + + // Version 40-H. (20 + 61) blocks. + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 0, numDataBytes, numEcBytes); + EXPECT_EQ(15, numDataBytes); + EXPECT_EQ(30, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 20, numDataBytes, numEcBytes); + EXPECT_EQ(16, numDataBytes); + EXPECT_EQ(30, numEcBytes); + GetNumDataBytesAndNumECBytesForBlockID(3706, 1276, 81, 80, numDataBytes, numEcBytes); + EXPECT_EQ(16, numDataBytes); + EXPECT_EQ(30, numEcBytes); +} + +// Numbers are from http://www.swetake.com/qr/qr3.html and +// http://www.swetake.com/qr/qr9.html +TEST(QREncoderTest, GenerateECBytes) +{ + ByteArray ecBytes; + GenerateECBytes({ 32, 65, 205, 69, 41, 220, 46, 128, 236 }, 17, ecBytes); + ByteArray expected = { 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61 }; + EXPECT_EQ(ecBytes, expected); + + GenerateECBytes({ 67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214 }, 18, ecBytes); + expected = { 175, 80, 155, 64, 178, 45, 214, 233, 65, 209, 12, 155, 117, 31, 140, 214, 27, 187 }; + EXPECT_EQ(ecBytes, expected); + + // High-order zero coefficient case. + GenerateECBytes({ 32, 49, 205, 69, 42, 20, 0, 236, 17 }, 17, ecBytes); + expected = { 0, 3, 130, 179, 194, 0, 55, 211, 110, 79, 98, 72, 170, 96, 211, 137, 213 }; + EXPECT_EQ(ecBytes, expected); +} + +TEST(QREncoderTest, InterleaveWithECBytes) +{ + BitArray in; + for (int dataByte : {32, 65, 205, 69, 41, 220, 46, 128, 236}) + in.appendBits(dataByte, 8); + + BitArray out = InterleaveWithECBytes(in, 26, 9, 1); + std::vector expected = { + 32, 65, 205, 69, 41, 220, 46, 128, 236, + + 42, 159, 74, 221, 244, 169, 239, 150, 138, 70, 237, 85, 224, 96, 74, 219, 61, + }; // Error correction bytes in second block + ASSERT_EQ(Size(expected), out.sizeInBytes()); + EXPECT_EQ(expected, out.toBytes()); + + // Numbers are from http://www.swetake.com/qr/qr8.html + in = BitArray(); + for (int dataByte : + {67, 70, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214, 230, 247, 7, 23, 39, 55, + 71, 87, 103, 119, 135, 151, 166, 22, 38, 54, 70, 86, 102, 118, 134, 150, 166, 182, 198, 214, 230, + 247, 7, 23, 39, 55, 71, 87, 103, 119, 135, 151, 160, 236, 17, 236, 17, 236, 17, 236, 17}) + in.appendBits(dataByte, 8); + + out = InterleaveWithECBytes(in, 134, 62, 4); + expected = { + 67, 230, 54, 55, 70, 247, 70, 71, 22, 7, 86, 87, 38, 23, 102, 103, 54, 39, 118, 119, 70, + 55, 134, 135, 86, 71, 150, 151, 102, 87, 166, 160, 118, 103, 182, 236, 134, 119, 198, 17, 150, 135, + 214, 236, 166, 151, 230, 17, 182, 166, 247, 236, 198, 22, 7, 17, 214, 38, 23, 236, 39, 17, + + 175, 155, 245, 236, 80, 146, 56, 74, 155, 165, 133, 142, 64, 183, 132, 13, 178, 54, 132, 108, 45, + 113, 53, 50, 214, 98, 193, 152, 233, 147, 50, 71, 65, 190, 82, 51, 209, 199, 171, 54, 12, 112, + 57, 113, 155, 117, 211, 164, 117, 30, 158, 225, 31, 190, 242, 38, 140, 61, 179, 154, 214, 138, 147, + 87, 27, 96, 77, 47, 187, 49, 156, 214, + }; // Error correction bytes in second block + EXPECT_EQ(Size(expected), out.sizeInBytes()); + EXPECT_EQ(expected, out.toBytes()); +} + +TEST(QREncoderTest, BugInBitVectorNumBytes) +{ + // There was a bug in BitVector.sizeInBytes() that caused it to return a + // smaller-by-one value (ex. 1465 instead of 1466) if the number of bits + // in the vector is not 8-bit aligned. In QRCodeEncoder::InitQRCode(), + // BitVector::sizeInBytes() is used for finding the smallest QR Code + // version that can fit the given data. Hence there were corner cases + // where we chose a wrong QR Code version that cannot fit the given + // data. Note that the issue did not occur with MODE_8BIT_BYTE, as the + // bits in the bit vector are always 8-bit aligned. + // + // Before the bug was fixed, the following test didn't pass, because: + // + // - MODE_NUMERIC is chosen as all bytes in the data are '0' + // - The 3518-byte numeric data needs 1466 bytes + // - 3518 / 3 * 10 + 7 = 11727 bits = 1465.875 bytes + // - 3 numeric bytes are encoded in 10 bits, hence the first + // 3516 bytes are encoded in 3516 / 3 * 10 = 11720 bits. + // - 2 numeric bytes can be encoded in 7 bits, hence the last + // 2 bytes are encoded in 7 bits. + // - The version 27 QR Code with the EC level L has 1468 bytes for data. + // - 1828 - 360 = 1468 + // - In InitQRCode(), 3 bytes are reserved for a header. Hence 1465 bytes + // (1468 -3) are left for data. + // - Because of the bug in BitVector::sizeInBytes(), InitQRCode() determines + // the given data can fit in 1465 bytes, despite it needs 1466 bytes. + // - Hence QRCodeEncoder.encode() failed and returned false. + // - To be precise, it needs 11727 + 4 (getMode info) + 14 (length info) = + // 11745 bits = 1468.125 bytes are needed (i.e. cannot fit in 1468 + // bytes). + Encode(std::wstring(3518, L'0'), ErrorCorrectionLevel::Low, CharacterSet::Unknown, 0, false, -1); +} diff --git a/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.cpp b/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.cpp new file mode 100644 index 0000000..77c1030 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRErrorCorrectionLevelTest.cpp @@ -0,0 +1,45 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "qrcode/QRErrorCorrectionLevel.h" + +#include "gtest/gtest.h" + +using namespace ZXing; +using namespace ZXing::QRCode; + +TEST(QRErrorCorrectionLevelTest, ForBits) +{ + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(0)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(1)); + EXPECT_EQ(ErrorCorrectionLevel::High, ECLevelFromBits(2)); + EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(3)); +} + +TEST(QRErrorCorrectionLevelTest, ForMicroBits) +{ + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(0, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(1, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(2, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(3, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(4, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(5, true)); + EXPECT_EQ(ErrorCorrectionLevel::Medium, ECLevelFromBits(6, true)); + EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(7, true)); + + EXPECT_EQ(ErrorCorrectionLevel::Quality, ECLevelFromBits(-1, true)); + EXPECT_EQ(ErrorCorrectionLevel::Low, ECLevelFromBits(8, true)); +} + +TEST(QRErrorCorrectionLevelTest, ToString) +{ + using namespace std::literals; + + EXPECT_EQ("L"s, ToString(ErrorCorrectionLevel::Low)); + EXPECT_EQ("M"s, ToString(ErrorCorrectionLevel::Medium)); + EXPECT_EQ("Q"s, ToString(ErrorCorrectionLevel::Quality)); + EXPECT_EQ("H"s, ToString(ErrorCorrectionLevel::High)); +} diff --git a/src/qrcode/cpp_port/test/QRFormatInformationTest.rs b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs new file mode 100644 index 0000000..0d2a867 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRFormatInformationTest.rs @@ -0,0 +1,124 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2007 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::qrcode::decoder::{ErrorCorrectionLevel, FormatInformation}; + +const MASKED_TEST_FORMAT_INFO: u32 = 0x2BED; +const MASKED_TEST_FORMAT_INFO2: u32 = + ((0x2BED << 1) & 0b1111111000000000) | 0b100000000 | (0x2BED & 0b11111111); // insert the 'Dark Module' +const UNMASKED_TEST_FORMAT_INFO: u32 = MASKED_TEST_FORMAT_INFO ^ 0x5412; +const MICRO_MASKED_TEST_FORMAT_INFO: u32 = 0x3BBA; +// const MICRO_UNMASKED_TEST_FORMAT_INFO: u32 = MICRO_MASKED_TEST_FORMAT_INFO ^ 0x4445; + +fn DoFormatInformationTest(formatInfo: u32, expectedMask: u8, expectedECL: ErrorCorrectionLevel) { + let parsedFormat = FormatInformation::DecodeMQR(formatInfo); + assert!(parsedFormat.isValid()); + assert_eq!(expectedMask, parsedFormat.data_mask); + assert_eq!(expectedECL, parsedFormat.error_correction_level); +} + +#[test] +fn Decode() { + // Normal case + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + assert!(expected.isValid()); + assert_eq!(0x07, expected.data_mask); + assert_eq!(ErrorCorrectionLevel::Q, expected.error_correction_level); + // where the code forgot the mask! + assert_eq!( + expected, + FormatInformation::DecodeQR(UNMASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2) + ); +} + +#[test] +fn DecodeWithBitDifference() { + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + // 1,2,3,4 bits difference + assert_eq!( + expected, + FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x01, + MASKED_TEST_FORMAT_INFO2 ^ 0x01 + ) + ); + assert_eq!( + expected, + FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x03, + MASKED_TEST_FORMAT_INFO2 ^ 0x03 + ) + ); + assert_eq!( + expected, + FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x07, + MASKED_TEST_FORMAT_INFO2 ^ 0x07 + ) + ); + assert!(!FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x0F, + MASKED_TEST_FORMAT_INFO2 ^ 0x0F + ) + .isValid()); +} + +#[test] +fn DecodeWithMisread() { + let expected = FormatInformation::DecodeQR(MASKED_TEST_FORMAT_INFO, MASKED_TEST_FORMAT_INFO2); + assert_eq!( + expected, + FormatInformation::DecodeQR( + MASKED_TEST_FORMAT_INFO ^ 0x03, + MASKED_TEST_FORMAT_INFO2 ^ 0x0F + ) + ); +} + +#[test] +fn DecodeMicro() { + // Normal cases. + DoFormatInformationTest(0x4445, 0x0, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x4172, 0x1, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x5fc0, 0x2, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x5af7, 0x3, ErrorCorrectionLevel::L); + DoFormatInformationTest(0x6793, 0x0, ErrorCorrectionLevel::M); + DoFormatInformationTest(0x62a4, 0x1, ErrorCorrectionLevel::M); + DoFormatInformationTest(0x3e8d, 0x2, ErrorCorrectionLevel::Q); + DoFormatInformationTest(MICRO_MASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Q); + + // where the code forgot the mask! + // DoFormatInformationTest(MICRO_UNMASKED_TEST_FORMAT_INFO, 0x3, ErrorCorrectionLevel::Quality); +} + +// This doesn't work as expected because the implementation of the decode tries with +// and without the mask (0x4445). This effectively adds a tolerance of 5 bits to the Hamming +// distance calculation. +#[test] +fn DecodeMicroWithBitDifference() { + let expected = FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO); + + // 1,2,3 bits difference + assert_eq!( + expected, + FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x01) + ); + assert_eq!( + expected, + FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x03) + ); + assert_eq!( + expected, + FormatInformation::DecodeMQR(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x07) + ); + + // Bigger bit differences can return valid FormatInformation objects but the data mask and error + // correction levels do not match. + // EXPECT_TRUE(FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).isValid()); + // EXPECT_NE(expected.dataMask(), FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).dataMask()); + // EXPECT_NE(expected.errorCorrectionLevel(), + // FormatInformation::DecodeFormatInformation(MICRO_MASKED_TEST_FORMAT_INFO ^ 0x3f).errorCorrectionLevel()); +} diff --git a/src/qrcode/cpp_port/test/QRModeTest.rs b/src/qrcode/cpp_port/test/QRModeTest.rs new file mode 100644 index 0000000..fa356a0 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRModeTest.rs @@ -0,0 +1,135 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::qrcode::decoder::{Mode, Version}; + +#[test] +fn ForBits() { + assert_eq!( + Mode::TERMINATOR, + Mode::CodecModeForBits(0x00, None).unwrap() + ); + assert_eq!(Mode::NUMERIC, Mode::CodecModeForBits(0x01, None).unwrap()); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x02, None).unwrap() + ); + assert_eq!(Mode::BYTE, Mode::CodecModeForBits(0x04, None).unwrap()); + assert_eq!(Mode::KANJI, Mode::CodecModeForBits(0x08, None).unwrap()); + assert!(Mode::CodecModeForBits(0x10, None).is_err()); +} + +#[test] +fn CharacterCount() { + // Spot check a few values + assert_eq!( + 10, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(5, false).unwrap()) + ); + assert_eq!( + 12, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(26, false).unwrap()) + ); + assert_eq!( + 14, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(40, false).unwrap()) + ); + assert_eq!( + 9, + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(6, false).unwrap()) + ); + assert_eq!( + 8, + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(7, false).unwrap()) + ); + assert_eq!( + 8, + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(8, false).unwrap()) + ); +} + +#[test] +fn MicroForBits() { + // M1 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + // M2 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + // M3 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x02, Some(true)).unwrap() + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x03, Some(true)).unwrap() + ); + // M4 + assert_eq!( + Mode::NUMERIC, + Mode::CodecModeForBits(0x00, Some(true)).unwrap() + ); + assert_eq!( + Mode::ALPHANUMERIC, + Mode::CodecModeForBits(0x01, Some(true)).unwrap() + ); + assert_eq!( + Mode::BYTE, + Mode::CodecModeForBits(0x02, Some(true)).unwrap() + ); + assert_eq!( + Mode::KANJI, + Mode::CodecModeForBits(0x03, Some(true)).unwrap() + ); + + assert!(Mode::CodecModeForBits(0x04, Some(true)).is_err()); +} + +#[test] +fn MicroCharacterCount() { + // Spot check a few values + assert_eq!( + 3, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(1, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(2, true).unwrap()) + ); + assert_eq!( + 6, + Mode::CharacterCountBits(&Mode::NUMERIC, Version::FromNumber(4, true).unwrap()) + ); + assert_eq!( + 3, + Mode::CharacterCountBits(&Mode::ALPHANUMERIC, Version::FromNumber(2, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::BYTE, Version::FromNumber(3, true).unwrap()) + ); + assert_eq!( + 4, + Mode::CharacterCountBits(&Mode::KANJI, Version::FromNumber(4, true).unwrap()) + ); +} diff --git a/src/qrcode/cpp_port/test/QRVersionTest.rs b/src/qrcode/cpp_port/test/QRVersionTest.rs new file mode 100644 index 0000000..0f79ecf --- /dev/null +++ b/src/qrcode/cpp_port/test/QRVersionTest.rs @@ -0,0 +1,122 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + common::BitMatrix, + qrcode::decoder::{Version, VersionRef}, +}; + +fn CheckVersion(version: VersionRef, number: u32, dimension: u32) { + // assert_ne!(version, nullptr); + assert_eq!(number, version.getVersionNumber()); + if (number > 1 && !version.isMicroQRCode()) { + assert!(!version.getAlignmentPatternCenters().is_empty()); + } + assert_eq!(dimension, version.getDimensionForVersion()); +} + +fn DoTestVersion(expectedVersion: u32, mask: i32) { + let version = Version::DecodeVersionInformation(mask, 0).expect("should exist"); + // assert_ne!(version, nullptr); + assert_eq!(expectedVersion, version.getVersionNumber()); +} + +#[test] +fn VersionForNumber() { + let version = Version::FromNumber(0, false); + assert!(version.is_err(), "There is version with number 0"); + + for i in 1..=40 { + // for (int i = 1; i <= 40; i++) { + CheckVersion( + Version::FromNumber(i, false).expect("version number found"), + i, + 4 * i + 17, + ); + } +} + +#[test] +fn GetProvisionalVersionForDimension() { + for i in 1..=40 { + // for (int i = 1; i <= 40; i++) { + let prov = + Version::FromDimension(4 * i + 17).expect(&format!("version should exist for {i}")); + // assert_ne!(prov, nullptr); + assert_eq!(i, prov.getVersionNumber()); + } +} + +#[test] +fn DecodeVersionInformation() { + // Spot check + DoTestVersion(7, 0x07C94); + DoTestVersion(12, 0x0C762); + DoTestVersion(17, 0x1145D); + DoTestVersion(22, 0x168C9); + DoTestVersion(27, 0x1B08E); + DoTestVersion(32, 0x209D5); +} + +#[test] +fn MicroVersionForNumber() { + let version = Version::FromNumber(0, true); + assert!(version.is_err(), "There is version with number 0"); + + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + CheckVersion( + Version::FromNumber(i, true).expect(&format!("version for {i} should exist")), + i, + 2 * i + 9, + ); + } +} + +#[test] +fn GetProvisionalMicroVersionForDimension() { + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + let prov = Version::FromDimension(2 * i + 9) + .expect(&format!("version for micro {i} should exist")); + // assert_ne!(prov, nullptr); + assert_eq!(i, prov.getVersionNumber()); + } +} + +#[test] +fn FunctionPattern() { + let testFinderPatternRegion = |bitMatrix: &BitMatrix| { + for row in 0..9 { + // for (int row = 0; row < 9; row++){ + for col in 0..9 { + // for (int col = 0; col < 9; col++) { + assert!(bitMatrix.get(col, row)); + } + } + }; + for i in 1..=4 { + // for (int i = 1; i <= 4; i++) { + let version = Version::FromNumber(i, true).expect("version must be found"); + let functionPattern = version + .buildFunctionPattern() + .expect("function pattern must be found"); + testFinderPatternRegion(&functionPattern); + + // Check timing pattern areas. + let dimension = version.getDimensionForVersion(); + for row in dimension..functionPattern.height() + // for (int row = dimension; row < functionPattern.height(); row++) + { + assert!(functionPattern.get(0, row)); + } + for col in dimension..functionPattern.width() + // for (int col = dimension; col < functionPattern.width(); col++) + { + assert!(functionPattern.get(col, 0)); + } + } +} diff --git a/src/qrcode/cpp_port/test/QRWriterTest.cpp b/src/qrcode/cpp_port/test/QRWriterTest.cpp new file mode 100644 index 0000000..bb33cf4 --- /dev/null +++ b/src/qrcode/cpp_port/test/QRWriterTest.cpp @@ -0,0 +1,160 @@ +/* +* Copyright 2017 Huy Cuong Nguyen +* Copyright 2008 ZXing authors +*/ +// SPDX-License-Identifier: Apache-2.0 + +#include "BitMatrixIO.h" +#include "qrcode/QRWriter.h" +#include "qrcode/QRErrorCorrectionLevel.h" + +#include "gtest/gtest.h" +#include + +using namespace ZXing; +using namespace ZXing::QRCode; + +namespace { + + void DoTest(const std::wstring& contents, ErrorCorrectionLevel ecLevel, int resolution, const char* expected) { + Writer writer; + writer.setErrorCorrectionLevel(ecLevel); + auto matrix = writer.encode(contents, resolution, resolution); + auto actual = ToString(matrix, 'X', ' ', true); + EXPECT_EQ(matrix.width(), resolution); + EXPECT_EQ(matrix.height(), resolution); + EXPECT_EQ(actual, expected); + } + +} + +TEST(QRWriterTest, OverSize) +{ + // The QR should be multiplied up to fit, with extra padding if necessary + int bigEnough = 256; + Writer writer; + BitMatrix matrix = writer.encode(L"http://www.google.com/", bigEnough, bigEnough); + EXPECT_EQ(matrix.width(), bigEnough); + EXPECT_EQ(matrix.height(), bigEnough); + + // The QR will not fit in this size, so the matrix should come back bigger + int tooSmall = 20; + matrix = writer.encode(L"http://www.google.com/", tooSmall, tooSmall); + EXPECT_GT(matrix.width(), tooSmall); + EXPECT_GT(matrix.height(), tooSmall); + + // We should also be able to handle non-square requests by padding them + int strangeWidth = 500; + int strangeHeight = 100; + matrix = writer.encode(L"http://www.google.com/", strangeWidth, strangeHeight); + EXPECT_EQ(matrix.width(), strangeWidth); + EXPECT_EQ(matrix.height(), strangeHeight); + + int overflowSize = 0x1000000; + EXPECT_THROW(writer.encode(L"http://www.google.com/", overflowSize, overflowSize), std::invalid_argument); +} + +TEST(QRWriterTest, RegressionTest) +{ + DoTest(L"http://www.google.com/", ErrorCorrectionLevel::Medium, 99, + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + " X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ); +} diff --git a/src/qrcode/cpp_port/test/mod.rs b/src/qrcode/cpp_port/test/mod.rs new file mode 100644 index 0000000..4aaae93 --- /dev/null +++ b/src/qrcode/cpp_port/test/mod.rs @@ -0,0 +1,4 @@ +mod QRModeTest; +mod QRVersionTest; + +mod QRFormatInformationTest; diff --git a/src/qrcode/decoder/version.rs b/src/qrcode/decoder/version.rs index 98a1ab2..c4edf30 100755 --- a/src/qrcode/decoder/version.rs +++ b/src/qrcode/decoder/version.rs @@ -108,7 +108,8 @@ impl Version { } pub fn getDimensionForVersion(&self) -> u32 { - 17 + 4 * self.versionNumber + Self::DimensionOfVersion(self.versionNumber, self.is_micro) + // 17 + 4 * self.versionNumber } pub fn getECBlocksForLevel(&self, ecLevel: ErrorCorrectionLevel) -> &ECBlocks { @@ -171,33 +172,42 @@ impl Version { // 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 - let max = self.alignmentPatternCenters.len(); - for x in 0..max { - let i = self.alignmentPatternCenters[x] - 2; - for y in 0..max { - if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) { - bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?; + if !self.is_micro { + // 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 + let max = self.alignmentPatternCenters.len(); + for x in 0..max { + let i = self.alignmentPatternCenters[x] - 2; + for y in 0..max { + if (x != 0 || (y != 0 && y != max - 1)) && (x != max - 1 || y != 0) { + bitMatrix.setRegion(self.alignmentPatternCenters[y] - 2, i, 5, 5)?; + } + // else no o alignment patterns near the three finder patterns } - // else no o alignment patterns near the three finder patterns } - } - // Vertical timing pattern - bitMatrix.setRegion(6, 9, 1, dimension - 17)?; - // Horizontal timing pattern - bitMatrix.setRegion(9, 6, dimension - 17, 1)?; + // Vertical timing pattern + bitMatrix.setRegion(6, 9, 1, dimension - 17)?; + // Horizontal timing pattern + bitMatrix.setRegion(9, 6, dimension - 17, 1)?; - if self.versionNumber > 6 { - // Version info, top right - bitMatrix.setRegion(dimension - 11, 0, 3, 6)?; - // Version info, bottom left - bitMatrix.setRegion(0, dimension - 11, 6, 3)?; + if self.versionNumber > 6 { + // Version info, top right + bitMatrix.setRegion(dimension - 11, 0, 3, 6)?; + // Version info, bottom left + bitMatrix.setRegion(0, dimension - 11, 6, 3)?; + } + } else { + // Vertical timing pattern + bitMatrix.setRegion(9, 0, dimension - 9, 1)?; + + // Horizontal timing pattern + bitMatrix.setRegion(0, 9, 1, dimension - 9)?; } Ok(bitMatrix)