1 /* 2 * Copyright 2008 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.oned; 18 19 import com.google.zxing.BarcodeFormat; 20 import com.google.zxing.ChecksumException; 21 import com.google.zxing.DecodeHintType; 22 import com.google.zxing.FormatException; 23 import com.google.zxing.NotFoundException; 24 import com.google.zxing.Result; 25 import com.google.zxing.ResultMetadataType; 26 import com.google.zxing.ResultPoint; 27 import com.google.zxing.common.BitArray; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.Map; 32 33 /** 34 * <p>Decodes Code 128 barcodes.</p> 35 * 36 * @author Sean Owen 37 */ 38 public final class Code128Reader extends OneDReader { 39 40 static final int[][] CODE_PATTERNS = { 41 {2, 1, 2, 2, 2, 2}, // 0 42 {2, 2, 2, 1, 2, 2}, 43 {2, 2, 2, 2, 2, 1}, 44 {1, 2, 1, 2, 2, 3}, 45 {1, 2, 1, 3, 2, 2}, 46 {1, 3, 1, 2, 2, 2}, // 5 47 {1, 2, 2, 2, 1, 3}, 48 {1, 2, 2, 3, 1, 2}, 49 {1, 3, 2, 2, 1, 2}, 50 {2, 2, 1, 2, 1, 3}, 51 {2, 2, 1, 3, 1, 2}, // 10 52 {2, 3, 1, 2, 1, 2}, 53 {1, 1, 2, 2, 3, 2}, 54 {1, 2, 2, 1, 3, 2}, 55 {1, 2, 2, 2, 3, 1}, 56 {1, 1, 3, 2, 2, 2}, // 15 57 {1, 2, 3, 1, 2, 2}, 58 {1, 2, 3, 2, 2, 1}, 59 {2, 2, 3, 2, 1, 1}, 60 {2, 2, 1, 1, 3, 2}, 61 {2, 2, 1, 2, 3, 1}, // 20 62 {2, 1, 3, 2, 1, 2}, 63 {2, 2, 3, 1, 1, 2}, 64 {3, 1, 2, 1, 3, 1}, 65 {3, 1, 1, 2, 2, 2}, 66 {3, 2, 1, 1, 2, 2}, // 25 67 {3, 2, 1, 2, 2, 1}, 68 {3, 1, 2, 2, 1, 2}, 69 {3, 2, 2, 1, 1, 2}, 70 {3, 2, 2, 2, 1, 1}, 71 {2, 1, 2, 1, 2, 3}, // 30 72 {2, 1, 2, 3, 2, 1}, 73 {2, 3, 2, 1, 2, 1}, 74 {1, 1, 1, 3, 2, 3}, 75 {1, 3, 1, 1, 2, 3}, 76 {1, 3, 1, 3, 2, 1}, // 35 77 {1, 1, 2, 3, 1, 3}, 78 {1, 3, 2, 1, 1, 3}, 79 {1, 3, 2, 3, 1, 1}, 80 {2, 1, 1, 3, 1, 3}, 81 {2, 3, 1, 1, 1, 3}, // 40 82 {2, 3, 1, 3, 1, 1}, 83 {1, 1, 2, 1, 3, 3}, 84 {1, 1, 2, 3, 3, 1}, 85 {1, 3, 2, 1, 3, 1}, 86 {1, 1, 3, 1, 2, 3}, // 45 87 {1, 1, 3, 3, 2, 1}, 88 {1, 3, 3, 1, 2, 1}, 89 {3, 1, 3, 1, 2, 1}, 90 {2, 1, 1, 3, 3, 1}, 91 {2, 3, 1, 1, 3, 1}, // 50 92 {2, 1, 3, 1, 1, 3}, 93 {2, 1, 3, 3, 1, 1}, 94 {2, 1, 3, 1, 3, 1}, 95 {3, 1, 1, 1, 2, 3}, 96 {3, 1, 1, 3, 2, 1}, // 55 97 {3, 3, 1, 1, 2, 1}, 98 {3, 1, 2, 1, 1, 3}, 99 {3, 1, 2, 3, 1, 1}, 100 {3, 3, 2, 1, 1, 1}, 101 {3, 1, 4, 1, 1, 1}, // 60 102 {2, 2, 1, 4, 1, 1}, 103 {4, 3, 1, 1, 1, 1}, 104 {1, 1, 1, 2, 2, 4}, 105 {1, 1, 1, 4, 2, 2}, 106 {1, 2, 1, 1, 2, 4}, // 65 107 {1, 2, 1, 4, 2, 1}, 108 {1, 4, 1, 1, 2, 2}, 109 {1, 4, 1, 2, 2, 1}, 110 {1, 1, 2, 2, 1, 4}, 111 {1, 1, 2, 4, 1, 2}, // 70 112 {1, 2, 2, 1, 1, 4}, 113 {1, 2, 2, 4, 1, 1}, 114 {1, 4, 2, 1, 1, 2}, 115 {1, 4, 2, 2, 1, 1}, 116 {2, 4, 1, 2, 1, 1}, // 75 117 {2, 2, 1, 1, 1, 4}, 118 {4, 1, 3, 1, 1, 1}, 119 {2, 4, 1, 1, 1, 2}, 120 {1, 3, 4, 1, 1, 1}, 121 {1, 1, 1, 2, 4, 2}, // 80 122 {1, 2, 1, 1, 4, 2}, 123 {1, 2, 1, 2, 4, 1}, 124 {1, 1, 4, 2, 1, 2}, 125 {1, 2, 4, 1, 1, 2}, 126 {1, 2, 4, 2, 1, 1}, // 85 127 {4, 1, 1, 2, 1, 2}, 128 {4, 2, 1, 1, 1, 2}, 129 {4, 2, 1, 2, 1, 1}, 130 {2, 1, 2, 1, 4, 1}, 131 {2, 1, 4, 1, 2, 1}, // 90 132 {4, 1, 2, 1, 2, 1}, 133 {1, 1, 1, 1, 4, 3}, 134 {1, 1, 1, 3, 4, 1}, 135 {1, 3, 1, 1, 4, 1}, 136 {1, 1, 4, 1, 1, 3}, // 95 137 {1, 1, 4, 3, 1, 1}, 138 {4, 1, 1, 1, 1, 3}, 139 {4, 1, 1, 3, 1, 1}, 140 {1, 1, 3, 1, 4, 1}, 141 {1, 1, 4, 1, 3, 1}, // 100 142 {3, 1, 1, 1, 4, 1}, 143 {4, 1, 1, 1, 3, 1}, 144 {2, 1, 1, 4, 1, 2}, 145 {2, 1, 1, 2, 1, 4}, 146 {2, 1, 1, 2, 3, 2}, // 105 147 {2, 3, 3, 1, 1, 1, 2} 148 }; 149 150 private static final float MAX_AVG_VARIANCE = 0.25f; 151 private static final float MAX_INDIVIDUAL_VARIANCE = 0.7f; 152 153 private static final int CODE_SHIFT = 98; 154 155 private static final int CODE_CODE_C = 99; 156 private static final int CODE_CODE_B = 100; 157 private static final int CODE_CODE_A = 101; 158 159 private static final int CODE_FNC_1 = 102; 160 private static final int CODE_FNC_2 = 97; 161 private static final int CODE_FNC_3 = 96; 162 private static final int CODE_FNC_4_A = 101; 163 private static final int CODE_FNC_4_B = 100; 164 165 private static final int CODE_START_A = 103; 166 private static final int CODE_START_B = 104; 167 private static final int CODE_START_C = 105; 168 private static final int CODE_STOP = 106; 169 findStartPattern(BitArray row)170 private static int[] findStartPattern(BitArray row) throws NotFoundException { 171 int width = row.getSize(); 172 int rowOffset = row.getNextSet(0); 173 174 int counterPosition = 0; 175 int[] counters = new int[6]; 176 int patternStart = rowOffset; 177 boolean isWhite = false; 178 int patternLength = counters.length; 179 180 for (int i = rowOffset; i < width; i++) { 181 if (row.get(i) != isWhite) { 182 counters[counterPosition]++; 183 } else { 184 if (counterPosition == patternLength - 1) { 185 float bestVariance = MAX_AVG_VARIANCE; 186 int bestMatch = -1; 187 for (int startCode = CODE_START_A; startCode <= CODE_START_C; startCode++) { 188 float variance = patternMatchVariance(counters, CODE_PATTERNS[startCode], 189 MAX_INDIVIDUAL_VARIANCE); 190 if (variance < bestVariance) { 191 bestVariance = variance; 192 bestMatch = startCode; 193 } 194 } 195 // Look for whitespace before start pattern, >= 50% of width of start pattern 196 if (bestMatch >= 0 && 197 row.isRange(Math.max(0, patternStart - (i - patternStart) / 2), patternStart, false)) { 198 return new int[]{patternStart, i, bestMatch}; 199 } 200 patternStart += counters[0] + counters[1]; 201 System.arraycopy(counters, 2, counters, 0, counterPosition - 1); 202 counters[counterPosition - 1] = 0; 203 counters[counterPosition] = 0; 204 counterPosition--; 205 } else { 206 counterPosition++; 207 } 208 counters[counterPosition] = 1; 209 isWhite = !isWhite; 210 } 211 } 212 throw NotFoundException.getNotFoundInstance(); 213 } 214 decodeCode(BitArray row, int[] counters, int rowOffset)215 private static int decodeCode(BitArray row, int[] counters, int rowOffset) 216 throws NotFoundException { 217 recordPattern(row, rowOffset, counters); 218 float bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept 219 int bestMatch = -1; 220 for (int d = 0; d < CODE_PATTERNS.length; d++) { 221 int[] pattern = CODE_PATTERNS[d]; 222 float variance = patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE); 223 if (variance < bestVariance) { 224 bestVariance = variance; 225 bestMatch = d; 226 } 227 } 228 // TODO We're overlooking the fact that the STOP pattern has 7 values, not 6. 229 if (bestMatch >= 0) { 230 return bestMatch; 231 } else { 232 throw NotFoundException.getNotFoundInstance(); 233 } 234 } 235 236 @Override decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints)237 public Result decodeRow(int rowNumber, BitArray row, Map<DecodeHintType,?> hints) 238 throws NotFoundException, FormatException, ChecksumException { 239 240 boolean convertFNC1 = hints != null && hints.containsKey(DecodeHintType.ASSUME_GS1); 241 242 int symbologyModifier = 0; 243 244 int[] startPatternInfo = findStartPattern(row); 245 int startCode = startPatternInfo[2]; 246 247 List<Byte> rawCodes = new ArrayList<>(20); 248 rawCodes.add((byte) startCode); 249 250 int codeSet; 251 switch (startCode) { 252 case CODE_START_A: 253 codeSet = CODE_CODE_A; 254 break; 255 case CODE_START_B: 256 codeSet = CODE_CODE_B; 257 break; 258 case CODE_START_C: 259 codeSet = CODE_CODE_C; 260 break; 261 default: 262 throw FormatException.getFormatInstance(); 263 } 264 265 boolean done = false; 266 boolean isNextShifted = false; 267 268 StringBuilder result = new StringBuilder(20); 269 270 int lastStart = startPatternInfo[0]; 271 int nextStart = startPatternInfo[1]; 272 int[] counters = new int[6]; 273 274 int lastCode = 0; 275 int code = 0; 276 int checksumTotal = startCode; 277 int multiplier = 0; 278 boolean lastCharacterWasPrintable = true; 279 boolean upperMode = false; 280 boolean shiftUpperMode = false; 281 282 while (!done) { 283 284 boolean unshift = isNextShifted; 285 isNextShifted = false; 286 287 // Save off last code 288 lastCode = code; 289 290 // Decode another code from image 291 code = decodeCode(row, counters, nextStart); 292 293 rawCodes.add((byte) code); 294 295 // Remember whether the last code was printable or not (excluding CODE_STOP) 296 if (code != CODE_STOP) { 297 lastCharacterWasPrintable = true; 298 } 299 300 // Add to checksum computation (if not CODE_STOP of course) 301 if (code != CODE_STOP) { 302 multiplier++; 303 checksumTotal += multiplier * code; 304 } 305 306 // Advance to where the next code will to start 307 lastStart = nextStart; 308 for (int counter : counters) { 309 nextStart += counter; 310 } 311 312 // Take care of illegal start codes 313 switch (code) { 314 case CODE_START_A: 315 case CODE_START_B: 316 case CODE_START_C: 317 throw FormatException.getFormatInstance(); 318 } 319 320 switch (codeSet) { 321 322 case CODE_CODE_A: 323 if (code < 64) { 324 if (shiftUpperMode == upperMode) { 325 result.append((char) (' ' + code)); 326 } else { 327 result.append((char) (' ' + code + 128)); 328 } 329 shiftUpperMode = false; 330 } else if (code < 96) { 331 if (shiftUpperMode == upperMode) { 332 result.append((char) (code - 64)); 333 } else { 334 result.append((char) (code + 64)); 335 } 336 shiftUpperMode = false; 337 } else { 338 // Don't let CODE_STOP, which always appears, affect whether whether we think the last 339 // code was printable or not. 340 if (code != CODE_STOP) { 341 lastCharacterWasPrintable = false; 342 } 343 switch (code) { 344 case CODE_FNC_1: 345 if (result.length() == 0) { // FNC1 at first or second character determines the symbology 346 symbologyModifier = 1; 347 } else if (result.length() == 1) { 348 symbologyModifier = 2; 349 } 350 if (convertFNC1) { 351 if (result.length() == 0) { 352 // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code 353 // is FNC1 then this is GS1-128. We add the symbology identifier. 354 result.append("]C1"); 355 } else { 356 // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) 357 result.append((char) 29); 358 } 359 } 360 break; 361 case CODE_FNC_2: 362 symbologyModifier = 4; 363 break; 364 case CODE_FNC_3: 365 // do nothing? 366 break; 367 case CODE_FNC_4_A: 368 if (!upperMode && shiftUpperMode) { 369 upperMode = true; 370 shiftUpperMode = false; 371 } else if (upperMode && shiftUpperMode) { 372 upperMode = false; 373 shiftUpperMode = false; 374 } else { 375 shiftUpperMode = true; 376 } 377 break; 378 case CODE_SHIFT: 379 isNextShifted = true; 380 codeSet = CODE_CODE_B; 381 break; 382 case CODE_CODE_B: 383 codeSet = CODE_CODE_B; 384 break; 385 case CODE_CODE_C: 386 codeSet = CODE_CODE_C; 387 break; 388 case CODE_STOP: 389 done = true; 390 break; 391 } 392 } 393 break; 394 case CODE_CODE_B: 395 if (code < 96) { 396 if (shiftUpperMode == upperMode) { 397 result.append((char) (' ' + code)); 398 } else { 399 result.append((char) (' ' + code + 128)); 400 } 401 shiftUpperMode = false; 402 } else { 403 if (code != CODE_STOP) { 404 lastCharacterWasPrintable = false; 405 } 406 switch (code) { 407 case CODE_FNC_1: 408 if (result.length() == 0) { // FNC1 at first or second character determines the symbology 409 symbologyModifier = 1; 410 } else if (result.length() == 1) { 411 symbologyModifier = 2; 412 } 413 if (convertFNC1) { 414 if (result.length() == 0) { 415 // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code 416 // is FNC1 then this is GS1-128. We add the symbology identifier. 417 result.append("]C1"); 418 } else { 419 // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) 420 result.append((char) 29); 421 } 422 } 423 break; 424 case CODE_FNC_2: 425 symbologyModifier = 4; 426 break; 427 case CODE_FNC_3: 428 // do nothing? 429 break; 430 case CODE_FNC_4_B: 431 if (!upperMode && shiftUpperMode) { 432 upperMode = true; 433 shiftUpperMode = false; 434 } else if (upperMode && shiftUpperMode) { 435 upperMode = false; 436 shiftUpperMode = false; 437 } else { 438 shiftUpperMode = true; 439 } 440 break; 441 case CODE_SHIFT: 442 isNextShifted = true; 443 codeSet = CODE_CODE_A; 444 break; 445 case CODE_CODE_A: 446 codeSet = CODE_CODE_A; 447 break; 448 case CODE_CODE_C: 449 codeSet = CODE_CODE_C; 450 break; 451 case CODE_STOP: 452 done = true; 453 break; 454 } 455 } 456 break; 457 case CODE_CODE_C: 458 if (code < 100) { 459 if (code < 10) { 460 result.append('0'); 461 } 462 result.append(code); 463 } else { 464 if (code != CODE_STOP) { 465 lastCharacterWasPrintable = false; 466 } 467 switch (code) { 468 case CODE_FNC_1: 469 if (result.length() == 0) { // FNC1 at first or second character determines the symbology 470 symbologyModifier = 1; 471 } else if (result.length() == 1) { 472 symbologyModifier = 2; 473 } 474 if (convertFNC1) { 475 if (result.length() == 0) { 476 // GS1 specification 5.4.3.7. and 5.4.6.4. If the first char after the start code 477 // is FNC1 then this is GS1-128. We add the symbology identifier. 478 result.append("]C1"); 479 } else { 480 // GS1 specification 5.4.7.5. Every subsequent FNC1 is returned as ASCII 29 (GS) 481 result.append((char) 29); 482 } 483 } 484 break; 485 case CODE_CODE_A: 486 codeSet = CODE_CODE_A; 487 break; 488 case CODE_CODE_B: 489 codeSet = CODE_CODE_B; 490 break; 491 case CODE_STOP: 492 done = true; 493 break; 494 } 495 } 496 break; 497 } 498 499 // Unshift back to another code set if we were shifted 500 if (unshift) { 501 codeSet = codeSet == CODE_CODE_A ? CODE_CODE_B : CODE_CODE_A; 502 } 503 504 } 505 506 int lastPatternSize = nextStart - lastStart; 507 508 // Check for ample whitespace following pattern, but, to do this we first need to remember that 509 // we fudged decoding CODE_STOP since it actually has 7 bars, not 6. There is a black bar left 510 // to read off. Would be slightly better to properly read. Here we just skip it: 511 nextStart = row.getNextUnset(nextStart); 512 if (!row.isRange(nextStart, 513 Math.min(row.getSize(), nextStart + (nextStart - lastStart) / 2), 514 false)) { 515 throw NotFoundException.getNotFoundInstance(); 516 } 517 518 // Pull out from sum the value of the penultimate check code 519 checksumTotal -= multiplier * lastCode; 520 // lastCode is the checksum then: 521 if (checksumTotal % 103 != lastCode) { 522 throw ChecksumException.getChecksumInstance(); 523 } 524 525 // Need to pull out the check digits from string 526 int resultLength = result.length(); 527 if (resultLength == 0) { 528 // false positive 529 throw NotFoundException.getNotFoundInstance(); 530 } 531 532 // Only bother if the result had at least one character, and if the checksum digit happened to 533 // be a printable character. If it was just interpreted as a control code, nothing to remove. 534 if (resultLength > 0 && lastCharacterWasPrintable) { 535 if (codeSet == CODE_CODE_C) { 536 result.delete(resultLength - 2, resultLength); 537 } else { 538 result.delete(resultLength - 1, resultLength); 539 } 540 } 541 542 float left = (startPatternInfo[1] + startPatternInfo[0]) / 2.0f; 543 float right = lastStart + lastPatternSize / 2.0f; 544 545 int rawCodesSize = rawCodes.size(); 546 byte[] rawBytes = new byte[rawCodesSize]; 547 for (int i = 0; i < rawCodesSize; i++) { 548 rawBytes[i] = rawCodes.get(i); 549 } 550 Result resultObject = new Result( 551 result.toString(), 552 rawBytes, 553 new ResultPoint[]{ 554 new ResultPoint(left, rowNumber), 555 new ResultPoint(right, rowNumber)}, 556 BarcodeFormat.CODE_128); 557 resultObject.putMetadata(ResultMetadataType.SYMBOLOGY_IDENTIFIER, "]C" + symbologyModifier); 558 return resultObject; 559 560 } 561 562 } 563