Decoder.java (zxing-zxing-3.4.1) | : | Decoder.java (zxing-zxing-3.5.0) | ||
---|---|---|---|---|
skipping to change at line 22 | skipping to change at line 22 | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | * See the License for the specific language governing permissions and | |||
* limitations under the License. | * limitations under the License. | |||
*/ | */ | |||
package com.google.zxing.aztec.decoder; | package com.google.zxing.aztec.decoder; | |||
import com.google.zxing.FormatException; | import com.google.zxing.FormatException; | |||
import com.google.zxing.aztec.AztecDetectorResult; | import com.google.zxing.aztec.AztecDetectorResult; | |||
import com.google.zxing.common.BitMatrix; | import com.google.zxing.common.BitMatrix; | |||
import com.google.zxing.common.CharacterSetECI; | ||||
import com.google.zxing.common.DecoderResult; | import com.google.zxing.common.DecoderResult; | |||
import com.google.zxing.common.reedsolomon.GenericGF; | import com.google.zxing.common.reedsolomon.GenericGF; | |||
import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; | import com.google.zxing.common.reedsolomon.ReedSolomonDecoder; | |||
import com.google.zxing.common.reedsolomon.ReedSolomonException; | import com.google.zxing.common.reedsolomon.ReedSolomonException; | |||
import java.io.ByteArrayOutputStream; | ||||
import java.io.UnsupportedEncodingException; | ||||
import java.nio.charset.Charset; | ||||
import java.nio.charset.StandardCharsets; | ||||
import java.util.Arrays; | import java.util.Arrays; | |||
/** | /** | |||
* <p>The main class which implements Aztec Code decoding -- as opposed to locat ing and extracting | * <p>The main class which implements Aztec Code decoding -- as opposed to locat ing and extracting | |||
* the Aztec Code from an image.</p> | * the Aztec Code from an image.</p> | |||
* | * | |||
* @author David Olivier | * @author David Olivier | |||
*/ | */ | |||
public final class Decoder { | public final class Decoder { | |||
skipping to change at line 63 | skipping to change at line 68 | |||
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "C TRL_DL", "CTRL_BS" | "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "CTRL_US", "CTRL_ML", "C TRL_DL", "CTRL_BS" | |||
}; | }; | |||
private static final String[] MIXED_TABLE = { | private static final String[] MIXED_TABLE = { | |||
"CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n" , | "CTRL_PS", " ", "\1", "\2", "\3", "\4", "\5", "\6", "\7", "\b", "\t", "\n" , | |||
"\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_", | "\13", "\f", "\r", "\33", "\34", "\35", "\36", "\37", "@", "\\", "^", "_", | |||
"`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS" | "`", "|", "~", "\177", "CTRL_LL", "CTRL_UL", "CTRL_PL", "CTRL_BS" | |||
}; | }; | |||
private static final String[] PUNCT_TABLE = { | private static final String[] PUNCT_TABLE = { | |||
"", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", "'", "( ", ")", | "FLG(n)", "\r", "\r\n", ". ", ", ", ": ", "!", "\"", "#", "$", "%", "&", " '", "(", ")", | |||
"*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL" | "*", "+", ",", "-", ".", "/", ":", ";", "<", "=", ">", "?", "[", "]", "{", "}", "CTRL_UL" | |||
}; | }; | |||
private static final String[] DIGIT_TABLE = { | private static final String[] DIGIT_TABLE = { | |||
"CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", "." , "CTRL_UL", "CTRL_US" | "CTRL_PS", " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", "." , "CTRL_UL", "CTRL_US" | |||
}; | }; | |||
private static final Charset DEFAULT_ENCODING = StandardCharsets.ISO_8859_1; | ||||
private AztecDetectorResult ddata; | private AztecDetectorResult ddata; | |||
public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatE xception { | public DecoderResult decode(AztecDetectorResult detectorResult) throws FormatE xception { | |||
ddata = detectorResult; | ddata = detectorResult; | |||
BitMatrix matrix = detectorResult.getBits(); | BitMatrix matrix = detectorResult.getBits(); | |||
boolean[] rawbits = extractBits(matrix); | boolean[] rawbits = extractBits(matrix); | |||
boolean[] correctedBits = correctBits(rawbits); | CorrectedBitsResult correctedBits = correctBits(rawbits); | |||
byte[] rawBytes = convertBoolArrayToByteArray(correctedBits); | byte[] rawBytes = convertBoolArrayToByteArray(correctedBits.correctBits); | |||
String result = getEncodedData(correctedBits); | String result = getEncodedData(correctedBits.correctBits); | |||
DecoderResult decoderResult = new DecoderResult(rawBytes, result, null, null | DecoderResult decoderResult = | |||
); | new DecoderResult(rawBytes, result, null, String.format("%d%%", correcte | |||
decoderResult.setNumBits(correctedBits.length); | dBits.ecLevel)); | |||
decoderResult.setNumBits(correctedBits.correctBits.length); | ||||
return decoderResult; | return decoderResult; | |||
} | } | |||
// This method is used for testing the high-level encoder | // This method is used for testing the high-level encoder | |||
public static String highLevelDecode(boolean[] correctedBits) { | public static String highLevelDecode(boolean[] correctedBits) throws FormatExc eption { | |||
return getEncodedData(correctedBits); | return getEncodedData(correctedBits); | |||
} | } | |||
/** | /** | |||
* Gets the string encoded in the aztec code bits | * Gets the string encoded in the aztec code bits | |||
* | * | |||
* @return the decoded string | * @return the decoded string | |||
*/ | */ | |||
private static String getEncodedData(boolean[] correctedBits) { | private static String getEncodedData(boolean[] correctedBits) throws FormatExc eption { | |||
int endIndex = correctedBits.length; | int endIndex = correctedBits.length; | |||
Table latchTable = Table.UPPER; // table most recently latched to | Table latchTable = Table.UPPER; // table most recently latched to | |||
Table shiftTable = Table.UPPER; // table to use for the next read | Table shiftTable = Table.UPPER; // table to use for the next read | |||
StringBuilder result = new StringBuilder(20); | ||||
// Final decoded string result | ||||
// (correctedBits-5) / 4 is an upper bound on the size (all-digit result) | ||||
StringBuilder result = new StringBuilder((correctedBits.length - 5) / 4); | ||||
// Intermediary buffer of decoded bytes, which is decoded into a string and | ||||
flushed | ||||
// when character encoding changes (ECI) or input ends. | ||||
ByteArrayOutputStream decodedBytes = new ByteArrayOutputStream(); | ||||
Charset encoding = DEFAULT_ENCODING; | ||||
int index = 0; | int index = 0; | |||
while (index < endIndex) { | while (index < endIndex) { | |||
if (shiftTable == Table.BINARY) { | if (shiftTable == Table.BINARY) { | |||
if (endIndex - index < 5) { | if (endIndex - index < 5) { | |||
break; | break; | |||
} | } | |||
int length = readCode(correctedBits, index, 5); | int length = readCode(correctedBits, index, 5); | |||
index += 5; | index += 5; | |||
if (length == 0) { | if (length == 0) { | |||
if (endIndex - index < 11) { | if (endIndex - index < 11) { | |||
skipping to change at line 121 | skipping to change at line 138 | |||
} | } | |||
length = readCode(correctedBits, index, 11) + 31; | length = readCode(correctedBits, index, 11) + 31; | |||
index += 11; | index += 11; | |||
} | } | |||
for (int charCount = 0; charCount < length; charCount++) { | for (int charCount = 0; charCount < length; charCount++) { | |||
if (endIndex - index < 8) { | if (endIndex - index < 8) { | |||
index = endIndex; // Force outer loop to exit | index = endIndex; // Force outer loop to exit | |||
break; | break; | |||
} | } | |||
int code = readCode(correctedBits, index, 8); | int code = readCode(correctedBits, index, 8); | |||
result.append((char) code); | decodedBytes.write((byte) code); | |||
index += 8; | index += 8; | |||
} | } | |||
// Go back to whatever mode we had been in | // Go back to whatever mode we had been in | |||
shiftTable = latchTable; | shiftTable = latchTable; | |||
} else { | } else { | |||
int size = shiftTable == Table.DIGIT ? 4 : 5; | int size = shiftTable == Table.DIGIT ? 4 : 5; | |||
if (endIndex - index < size) { | if (endIndex - index < size) { | |||
break; | break; | |||
} | } | |||
int code = readCode(correctedBits, index, size); | int code = readCode(correctedBits, index, size); | |||
index += size; | index += size; | |||
String str = getCharacter(shiftTable, code); | String str = getCharacter(shiftTable, code); | |||
if (str.startsWith("CTRL_")) { | if ("FLG(n)".equals(str)) { | |||
if (endIndex - index < 3) { | ||||
break; | ||||
} | ||||
int n = readCode(correctedBits, index, 3); | ||||
index += 3; | ||||
// flush bytes, FLG changes state | ||||
try { | ||||
result.append(decodedBytes.toString(encoding.name())); | ||||
} catch (UnsupportedEncodingException uee) { | ||||
throw new IllegalStateException(uee); | ||||
} | ||||
decodedBytes.reset(); | ||||
switch (n) { | ||||
case 0: | ||||
result.append((char) 29); // translate FNC1 as ASCII 29 | ||||
break; | ||||
case 7: | ||||
throw FormatException.getFormatInstance(); // FLG(7) is reserved a | ||||
nd illegal | ||||
default: | ||||
// ECI is decimal integer encoded as 1-6 codes in DIGIT mode | ||||
int eci = 0; | ||||
if (endIndex - index < 4 * n) { | ||||
break; | ||||
} | ||||
while (n-- > 0) { | ||||
int nextDigit = readCode(correctedBits, index, 4); | ||||
index += 4; | ||||
if (nextDigit < 2 || nextDigit > 11) { | ||||
throw FormatException.getFormatInstance(); // Not a decimal di | ||||
git | ||||
} | ||||
eci = eci * 10 + (nextDigit - 2); | ||||
} | ||||
CharacterSetECI charsetECI = CharacterSetECI.getCharacterSetECIByV | ||||
alue(eci); | ||||
if (charsetECI == null) { | ||||
throw FormatException.getFormatInstance(); | ||||
} | ||||
encoding = charsetECI.getCharset(); | ||||
} | ||||
// Go back to whatever mode we had been in | ||||
shiftTable = latchTable; | ||||
} else if (str.startsWith("CTRL_")) { | ||||
// Table changes | // Table changes | |||
// ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode f rom which it was invoked. | // ISO/IEC 24778:2008 prescribes ending a shift sequence in the mode f rom which it was invoked. | |||
// That's including when that mode is a shift. | // That's including when that mode is a shift. | |||
// Our test case dlusbs.png for issue #642 exercises that. | // Our test case dlusbs.png for issue #642 exercises that. | |||
latchTable = shiftTable; // Latch the current mode, so as to return t o Upper after U/S B/S | latchTable = shiftTable; // Latch the current mode, so as to return t o Upper after U/S B/S | |||
shiftTable = getTable(str.charAt(5)); | shiftTable = getTable(str.charAt(5)); | |||
if (str.charAt(6) == 'L') { | if (str.charAt(6) == 'L') { | |||
latchTable = shiftTable; | latchTable = shiftTable; | |||
} | } | |||
} else { | } else { | |||
result.append(str); | // Though stored as a table of strings for convenience, codes actually | |||
represent 1 or 2 *bytes*. | ||||
byte[] b = str.getBytes(StandardCharsets.US_ASCII); | ||||
decodedBytes.write(b, 0, b.length); | ||||
// Go back to whatever mode we had been in | // Go back to whatever mode we had been in | |||
shiftTable = latchTable; | shiftTable = latchTable; | |||
} | } | |||
} | } | |||
} | } | |||
try { | ||||
result.append(decodedBytes.toString(encoding.name())); | ||||
} catch (UnsupportedEncodingException uee) { | ||||
// can't happen | ||||
throw new IllegalStateException(uee); | ||||
} | ||||
return result.toString(); | return result.toString(); | |||
} | } | |||
/** | /** | |||
* gets the table corresponding to the char passed | * gets the table corresponding to the char passed | |||
*/ | */ | |||
private static Table getTable(char t) { | private static Table getTable(char t) { | |||
switch (t) { | switch (t) { | |||
case 'L': | case 'L': | |||
return Table.LOWER; | return Table.LOWER; | |||
skipping to change at line 199 | skipping to change at line 265 | |||
case PUNCT: | case PUNCT: | |||
return PUNCT_TABLE[code]; | return PUNCT_TABLE[code]; | |||
case DIGIT: | case DIGIT: | |||
return DIGIT_TABLE[code]; | return DIGIT_TABLE[code]; | |||
default: | default: | |||
// Should not reach here. | // Should not reach here. | |||
throw new IllegalStateException("Bad table"); | throw new IllegalStateException("Bad table"); | |||
} | } | |||
} | } | |||
static final class CorrectedBitsResult { | ||||
private final boolean[] correctBits; | ||||
private final int ecLevel; | ||||
CorrectedBitsResult(boolean[] correctBits, int ecLevel) { | ||||
this.correctBits = correctBits; | ||||
this.ecLevel = ecLevel; | ||||
} | ||||
} | ||||
/** | /** | |||
* <p>Performs RS error correction on an array of bits.</p> | * <p>Performs RS error correction on an array of bits.</p> | |||
* | * | |||
* @return the corrected array | * @return the corrected array | |||
* @throws FormatException if the input contains too many errors | * @throws FormatException if the input contains too many errors | |||
*/ | */ | |||
private boolean[] correctBits(boolean[] rawbits) throws FormatException { | private CorrectedBitsResult correctBits(boolean[] rawbits) throws FormatExcept ion { | |||
GenericGF gf; | GenericGF gf; | |||
int codewordSize; | int codewordSize; | |||
if (ddata.getNbLayers() <= 2) { | if (ddata.getNbLayers() <= 2) { | |||
codewordSize = 6; | codewordSize = 6; | |||
gf = GenericGF.AZTEC_DATA_6; | gf = GenericGF.AZTEC_DATA_6; | |||
} else if (ddata.getNbLayers() <= 8) { | } else if (ddata.getNbLayers() <= 8) { | |||
codewordSize = 8; | codewordSize = 8; | |||
gf = GenericGF.AZTEC_DATA_8; | gf = GenericGF.AZTEC_DATA_8; | |||
} else if (ddata.getNbLayers() <= 22) { | } else if (ddata.getNbLayers() <= 22) { | |||
skipping to change at line 269 | skipping to change at line 345 | |||
if (dataWord == 1 || dataWord == mask - 1) { | if (dataWord == 1 || dataWord == mask - 1) { | |||
// next codewordSize-1 bits are all zeros or all ones | // next codewordSize-1 bits are all zeros or all ones | |||
Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1 ); | Arrays.fill(correctedBits, index, index + codewordSize - 1, dataWord > 1 ); | |||
index += codewordSize - 1; | index += codewordSize - 1; | |||
} else { | } else { | |||
for (int bit = codewordSize - 1; bit >= 0; --bit) { | for (int bit = codewordSize - 1; bit >= 0; --bit) { | |||
correctedBits[index++] = (dataWord & (1 << bit)) != 0; | correctedBits[index++] = (dataWord & (1 << bit)) != 0; | |||
} | } | |||
} | } | |||
} | } | |||
return correctedBits; | ||||
return new CorrectedBitsResult(correctedBits, 100 * (numCodewords - numDataC | ||||
odewords) / numCodewords); | ||||
} | } | |||
/** | /** | |||
* Gets the array of bits from an Aztec Code matrix | * Gets the array of bits from an Aztec Code matrix | |||
* | * | |||
* @return the array of bits | * @return the array of bits | |||
*/ | */ | |||
private boolean[] extractBits(BitMatrix matrix) { | private boolean[] extractBits(BitMatrix matrix) { | |||
boolean compact = ddata.isCompact(); | boolean compact = ddata.isCompact(); | |||
int layers = ddata.getNbLayers(); | int layers = ddata.getNbLayers(); | |||
End of changes. 15 change blocks. | ||||
15 lines changed or deleted | 98 lines changed or added |