"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "core/src/main/java/com/google/zxing/qrcode/encoder/Encoder.java" between
zxing-zxing-3.4.1.tar.gz and zxing-zxing-3.5.0.tar.gz

About: ZXing ("zebra crossing") is a multi-format 1D/2D barcode image processing library implemented in Java, with ports to other languages. Info: Project is in maintenance mode (no active development).

Encoder.java  (zxing-zxing-3.4.1):Encoder.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.qrcode.encoder; package com.google.zxing.qrcode.encoder;
import com.google.zxing.EncodeHintType; import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException; import com.google.zxing.WriterException;
import com.google.zxing.common.BitArray; import com.google.zxing.common.BitArray;
import com.google.zxing.common.StringUtils;
import com.google.zxing.common.CharacterSetECI; import com.google.zxing.common.CharacterSetECI;
import com.google.zxing.common.reedsolomon.GenericGF; import com.google.zxing.common.reedsolomon.GenericGF;
import com.google.zxing.common.reedsolomon.ReedSolomonEncoder; import com.google.zxing.common.reedsolomon.ReedSolomonEncoder;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.google.zxing.qrcode.decoder.Mode; import com.google.zxing.qrcode.decoder.Mode;
import com.google.zxing.qrcode.decoder.Version; import com.google.zxing.qrcode.decoder.Version;
import java.io.UnsupportedEncodingException; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
/** /**
* @author satorux@google.com (Satoru Takabayashi) - creator * @author satorux@google.com (Satoru Takabayashi) - creator
* @author dswitkin@google.com (Daniel Switkin) - ported from C++ * @author dswitkin@google.com (Daniel Switkin) - ported from C++
*/ */
public final class Encoder { public final class Encoder {
// The original table is defined in the table 5 of JISX0510:2004 (p.19). // The original table is defined in the table 5 of JISX0510:2004 (p.19).
private static final int[] ALPHANUMERIC_TABLE = { private static final int[] ALPHANUMERIC_TABLE = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0 x0f -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0 x0f
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0 x1f -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0 x1f
36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0 x2f 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0 x2f
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0 x3f 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0 x3f
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0 x4f -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0 x4f
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0 x5f 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0 x5f
}; };
static final String DEFAULT_BYTE_MODE_ENCODING = "ISO-8859-1"; static final Charset DEFAULT_BYTE_MODE_ENCODING = StandardCharsets.ISO_8859_1;
private Encoder() { private Encoder() {
} }
// The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details. // The mask penalty calculation is complicated. See Table 21 of JISX0510:2004 (p.45) for details.
// Basically it applies four rules and summate all penalties. // Basically it applies four rules and summate all penalties.
private static int calculateMaskPenalty(ByteMatrix matrix) { private static int calculateMaskPenalty(ByteMatrix matrix) {
return MaskUtil.applyMaskPenaltyRule1(matrix) return MaskUtil.applyMaskPenaltyRule1(matrix)
+ MaskUtil.applyMaskPenaltyRule2(matrix) + MaskUtil.applyMaskPenaltyRule2(matrix)
+ MaskUtil.applyMaskPenaltyRule3(matrix) + MaskUtil.applyMaskPenaltyRule3(matrix)
skipping to change at line 79 skipping to change at line 81
* or configuration * or configuration
*/ */
public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) thro ws WriterException { public static QRCode encode(String content, ErrorCorrectionLevel ecLevel) thro ws WriterException {
return encode(content, ecLevel, null); return encode(content, ecLevel, null);
} }
public static QRCode encode(String content, public static QRCode encode(String content,
ErrorCorrectionLevel ecLevel, ErrorCorrectionLevel ecLevel,
Map<EncodeHintType,?> hints) throws WriterExceptio n { Map<EncodeHintType,?> hints) throws WriterExceptio n {
Version version;
BitArray headerAndDataBits;
Mode mode;
boolean hasGS1FormatHint = hints != null && hints.containsKey(EncodeHintType
.GS1_FORMAT) &&
Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FORMAT).toString());
boolean hasCompactionHint = hints != null && hints.containsKey(EncodeHintTyp
e.QR_COMPACT) &&
Boolean.parseBoolean(hints.get(EncodeHintType.QR_COMPACT).toString());
// Determine what character encoding has been specified by the caller, if an y // Determine what character encoding has been specified by the caller, if an y
String encoding = DEFAULT_BYTE_MODE_ENCODING; Charset encoding = DEFAULT_BYTE_MODE_ENCODING;
boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType. CHARACTER_SET); boolean hasEncodingHint = hints != null && hints.containsKey(EncodeHintType. CHARACTER_SET);
if (hasEncodingHint) { if (hasEncodingHint) {
encoding = hints.get(EncodeHintType.CHARACTER_SET).toString(); encoding = Charset.forName(hints.get(EncodeHintType.CHARACTER_SET).toStrin g());
} }
// Pick an encoding mode appropriate for the content. Note that this will no if (hasCompactionHint) {
t attempt to use mode = Mode.BYTE;
// multiple modes / segments even if that were more efficient. Twould be nic
e.
Mode mode = chooseMode(content, encoding);
// This will store the header information, like mode and Charset priorityEncoding = encoding.equals(DEFAULT_BYTE_MODE_ENCODING) ? n
// length, as well as "header" segments like an ECI segment. ull : encoding;
BitArray headerBits = new BitArray(); MinimalEncoder.ResultList rn = MinimalEncoder.encode(content, null, priori
tyEncoding, hasGS1FormatHint, ecLevel);
// Append ECI segment if applicable headerAndDataBits = new BitArray();
if (mode == Mode.BYTE && hasEncodingHint) { rn.getBits(headerAndDataBits);
CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding); version = rn.getVersion();
if (eci != null) {
appendECI(eci, headerBits);
}
}
// Append the FNC1 mode header for GS1 formatted data if applicable } else {
boolean hasGS1FormatHint = hints != null && hints.containsKey(EncodeHintType
.GS1_FORMAT);
if (hasGS1FormatHint && Boolean.parseBoolean(hints.get(EncodeHintType.GS1_FO
RMAT).toString())) {
// GS1 formatted codes are prefixed with a FNC1 in first position mode hea
der
appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits);
}
// (With ECI in place,) Write the mode marker
appendModeInfo(mode, headerBits);
// Collect data within the main segment, separately, to count its size if ne // Pick an encoding mode appropriate for the content. Note that this will
eded. Don't add it to not attempt to use
// main payload yet. // multiple modes / segments even if that were more efficient.
BitArray dataBits = new BitArray(); mode = chooseMode(content, encoding);
appendBytes(content, mode, dataBits, encoding);
// This will store the header information, like mode and
// length, as well as "header" segments like an ECI segment.
BitArray headerBits = new BitArray();
// Append ECI segment if applicable
if (mode == Mode.BYTE && hasEncodingHint) {
CharacterSetECI eci = CharacterSetECI.getCharacterSetECI(encoding);
if (eci != null) {
appendECI(eci, headerBits);
}
}
Version version; // Append the FNC1 mode header for GS1 formatted data if applicable
if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) { if (hasGS1FormatHint) {
int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION). // GS1 formatted codes are prefixed with a FNC1 in first position mode h
toString()); eader
version = Version.getVersionForNumber(versionNumber); appendModeInfo(Mode.FNC1_FIRST_POSITION, headerBits);
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version); }
if (!willFit(bitsNeeded, version, ecLevel)) {
throw new WriterException("Data too big for requested version"); // (With ECI in place,) Write the mode marker
appendModeInfo(mode, headerBits);
// Collect data within the main segment, separately, to count its size if
needed. Don't add it to
// main payload yet.
BitArray dataBits = new BitArray();
appendBytes(content, mode, dataBits, encoding);
if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) {
int versionNumber = Integer.parseInt(hints.get(EncodeHintType.QR_VERSION
).toString());
version = Version.getVersionForNumber(versionNumber);
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version
);
if (!willFit(bitsNeeded, version, ecLevel)) {
throw new WriterException("Data too big for requested version");
}
} else {
version = recommendVersion(ecLevel, mode, headerBits, dataBits);
} }
} else {
version = recommendVersion(ecLevel, mode, headerBits, dataBits);
}
BitArray headerAndDataBits = new BitArray(); headerAndDataBits = new BitArray();
headerAndDataBits.appendBitArray(headerBits); headerAndDataBits.appendBitArray(headerBits);
// Find "length" of main segment and write it // Find "length" of main segment and write it
int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.len int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.l
gth(); ength();
appendLengthInfo(numLetters, version, mode, headerAndDataBits); appendLengthInfo(numLetters, version, mode, headerAndDataBits);
// Put data together into the overall payload // Put data together into the overall payload
headerAndDataBits.appendBitArray(dataBits); headerAndDataBits.appendBitArray(dataBits);
}
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodeword s(); int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodeword s();
// Terminate the bits properly. // Terminate the bits properly.
terminateBits(numDataBytes, headerAndDataBits); terminateBits(numDataBytes, headerAndDataBits);
// Interleave data bits with error correction code. // Interleave data bits with error correction code.
BitArray finalBits = interleaveWithECBytes(headerAndDataBits, BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
version.getTotalCodewords(), version.getTotalCodewords(),
skipping to change at line 224 skipping to change at line 246
} }
public static Mode chooseMode(String content) { public static Mode chooseMode(String content) {
return chooseMode(content, null); return chooseMode(content, null);
} }
/** /**
* Choose the best mode by examining the content. Note that 'encoding' is used as a hint; * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
* if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}. * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
*/ */
private static Mode chooseMode(String content, String encoding) { private static Mode chooseMode(String content, Charset encoding) {
if ("Shift_JIS".equals(encoding) && isOnlyDoubleByteKanji(content)) { if (StringUtils.SHIFT_JIS_CHARSET.equals(encoding) && isOnlyDoubleByteKanji(
content)) {
// Choose Kanji mode if all input are double-byte characters // Choose Kanji mode if all input are double-byte characters
return Mode.KANJI; return Mode.KANJI;
} }
boolean hasNumeric = false; boolean hasNumeric = false;
boolean hasAlphanumeric = false; boolean hasAlphanumeric = false;
for (int i = 0; i < content.length(); ++i) { for (int i = 0; i < content.length(); ++i) {
char c = content.charAt(i); char c = content.charAt(i);
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
hasNumeric = true; hasNumeric = true;
} else if (getAlphanumericCode(c) != -1) { } else if (getAlphanumericCode(c) != -1) {
skipping to change at line 250 skipping to change at line 272
} }
if (hasAlphanumeric) { if (hasAlphanumeric) {
return Mode.ALPHANUMERIC; return Mode.ALPHANUMERIC;
} }
if (hasNumeric) { if (hasNumeric) {
return Mode.NUMERIC; return Mode.NUMERIC;
} }
return Mode.BYTE; return Mode.BYTE;
} }
private static boolean isOnlyDoubleByteKanji(String content) { static boolean isOnlyDoubleByteKanji(String content) {
byte[] bytes; byte[] bytes = content.getBytes(StringUtils.SHIFT_JIS_CHARSET);
try {
bytes = content.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException ignored) {
return false;
}
int length = bytes.length; int length = bytes.length;
if (length % 2 != 0) { if (length % 2 != 0) {
return false; return false;
} }
for (int i = 0; i < length; i += 2) { for (int i = 0; i < length; i += 2) {
int byte1 = bytes[i] & 0xFF; int byte1 = bytes[i] & 0xFF;
if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) { if ((byte1 < 0x81 || byte1 > 0x9F) && (byte1 < 0xE0 || byte1 > 0xEB)) {
return false; return false;
} }
} }
skipping to change at line 303 skipping to change at line 320
return version; return version;
} }
} }
throw new WriterException("Data too big"); throw new WriterException("Data too big");
} }
/** /**
* @return true if the number of input bits will fit in a code with the specif ied version and * @return true if the number of input bits will fit in a code with the specif ied version and
* error correction level. * error correction level.
*/ */
private static boolean willFit(int numInputBits, Version version, ErrorCorrect static boolean willFit(int numInputBits, Version version, ErrorCorrectionLevel
ionLevel ecLevel) { ecLevel) {
// In the following comments, we use numbers of Version 7-H. // In the following comments, we use numbers of Version 7-H.
// numBytes = 196 // numBytes = 196
int numBytes = version.getTotalCodewords(); int numBytes = version.getTotalCodewords();
// getNumECBytes = 130 // getNumECBytes = 130
Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel); Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
int numEcBytes = ecBlocks.getTotalECCodewords(); int numEcBytes = ecBlocks.getTotalECCodewords();
// getNumDataBytes = 196 - 130 = 66 // getNumDataBytes = 196 - 130 = 66
int numDataBytes = numBytes - numEcBytes; int numDataBytes = numBytes - numEcBytes;
int totalInputBytes = (numInputBits + 7) / 8; int totalInputBytes = (numInputBits + 7) / 8;
return numDataBytes >= totalInputBytes; return numDataBytes >= totalInputBytes;
} }
/** /**
* Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24). * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
*/ */
static void terminateBits(int numDataBytes, BitArray bits) throws WriterExcept ion { static void terminateBits(int numDataBytes, BitArray bits) throws WriterExcept ion {
int capacity = numDataBytes * 8; int capacity = numDataBytes * 8;
if (bits.getSize() > capacity) { if (bits.getSize() > capacity) {
throw new WriterException("data bits cannot fit in the QR Code" + bits.get Size() + " > " + throw new WriterException("data bits cannot fit in the QR Code" + bits.get Size() + " > " +
capacity); capacity);
} }
// Append Mode.TERMINATE if there is enough space (value is 0000)
for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) { for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
bits.appendBit(false); bits.appendBit(false);
} }
// Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details. // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
// If the last byte isn't 8-bit aligned, we'll add padding bits. // If the last byte isn't 8-bit aligned, we'll add padding bits.
int numBitsInLastByte = bits.getSize() & 0x07; int numBitsInLastByte = bits.getSize() & 0x07;
if (numBitsInLastByte > 0) { if (numBitsInLastByte > 0) {
for (int i = numBitsInLastByte; i < 8; i++) { for (int i = numBitsInLastByte; i < 8; i++) {
bits.appendBit(false); bits.appendBit(false);
} }
skipping to change at line 514 skipping to change at line 532
} }
bits.appendBits(numLetters, numBits); bits.appendBits(numLetters, numBits);
} }
/** /**
* Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits". * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
*/ */
static void appendBytes(String content, static void appendBytes(String content,
Mode mode, Mode mode,
BitArray bits, BitArray bits,
String encoding) throws WriterException { Charset encoding) throws WriterException {
switch (mode) { switch (mode) {
case NUMERIC: case NUMERIC:
appendNumericBytes(content, bits); appendNumericBytes(content, bits);
break; break;
case ALPHANUMERIC: case ALPHANUMERIC:
appendAlphanumericBytes(content, bits); appendAlphanumericBytes(content, bits);
break; break;
case BYTE: case BYTE:
append8BitBytes(content, bits, encoding); append8BitBytes(content, bits, encoding);
break; break;
skipping to change at line 581 skipping to change at line 599
bits.appendBits(code1 * 45 + code2, 11); bits.appendBits(code1 * 45 + code2, 11);
i += 2; i += 2;
} else { } else {
// Encode one alphanumeric letter in six bits. // Encode one alphanumeric letter in six bits.
bits.appendBits(code1, 6); bits.appendBits(code1, 6);
i++; i++;
} }
} }
} }
static void append8BitBytes(String content, BitArray bits, String encoding) static void append8BitBytes(String content, BitArray bits, Charset encoding) {
throws WriterException { byte[] bytes = content.getBytes(encoding);
byte[] bytes;
try {
bytes = content.getBytes(encoding);
} catch (UnsupportedEncodingException uee) {
throw new WriterException(uee);
}
for (byte b : bytes) { for (byte b : bytes) {
bits.appendBits(b, 8); bits.appendBits(b, 8);
} }
} }
static void appendKanjiBytes(String content, BitArray bits) throws WriterExcep tion { static void appendKanjiBytes(String content, BitArray bits) throws WriterExcep tion {
byte[] bytes; byte[] bytes = content.getBytes(StringUtils.SHIFT_JIS_CHARSET);
try {
bytes = content.getBytes("Shift_JIS");
} catch (UnsupportedEncodingException uee) {
throw new WriterException(uee);
}
if (bytes.length % 2 != 0) { if (bytes.length % 2 != 0) {
throw new WriterException("Kanji byte size not even"); throw new WriterException("Kanji byte size not even");
} }
int maxI = bytes.length - 1; // bytes.length must be even int maxI = bytes.length - 1; // bytes.length must be even
for (int i = 0; i < maxI; i += 2) { for (int i = 0; i < maxI; i += 2) {
int byte1 = bytes[i] & 0xFF; int byte1 = bytes[i] & 0xFF;
int byte2 = bytes[i + 1] & 0xFF; int byte2 = bytes[i + 1] & 0xFF;
int code = (byte1 << 8) | byte2; int code = (byte1 << 8) | byte2;
int subtracted = -1; int subtracted = -1;
if (code >= 0x8140 && code <= 0x9ffc) { if (code >= 0x8140 && code <= 0x9ffc) {
 End of changes. 21 change blocks. 
91 lines changed or deleted 101 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)