/**
* See ISO 18004:2006, 6.4.4 Table 5
*/
- private static final char[] ALPHANUMERIC_CHARS = new char[]{
+ private static final char[] ALPHANUMERIC_CHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
Mode mode;
do {
// While still another segment to read...
- mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
+ if (bits.available() == 0) {
+ // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
+ mode = Mode.TERMINATOR;
+ } else {
+ mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
+ }
if (!mode.equals(Mode.TERMINATOR)) {
- // How many characters will follow, encoded in this mode?
- int count = bits.readBits(mode.getCharacterCountBits(version));
- if (mode.equals(Mode.NUMERIC)) {
- decodeNumericSegment(bits, result, count);
- } else if (mode.equals(Mode.ALPHANUMERIC)) {
- decodeAlphanumericSegment(bits, result, count);
- } else if (mode.equals(Mode.BYTE)) {
- decodeByteSegment(bits, result, count);
- } else if (mode.equals(Mode.KANJI)) {
- decodeKanjiSegment(bits, result, count);
+ if (mode.equals(Mode.ECI)) {
+ // Count doesn't apply to ECI
+ parseECI(bits);
+ // We don't currently do anything with ECI, since there seems to be no reference
+ // defining what each value means. AIM's "Extended Channel Interpretations" does
+ // not define it. I have never observed a QR Code using it. So for now, we at least
+ // parse it but don't know how to take action on it.
} else {
- throw new ReaderException("Unsupported mode indicator");
+ // How many characters will follow, encoded in this mode?
+ int count = bits.readBits(mode.getCharacterCountBits(version));
+ if (mode.equals(Mode.NUMERIC)) {
+ decodeNumericSegment(bits, result, count);
+ } else if (mode.equals(Mode.ALPHANUMERIC)) {
+ decodeAlphanumericSegment(bits, result, count);
+ } else if (mode.equals(Mode.BYTE)) {
+ decodeByteSegment(bits, result, count);
+ } else if (mode.equals(Mode.KANJI)) {
+ decodeKanjiSegment(bits, result, count);
+ } else {
+ throw new ReaderException("Unsupported mode indicator");
+ }
}
}
} while (!mode.equals(Mode.TERMINATOR));
return result.toString();
}
+ private static int parseECI(BitSource bits) {
+ int firstByte = bits.readBits(8);
+ if ((firstByte & 0x80) == 0) {
+ // just one byte
+ return firstByte & 0x7F;
+ } else if ((firstByte & 0xC0) == 0x80) {
+ // two bytes
+ int secondByte = bits.readBits(8);
+ return ((firstByte & 0x3F) << 8) | secondByte;
+ } else if ((firstByte & 0xE0) == 0xC0) {
+ // three bytes
+ int secondByte = bits.readBits(8);
+ int thirdByte = bits.readBits(8);
+ return ((firstByte & 0x1F) << 16) | (secondByte << 8) | thirdByte;
+ }
+ // FIXME: What should we return here?
+ return 0;
+ }
+
private static void decodeKanjiSegment(BitSource bits,
StringBuffer result,
int count) throws ReaderException {