parent
7d85d0346b
commit
479357cc3b
@ -0,0 +1,25 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class AlphanumericMode extends QrMode {
|
||||
protected AlphanumericMode(int mode, int... ccbits) {
|
||||
modeBits = mode;
|
||||
numBitsCharCount = ccbits;
|
||||
}
|
||||
|
||||
protected AlphanumericMode() {
|
||||
modeBits = 0x2;
|
||||
numBitsCharCount[0] = 9;
|
||||
numBitsCharCount[1] = 11;
|
||||
numBitsCharCount[2] = 13;
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre + 33;
|
||||
}
|
||||
|
||||
public QrSegment making(String str) {
|
||||
return QrSegment.makeAlphanumeric(str);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ByteMode extends QrMode {
|
||||
protected ByteMode(int mode, int... ccbits) {
|
||||
modeBits = mode;
|
||||
numBitsCharCount = ccbits;
|
||||
}
|
||||
|
||||
protected ByteMode() {
|
||||
modeBits = 0x4;
|
||||
numBitsCharCount[0] = 8;
|
||||
numBitsCharCount[1] = 16;
|
||||
numBitsCharCount[2] = 16;
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre + QrSegmentAdvanced.countUtf8Bytes(codePoint) * 8 * 6;
|
||||
}
|
||||
|
||||
public QrSegment making(String str) {
|
||||
return QrSegment.makeBytes(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
/**
|
||||
* The error correction level in a QR Code symbol.
|
||||
*/
|
||||
public class Ecc {
|
||||
/** The QR Code can tolerate about 7% erroneous codewords. */
|
||||
public static final Ecc LOW = new Ecc(1,
|
||||
new byte[] { -1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30,
|
||||
30, 26, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 },
|
||||
new byte[] { -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13,
|
||||
14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 24, 25 });
|
||||
/** The QR Code can tolerate about 15% erroneous codewords. */
|
||||
public static final Ecc MEDIUM = new Ecc(0,
|
||||
new byte[] { -1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28,
|
||||
28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28 },
|
||||
new byte[] { -1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23,
|
||||
25, 26, 28, 29, 31, 33, 35, 37, 38, 40, 43, 45, 47, 49 });
|
||||
/** The QR Code can tolerate about 25% erroneous codewords. */
|
||||
public static final Ecc QUARTILE = new Ecc(3,
|
||||
new byte[] { -1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30,
|
||||
30, 30, 28, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 },
|
||||
new byte[] { -1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29,
|
||||
34, 34, 35, 38, 40, 43, 45, 48, 51, 53, 56, 59, 62, 65, 68 });
|
||||
/** The QR Code can tolerate about 30% erroneous codewords. */
|
||||
public static final Ecc HIGH = new Ecc(2,
|
||||
new byte[] { -1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30,
|
||||
30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30 },
|
||||
new byte[] { -1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35,
|
||||
37, 40, 42, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81 });
|
||||
|
||||
private final int formatBits;
|
||||
private final byte[] blockLength;
|
||||
private final byte[] numberOfBlocks;
|
||||
|
||||
// For formatBits and blockLength, array index is Version: (note that index 0 is
|
||||
// for padding, and is set to an illegal value)
|
||||
private Ecc(int formatBits, byte[] blockLength, byte[] numberOfBlocks) {
|
||||
this.formatBits = formatBits;
|
||||
this.blockLength = blockLength;
|
||||
this.numberOfBlocks = numberOfBlocks;
|
||||
}
|
||||
|
||||
public int getFormatBits() {
|
||||
return formatBits;
|
||||
}
|
||||
|
||||
public byte getBlockLength(int version) {
|
||||
return blockLength[version];
|
||||
}
|
||||
|
||||
public byte getNumberOfBlock(int version) {
|
||||
return numberOfBlocks[version];
|
||||
}
|
||||
|
||||
// Must in ascending order of error protection
|
||||
// so that values() work properly
|
||||
public static Ecc[] values() {
|
||||
return new Ecc[] { LOW, MEDIUM, QUARTILE, HIGH };
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
public class EciMode extends QrMode {
|
||||
protected EciMode(int mode, int... ccbits) {
|
||||
modeBits = mode;
|
||||
numBitsCharCount = ccbits;
|
||||
}
|
||||
|
||||
protected EciMode() {
|
||||
modeBits = 0x7;
|
||||
numBitsCharCount[0] = 0;
|
||||
numBitsCharCount[1] = 0;
|
||||
numBitsCharCount[2] = 0;
|
||||
}
|
||||
|
||||
public QrSegment making(int prmt) {
|
||||
return QrSegment.makeEci(prmt);
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
public class KanjiMode extends QrMode {
|
||||
protected KanjiMode(int mode, int... ccbits) {
|
||||
modeBits = mode;
|
||||
numBitsCharCount = ccbits;
|
||||
}
|
||||
|
||||
protected KanjiMode() {
|
||||
modeBits = 0x8;
|
||||
numBitsCharCount[0] = 8;
|
||||
numBitsCharCount[1] = 10;
|
||||
numBitsCharCount[2] = 12;
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre + 78;
|
||||
}
|
||||
|
||||
public QrSegment making(String str) {
|
||||
return QrSegmentAdvanced.makeKanji(str);
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public class MakeAlphaNumericToSegment implements MakeSegment {
|
||||
|
||||
/**
|
||||
* Returns a segment representing the specified text string encoded in alphanumeric mode.
|
||||
* The characters allowed are: 0 to 9, A to Z (uppercase only), space,
|
||||
* dollar, percent, asterisk, plus, hyphen, period, slash, colon.
|
||||
* @param text the text (not {@code null}), with only certain characters allowed
|
||||
* @return a segment (not {@code null}) containing the text
|
||||
* @throws NullPointerException if the string is {@code null}
|
||||
* @throws IllegalArgumentException if the string contains non-encodable characters
|
||||
*/
|
||||
public QrSegment excute(String text) {
|
||||
Objects.requireNonNull(text);
|
||||
if (!QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches())
|
||||
throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode");
|
||||
|
||||
BitBuffer bitBuffer = new BitBuffer();
|
||||
changeAlphaNumericStringToSegment(text, bitBuffer);
|
||||
return new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length(), bitBuffer);
|
||||
}
|
||||
|
||||
public static void changeAlphaNumericStringToSegment(String text, BitBuffer bitBuffer) {
|
||||
int i;
|
||||
for (i = 0; i <= text.length() - 2; i += 2) { // Process groups of 2
|
||||
int temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;
|
||||
temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
|
||||
bitBuffer.appendBits(temp, 11);
|
||||
}
|
||||
if (i < text.length()) // 1 character remaining
|
||||
bitBuffer.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6);
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MakeBytesToSegment implements MakeSegment {
|
||||
|
||||
/**
|
||||
* Returns a segment representing the specified binary data
|
||||
* encoded in byte mode. All input byte arrays are acceptable.
|
||||
* <p>Any text string can be converted to UTF-8 bytes ({@code
|
||||
* s.getBytes(StandardCharsets.UTF_8)}) and encoded as a byte mode segment.</p>
|
||||
* @param data the binary data (not {@code null})
|
||||
* @return a segment (not {@code null}) containing the data
|
||||
* @throws NullPointerException if the array is {@code null}
|
||||
*/
|
||||
public QrSegment excute(String text) {
|
||||
byte[] data = text.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
Objects.requireNonNull(data);
|
||||
BitBuffer bitBuffer = new BitBuffer();
|
||||
for (byte bits : data)
|
||||
changeByteToSegment(bitBuffer, bits);
|
||||
return new QrSegment(QrSegment.Mode.BYTE, data.length, bitBuffer);
|
||||
}
|
||||
|
||||
public QrSegment excuteForBytedata(byte[] data) {
|
||||
Objects.requireNonNull(data);
|
||||
BitBuffer bitBuffer = new BitBuffer();
|
||||
for (byte bits : data)
|
||||
changeByteToSegment(bitBuffer, bits);
|
||||
return new QrSegment(QrSegment.Mode.BYTE, data.length, bitBuffer);
|
||||
}
|
||||
|
||||
public static void changeByteToSegment(BitBuffer bitBuffer, byte bits) {
|
||||
bitBuffer.appendBits(bits & 0xFF, 8);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import java.util.Objects;
|
||||
|
||||
public class MakeNumericToSegment implements MakeSegment {
|
||||
|
||||
/**
|
||||
* Returns a segment representing the specified string of decimal digits encoded in numeric mode.
|
||||
* @param digits the text (not {@code null}), with only digits from 0 to 9 allowed
|
||||
* @return a segment (not {@code null}) containing the text
|
||||
* @throws NullPointerException if the string is {@code null}
|
||||
* @throws IllegalArgumentException if the string contains non-digit characters
|
||||
*/
|
||||
public QrSegment excute(String digits) {
|
||||
Objects.requireNonNull(digits);
|
||||
if (containNonNumericCharaters(digits))
|
||||
throw new IllegalArgumentException("String contains non-numeric characters");
|
||||
|
||||
BitBuffer bitBuffer = new BitBuffer();
|
||||
changeNumericToSegment(digits, bitBuffer);
|
||||
return new QrSegment(QrSegment.Mode.NUMERIC, digits.length(), bitBuffer);
|
||||
}
|
||||
|
||||
public static void changeNumericToSegment(String digits, BitBuffer bitBuffer) {
|
||||
for (int i = 0; i < digits.length(); ) { // Consume up to 3 digits per iteration
|
||||
int n = Math.min(digits.length() - i, 3);
|
||||
bitBuffer.appendBits(Integer.parseInt(digits.substring(i, i + n)), n * 3 + 1);
|
||||
i += n;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean containNonNumericCharaters(String digits) {
|
||||
return !QrSegment.NUMERIC_REGEX.matcher(digits).matches();
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
|
||||
public interface MakeSegment {
|
||||
public QrSegment excute(String text);
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
public class MakeSegmentFactory {
|
||||
public static MakeSegment getMakeSegment(String text) {
|
||||
MakeSegment makeSegment = null;
|
||||
|
||||
if (text.equals("")); // Leave result empty
|
||||
else if (QrSegment.NUMERIC_REGEX.matcher(text).matches())
|
||||
makeSegment = new MakeNumericToSegment();
|
||||
else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches())
|
||||
makeSegment = new MakeAlphaNumericToSegment();
|
||||
else
|
||||
makeSegment = new MakeBytesToSegment();
|
||||
|
||||
return makeSegment;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
public class NumericMode extends QrMode {
|
||||
protected NumericMode(int mode, int... ccbits) {
|
||||
modeBits = mode;
|
||||
numBitsCharCount = ccbits;
|
||||
}
|
||||
|
||||
protected NumericMode() {
|
||||
modeBits = 0x1;
|
||||
numBitsCharCount[0] = 10;
|
||||
numBitsCharCount[1] = 12;
|
||||
numBitsCharCount[2] = 1;
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre + 20;
|
||||
}
|
||||
|
||||
public QrSegment making(String str) {
|
||||
return QrSegment.makeNumeric(str);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package io.nayuki.qrcodegen;
|
||||
|
||||
public abstract class QrMode {
|
||||
/*-- Fields --*/
|
||||
|
||||
// The mode indicator bits, which is a uint4 value (range 0 to 15).
|
||||
int modeBits;
|
||||
|
||||
// Number of character count bits for three different version ranges.
|
||||
protected int[] numBitsCharCount;
|
||||
|
||||
int headCost;
|
||||
/*-- Method --*/
|
||||
|
||||
// Returns the bit width of the character count field for a segment in this mode
|
||||
// in a QR Code at the given version number. The result is in the range [0, 16].
|
||||
int numCharCountBits(int ver) {
|
||||
assert QrCode.MIN_VERSION <= ver && ver <= QrCode.MAX_VERSION;
|
||||
return numBitsCharCount[(ver + 7) / 17];
|
||||
}
|
||||
|
||||
protected QrMode() {
|
||||
this.modeBits = 0;
|
||||
this.numBitsCharCount = null;
|
||||
}
|
||||
|
||||
public void get(QrMode md) {
|
||||
this.modeBits = md.modeBits;
|
||||
this.numBitsCharCount = md.numBitsCharCount;
|
||||
}
|
||||
|
||||
public int whichMode() {
|
||||
return modeBits;
|
||||
}
|
||||
|
||||
protected QrSegment making(String s) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getcost(int pre, int codePoint) {
|
||||
return pre;
|
||||
}
|
||||
}
|
Loading…
Reference in new issue