pull/90/head
wslbal 5 years ago
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);
}
}

@ -421,7 +421,7 @@ public final class QrCode {
// based on the given mask and this object's error correction level field.
private void drawFormatBits(int msk) {
// Calculate error correction code and pack bits
int data = errorCorrectionLevel.formatBits << 3 | msk; // errCorrLvl is uint2, mask is uint3
int data = errorCorrectionLevel.getFormatBits() << 3 | msk; // errCorrLvl is uint2, mask is uint3
int rem = data;
for (int i = 0; i < 10; i++)
rem = (rem << 1) ^ ((rem >>> 9) * 0x537);
@ -512,8 +512,8 @@ public final class QrCode {
throw new IllegalArgumentException();
// Calculate parameter numbers
int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version];
int blockEccLen = ECC_CODEWORDS_PER_BLOCK [errorCorrectionLevel.ordinal()][version];
int numBlocks = errorCorrectionLevel.getNumberOfBlock(version);
int blockEccLen = errorCorrectionLevel.getBlockLength(version);
int rawCodewords = getNumRawDataModules(version) / 8;
int numShortBlocks = numBlocks - rawCodewords % numBlocks;
int shortBlockLen = rawCodewords / numBlocks;
@ -831,8 +831,8 @@ public final class QrCode {
// This stateless pure function could be implemented as a (40*4)-cell lookup table.
static int getNumDataCodewords(int ver, Ecc ecl) {
return getNumRawDataModules(ver) / 8
- ECC_CODEWORDS_PER_BLOCK [ecl.ordinal()][ver]
* NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver];
- ecl.getBlockLength(ver)
* ecl.getNumberOfBlock(ver);
}
@ -889,47 +889,4 @@ public final class QrCode {
private static final int PENALTY_N3 = 40;
private static final int PENALTY_N4 = 10;
private static final byte[][] ECC_CODEWORDS_PER_BLOCK = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-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}, // Low
{-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}, // Medium
{-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}, // Quartile
{-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}, // High
};
private static final byte[][] NUM_ERROR_CORRECTION_BLOCKS = {
// Version: (note that index 0 is for padding, and is set to an illegal value)
//0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level
{-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}, // Low
{-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}, // Medium
{-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}, // Quartile
{-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}, // High
};
/*---- Public helper enumeration ----*/
/**
* The error correction level in a QR Code symbol.
*/
public enum Ecc {
// Must be declared in ascending order of error protection
// so that the implicit ordinal() and values() work properly
/** The QR Code can tolerate about 7% erroneous codewords. */ LOW(1),
/** The QR Code can tolerate about 15% erroneous codewords. */ MEDIUM(0),
/** The QR Code can tolerate about 25% erroneous codewords. */ QUARTILE(3),
/** The QR Code can tolerate about 30% erroneous codewords. */ HIGH(2);
// In the range 0 to 3 (unsigned 2-bit integer).
final int formatBits;
// Constructor.
private Ecc(int fb) {
formatBits = fb;
}
}
}

@ -53,15 +53,15 @@ public final class QrCodeGeneratorDemo {
// Creates a single QR Code, then writes it to a PNG file and an SVG file.
private static void doBasicDemo() throws IOException {
String text = "Hello, world!"; // User-supplied Unicode text
QrCode.Ecc errCorLvl = QrCode.Ecc.LOW; // Error correction level
Ecc errCorLvl = Ecc.LOW; // Error correction level
QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol
QrCode qrCode = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol
BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image
BufferedImage img = qrCode.toImage(10, 4); // Convert to bitmap image
File imgFile = new File("hello-world-QR.png"); // File path for output
ImageIO.write(img, "png", imgFile); // Write image to file
String svg = qr.toSvgString(4); // Convert to SVG XML code
String svg = qrCode.toSvgString(4); // Convert to SVG XML code
File svgFile = new File("hello-world-QR.svg"); // File path for output
Files.write(svgFile.toPath(), // Write image to file
svg.getBytes(StandardCharsets.UTF_8));
@ -70,97 +70,112 @@ public final class QrCodeGeneratorDemo {
// Creates a variety of QR Codes that exercise different features of the library, and writes each one to file.
private static void doVarietyDemo() throws IOException {
QrCode qr;
QrCode qrCode;
// Numeric mode encoding (3.33 bits per digit)
qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM);
writePng(qr.toImage(13, 1), "pi-digits-QR.png");
qrCode = QrCode.encodeText("314159265358979323846264338327950288419716939937510", Ecc.MEDIUM);
writePng(qrCode.toImage(13, 1), "pi-digits-QR.png");
// Alphanumeric mode encoding (5.5 bits per character)
qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH);
writePng(qr.toImage(10, 2), "alphanumeric-QR.png");
qrCode = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", Ecc.HIGH);
writePng(qrCode.toImage(10, 2), "alphanumeric-QR.png");
// Unicode text as UTF-8
qr = QrCode.encodeText("こんにちwa、世界 αβγδ", QrCode.Ecc.QUARTILE);
writePng(qr.toImage(10, 3), "unicode-QR.png");
qrCode = QrCode.encodeText("占쎄괭占쎄뎐占쎄쾽占쎄굶wa占쎄낌<EC8E84>닟占쎈르塋딉옙 <20>뀭汝뷸Ь<EBB7B8>걣", Ecc.QUARTILE);
writePng(qrCode.toImage(10, 3), "unicode-QR.png");
// Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland)
qr = QrCode.encodeText(
qrCode = QrCode.encodeText(
"Alice was beginning to get very tired of sitting by her sister on the bank, "
+ "and of having nothing to do: once or twice she had peeped into the book her sister was reading, "
+ "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice "
+ "'without pictures or conversations?' So she was considering in her own mind (as well as she could, "
+ "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a "
+ "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly "
+ "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH);
writePng(qr.toImage(6, 10), "alice-wonderland-QR.png");
+ "a White Rabbit with pink eyes ran close by her.", Ecc.HIGH);
writePng(qrCode.toImage(6, 10), "alice-wonderland-QR.png");
}
// Creates QR Codes with manually specified segments for better compactness.
private static void doSegmentDemo() throws IOException {
QrCode qr;
List<QrSegment> segs;
QrCode qrCode;
List<QrSegment> segments;
// Illustration "silver"
String silver0 = "THE SQUARE ROOT OF 2 IS 1.";
String silver1 = "41421356237309504880168872420969807856967187537694807317667973799";
qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW);
writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png");
segs = Arrays.asList(
qrCode = QrCode.encodeText(silver0 + silver1, Ecc.LOW);
writePng(qrCode.toImage(10, 3), "sqrt2-monolithic-QR.png");
segments = Arrays.asList(
QrSegment.makeAlphanumeric(silver0),
QrSegment.makeNumeric(silver1));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
writePng(qr.toImage(10, 3), "sqrt2-segmented-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.LOW);
writePng(qrCode.toImage(10, 3), "sqrt2-segmented-QR.png");
// Illustration "golden"
String golden0 = "Golden ratio φ = 1.";
String golden0 = "Golden ratio 占쏙옙 = 1.";
String golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374";
String golden2 = "......";
qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW);
writePng(qr.toImage(8, 5), "phi-monolithic-QR.png");
segs = Arrays.asList(
qrCode = QrCode.encodeText(golden0 + golden1 + golden2, Ecc.LOW);
writePng(qrCode.toImage(8, 5), "phi-monolithic-QR.png");
segments = Arrays.asList(
QrSegment.makeBytes(golden0.getBytes(StandardCharsets.UTF_8)),
QrSegment.makeNumeric(golden1),
QrSegment.makeAlphanumeric(golden2));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
writePng(qr.toImage(8, 5), "phi-segmented-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.LOW);
writePng(qrCode.toImage(8, 5), "phi-segmented-QR.png");
// Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters
String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?";
qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW);
writePng(qr.toImage(9, 4), "madoka-utf8-QR.png");
String madoka = "占쎈슞異몌쫲類앹빼阿잙뀍寃뀐옙寃묕옙嫄<EC9899>占쎌겳占쎄묻占쎄텤占쎄텠占쎈씞寃귨옙寃랃옙怨ο옙占쏙옙<EC8F99>꺂癒믪꼨占쏙옙影<EC9899>袁ы맀影<EBA780>蹂⑺맟占쏙옙鰲<EC9899><E9B0B2>뀭塋딉옙";
qrCode = QrCode.encodeText(madoka, Ecc.LOW);
writePng(qrCode.toImage(9, 4), "madoka-utf8-QR.png");
segments = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka));
qrCode = QrCode.encodeSegments(segments, Ecc.LOW);
writePng(qrCode.toImage(9, 4), "madoka-kanji-QR.png");
segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka));
qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW);
writePng(qr.toImage(9, 4), "madoka-kanji-QR.png");
}
// Creates QR Codes with the same size and contents but different mask patterns.
private static void doMaskDemo() throws IOException {
QrCode qr;
List<QrSegment> segs;
QrCode qrCode;
List<QrSegment> segments;
// Project Nayuki URL
segs = QrSegment.makeSegments("https://www.nayuki.io/");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask
writePng(qr.toImage(8, 6), "project-nayuki-automask-QR.png");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3
writePng(qr.toImage(8, 6), "project-nayuki-mask3-QR.png");
segments = QrSegment.makeSegments("https://www.nayuki.io/");
qrCode = QrCode.encodeSegments(segments, Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask
writePng(qrCode.toImage(8, 6), "project-nayuki-automask-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3
writePng(qrCode.toImage(8, 6), "project-nayuki-mask3-QR.png");
// Chinese text as UTF-8
segs = QrSegment.makeSegments("維基百科Wikipedia聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0
writePng(qr.toImage(10, 3), "unicode-mask0-QR.png");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1
writePng(qr.toImage(10, 3), "unicode-mask1-QR.png");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5
writePng(qr.toImage(10, 3), "unicode-mask5-QR.png");
qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7
writePng(qr.toImage(10, 3), "unicode-mask7-QR.png");
segments = QrSegment.makeSegments("力놂옙占쎌쓢占쎌뇥<EC8E8C>뇖臾뺥렩Wikipedia塋딅슜嫄앾옙寃켲/占쎈샆<EC8E88>뎚藥<EB8E9A>戮먮뒰i占쎈쫨i.占쏙옙/塋딅맚<EB9485>궦鼇앾옙占쎈뿨<EC8E88>닅占쎈뎨占쎈<E58DA0>諛ㅿ옙怨⑸<E680A8>띰옙堉<EC9899>歷뜯몼<EB9CAF>꽎鼇앸떱姨<EB96B1>亦껋쉮占쏙옙<EC8F99>돦力녠엽<EB85A0>윭占쎌뇥<EC8E8C>뇖臾덈<E887BE>뀐옙<EB8090>럱占쎈쐭俑앹뮂怡ワ옙鍮<EC9899>");
qrCode = QrCode.encodeSegments(segments, Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0
writePng(qrCode.toImage(10, 3), "unicode-mask0-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1
writePng(qrCode.toImage(10, 3), "unicode-mask1-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5
writePng(qrCode.toImage(10, 3), "unicode-mask5-QR.png");
qrCode = QrCode.encodeSegments(segments, Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7
writePng(qrCode.toImage(10, 3), "unicode-mask7-QR.png");
}

@ -57,11 +57,11 @@ public final class QrCodeGeneratorWorker {
boolean isAscii = true;
byte[] data = new byte[length];
for (int i = 0; i < data.length; i++) {
int b = input.nextInt();
if (b < 0 || b > 255)
int one_byte = input.nextInt();
if (one_byte < 0 || one_byte > 255)
throw new RuntimeException();
data[i] = (byte)b;
isAscii &= b < 128;
data[i] = (byte)one_byte;
isAscii &= one_byte < 128;
}
// Read encoding parameters
@ -75,14 +75,15 @@ public final class QrCodeGeneratorWorker {
throw new RuntimeException();
// Make segments for encoding
List<QrSegment> segs;
List<QrSegment> segments;
if (isAscii)
segs = QrSegment.makeSegments(new String(data, StandardCharsets.US_ASCII));
segments = QrSegment.makeSegments(new String(data, StandardCharsets.US_ASCII));
else
segs = Arrays.asList(QrSegment.makeBytes(data));
segments = Arrays.asList(QrSegment.makeBytes(data));
try { // Try to make QR Code symbol
QrCode qr = QrCode.encodeSegments(segs, QrCode.Ecc.values()[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0);
QrCode qr = QrCode.encodeSegments(segments, Ecc.values()[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0);
// Print grid of modules
System.out.println(qr.version);
for (int y = 0; y < qr.size; y++) {

@ -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;
}
}

@ -29,6 +29,8 @@ import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import QrSegment.Mode;
/**
* A segment of character/binary/control data in a QR Code symbol.
@ -46,7 +48,6 @@ import java.util.regex.Pattern;
public final class QrSegment {
/*---- Static factory functions (mid level) ----*/
/**
* Returns a segment representing the specified binary data
* encoded in byte mode. All input byte arrays are acceptable.
@ -57,11 +58,9 @@ public final class QrSegment {
* @throws NullPointerException if the array is {@code null}
*/
public static QrSegment makeBytes(byte[] data) {
Objects.requireNonNull(data);
BitBuffer bb = new BitBuffer();
for (byte b : data)
bb.appendBits(b & 0xFF, 8);
return new QrSegment(Mode.BYTE, data.length, bb);
MakeBytesToSegment makeBytesToSegment = new MakeBytesToSegment();
return makeBytesToSegment.excuteForBytedata(data);
}
@ -73,17 +72,9 @@ public final class QrSegment {
* @throws IllegalArgumentException if the string contains non-digit characters
*/
public static QrSegment makeNumeric(String digits) {
Objects.requireNonNull(digits);
if (!NUMERIC_REGEX.matcher(digits).matches())
throw new IllegalArgumentException("String contains non-numeric characters");
BitBuffer bb = new BitBuffer();
for (int i = 0; i < digits.length(); ) { // Consume up to 3 digits per iteration
int n = Math.min(digits.length() - i, 3);
bb.appendBits(Integer.parseInt(digits.substring(i, i + n)), n * 3 + 1);
i += n;
}
return new QrSegment(Mode.NUMERIC, digits.length(), bb);
MakeSegment makeSegment = new MakeNumericToSegment();
return makeSegment.excute(digits);
}
@ -97,23 +88,10 @@ public final class QrSegment {
* @throws IllegalArgumentException if the string contains non-encodable characters
*/
public static QrSegment makeAlphanumeric(String text) {
Objects.requireNonNull(text);
if (!ALPHANUMERIC_REGEX.matcher(text).matches())
throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode");
BitBuffer bb = new BitBuffer();
int i;
for (i = 0; i <= text.length() - 2; i += 2) { // Process groups of 2
int temp = ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45;
temp += ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1));
bb.appendBits(temp, 11);
}
if (i < text.length()) // 1 character remaining
bb.appendBits(ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6);
return new QrSegment(Mode.ALPHANUMERIC, text.length(), bb);
}
MakeSegment makeSegment = new MakeAlphaNumericToSegment();
return makeSegment.excute(text);
}
/**
* Returns a list of zero or more segments to represent the specified Unicode text string.
* The result may use various segment modes and switch modes to optimize the length of the bit stream.
@ -121,22 +99,18 @@ public final class QrSegment {
* @return a new mutable list (not {@code null}) of segments (not {@code null}) containing the text
* @throws NullPointerException if the text is {@code null}
*/
public static List<QrSegment> makeSegments(String text) {
Objects.requireNonNull(text);
// Select the most efficient segment encoding automatically
List<QrSegment> result = new ArrayList<>();
if (text.equals("")); // Leave result empty
else if (NUMERIC_REGEX.matcher(text).matches())
result.add(makeNumeric(text));
else if (ALPHANUMERIC_REGEX.matcher(text).matches())
result.add(makeAlphanumeric(text));
else
result.add(makeBytes(text.getBytes(StandardCharsets.UTF_8)));
return result;
}
List<QrSegment> segments = new ArrayList<>();
MakeSegment makeSegment = MakeSegmentFactory.getMakeSegment(text);
segments.add(makeSegment.excute(text));
return segments;
}
/**
* Returns a segment representing an Extended Channel Interpretation
* (ECI) designator with the specified assignment value.
@ -144,21 +118,21 @@ public final class QrSegment {
* @return a segment (not {@code null}) containing the data
* @throws IllegalArgumentException if the value is outside the range [0, 10<sup>6</sup>)
*/
public static QrSegment makeEci(int assignVal) {
BitBuffer bb = new BitBuffer();
if (assignVal < 0)
public static QrSegment makeEci(int assignValue) {
BitBuffer bitBuffer = new BitBuffer();
if (assignValue < 0)
throw new IllegalArgumentException("ECI assignment value out of range");
else if (assignVal < (1 << 7))
bb.appendBits(assignVal, 8);
else if (assignVal < (1 << 14)) {
bb.appendBits(2, 2);
bb.appendBits(assignVal, 14);
} else if (assignVal < 1_000_000) {
bb.appendBits(6, 3);
bb.appendBits(assignVal, 21);
else if (assignValue < (1 << 7))
bitBuffer.appendBits(assignValue, 8);
else if (assignValue < (1 << 14)) {
bitBuffer.appendBits(2, 2);
bitBuffer.appendBits(assignValue, 14);
} else if (assignValue < 1_000_000) {
bitBuffer.appendBits(6, 3);
bitBuffer.appendBits(assignValue, 21);
} else
throw new IllegalArgumentException("ECI assignment value out of range");
return new QrSegment(Mode.ECI, 0, bb);
return new QrSegment(Mode.ECI, 0, bitBuffer);
}
@ -189,16 +163,15 @@ public final class QrSegment {
* @throws NullPointerException if the mode or data is {@code null}
* @throws IllegalArgumentException if the character count is negative
*/
public QrSegment(Mode md, int numCh, BitBuffer data) {
mode = Objects.requireNonNull(md);
public QrSegment(Mode _mode, int _numberOfCharacters, BitBuffer data) {
mode = Objects.requireNonNull(_mode);
Objects.requireNonNull(data);
if (numCh < 0)
if (_numberOfCharacters < 0)
throw new IllegalArgumentException("Invalid value");
numChars = numCh;
numberOfCharacters = _numberOfCharacters;
this.data = data.clone(); // Make defensive copy
}
/*---- Methods ----*/
/**
@ -213,22 +186,21 @@ public final class QrSegment {
// Calculates the number of bits needed to encode the given segments at the given version.
// Returns a non-negative number if successful. Otherwise returns -1 if a segment has too
// many characters to fit its length field, or the total bits exceeds Integer.MAX_VALUE.
static int getTotalBits(List<QrSegment> segs, int version) {
Objects.requireNonNull(segs);
long result = 0;
for (QrSegment seg : segs) {
Objects.requireNonNull(seg);
int ccbits = seg.mode.numCharCountBits(version);
if (seg.numChars >= (1 << ccbits))
static int getTotalBits(List<QrSegment> segments, int version) {
Objects.requireNonNull(segments);
long TotalBits = 0;
for (QrSegment segment : segments) {
Objects.requireNonNull(segment);
int characterCountBits = segment.mode.numCharCountBits(version);
if (segment.numberOfCharacters >= (1 << characterCountBits))
return -1; // The segment's length doesn't fit the field's bit width
result += 4L + ccbits + seg.data.bitLength();
if (result > Integer.MAX_VALUE)
TotalBits += 4L + characterCountBits + segment.data.bitLength();
if (TotalBits > Integer.MAX_VALUE)
return -1; // The sum will overflow an int type
}
return (int)result;
return (int)TotalBits;
}
/*---- Constants ----*/
/** Describes precisely all strings that are encodable in numeric mode. To test whether a

@ -68,7 +68,7 @@ public final class QrSegmentAdvanced {
* @throws IllegalArgumentException if 1 &#x2264; minVersion &#x2264; maxVersion &#x2264; 40 is violated
* @throws DataTooLongException if the text fails to fit in the maxVersion QR Code at the ECL
*/
public static List<QrSegment> makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) {
public static List<QrSegment> makeSegmentsOptimally(String text, Ecc ecl, int minVersion, int maxVersion) {
// Check arguments
Objects.requireNonNull(text);
Objects.requireNonNull(ecl);

Loading…
Cancel
Save