mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Add ability to specify a QR code version hint. (#637)
* Add ability to specify a QR code version hint. * Checkstyle.
This commit is contained in:
parent
f6a1e20e1b
commit
d60c0f14bd
|
@ -95,4 +95,10 @@ public enum EncodeHintType {
|
||||||
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
* (Type {@link Integer}, or {@link String} representation of the integer value).
|
||||||
*/
|
*/
|
||||||
AZTEC_LAYERS,
|
AZTEC_LAYERS,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the exact version of QR code to be encoded. An integer. If the data specified
|
||||||
|
* cannot fit within the required version, a WriterException will be thrown.
|
||||||
|
*/
|
||||||
|
QR_VERSION
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,21 +106,18 @@ public final class Encoder {
|
||||||
BitArray dataBits = new BitArray();
|
BitArray dataBits = new BitArray();
|
||||||
appendBytes(content, mode, dataBits, encoding);
|
appendBytes(content, mode, dataBits, encoding);
|
||||||
|
|
||||||
// Hard part: need to know version to know how many bits length takes. But need to know how many
|
Version version = null;
|
||||||
// bits it takes to know version. First we take a guess at version by assuming version will be
|
if (hints != null && hints.containsKey(EncodeHintType.QR_VERSION)) {
|
||||||
// the minimum, 1:
|
Version requestedVersion = Version.getVersionForNumber((Integer) hints.get(EncodeHintType.QR_VERSION));
|
||||||
|
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, requestedVersion);
|
||||||
int provisionalBitsNeeded = headerBits.getSize()
|
if (willFit(bitsNeeded, requestedVersion, ecLevel)) {
|
||||||
+ mode.getCharacterCountBits(Version.getVersionForNumber(1))
|
version = requestedVersion;
|
||||||
+ dataBits.getSize();
|
} else {
|
||||||
Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
|
throw new WriterException("Data too big for requested version");
|
||||||
|
}
|
||||||
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
|
} else {
|
||||||
|
version = recommendVersion(ecLevel, mode, headerBits, dataBits);
|
||||||
int bitsNeeded = headerBits.getSize()
|
}
|
||||||
+ mode.getCharacterCountBits(provisionalVersion)
|
|
||||||
+ dataBits.getSize();
|
|
||||||
Version version = chooseVersion(bitsNeeded, ecLevel);
|
|
||||||
|
|
||||||
BitArray headerAndDataBits = new BitArray();
|
BitArray headerAndDataBits = new BitArray();
|
||||||
headerAndDataBits.appendBitArray(headerBits);
|
headerAndDataBits.appendBitArray(headerBits);
|
||||||
|
@ -161,6 +158,36 @@ public final class Encoder {
|
||||||
return qrCode;
|
return qrCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decides the smallest version of QR code that will contain all of the provided data.
|
||||||
|
* @throws WriterException if the data cannot fit in any version
|
||||||
|
*/
|
||||||
|
private static Version recommendVersion(ErrorCorrectionLevel ecLevel,
|
||||||
|
Mode mode,
|
||||||
|
BitArray headerBits,
|
||||||
|
BitArray dataBits) throws WriterException {
|
||||||
|
// Hard part: need to know version to know how many bits length takes. But need to know how many
|
||||||
|
// bits it takes to know version. First we take a guess at version by assuming version will be
|
||||||
|
// the minimum, 1:
|
||||||
|
int provisionalBitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, Version.getVersionForNumber(1));
|
||||||
|
Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);
|
||||||
|
|
||||||
|
// Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
|
||||||
|
int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion);
|
||||||
|
Version version = chooseVersion(bitsNeeded, ecLevel);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int calculateBitsNeeded(Mode mode,
|
||||||
|
BitArray headerBits,
|
||||||
|
BitArray dataBits,
|
||||||
|
Version version) {
|
||||||
|
int bitsNeeded = headerBits.getSize()
|
||||||
|
+ mode.getCharacterCountBits(version)
|
||||||
|
+ dataBits.getSize();
|
||||||
|
return bitsNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the code point of the table used in alphanumeric mode or
|
* @return the code point of the table used in alphanumeric mode or
|
||||||
* -1 if there is no corresponding code in the table.
|
* -1 if there is no corresponding code in the table.
|
||||||
|
@ -246,9 +273,21 @@ public final class Encoder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {
|
private static Version chooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) throws WriterException {
|
||||||
// In the following comments, we use numbers of Version 7-H.
|
|
||||||
for (int versionNum = 1; versionNum <= 40; versionNum++) {
|
for (int versionNum = 1; versionNum <= 40; versionNum++) {
|
||||||
Version version = Version.getVersionForNumber(versionNum);
|
Version version = Version.getVersionForNumber(versionNum);
|
||||||
|
if (willFit(numInputBits, version, ecLevel)) {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new WriterException("Data too big");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the number of input bits will fit in a code with the specified version and
|
||||||
|
* error correction level.
|
||||||
|
*/
|
||||||
|
private static boolean willFit(int numInputBits, Version version, ErrorCorrectionLevel ecLevel) {
|
||||||
|
// 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
|
||||||
|
@ -257,11 +296,7 @@ public final class Encoder {
|
||||||
// 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;
|
||||||
if (numDataBytes >= totalInputBytes) {
|
return numDataBytes >= totalInputBytes;
|
||||||
return version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new WriterException("Data too big");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,11 +22,13 @@ import com.google.zxing.common.BitArray;
|
||||||
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 org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -126,6 +128,26 @@ public final class EncoderTestCase extends Assert {
|
||||||
">>\n";
|
">>\n";
|
||||||
assertEquals(expected, qrCode.toString());
|
assertEquals(expected, qrCode.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeWithVersion() throws WriterException {
|
||||||
|
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||||
|
hints.put(EncodeHintType.QR_VERSION, 7);
|
||||||
|
QRCode qrCode = Encoder.encode("ABCDEF", ErrorCorrectionLevel.H, hints);
|
||||||
|
assertTrue(qrCode.toString().contains(" version: 7\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeWithVersionTooSmall() throws WriterException {
|
||||||
|
Map<EncodeHintType, Object> hints = new HashMap<>();
|
||||||
|
hints.put(EncodeHintType.QR_VERSION, 3);
|
||||||
|
try {
|
||||||
|
Encoder.encode("THISMESSAGEISTOOLONGFORAQRCODEVERSION3", ErrorCorrectionLevel.H, hints);
|
||||||
|
fail();
|
||||||
|
} catch (WriterException e) {
|
||||||
|
assertEquals("Data too big for requested version", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleUTF8ECI() throws WriterException {
|
public void testSimpleUTF8ECI() throws WriterException {
|
||||||
|
|
Loading…
Reference in a new issue