1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
/*
* Copyright 2017 Huy Cuong Nguyen
* Copyright 2013 ZXing authors
*/
// SPDX-License-Identifier: Apache-2.0
#include "aztec/AZHighLevelEncoder.h"
#include "BitArray.h"
#include "BitArrayUtility.h"
#include "DecoderResult.h"
#include "StructuredAppend.h"
#include "gtest/gtest.h"
#include <algorithm>
namespace ZXing::Aztec {
DecoderResult Decode(const BitArray& bits);
}
using namespace ZXing;
namespace {
std::string StripSpaces(std::string str) {
#ifdef __cpp_lib_erase_if
std::erase_if(str, isspace);
#else
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
#endif
return str;
}
void TestHighLevelEncodeString(const std::string& s, const std::string& expectedBits) {
BitArray bits = Aztec::HighLevelEncoder::Encode(s);
EXPECT_EQ(Utility::ToString(bits), StripSpaces(expectedBits)) << "highLevelEncode() failed for input string: " + s;
EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().bytes);
}
void TestHighLevelEncodeString(const std::string& s, int expectedReceivedBits) {
BitArray bits = Aztec::HighLevelEncoder::Encode(s);
int receivedBitCount = Size(Utility::ToString(bits));
EXPECT_EQ(receivedBitCount, expectedReceivedBits) << "highLevelEncode() failed for input string: " + s;
EXPECT_EQ(ByteArray(s), Aztec::Decode(bits).content().bytes);
}
}
TEST(AZHighLevelEncoderTest, HighLevelEncode)
{
TestHighLevelEncodeString("A. b.",
// 'A' P/S '. ' L/L b D/L '.'
"...X. ..... ...XX XXX.. ...XX XXXX. XX.X");
TestHighLevelEncodeString("Lorem ipsum.",
// 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.'
".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X");
TestHighLevelEncodeString("Lo. Test 123.",
// 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.'
".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X");
TestHighLevelEncodeString("Lo...x",
// 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x'
".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X");
TestHighLevelEncodeString(". x://abc/.",
//P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.'
"..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X");
// Uses Binary/Shift rather than Lower/Shift to save two bits.
TestHighLevelEncodeString("ABCdEFG",
//'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G'
"...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X...");
TestHighLevelEncodeString(
// Found on an airline boarding pass. Several stretches of Binary shift are
// necessary to keep the bitcount so low.
"09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi"
"Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=",
823);
}
TEST(AZHighLevelEncoderTest, HighLevelEncodeBinary)
{
// binary short form single byte
TestHighLevelEncodeString(std::string("N\0N", 3),
// 'N' B/S =1 '\0' N
".XXXX XXXXX ....X ........ .XXXX"); // Encode "N" in UPPER
TestHighLevelEncodeString(std::string("N\0n", 3),
// 'N' B/S =2 '\0' 'n'
".XXXX XXXXX ...X. ........ .XX.XXX."); // Encode "n" in BINARY
// binary short form consecutive bytes
TestHighLevelEncodeString(std::string("N\0\x80 A", 5),
// 'N' B/S =2 '\0' \u0080 ' ' 'A'
".XXXX XXXXX ...X. ........ X....... ....X ...X.");
// binary skipping over single character
TestHighLevelEncodeString(std::string("\0a\xff\x80 A", 6),
// B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A'
"XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X.");
// getting into binary mode from digit mode
TestHighLevelEncodeString(std::string("1234\0", 5),
//D/L '1' '2' '3' '4' U/L B/S =1 \0
"XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........"
);
// Create a string in which every character requires binary
std::string sb;
sb.reserve(3000);
for (int i = 0; i <= 3000; i++) {
sb.push_back((char)(128 + (i % 30)));
}
// Test the output generated by Binary/Switch, particularly near the
// places where the encoding changes: 31, 62, and 2047+31=2078
for (int i : { 1, 2, 3, 10, 29, 30, 31, 32, 33, 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) {
// This is the expected length of a binary string of length "i"
int expectedLength = (8 * i) +
((i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31);
// Verify that we are correct about the length.
TestHighLevelEncodeString(sb.substr(0, i), expectedLength);
if (i != 1 && i != 32 && i != 2079) {
// The addition of an 'a' at the beginning or end gets merged into the binary code
// in those cases where adding another binary character only adds 8 or 9 bits to the result.
// So we exclude the border cases i=1,32,2079
// A lower case letter at the beginning will be merged into binary mode
TestHighLevelEncodeString('a' + sb.substr(0, i - 1), expectedLength);
// A lower case letter at the end will also be merged into binary mode
TestHighLevelEncodeString(sb.substr(0, i - 1) + 'a', expectedLength);
}
// A lower case letter at both ends will enough to latch us into LOWER.
TestHighLevelEncodeString('a' + sb.substr(0, i) + 'b', expectedLength + 15);
}
sb.clear();
for (int i = 0; i < 32; i++) {
sb.push_back('\xA7'); // ยง forces binary encoding
}
sb[1] = 'A';
// expect B/S(1) A B/S(30)
TestHighLevelEncodeString(sb, 5 + 20 + 31 * 8);
sb.clear();
for (int i = 0; i < 31; i++) {
sb.push_back('\xA7');
}
sb[1] = 'A';
// expect B/S(31)
TestHighLevelEncodeString(sb, 10 + 31 * 8);
sb.clear();
for (int i = 0; i < 34; i++) {
sb.push_back('\xA7');
}
sb[1] = 'A';
// expect B/S(31) B/S(3)
TestHighLevelEncodeString(sb, 20 + 34 * 8);
sb.clear();
for (int i = 0; i < 64; i++) {
sb.push_back('\xA7');
}
sb[30] = 'A';
// expect B/S(64)
TestHighLevelEncodeString(sb, 21 + 64 * 8);
}
TEST(AZHighLevelEncoderTest, HighLevelEncodePairs)
{
// Typical usage
TestHighLevelEncodeString("ABC. DEF\r\n",
// A B C P/S .<sp> D E F P/S \r\n
"...X. ...XX ..X.. ..... ...XX ..X.X ..XX. ..XXX ..... ...X.");
// We should latch to PUNCT mode, rather than shift. Also check all pairs
TestHighLevelEncodeString("A. : , \r\n",
// 'A' M/L P/L ". " ": " ", " "\r\n"
"...X. XXX.X XXXX. ...XX ..X.X ..X.. ...X.");
// Latch to DIGIT rather than shift to PUNCT
TestHighLevelEncodeString("A. 1234",
// 'A' D/L '.' ' ' '1' '2' '3' '4'
"...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X."
);
// Don't bother leaving Binary Shift.
TestHighLevelEncodeString("A\200. \200",
// 'A' B/S =2 \200 "." " " \200
"...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X.......");
}
|