1 /* 2 * Copyright 2013 ZXing authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.zxing.aztec.encoder; 18 19 import com.google.zxing.BarcodeFormat; 20 import com.google.zxing.EncodeHintType; 21 import com.google.zxing.FormatException; 22 import com.google.zxing.ResultPoint; 23 import com.google.zxing.aztec.AztecDetectorResult; 24 import com.google.zxing.aztec.AztecWriter; 25 import com.google.zxing.aztec.decoder.Decoder; 26 import com.google.zxing.common.BitArray; 27 import com.google.zxing.common.BitMatrix; 28 import com.google.zxing.common.DecoderResult; 29 import org.junit.Assert; 30 import org.junit.Test; 31 32 import java.nio.charset.Charset; 33 import java.nio.charset.StandardCharsets; 34 import java.util.EnumMap; 35 import java.util.Map; 36 import java.util.Random; 37 import java.util.regex.Pattern; 38 39 /** 40 * Aztec 2D generator unit tests. 41 * 42 * @author Rustam Abdullaev 43 * @author Frank Yellin 44 */ 45 public final class EncoderTest extends Assert { 46 47 private static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1; 48 private static final Charset UTF_8 = StandardCharsets.UTF_8; 49 private static final Charset SHIFT_JIS = Charset.forName("Shift_JIS"); 50 private static final Charset ISO_8859_15 = Charset.forName("ISO-8859-15"); 51 private static final Charset WINDOWS_1252 = Charset.forName("Windows-1252"); 52 53 private static final Pattern DOTX = Pattern.compile("[^.X]"); 54 private static final Pattern SPACES = Pattern.compile("\\s+"); 55 private static final ResultPoint[] NO_POINTS = new ResultPoint[0]; 56 57 // real life tests 58 59 @Test testEncode1()60 public void testEncode1() { 61 testEncode("This is an example Aztec symbol for Wikipedia.", true, 3, 62 "X X X X X X X X \n" + 63 "X X X X X X X X X X \n" + 64 "X X X X X X X X X X X \n" + 65 "X X X X X X X X X X X \n" + 66 " X X X X X X X X X X X \n" + 67 " X X X X X X X X X X X X X \n" + 68 " X X X X X X X X X X X X \n" + 69 "X X X X X X X X X X X X X X X X \n" + 70 "X X X X X X X X X X X \n" + 71 "X X X X X X X X X X X X X X X X \n" + 72 "X X X X X X X X X X \n" + 73 "X X X X X X X X X X \n" + 74 " X X X X X X X X X X \n" + 75 " X X X X X X X X X X X X X X X X X X \n" + 76 " X X X X X X X X X X X X \n" + 77 " X X X X X X X X X X X X X X X X \n" + 78 " X X X X X X X X X X X \n" + 79 " X X X X X X X X \n" + 80 " X X X X X X X X X X X X X X X X \n" + 81 " X X X X X X X X X X X X \n" + 82 " X X X \n" + 83 " X X X X X X X X X X \n" + 84 " X X X X X X X X X X \n"); 85 } 86 87 @Test testEncode2()88 public void testEncode2() { 89 testEncode("Aztec Code is a public domain 2D matrix barcode symbology" + 90 " of nominally square symbols built on a square grid with a " + 91 "distinctive square bullseye pattern at their center.", false, 6, 92 " X X X X X X X X X X X X X X X \n" + 93 " X X X X X X X X X X X X X X X \n" + 94 " X X X X X X X X X X X X X X X X X X X \n" + 95 "X X X X X X X X X X X X X X \n" + 96 "X X X X X X X X X X X X X X X X X X X X X \n" + 97 " X X X X X X X X X X X X X X X X \n" + 98 "X X X X X X X X X X X X X X X X X X X X \n" + 99 " X X X X X X X X X X X X X X X X X X X X X X \n" + 100 "X X X X 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" + 101 " X X X X X X X X X X X X X X X X X X X X \n" + 102 " X X X X X X X X X X X X X X X X X X X X \n" + 103 " X X X X X X X X X X X X X X X X X X X X X X X \n" + 104 "X X X X X X X X X X X X X X X X X X X X X \n" + 105 " X X X X X X X X X X X X X X X \n" + 106 " X X X 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" + 107 " X X X X X X X X X X X \n" + 108 " X 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" + 109 " X X X X X X X X X X X X X X X \n" + 110 "X X 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" + 111 "X X X X X X X X X X X X X X X X X X X X \n" + 112 "X X X X X X X X X X X X X X X X X X X X X \n" + 113 " X X X X X X X X X X X X \n" + 114 " X X X X X X X X X X X X X X X X X X X X X X X \n" + 115 "X X X X X X X X X X X X X X X X X \n" + 116 " X X X 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" + 117 " X X X X X X X X X X X X X X X X \n" + 118 " X X X 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" + 119 " X X X X X X X X X X X X X X X X X \n" + 120 "X X X X X X X X X X X X X X X X X \n" + 121 "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" + 122 " X X X X X X X X X X X X X X X X X X X X \n" + 123 "X X X X X X X X X X X X X X X \n" + 124 " X 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" + 125 " X X X X X X X X X X X X X X X X X \n" + 126 "X X X X X X X X X X X X X X X X X X \n" + 127 "X X X X X X X X X X X X X X X X X X X X X X X \n" + 128 "X X X X X X X X X X X X X X X X X X X X X \n" + 129 "X X X X X X X X X X X X X X X X \n" + 130 "X X X X X X X X X X X X X X X X X X X X X \n" + 131 " X X X X X X X X X X X X X X X X \n" + 132 "X X X X X X X X X X X X X \n"); 133 } 134 135 @Test testAztecWriter()136 public void testAztecWriter() throws Exception { 137 testWriter("Espa\u00F1ol", null, 25, true, 1); // Without ECI (implicit ISO-8859-1) 138 testWriter("Espa\u00F1ol", ISO_8859_1, 25, true, 1); // Explicit ISO-8859-1 139 testWriter("\u20AC 1 sample data.", WINDOWS_1252, 25, true, 2); // ISO-8859-1 can't encode Euro; Windows-1252 can 140 testWriter("\u20AC 1 sample data.", ISO_8859_15, 25, true, 2); 141 testWriter("\u20AC 1 sample data.", UTF_8, 25, true, 2); 142 testWriter("\u20AC 1 sample data.", UTF_8, 100, true, 3); 143 testWriter("\u20AC 1 sample data.", UTF_8, 300, true, 4); 144 testWriter("\u20AC 1 sample data.", UTF_8, 500, false, 5); 145 testWriter("The capital of Japan is named \u6771\u4EAC.", SHIFT_JIS, 25, true, 3); 146 // Test AztecWriter defaults 147 String data = "In ut magna vel mauris malesuada"; 148 AztecWriter writer = new AztecWriter(); 149 BitMatrix matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0); 150 AztecCode aztec = Encoder.encode(data, 151 Encoder.DEFAULT_EC_PERCENT, Encoder.DEFAULT_AZTEC_LAYERS); 152 BitMatrix expectedMatrix = aztec.getMatrix(); 153 assertEquals(matrix, expectedMatrix); 154 } 155 156 // synthetic tests (encode-decode round-trip) 157 158 @Test testEncodeDecode1()159 public void testEncodeDecode1() throws Exception { 160 testEncodeDecode("Abc123!", true, 1); 161 } 162 163 @Test testEncodeDecode2()164 public void testEncodeDecode2() throws Exception { 165 testEncodeDecode("Lorem ipsum. http://test/", true, 2); 166 } 167 168 @Test testEncodeDecode3()169 public void testEncodeDecode3() throws Exception { 170 testEncodeDecode("AAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAANAAAAN", true, 3); 171 } 172 173 @Test testEncodeDecode4()174 public void testEncodeDecode4() throws Exception { 175 testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384", true, 4); 176 } 177 178 @Test testEncodeDecode5()179 public void testEncodeDecode5() throws Exception { 180 testEncodeDecode("http://test/~!@#*^%&)__ ;:'\"[]{}\\|-+-=`1029384756<>/?abc" 181 + "Four score and seven our forefathers brought forth", false, 5); 182 } 183 184 @Test testEncodeDecode10()185 public void testEncodeDecode10() throws Exception { 186 testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + 187 " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + 188 " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + 189 " auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" + 190 " ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" + 191 " elementum sapien dolor et diam.", false, 10); 192 } 193 194 @Test testEncodeDecode23()195 public void testEncodeDecode23() throws Exception { 196 testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + 197 " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + 198 " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + 199 " auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" + 200 " ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" + 201 " elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." + 202 " Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" + 203 " justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu" + 204 " tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus" + 205 " quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec" + 206 " laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," + 207 " justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" + 208 " lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" + 209 " nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" + 210 " eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" + 211 " fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" + 212 " diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla" + 213 " ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" + 214 " sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." + 215 " Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" + 216 " felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" + 217 " erat pulvinar nisi, id elementum sapien dolor et diam.", false, 23); 218 } 219 220 @Test testEncodeDecode31()221 public void testEncodeDecode31() throws Exception { 222 testEncodeDecode("In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus quis diam" + 223 " cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec laoreet rutrum" + 224 " est, nec convallis mauris condimentum sit amet. Phasellus gravida, justo et congue" + 225 " auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec lorem. Nulla" + 226 " ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar nisi, id" + 227 " elementum sapien dolor et diam. Donec ac nunc sodales elit placerat eleifend." + 228 " Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra fringilla, risus" + 229 " justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo diam, lobortis eu" + 230 " tristique ac, p.In ut magna vel mauris malesuada dictum. Nulla ullamcorper metus" + 231 " quis diam cursus facilisis. Sed mollis quam id justo rutrum sagittis. Donec" + 232 " laoreet rutrum est, nec convallis mauris condimentum sit amet. Phasellus gravida," + 233 " justo et congue auctor, nisi ipsum viverra erat, eget hendrerit felis turpis nec" + 234 " lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo erat pulvinar" + 235 " nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit placerat" + 236 " eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at pharetra" + 237 " fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est. Ut justo" + 238 " diam, lobortis eu tristique ac, p. In ut magna vel mauris malesuada dictum. Nulla" + 239 " ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" + 240 " sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." + 241 " Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget hendrerit" + 242 " felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet laoreet, justo" + 243 " erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac nunc sodales elit" + 244 " placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula, massa at" + 245 " pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus sed est." + 246 " Ut justo diam, lobortis eu tristique ac, p.In ut magna vel mauris malesuada" + 247 " dictum. Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id" + 248 " justo rutrum sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum" + 249 " sit amet. Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat," + 250 " eget hendrerit felis turpis nec lorem. Nulla ultrices, elit pellentesque aliquet" + 251 " laoreet, justo erat pulvinar nisi, id elementum sapien dolor et diam. Donec ac" + 252 " nunc sodales elit placerat eleifend. Sed ornare luctus ornare. Vestibulum vehicula," + 253 " massa at pharetra fringilla, risus justo faucibus erat, nec porttitor nibh tellus" + 254 " sed est. Ut justo diam, lobortis eu tris. In ut magna vel mauris malesuada dictum." + 255 " Nulla ullamcorper metus quis diam cursus facilisis. Sed mollis quam id justo rutrum" + 256 " sagittis. Donec laoreet rutrum est, nec convallis mauris condimentum sit amet." + 257 " Phasellus gravida, justo et congue auctor, nisi ipsum viverra erat, eget" + 258 " hendrerit felis turpis nec lorem.", false, 31); 259 } 260 261 @Test testGenerateModeMessage()262 public void testGenerateModeMessage() { 263 testModeMessage(true, 2, 29, ".X .XXX.. ...X XX.. ..X .XX. .XX.X"); 264 testModeMessage(true, 4, 64, "XX XXXXXX .X.. ...X ..XX .X.. XX.."); 265 testModeMessage(false, 21, 660, "X.X.. .X.X..X..XX .XXX ..X.. .XXX. .X... ..XXX"); 266 testModeMessage(false, 32, 4096, "XXXXX XXXXXXXXXXX X.X. ..... XXX.X ..X.. X.XXX"); 267 } 268 269 @Test testStuffBits()270 public void testStuffBits() { 271 testStuffBits(5, ".X.X. X.X.X .X.X.", 272 ".X.X. X.X.X .X.X."); 273 testStuffBits(5, ".X.X. ..... .X.X", 274 ".X.X. ....X ..X.X"); 275 testStuffBits(3, "XX. ... ... ..X XXX .X. ..", 276 "XX. ..X ..X ..X ..X .XX XX. .X. ..X"); 277 testStuffBits(6, ".X.X.. ...... ..X.XX", 278 ".X.X.. .....X. ..X.XX XXXX."); 279 testStuffBits(6, ".X.X.. ...... ...... ..X.X.", 280 ".X.X.. .....X .....X ....X. X.XXXX"); 281 testStuffBits(6, ".X.X.. XXXXXX ...... ..X.XX", 282 ".X.X.. XXXXX. X..... ...X.X XXXXX."); 283 testStuffBits(6, 284 "...... ..XXXX X..XX. .X.... .X.X.X .....X .X.... ...X.X .....X ....XX ..X... ....X. X..XXX X.XX.X", 285 ".....X ...XXX XX..XX ..X... ..X.X. X..... X.X... ....X. X..... X....X X..X.. .....X X.X..X XXX.XX .XXXXX"); 286 } 287 288 @Test testHighLevelEncode()289 public void testHighLevelEncode() throws FormatException { 290 testHighLevelEncodeString("A. b.", 291 // 'A' P/S '. ' L/L b D/L '.' 292 "...X. ..... ...XX XXX.. ...XX XXXX. XX.X"); 293 testHighLevelEncodeString("Lorem ipsum.", 294 // 'L' L/L 'o' 'r' 'e' 'm' ' ' 'i' 'p' 's' 'u' 'm' D/L '.' 295 ".XX.X XXX.. X.... X..XX ..XX. .XXX. ....X .X.X. X...X X.X.. X.XX. .XXX. XXXX. XX.X"); 296 testHighLevelEncodeString("Lo. Test 123.", 297 // 'L' L/L 'o' P/S '. ' U/S 'T' 'e' 's' 't' D/L ' ' '1' '2' '3' '.' 298 ".XX.X XXX.. X.... ..... ...XX XXX.. X.X.X ..XX. X.X.. X.X.X XXXX. ...X ..XX .X.. .X.X XX.X"); 299 testHighLevelEncodeString("Lo...x", 300 // 'L' L/L 'o' D/L '.' '.' '.' U/L L/L 'x' 301 ".XX.X XXX.. X.... XXXX. XX.X XX.X XX.X XXX. XXX.. XX..X"); 302 testHighLevelEncodeString(". x://abc/.", 303 //P/S '. ' L/L 'x' P/S ':' P/S '/' P/S '/' 'a' 'b' 'c' P/S '/' D/L '.' 304 "..... ...XX XXX.. XX..X ..... X.X.X ..... X.X.. ..... X.X.. ...X. ...XX ..X.. ..... X.X.. XXXX. XX.X"); 305 // Uses Binary/Shift rather than Lower/Shift to save two bits. 306 testHighLevelEncodeString("ABCdEFG", 307 //'A' 'B' 'C' B/S =1 'd' 'E' 'F' 'G' 308 "...X. ...XX ..X.. XXXXX ....X .XX..X.. ..XX. ..XXX .X..."); 309 310 testHighLevelEncodeString( 311 // Found on an airline boarding pass. Several stretches of Binary shift are 312 // necessary to keep the bitcount so low. 313 "09 UAG ^160MEUCIQC0sYS/HpKxnBELR1uB85R20OoqqwFGa0q2uEi" 314 + "Ygh6utAIgLl1aBVM4EOTQtMQQYH9M2Z3Dp4qnA/fwWuQ+M8L3V8U=", 315 823); 316 } 317 318 @Test testHighLevelEncodeBinary()319 public void testHighLevelEncodeBinary() throws FormatException { 320 // binary short form single byte 321 testHighLevelEncodeString("N\0N", 322 // 'N' B/S =1 '\0' N 323 ".XXXX XXXXX ....X ........ .XXXX"); // Encode "N" in UPPER 324 325 testHighLevelEncodeString("N\0n", 326 // 'N' B/S =2 '\0' 'n' 327 ".XXXX XXXXX ...X. ........ .XX.XXX."); // Encode "n" in BINARY 328 329 // binary short form consecutive bytes 330 testHighLevelEncodeString("N\0\u0080 A", 331 // 'N' B/S =2 '\0' \u0080 ' ' 'A' 332 ".XXXX XXXXX ...X. ........ X....... ....X ...X."); 333 334 // binary skipping over single character 335 testHighLevelEncodeString("\0a\u00FF\u0080 A", 336 // B/S =4 '\0' 'a' '\3ff' '\200' ' ' 'A' 337 "XXXXX ..X.. ........ .XX....X XXXXXXXX X....... ....X ...X."); 338 339 // getting into binary mode from digit mode 340 testHighLevelEncodeString("1234\0", 341 //D/L '1' '2' '3' '4' U/L B/S =1 \0 342 "XXXX. ..XX .X.. .X.X .XX. XXX. XXXXX ....X ........" 343 ); 344 345 // Create a string in which every character requires binary 346 StringBuilder sb = new StringBuilder(); 347 for (int i = 0; i <= 3000; i++) { 348 sb.append((char) (128 + (i % 30))); 349 } 350 // Test the output generated by Binary/Switch, particularly near the 351 // places where the encoding changes: 31, 62, and 2047+31=2078 352 for (int i : new int[] { 1, 2, 3, 10, 29, 30, 31, 32, 33, 353 60, 61, 62, 63, 64, 2076, 2077, 2078, 2079, 2080, 2100 }) { 354 // This is the expected length of a binary string of length "i" 355 int expectedLength = (8 * i) + 356 ((i <= 31) ? 10 : (i <= 62) ? 20 : (i <= 2078) ? 21 : 31); 357 // Verify that we are correct about the length. 358 testHighLevelEncodeString(sb.substring(0, i), expectedLength); 359 if (i != 1 && i != 32 && i != 2079) { 360 // The addition of an 'a' at the beginning or end gets merged into the binary code 361 // in those cases where adding another binary character only adds 8 or 9 bits to the result. 362 // So we exclude the border cases i=1,32,2079 363 // A lower case letter at the beginning will be merged into binary mode 364 testHighLevelEncodeString('a' + sb.substring(0, i - 1), expectedLength); 365 // A lower case letter at the end will also be merged into binary mode 366 testHighLevelEncodeString(sb.substring(0, i - 1) + 'a', expectedLength); 367 } 368 // A lower case letter at both ends will enough to latch us into LOWER. 369 testHighLevelEncodeString('a' + sb.substring(0, i) + 'b', expectedLength + 15); 370 } 371 372 sb = new StringBuilder(); 373 for (int i = 0; i < 32; i++) { 374 sb.append('§'); // § forces binary encoding 375 } 376 sb.setCharAt(1, 'A'); 377 // expect B/S(1) A B/S(30) 378 testHighLevelEncodeString(sb.toString(), 5 + 20 + 31 * 8); 379 380 sb = new StringBuilder(); 381 for (int i = 0; i < 31; i++) { 382 sb.append('§'); 383 } 384 sb.setCharAt(1, 'A'); 385 // expect B/S(31) 386 testHighLevelEncodeString(sb.toString(), 10 + 31 * 8); 387 388 sb = new StringBuilder(); 389 for (int i = 0; i < 34; i++) { 390 sb.append('§'); 391 } 392 sb.setCharAt(1, 'A'); 393 // expect B/S(31) B/S(3) 394 testHighLevelEncodeString(sb.toString(), 20 + 34 * 8); 395 396 sb = new StringBuilder(); 397 for (int i = 0; i < 64; i++) { 398 sb.append('§'); 399 } 400 sb.setCharAt(30, 'A'); 401 // expect B/S(64) 402 testHighLevelEncodeString(sb.toString(), 21 + 64 * 8); 403 } 404 405 @Test testHighLevelEncodePairs()406 public void testHighLevelEncodePairs() throws FormatException { 407 // Typical usage 408 testHighLevelEncodeString("ABC. DEF\r\n", 409 // A B C P/S .<sp> D E F P/S \r\n 410 "...X. ...XX ..X.. ..... ...XX ..X.X ..XX. ..XXX ..... ...X."); 411 412 // We should latch to PUNCT mode, rather than shift. Also check all pairs 413 testHighLevelEncodeString("A. : , \r\n", 414 // 'A' M/L P/L ". " ": " ", " "\r\n" 415 "...X. XXX.X XXXX. ...XX ..X.X ..X.. ...X."); 416 417 // Latch to DIGIT rather than shift to PUNCT 418 testHighLevelEncodeString("A. 1234", 419 // 'A' D/L '.' ' ' '1' '2' '3' '4' 420 "...X. XXXX. XX.X ...X ..XX .X.. .X.X .X X."); 421 // Don't bother leaving Binary Shift. 422 testHighLevelEncodeString("A\200. \200", 423 // 'A' B/S =2 \200 "." " " \200 424 "...X. XXXXX ..X.. X....... ..X.XXX. ..X..... X......."); 425 } 426 427 @Test(expected = IllegalArgumentException.class) testUserSpecifiedLayers()428 public void testUserSpecifiedLayers() { 429 doTestUserSpecifiedLayers(33); 430 } 431 432 @Test(expected = IllegalArgumentException.class) testUserSpecifiedLayers2()433 public void testUserSpecifiedLayers2() { 434 doTestUserSpecifiedLayers(-1); 435 } 436 doTestUserSpecifiedLayers(int userSpecifiedLayers)437 private void doTestUserSpecifiedLayers(int userSpecifiedLayers) { 438 String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 439 AztecCode aztec = Encoder.encode(alphabet, 25, -2); 440 assertEquals(2, aztec.getLayers()); 441 assertTrue(aztec.isCompact()); 442 443 aztec = Encoder.encode(alphabet, 25, 32); 444 assertEquals(32, aztec.getLayers()); 445 assertFalse(aztec.isCompact()); 446 447 Encoder.encode(alphabet, 25, userSpecifiedLayers); 448 } 449 450 @Test(expected = IllegalArgumentException.class) testBorderCompact4CaseFailed()451 public void testBorderCompact4CaseFailed() { 452 // Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must 453 // be error correction 454 String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 455 // encodes as 26 * 5 * 4 = 520 bits of data 456 String alphabet4 = alphabet + alphabet + alphabet + alphabet; 457 Encoder.encode(alphabet4, 0, -4); 458 } 459 460 @Test testBorderCompact4Case()461 public void testBorderCompact4Case() { 462 // Compact(4) con hold 608 bits of information, but at most 504 can be data. Rest must 463 // be error correction 464 String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 465 // encodes as 26 * 5 * 4 = 520 bits of data 466 String alphabet4 = alphabet + alphabet + alphabet + alphabet; 467 468 // If we just try to encode it normally, it will go to a non-compact 4 layer 469 AztecCode aztecCode = Encoder.encode(alphabet4, 0, Encoder.DEFAULT_AZTEC_LAYERS); 470 assertFalse(aztecCode.isCompact()); 471 assertEquals(4, aztecCode.getLayers()); 472 473 // But shortening the string to 100 bytes (500 bits of data), compact works fine, even if we 474 // include more error checking. 475 aztecCode = Encoder.encode(alphabet4.substring(0, 100), 10, Encoder.DEFAULT_AZTEC_LAYERS); 476 assertTrue(aztecCode.isCompact()); 477 assertEquals(4, aztecCode.getLayers()); 478 } 479 480 // Helper routines 481 testEncode(String data, boolean compact, int layers, String expected)482 private static void testEncode(String data, boolean compact, int layers, String expected) { 483 AztecCode aztec = Encoder.encode(data, 33, Encoder.DEFAULT_AZTEC_LAYERS); 484 assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact()); 485 assertEquals("Unexpected nr. of layers", layers, aztec.getLayers()); 486 BitMatrix matrix = aztec.getMatrix(); 487 assertEquals("encode() failed", expected, matrix.toString()); 488 } 489 testEncodeDecode(String data, boolean compact, int layers)490 private static void testEncodeDecode(String data, boolean compact, int layers) throws Exception { 491 AztecCode aztec = Encoder.encode(data, 25, Encoder.DEFAULT_AZTEC_LAYERS); 492 assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact()); 493 assertEquals("Unexpected nr. of layers", layers, aztec.getLayers()); 494 BitMatrix matrix = aztec.getMatrix(); 495 AztecDetectorResult r = 496 new AztecDetectorResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers(), 0); 497 DecoderResult res = new Decoder().decode(r); 498 assertEquals(data, res.getText()); 499 // Check error correction by introducing a few minor errors 500 Random random = getPseudoRandom(); 501 matrix.flip(random.nextInt(matrix.getWidth()), random.nextInt(2)); 502 matrix.flip(random.nextInt(matrix.getWidth()), matrix.getHeight() - 2 + random.nextInt(2)); 503 matrix.flip(random.nextInt(2), random.nextInt(matrix.getHeight())); 504 matrix.flip(matrix.getWidth() - 2 + random.nextInt(2), random.nextInt(matrix.getHeight())); 505 r = new AztecDetectorResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers(), 0); 506 res = new Decoder().decode(r); 507 assertEquals(data, res.getText()); 508 } 509 testWriter(String data, Charset charset, int eccPercent, boolean compact, int layers)510 private static void testWriter(String data, 511 Charset charset, 512 int eccPercent, 513 boolean compact, 514 int layers) throws FormatException { 515 // Perform an encode-decode round-trip because it can be lossy. 516 Map<EncodeHintType,Object> hints = new EnumMap<>(EncodeHintType.class); 517 if (null != charset) { 518 hints.put(EncodeHintType.CHARACTER_SET, charset.name()); 519 } 520 hints.put(EncodeHintType.ERROR_CORRECTION, eccPercent); 521 AztecWriter writer = new AztecWriter(); 522 BitMatrix matrix = writer.encode(data, BarcodeFormat.AZTEC, 0, 0, hints); 523 AztecCode aztec = Encoder.encode(data, eccPercent, 524 Encoder.DEFAULT_AZTEC_LAYERS, charset); 525 assertEquals("Unexpected symbol format (compact)", compact, aztec.isCompact()); 526 assertEquals("Unexpected nr. of layers", layers, aztec.getLayers()); 527 BitMatrix matrix2 = aztec.getMatrix(); 528 assertEquals(matrix, matrix2); 529 AztecDetectorResult r = 530 new AztecDetectorResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers(), 0); 531 DecoderResult res = new Decoder().decode(r); 532 assertEquals(data, res.getText()); 533 // Check error correction by introducing up to eccPercent/2 errors 534 int ecWords = aztec.getCodeWords() * eccPercent / 100 / 2; 535 Random random = getPseudoRandom(); 536 for (int i = 0; i < ecWords; i++) { 537 // don't touch the core 538 int x = random.nextBoolean() ? 539 random.nextInt(aztec.getLayers() * 2) 540 : matrix.getWidth() - 1 - random.nextInt(aztec.getLayers() * 2); 541 int y = random.nextBoolean() ? 542 random.nextInt(aztec.getLayers() * 2) 543 : matrix.getHeight() - 1 - random.nextInt(aztec.getLayers() * 2); 544 matrix.flip(x, y); 545 } 546 r = new AztecDetectorResult(matrix, NO_POINTS, aztec.isCompact(), aztec.getCodeWords(), aztec.getLayers(), 0); 547 res = new Decoder().decode(r); 548 assertEquals(data, res.getText()); 549 } 550 getPseudoRandom()551 private static Random getPseudoRandom() { 552 return new Random(0xDEADBEEF); 553 } 554 testModeMessage(boolean compact, int layers, int words, String expected)555 private static void testModeMessage(boolean compact, int layers, int words, String expected) { 556 BitArray in = Encoder.generateModeMessage(compact, layers, words); 557 assertEquals("generateModeMessage() failed", stripSpace(expected), stripSpace(in.toString())); 558 } 559 testStuffBits(int wordSize, String bits, String expected)560 private static void testStuffBits(int wordSize, String bits, String expected) { 561 BitArray in = toBitArray(bits); 562 BitArray stuffed = Encoder.stuffBits(in, wordSize); 563 assertEquals("stuffBits() failed for input string: " + bits, 564 stripSpace(expected), stripSpace(stuffed.toString())); 565 } 566 toBitArray(CharSequence bits)567 public static BitArray toBitArray(CharSequence bits) { 568 BitArray in = new BitArray(); 569 char[] str = DOTX.matcher(bits).replaceAll("").toCharArray(); 570 for (char aStr : str) { 571 in.appendBit(aStr == 'X'); 572 } 573 return in; 574 } 575 toBooleanArray(BitArray bitArray)576 public static boolean[] toBooleanArray(BitArray bitArray) { 577 boolean[] result = new boolean[bitArray.getSize()]; 578 for (int i = 0; i < result.length; i++) { 579 result[i] = bitArray.get(i); 580 } 581 return result; 582 } 583 testHighLevelEncodeString(String s, String expectedBits)584 private static void testHighLevelEncodeString(String s, String expectedBits) throws FormatException { 585 BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode(); 586 String receivedBits = stripSpace(bits.toString()); 587 assertEquals("highLevelEncode() failed for input string: " + s, stripSpace(expectedBits), receivedBits); 588 assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits))); 589 } 590 testHighLevelEncodeString(String s, int expectedReceivedBits)591 private static void testHighLevelEncodeString(String s, int expectedReceivedBits) throws FormatException { 592 BitArray bits = new HighLevelEncoder(s.getBytes(StandardCharsets.ISO_8859_1)).encode(); 593 int receivedBitCount = stripSpace(bits.toString()).length(); 594 assertEquals("highLevelEncode() failed for input string: " + s, 595 expectedReceivedBits, receivedBitCount); 596 assertEquals(s, Decoder.highLevelDecode(toBooleanArray(bits))); 597 } 598 stripSpace(String s)599 public static String stripSpace(String s) { 600 return SPACES.matcher(s).replaceAll(""); 601 } 602 603 } 604