From 9a7a01c9a6b6fb76744eacb2e6fab11eccedfabd Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Sun, 24 May 2020 16:24:44 +0900 Subject: [PATCH 01/28] test commit --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ea0196c..58c4534 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -198,8 +198,6 @@ public final class QrCode { return new QrCode(version, ecl, dataCodewords, mask); } - - /*---- Instance fields ----*/ // Public immutable scalar parameters: From ce24efed3ccacc18570a16b5a1ac728c116a3fef Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Mon, 25 May 2020 08:40:17 +0900 Subject: [PATCH 02/28] 1. Extract Method 2. QrSegmenetAdvanced 3. THis class's 'if condition' is so complex, changed more intelligiblement. --- .../io/nayuki/qrcodegen/QrSegmentAdvanced.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 1efe7b5..3230d9b 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -64,14 +64,14 @@ public final class QrSegmentAdvanced { // Check arguments Objects.requireNonNull(text); Objects.requireNonNull(ecl); - if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) + if (not_Valid_Version(minVersion, maxVersion)) throw new IllegalArgumentException("Invalid value"); // Iterate through version numbers, and make tentative segments List segs = null; int[] codePoints = toCodePoints(text); for (int version = minVersion; ; version++) { - if (version == minVersion || version == 10 || version == 27) + if (is_valid_version(minVersion, version)) segs = makeSegmentsOptimally(codePoints, version); assert segs != null; @@ -88,6 +88,16 @@ public final class QrSegmentAdvanced { } } } + + + private static boolean is_valid_version(int minVersion, int version) { + return version == minVersion || version == 10 || version == 27; + } + + + private static boolean not_Valid_Version(int minVersion, int maxVersion) { + return !(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION); + } // Returns a new list of segments that is optimal for the given text at the given version number. @@ -277,8 +287,7 @@ public final class QrSegmentAdvanced { */ public static boolean isEncodableAsKanji(String text) { Objects.requireNonNull(text); - return text.chars().allMatch( - c -> isKanji((char)c)); + return text.chars().allMatch(c -> isKanji((char)c)); } From 92b35766fbfe960a5fde58f731817ebb475b75fb Mon Sep 17 00:00:00 2001 From: wslbal Date: Mon, 25 May 2020 10:29:57 +0900 Subject: [PATCH 03/28] =?UTF-8?q?0525=20=EB=A6=AC=ED=8E=99=ED=86=A0?= =?UTF-8?q?=EB=A7=81=20=EC=A7=84=ED=96=89=ED=95=A0=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=EC=97=90=20=EC=A3=BC=EC=84=9D=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ea0196c..c12da77 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -542,7 +542,7 @@ public final class QrCode { // The function modules must be marked and the codeword bits must be drawn // before masking. Due to the arithmetic of XOR, calling applyMask() with // the same mask value a second time will undo the mask. A final well-formed - // QR Code needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. switch-statement refactoring private void applyMask(int msk) { if (msk < 0 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); @@ -589,9 +589,9 @@ public final class QrCode { return msk; // The caller shall assign this value to the final-declared field } - + // Calculates and returns the penalty score based on state of this QR Code's current modules. - // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. duplication code extract private int getPenaltyScore() { int result = 0; @@ -600,6 +600,7 @@ public final class QrCode { boolean runColor = false; int runX = 0; int[] runHistory = new int[7]; + result += havingSameColor(); for (int x = 0; x < size; x++) { if (modules[y][x] == runColor) { runX++; @@ -640,7 +641,7 @@ public final class QrCode { result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; } - // 2*2 blocks of modules having same color + // 2*2 blocks of modules having same color. extract method to reduce method size for (int y = 0; y < size - 1; y++) { for (int x = 0; x < size - 1; x++) { boolean color = modules[y][x]; @@ -694,7 +695,7 @@ public final class QrCode { // Returns the number of data bits that can be stored in a QR Code of the given version number, after // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. - // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. replace integer to understandable word private static int getNumRawDataModules(int ver) { if (ver < MIN_VERSION || ver > MAX_VERSION) throw new IllegalArgumentException("Version number out of range"); From 750c8ba7aedb55c1a7b1ed6439dac731ac6a5240 Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Mon, 25 May 2020 14:34:59 +0900 Subject: [PATCH 04/28] 1. Extract Method, introduce explain value 2. QrSegmenetAdvanced.java 3. THis class had many function. So, declare more method. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 3230d9b..6480897 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -133,22 +133,22 @@ public final class QrSegmentAdvanced { // Calculate costs using dynamic programming for (int i = 0; i < codePoints.length; i++) { - int c = codePoints[i]; + int cPoint = codePoints[i]; int[] curCosts = new int[numModes]; { // Always extend a byte mode segment - curCosts[0] = prevCosts[0] + countUtf8Bytes(c) * 8 * 6; + curCosts[0] = prevCosts[0] + countUtf8Bytes(cPoint) * 8 * 6; charModes[i][0] = modeTypes[0]; } // Extend a segment if possible - if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1) { // Is alphanumeric + if (is_alphanumeric(cPoint)) { // Is alphanumeric curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char charModes[i][1] = modeTypes[1]; } - if ('0' <= c && c <= '9') { // Is numeric + if (is_numeric(cPoint)) { // Is numeric curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit charModes[i][2] = modeTypes[2]; } - if (isKanji(c)) { + if (isKanji(cPoint)) { curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char charModes[i][3] = modeTypes[3]; } @@ -157,7 +157,8 @@ public final class QrSegmentAdvanced { for (int j = 0; j < numModes; j++) { // To mode for (int k = 0; k < numModes; k++) { // From mode int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]; - if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) { + boolean is_notNull_or_lower = charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j]); + if (is_notNull_or_lower) { curCosts[j] = newCost; charModes[i][j] = modeTypes[k]; } @@ -189,6 +190,16 @@ public final class QrSegmentAdvanced { } return result; } + + + private static boolean is_numeric(int c) { + return '0' <= c && c <= '9'; + } + + + private static boolean is_alphanumeric(int c) { + return QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1; + } // Returns a new list of segments based on the given text and modes, such that From 8e33477b7447068dcda23c8d552bc132a6d97103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B2=BD=EC=A7=84?= <김경진@DESKTOP-6URC5U8> Date: Wed, 27 May 2020 09:59:35 +0900 Subject: [PATCH 05/28] Refactoring Rename Constructor of QrCode class Not using abbrebiations --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ea0196c..fe05d48 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -238,32 +238,32 @@ public final class QrCode { * error correction level, data codeword bytes, and mask number. *

This is a low-level API that most users should not use directly. A mid-level * API is the {@link #encodeSegments(List,Ecc,int,int,int,boolean)} function.

- * @param ver the version number to use, which must be in the range 1 to 40 (inclusive) - * @param ecl the error correction level to use + * @param version the version number to use, which must be in the range 1 to 40 (inclusive) + * @param errorCorrectionLevel the error correction level to use * @param dataCodewords the bytes representing segments to encode (without ECC) - * @param msk the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice + * @param mask the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice * @throws NullPointerException if the byte array or error correction level is {@code null} * @throws IllegalArgumentException if the version or mask value is out of range, * or if the data is the wrong length for the specified version and error correction level */ - public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int msk) { + public QrCode(int version, Ecc errorCorrectionLevel, byte[] dataCodewords, int mask) { // Check arguments and initialize fields - if (ver < MIN_VERSION || ver > MAX_VERSION) + if (version < MIN_VERSION || version > MAX_VERSION) throw new IllegalArgumentException("Version value out of range"); - if (msk < -1 || msk > 7) + if (mask < -1 || mask > 7) throw new IllegalArgumentException("Mask value out of range"); - version = ver; - size = ver * 4 + 17; - errorCorrectionLevel = Objects.requireNonNull(ecl); + this.version = version; + this.size = version * 4 + 17; + this.errorCorrectionLevel = Objects.requireNonNull(errorCorrectionLevel); Objects.requireNonNull(dataCodewords); - modules = new boolean[size][size]; // Initially all white - isFunction = new boolean[size][size]; + this.modules = new boolean[size][size]; // Initially all white + this.isFunction = new boolean[size][size]; // Compute ECC, draw modules, do masking drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this.mask = handleConstructorMasking(msk); + this.mask = handleConstructorMasking(mask); isFunction = null; } @@ -543,7 +543,7 @@ public final class QrCode { // before masking. Due to the arithmetic of XOR, calling applyMask() with // the same mask value a second time will undo the mask. A final well-formed // QR Code needs exactly one (not zero, two, etc.) mask applied. - private void applyMask(int msk) { + private void applyMask(int msk) { if (msk < 0 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); for (int y = 0; y < size; y++) { @@ -592,7 +592,7 @@ public final class QrCode { // Calculates and returns the penalty score based on state of this QR Code's current modules. // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. - private int getPenaltyScore() { + private int getPenaltyScore() { int result = 0; // Adjacent modules in row having same color, and finder-like patterns @@ -695,7 +695,7 @@ public final class QrCode { // Returns the number of data bits that can be stored in a QR Code of the given version number, after // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. - private static int getNumRawDataModules(int ver) { + private static int getNumRawDataModules(int ver) { if (ver < MIN_VERSION || ver > MAX_VERSION) throw new IllegalArgumentException("Version number out of range"); From 15f548220b098a799ef97b3c74a27233147b8963 Mon Sep 17 00:00:00 2001 From: wslbal Date: Wed, 27 May 2020 10:18:04 +0900 Subject: [PATCH 06/28] =?UTF-8?q?0527=20extract=20method=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20/=20switch=20=EB=AC=B8=EC=9E=A5=20Command=20pattern?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=ED=95=A0=20=EC=98=88=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 432 +++++++++--------- 1 file changed, 220 insertions(+), 212 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index c12da77..fbc50d9 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -51,9 +51,9 @@ import java.util.Objects; * @see QrSegment */ public final class QrCode { - + /*---- Static factory functions (high level) ----*/ - + /** * Returns a QR Code representing the specified Unicode text string at the specified error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer @@ -73,8 +73,8 @@ public final class QrCode { List segs = QrSegment.makeSegments(text); return encodeSegments(segs, ecl); } - - + + /** * Returns a QR Code representing the specified binary data at the specified error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of @@ -93,10 +93,10 @@ public final class QrCode { QrSegment seg = QrSegment.makeBytes(data); return encodeSegments(Arrays.asList(seg), ecl); } - - + + /*---- Static factory functions (mid level) ----*/ - + /** * Returns a QR Code representing the specified segments at the specified error correction * level. The smallest possible QR Code version is automatically chosen for the output. The ECC level @@ -115,8 +115,8 @@ public final class QrCode { public static QrCode encodeSegments(List segs, Ecc ecl) { return encodeSegments(segs, ecl, MIN_VERSION, MAX_VERSION, -1, true); } - - + + /** * Returns a QR Code representing the specified segments with the specified encoding parameters. * The smallest possible QR Code version within the specified range is automatically @@ -146,7 +146,7 @@ public final class QrCode { Objects.requireNonNull(ecl); if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) throw new IllegalArgumentException("Invalid value"); - + // Find the minimal version number to use int version, dataUsedBits; for (version = minVersion; ; version++) { @@ -162,13 +162,13 @@ public final class QrCode { } } assert dataUsedBits != -1; - + // Increase the error correction level while the data still fits in the current version number for (Ecc newEcl : Ecc.values()) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } - + // Concatenate all segments to create the data bit string BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { @@ -177,62 +177,62 @@ public final class QrCode { bb.appendData(seg.data); } assert bb.bitLength() == dataUsedBits; - + // Add terminator and pad up to a byte if applicable int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; assert bb.bitLength() <= dataCapacityBits; bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); assert bb.bitLength() % 8 == 0; - + // Pad with alternating bytes until data capacity is reached for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - + // Pack bits into bytes in big endian byte[] dataCodewords = new byte[bb.bitLength() / 8]; for (int i = 0; i < bb.bitLength(); i++) dataCodewords[i >>> 3] |= bb.getBit(i) << (7 - (i & 7)); - + // Create the QR Code object return new QrCode(version, ecl, dataCodewords, mask); } - - - + + + /*---- Instance fields ----*/ - + // Public immutable scalar parameters: - + /** The version number of this QR Code, which is between 1 and 40 (inclusive). * This determines the size of this barcode. */ public final int version; - + /** The width and height of this QR Code, measured in modules, between * 21 and 177 (inclusive). This is equal to version × 4 + 17. */ public final int size; - + /** The error correction level used in this QR Code, which is not {@code null}. */ public final Ecc errorCorrectionLevel; - + /** The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). *

Even if a QR Code is created with automatic masking requested (mask = * −1), the resulting object still has a mask value between 0 and 7. */ public final int mask; - + // Private grids of modules/pixels, with dimensions of size*size: - + // The modules of this QR Code (false = white, true = black). // Immutable after constructor finishes. Accessed through getModule(). private boolean[][] modules; - + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. private boolean[][] isFunction; - - - + + + /*---- Constructor (low level) ----*/ - + /** * Constructs a QR Code with the specified version number, * error correction level, data codeword bytes, and mask number. @@ -258,7 +258,7 @@ public final class QrCode { Objects.requireNonNull(dataCodewords); modules = new boolean[size][size]; // Initially all white isFunction = new boolean[size][size]; - + // Compute ECC, draw modules, do masking drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); @@ -266,11 +266,11 @@ public final class QrCode { this.mask = handleConstructorMasking(msk); isFunction = null; } - - - + + + /*---- Public instance methods ----*/ - + /** * Returns the color of the module (pixel) at the specified coordinates, which is {@code false} * for white or {@code true} for black. The top left corner has the coordinates (x=0, y=0). @@ -283,8 +283,8 @@ public final class QrCode { public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; } - - + + /** * Returns a raster image depicting this QR Code, with the specified module scale and border modules. *

For example, toImage(scale=10, border=4) means to pad the QR Code with 4 white @@ -301,7 +301,7 @@ public final class QrCode { throw new IllegalArgumentException("Value out of range"); if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale) throw new IllegalArgumentException("Scale or border too large"); - + BufferedImage result = new BufferedImage((size + border * 2) * scale, (size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); for (int y = 0; y < result.getHeight(); y++) { for (int x = 0; x < result.getWidth(); x++) { @@ -311,8 +311,8 @@ public final class QrCode { } return result; } - - + + /** * Returns a string of SVG code for an image depicting this QR Code, with the specified number * of border modules. The string always uses Unix newlines (\n), regardless of the platform. @@ -325,12 +325,12 @@ public final class QrCode { throw new IllegalArgumentException("Border must be non-negative"); long brd = border; StringBuilder sb = new StringBuilder() - .append("\n") - .append("\n") - .append(String.format("\n", - size + brd * 2)) - .append("\t\n") - .append("\t\n") + .append("\n") + .append(String.format("\n", + size + brd * 2)) + .append("\t\n") + .append("\t\n") - .append("\n") - .toString(); + .append("\" fill=\"#000000\"/>\n") + .append("\n") + .toString(); } - - - + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ - + // Reads this object's version field, and draws and marks all function modules. private void drawFunctionPatterns() { // Draw horizontal and vertical timing patterns @@ -357,12 +357,12 @@ public final class QrCode { setFunctionModule(6, i, i % 2 == 0); setFunctionModule(i, 6, i % 2 == 0); } - + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) drawFinderPattern(3, 3); drawFinderPattern(size - 4, 3); drawFinderPattern(3, size - 4); - + // Draw numerous alignment patterns int[] alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.length; @@ -373,13 +373,13 @@ public final class QrCode { drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } - + // Draw configuration data drawFormatBits(0); // Dummy mask value; overwritten later in the constructor drawVersion(); } - - + + // Draws two copies of the format bits (with its own error correction code) // based on the given mask and this object's error correction level field. private void drawFormatBits(int msk) { @@ -390,7 +390,7 @@ public final class QrCode { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); int bits = (data << 10 | rem) ^ 0x5412; // uint15 assert bits >>> 15 == 0; - + // Draw first copy for (int i = 0; i <= 5; i++) setFunctionModule(8, i, getBit(bits, i)); @@ -399,7 +399,7 @@ public final class QrCode { setFunctionModule(7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) setFunctionModule(14 - i, 8, getBit(bits, i)); - + // Draw second copy for (int i = 0; i < 8; i++) setFunctionModule(size - 1 - i, 8, getBit(bits, i)); @@ -407,21 +407,21 @@ public final class QrCode { setFunctionModule(8, size - 15 + i, getBit(bits, i)); setFunctionModule(8, size - 8, true); // Always black } - - + + // Draws two copies of the version bits (with its own error correction code), // based on this object's version field, iff 7 <= version <= 40. private void drawVersion() { if (version < 7) return; - + // Calculate error correction code and pack bits int rem = version; // version is uint6, in the range [7, 40] for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); int bits = version << 12 | rem; // uint18 assert bits >>> 18 == 0; - + // Draw two copies for (int i = 0; i < 18; i++) { boolean bit = getBit(bits, i); @@ -431,8 +431,8 @@ public final class QrCode { setFunctionModule(b, a, bit); } } - - + + // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. private void drawFinderPattern(int x, int y) { @@ -445,8 +445,8 @@ public final class QrCode { } } } - - + + // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. private void drawAlignmentPattern(int x, int y) { @@ -455,32 +455,32 @@ public final class QrCode { setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1); } } - - + + // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. private void setFunctionModule(int x, int y, boolean isBlack) { modules[y][x] = isBlack; isFunction[y][x] = true; } - - + + /*---- Private helper methods for constructor: Codewords and masking ----*/ - + // Returns a new byte string representing the given data with the appropriate error correction // codewords appended to it, based on this object's version and error correction level. private byte[] addEccAndInterleave(byte[] data) { Objects.requireNonNull(data); if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) 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 rawCodewords = getNumRawDataModules(version) / 8; int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockLen = rawCodewords / numBlocks; - + // Split data into blocks and append ECC to each block byte[][] blocks = new byte[numBlocks][]; byte[] rsDiv = reedSolomonComputeDivisor(blockEccLen); @@ -492,7 +492,7 @@ public final class QrCode { System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); blocks[i] = block; } - + // Interleave (not concatenate) the bytes from every block into a single sequence byte[] result = new byte[rawCodewords]; for (int i = 0, k = 0; i < blocks[0].length; i++) { @@ -506,15 +506,15 @@ public final class QrCode { } return result; } - - + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire // data area of this QR Code. Function modules need to be marked off before this is called. private void drawCodewords(byte[] data) { Objects.requireNonNull(data); if (data.length != getNumRawDataModules(version) / 8) throw new IllegalArgumentException(); - + int i = 0; // Bit index into the data // Do the funny zigzag scan for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair @@ -536,13 +536,13 @@ public final class QrCode { } assert i == data.length * 8; } - - + + // XORs the codeword modules in this QR Code with the given mask pattern. // The function modules must be marked and the codeword bits must be drawn // before masking. Due to the arithmetic of XOR, calling applyMask() with // the same mask value a second time will undo the mask. A final well-formed - // QR Code needs exactly one (not zero, two, etc.) mask applied. switch-statement refactoring + // QR Code needs exactly one (not zero, two, etc.) mask applied. Command pattern private void applyMask(int msk) { if (msk < 0 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); @@ -550,38 +550,28 @@ public final class QrCode { for (int x = 0; x < size; x++) { boolean invert; switch (msk) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: throw new AssertionError(); + case 0: invert = (x + y) % 2 == 0; break; + case 1: invert = y % 2 == 0; break; + case 2: invert = x % 3 == 0; break; + case 3: invert = (x + y) % 3 == 0; break; + case 4: invert = (x / 3 + y / 2) % 2 == 0; break; + case 5: invert = x * y % 2 + x * y % 3 == 0; break; + case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; + case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; + default: throw new AssertionError(); } modules[y][x] ^= invert & !isFunction[y][x]; } } } - - + + // A messy helper function for the constructor. This QR Code must be in an unmasked state when this // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. // This method applies and returns the actual mask chosen, from 0 to 7. private int handleConstructorMasking(int msk) { if (msk == -1) { // Automatically choose best mask - int minPenalty = Integer.MAX_VALUE; - for (int i = 0; i < 8; i++) { - applyMask(i); - drawFormatBits(i); - int penalty = getPenaltyScore(); - if (penalty < minPenalty) { - msk = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } + msk = chooseBestMask(msk); } assert 0 <= msk && msk <= 7; applyMask(msk); // Apply the final choice of mask @@ -589,32 +579,67 @@ public final class QrCode { return msk; // The caller shall assign this value to the final-declared field } - + private int chooseBestMask(int msk) { + int minPenalty = Integer.MAX_VALUE; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + int penalty = getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + return msk; + } + + private int havingSameColor(int run, boolean runColor, int[] runHistory, int result, int y, int x) { + if (modules[y][x] == runColor) { + run++; + if (run == 5) + result += PENALTY_N1; + else if (run > 5) + result++; + } else { + finderPenaltyAddHistory(run, runHistory); + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = modules[y][x]; + run = 1; + } + + return result; + } + + private int twobytwoHavingSameColor(boolean[][] modules) { + int result = 0; + // 2*2 blocks of modules having same color. + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + boolean color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) + result += PENALTY_N2; + } + } + return result; + } + // Calculates and returns the penalty score based on state of this QR Code's current modules. - // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. duplication code extract + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. private int getPenaltyScore() { - int result = 0; + int result = 0; + // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < size; y++) { boolean runColor = false; int runX = 0; int[] runHistory = new int[7]; - result += havingSameColor(); for (int x = 0; x < size; x++) { - if (modules[y][x] == runColor) { - runX++; - if (runX == 5) - result += PENALTY_N1; - else if (runX > 5) - result++; - } else { - finderPenaltyAddHistory(runX, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = modules[y][x]; - runX = 1; - } + result += havingSameColor(runX, runColor, runHistory, result, y, x); } result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; } @@ -624,34 +649,14 @@ public final class QrCode { int runY = 0; int[] runHistory = new int[7]; for (int y = 0; y < size; y++) { - if (modules[y][x] == runColor) { - runY++; - if (runY == 5) - result += PENALTY_N1; - else if (runY > 5) - result++; - } else { - finderPenaltyAddHistory(runY, runHistory); - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; - runColor = modules[y][x]; - runY = 1; - } + result += havingSameColor(runY, runColor, runHistory, result, y, x); } result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; } - - // 2*2 blocks of modules having same color. extract method to reduce method size - for (int y = 0; y < size - 1; y++) { - for (int x = 0; x < size - 1; x++) { - boolean color = modules[y][x]; - if ( color == modules[y][x + 1] && - color == modules[y + 1][x] && - color == modules[y + 1][x + 1]) - result += PENALTY_N2; - } - } - + + result += twobytwoHavingSameColor(modules); + + // Balance of black and white modules int black = 0; for (boolean[] row : modules) { @@ -666,11 +671,9 @@ public final class QrCode { result += k * PENALTY_N4; return result; } - - - + /*---- Private helper functions ----*/ - + // Returns an ascending list of positions of alignment patterns for this version number. // Each position is in the range [0,177), and are used on both the x and y axes. // This could be implemented as lookup table of 40 variable-length lists of unsigned bytes. @@ -691,15 +694,22 @@ public final class QrCode { return result; } } - - + + // Returns the number of data bits that can be stored in a QR Code of the given version number, after // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. - // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. replace integer to understandable word + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. private static int getNumRawDataModules(int ver) { if (ver < MIN_VERSION || ver > MAX_VERSION) throw new IllegalArgumentException("Version number out of range"); - + + int result = calculateNumOfModules(ver); + + assert 208 <= result && result <= 29648; + return result; + } + + private static int calculateNumOfModules(int ver) { int size = ver * 4 + 17; int result = size * size; // Number of modules in the whole QR Code square result -= 8 * 8 * 3; // Subtract the three finders with separators @@ -714,11 +724,9 @@ public final class QrCode { if (ver >= 7) result -= 6 * 3 * 2; // Subtract version information } - assert 208 <= result && result <= 29648; return result; } - - + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be // implemented as a lookup table over all possible parameter values, instead of as an algorithm. private static byte[] reedSolomonComputeDivisor(int degree) { @@ -728,7 +736,7 @@ public final class QrCode { // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. byte[] result = new byte[degree]; result[degree - 1] = 1; // Start off with the monomial x^0 - + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), // and drop the highest monomial term which is always 1x^degree. // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). @@ -744,8 +752,8 @@ public final class QrCode { } return result; } - - + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. private static byte[] reedSolomonComputeRemainder(byte[] data, byte[] divisor) { Objects.requireNonNull(data); @@ -760,33 +768,33 @@ public final class QrCode { } return result; } - - + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result // are unsigned 8-bit integers. This could be implemented as a lookup table of 256*256 entries of uint8. private static int reedSolomonMultiply(int x, int y) { assert x >> 8 == 0 && y >> 8 == 0; - // Russian peasant multiplication - int z = 0; - for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >>> 7) * 0x11D); - z ^= ((y >>> i) & 1) * x; - } - assert z >>> 8 == 0; - return z; + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + assert z >>> 8 == 0; + return z; } - - + + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any // QR Code of the given version number and error correction level, with remainder bits discarded. // 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]; + - ECC_CODEWORDS_PER_BLOCK [ecl.ordinal()][ver] + * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; } - - + + // Can only be called immediately after a white run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private int finderPenaltyCountPatterns(int[] runHistory) { @@ -794,10 +802,10 @@ public final class QrCode { assert n <= size * 3; boolean core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) - + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); } - - + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). private int finderPenaltyTerminateAndCount(boolean currentRunColor, int currentRunLength, int[] runHistory) { if (currentRunColor) { // Terminate black run @@ -808,8 +816,8 @@ public final class QrCode { finderPenaltyAddHistory(currentRunLength, runHistory); return finderPenaltyCountPatterns(runHistory); } - - + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). private void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { if (runHistory[0] == 0) @@ -817,52 +825,52 @@ public final class QrCode { System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); runHistory[0] = currentRunLength; } - - + + // Returns true iff the i'th bit of x is set to 1. static boolean getBit(int x, int i) { return ((x >>> i) & 1) != 0; } - - + + /*---- Constants and tables ----*/ - + /** The minimum version number (1) supported in the QR Code Model 2 standard. */ public static final int MIN_VERSION = 1; - + /** The maximum version number (40) supported in the QR Code Model 2 standard. */ public static final int MAX_VERSION = 40; - - + + // For use in getPenaltyScore(), when evaluating which mask is best. private static final int PENALTY_N1 = 3; private static final int PENALTY_N2 = 3; 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 + // 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 + // 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. */ @@ -873,14 +881,14 @@ public final class QrCode { /** 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; } } - + } From c8c3b46c75fa775672fb2f746349f1cbee66b4e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B2=BD=EC=A7=84?= <김경진@DESKTOP-6URC5U8> Date: Wed, 27 May 2020 10:40:22 +0900 Subject: [PATCH 07/28] Refactoring Replace Magic Number with Symbolic Constant drawFunctionPatterns() Not to use magic number --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index fe05d48..512e314 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -354,8 +354,9 @@ public final class QrCode { private void drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (int i = 0; i < size; i++) { - setFunctionModule(6, i, i % 2 == 0); - setFunctionModule(i, 6, i % 2 == 0); + final boolean isBlack = i % 2 == 0; + setFunctionModule(6, i, isBlack); + setFunctionModule(i, 6, isBlack); } // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) From bd9e5f3882fe6b56e71633e37529fbc95dc762ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B2=BD=EC=A7=84?= <김경진@DESKTOP-6URC5U8> Date: Wed, 27 May 2020 10:45:15 +0900 Subject: [PATCH 08/28] Refactoring Replace Magic Number with Symbolic Constant drawFunctionPatterns() Not to use magic number --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 512e314..7013ed7 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -52,6 +52,12 @@ import java.util.Objects; */ public final class QrCode { + private static final int FINDER_SIZE = 3; + + private static final int TIMING_COORDINATE = 6; + + + /*---- Static factory functions (high level) ----*/ /** @@ -354,15 +360,14 @@ public final class QrCode { private void drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (int i = 0; i < size; i++) { - final boolean isBlack = i % 2 == 0; - setFunctionModule(6, i, isBlack); - setFunctionModule(i, 6, isBlack); + setFunctionModule(TIMING_COORDINATE, i, i % 2 == 0); + setFunctionModule(i, TIMING_COORDINATE, i % 2 == 0); } // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(3, 3); - drawFinderPattern(size - 4, 3); - drawFinderPattern(3, size - 4); + drawFinderPattern(FINDER_SIZE, FINDER_SIZE); + drawFinderPattern(size - 1 - FINDER_SIZE, FINDER_SIZE); + drawFinderPattern(FINDER_SIZE, size - 1 - FINDER_SIZE); // Draw numerous alignment patterns int[] alignPatPos = getAlignmentPatternPositions(); From 52ee4d068f351079b8b5de8ec366b5d3f476ebbc Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Wed, 27 May 2020 11:02:04 +0900 Subject: [PATCH 09/28] 1. Too long String extracted text file 2. QrSegmenetAdvanced.java 3. The String PACKED_QR_KANJI_TO_UNICODE is too long. So extract to outside. But it spend at least 3 seconds. So, think about that using text scanner. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 94 +++++++++++++------ 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 6480897..19a32c1 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -23,12 +23,20 @@ package io.nayuki.qrcodegen; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Objects; +import java.util.Scanner; + import io.nayuki.qrcodegen.QrSegment.Mode; @@ -132,6 +140,20 @@ public final class QrSegmentAdvanced { int[] prevCosts = headCosts.clone(); // Calculate costs using dynamic programming + prevCosts = calculate_cost(codePoints, modeTypes, numModes, headCosts, charModes, prevCosts); + + // Find optimal ending mode + Mode curMode = find_optimal(modeTypes, numModes, prevCosts); + + // Get optimal mode for each code point by tracing backwards + Mode[] result = new Mode[charModes.length]; + get_optimal(modeTypes, numModes, charModes, curMode, result); + return result; + } + + + private static int[] calculate_cost(int[] codePoints, final Mode[] modeTypes, final int numModes, + final int[] headCosts, Mode[][] charModes, int[] prevCosts) { for (int i = 0; i < codePoints.length; i++) { int cPoint = codePoints[i]; int[] curCosts = new int[numModes]; @@ -157,8 +179,8 @@ public final class QrSegmentAdvanced { for (int j = 0; j < numModes; j++) { // To mode for (int k = 0; k < numModes; k++) { // From mode int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]; - boolean is_notNull_or_lower = charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j]); - if (is_notNull_or_lower) { + boolean is_lower = charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j]); + if (is_lower) { curCosts[j] = newCost; charModes[i][j] = modeTypes[k]; } @@ -167,18 +189,12 @@ public final class QrSegmentAdvanced { prevCosts = curCosts; } - - // Find optimal ending mode - Mode curMode = null; - for (int i = 0, minCost = 0; i < numModes; i++) { - if (curMode == null || prevCosts[i] < minCost) { - minCost = prevCosts[i]; - curMode = modeTypes[i]; - } - } - - // Get optimal mode for each code point by tracing backwards - Mode[] result = new Mode[charModes.length]; + return prevCosts; + } + + + private static void get_optimal(final Mode[] modeTypes, final int numModes, Mode[][] charModes, Mode curMode, + Mode[] result) { for (int i = result.length - 1; i >= 0; i--) { for (int j = 0; j < numModes; j++) { if (modeTypes[j] == curMode) { @@ -188,7 +204,18 @@ public final class QrSegmentAdvanced { } } } - return result; + } + + + private static Mode find_optimal(final Mode[] modeTypes, final int numModes, int[] prevCosts) { + Mode curMode = null; + for (int i = 0, minCost = 0; i < numModes; i++) { + if (curMode == null || prevCosts[i] < minCost) { + minCost = prevCosts[i]; + curMode = modeTypes[i]; + } + } + return curMode; } @@ -247,12 +274,12 @@ public final class QrSegmentAdvanced { // Returns the number of UTF-8 bytes needed to encode the given Unicode code point. - private static int countUtf8Bytes(int cp) { - if (cp < 0) throw new IllegalArgumentException("Invalid code point"); - else if (cp < 0x80) return 1; - else if (cp < 0x800) return 2; - else if (cp < 0x10000) return 3; - else if (cp < 0x110000) return 4; + private static int countUtf8Bytes(int currentPoint) { + if (currentPoint < 0) throw new IllegalArgumentException("Invalid code point"); + else if (currentPoint < 0x80) return 1; + else if (currentPoint < 0x800) return 2; + else if (currentPoint < 0x10000) return 3; + else if (currentPoint < 0x110000) return 4; else throw new IllegalArgumentException("Invalid code point"); } @@ -306,8 +333,8 @@ public final class QrSegmentAdvanced { return c < UNICODE_TO_QR_KANJI.length && UNICODE_TO_QR_KANJI[c] != -1; } - - // Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT +// Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT +// private static final String PACKED_QR_KANJI_TO_UNICODE = readPacked_KANJI(); private static final String PACKED_QR_KANJI_TO_UNICODE = "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZA" + "ALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA///////////////////" + @@ -427,14 +454,27 @@ public final class QrSegmentAdvanced { Arrays.fill(UNICODE_TO_QR_KANJI, (short)-1); byte[] bytes = Base64.getDecoder().decode(PACKED_QR_KANJI_TO_UNICODE); for (int i = 0; i < bytes.length; i += 2) { - char c = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF)); - if (c == 0xFFFF) + char convertChar = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF)); + if (convertChar == 0xFFFF) continue; - assert UNICODE_TO_QR_KANJI[c] == -1; - UNICODE_TO_QR_KANJI[c] = (short)(i / 2); + assert UNICODE_TO_QR_KANJI[convertChar] == -1; + UNICODE_TO_QR_KANJI[convertChar] = (short)(i / 2); } } +// private static String readPacked_KANJI() { +// String str = ""; +// try { +// File file = new File("./packet_KanJI.txt"); +// Scanner scan = new Scanner(file); +// str +=scan.nextLine(); +// scan.close(); +// } +// catch(FileNotFoundException e){ +// } +// +// return str; +// } /*---- Miscellaneous ----*/ From e82e40f30ca3c09a630a1424a0e2475f867d5b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EA=B2=BD=EC=A7=84?= <김경진@DESKTOP-6URC5U8> Date: Sun, 31 May 2020 21:25:14 +0900 Subject: [PATCH 10/28] 1. Extract Variable 2. drawFunctionPatterns() in QrCode 3. To improve readability of conditional expression --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 7013ed7..6920c32 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -374,8 +374,11 @@ public final class QrCode { int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - // Don't draw on the three finder corners - if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) + final boolean isLeftTop = i == 0 && j == 0; + final boolean isLeftBottom = i == 0 && j == numAlign - 1; + final boolean isRightTop = i == numAlign - 1 && j == 0; + final boolean onThreeFinderCorners = isLeftTop || isLeftBottom || isRightTop; + if (!onThreeFinderCorners) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } From 580485c189e35b8a6ef3b5cee2a42d6d13f2d35b Mon Sep 17 00:00:00 2001 From: gerzees Date: Sun, 31 May 2020 22:23:00 +0900 Subject: [PATCH 11/28] 1. Rename, Extract Variable 2. encodSegments() in QrCode 3. To improve readability of conditional expressions and variable names --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 63 ++++++++++--------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index a0afaa6..bf3a11a 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -134,8 +134,8 @@ public final class QrCode { * between modes (such as alphanumeric and byte) to encode text in less space. * This is a mid-level API; the high-level API is {@link #encodeText(String,Ecc)} * and {@link #encodeBinary(byte[],Ecc)}.

- * @param segs the segments to encode - * @param ecl the error correction level to use (not {@code null}) (boostable) + * @param segments the segments to encode + * @param errorCorrectionLevel the error correction level to use (not {@code null}) (boostable) * @param minVersion the minimum allowed version of the QR Code (at least 1) * @param maxVersion the maximum allowed version of the QR Code (at most 40) * @param mask the mask number to use (between 0 and 7 (inclusive)), or −1 for automatic mask @@ -147,61 +147,64 @@ public final class QrCode { * @throws DataTooLongException if the segments fail to fit in * the maxVersion QR Code at the ECL, which means they are too long */ - public static QrCode encodeSegments(List segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { - Objects.requireNonNull(segs); - Objects.requireNonNull(ecl); - if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + public static QrCode encodeSegments(List segments, Ecc errorCorrectionLevel, int minVersion, int maxVersion, int mask, boolean boostEcl) { + Objects.requireNonNull(segments); + Objects.requireNonNull(errorCorrectionLevel); + final boolean isVersionInRange = MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION; + final boolean isMaskValid = mask < -1 || mask > 7; + if (!isVersionInRange || isMaskValid) throw new IllegalArgumentException("Invalid value"); // Find the minimal version number to use int version, dataUsedBits; for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = QrSegment.getTotalBits(segs, version); + int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; // Number of data bits available + dataUsedBits = QrSegment.getTotalBits(segments, version); if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) break; // This version number is found to be suitable if (version >= maxVersion) { // All versions in the range could not fit the given data - String msg = "Segment too long"; + String message = "Segment too long"; if (dataUsedBits != -1) - msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); - throw new DataTooLongException(msg); + message = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(message); } } assert dataUsedBits != -1; // Increase the error correction level while the data still fits in the current version number for (Ecc newEcl : Ecc.values()) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; + final boolean canIncreaseErrorCorrectionLevel = dataUsedBits <= getNumDataCodewords(version, newEcl) * 8; + if (boostEcl && canIncreaseErrorCorrectionLevel) + errorCorrectionLevel = newEcl; } // Concatenate all segments to create the data bit string - BitBuffer bb = new BitBuffer(); - for (QrSegment seg : segs) { - bb.appendBits(seg.mode.modeBits, 4); - bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - bb.appendData(seg.data); + BitBuffer bitBuffer = new BitBuffer(); + for (QrSegment segment : segments) { + bitBuffer.appendBits(segment.mode.modeBits, 4); + bitBuffer.appendBits(segment.numChars, segment.mode.numCharCountBits(version)); + bitBuffer.appendData(segment.data); } - assert bb.bitLength() == dataUsedBits; + assert bitBuffer.bitLength() == dataUsedBits; // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; - assert bb.bitLength() <= dataCapacityBits; - bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); - bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); - assert bb.bitLength() % 8 == 0; + int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; + assert bitBuffer.bitLength() <= dataCapacityBits; + bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); + bitBuffer.appendBits(0, (8 - bitBuffer.bitLength() % 8) % 8); + assert bitBuffer.bitLength() % 8 == 0; // Pad with alternating bytes until data capacity is reached - for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); + for (int padByte = 0xEC; bitBuffer.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bitBuffer.appendBits(padByte, 8); // Pack bits into bytes in big endian - byte[] dataCodewords = new byte[bb.bitLength() / 8]; - for (int i = 0; i < bb.bitLength(); i++) - dataCodewords[i >>> 3] |= bb.getBit(i) << (7 - (i & 7)); + byte[] dataCodewords = new byte[bitBuffer.bitLength() / 8]; + for (int i = 0; i < bitBuffer.bitLength(); i++) + dataCodewords[i >>> 3] |= bitBuffer.getBit(i) << (7 - (i & 7)); // Create the QR Code object - return new QrCode(version, ecl, dataCodewords, mask); + return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); } /*---- Instance fields ----*/ From d99f534b6120aefc6cfcee4236f06061faf85d68 Mon Sep 17 00:00:00 2001 From: wslbal Date: Mon, 1 Jun 2020 09:18:12 +0900 Subject: [PATCH 12/28] 1) extract method 2) getPenaltyScore, handleConstructorMasking, getNumRawDataModules 3) method is too long --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index fbc50d9..437a16c 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -614,7 +614,7 @@ public final class QrCode { private int twobytwoHavingSameColor(boolean[][] modules) { int result = 0; - // 2*2 blocks of modules having same color. + // 2*2 blocks of modules having same color for (int y = 0; y < size - 1; y++) { for (int x = 0; x < size - 1; x++) { boolean color = modules[y][x]; From ef3a6f70213c31a13f791ee3209b4717258eb070 Mon Sep 17 00:00:00 2001 From: gerzees Date: Mon, 1 Jun 2020 10:20:17 +0900 Subject: [PATCH 13/28] 1. Extract Method 2. encodeSegments() in QrCode 3. encodeSegments() is long method --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 116 ++++++++++++------ 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index bf3a11a..2c65b16 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -155,7 +155,85 @@ public final class QrCode { if (!isVersionInRange || isMaskValid) throw new IllegalArgumentException("Invalid value"); - // Find the minimal version number to use + + int version = findMinimalVersion(segments, errorCorrectionLevel, minVersion, maxVersion); + + int dataUsedBits = QrSegment.getTotalBits(segments, version); + + errorCorrectionLevel = findMaximalErrorCorrectionLevel(errorCorrectionLevel, boostEcl, version, dataUsedBits); + + + BitBuffer bitBuffer = segmentsToBitBuffer(segments, version); + assert bitBuffer.bitLength() == dataUsedBits; + + + int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; + assert bitBuffer.bitLength() <= dataCapacityBits; + addTerminator(bitBuffer, dataCapacityBits); + + + addPad(bitBuffer, dataCapacityBits); + + + byte[] dataCodewords = bitBufferToCodewords(bitBuffer); + + // Create the QR Code object + return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); + } + + + // Pack bits into bytes in big endian + private static byte[] bitBufferToCodewords(BitBuffer bitBuffer) { + byte[] dataCodewords = new byte[bitBuffer.bitLength() / 8]; + for (int i = 0; i < bitBuffer.bitLength(); i++) + dataCodewords[i >>> 3] |= bitBuffer.getBit(i) << (7 - (i & 7)); + return dataCodewords; + } + + + // Pad with alternating bytes until data capacity is reached + private static void addPad(BitBuffer bitBuffer, int dataCapacityBits) { + for (int padByte = 0xEC; bitBuffer.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bitBuffer.appendBits(padByte, 8); + } + + + /*---- Private helper methods for encodeSegments ----*/ + + // Add terminator and pad up to a byte if applicable + private static void addTerminator(BitBuffer bitBuffer, int dataCapacityBits) { + bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); + bitBuffer.appendBits(0, (8 - bitBuffer.bitLength() % 8) % 8); + assert bitBuffer.bitLength() % 8 == 0; + } + + // Concatenate all segments to create the data bit string + private static BitBuffer segmentsToBitBuffer(List segments, int version) { + BitBuffer bitBuffer = new BitBuffer(); + for (QrSegment segment : segments) { + bitBuffer.appendBits(segment.mode.modeBits, 4); + bitBuffer.appendBits(segment.numChars, segment.mode.numCharCountBits(version)); + bitBuffer.appendData(segment.data); + } + return bitBuffer; + } + + + // Increase the error correction level while the data still fits in the current version number + private static Ecc findMaximalErrorCorrectionLevel(Ecc errorCorrectionLevel, boolean boostEcl, int version, + int dataUsedBits) { + for (Ecc newEcl : Ecc.values()) { // From low to high + final boolean canIncreaseErrorCorrectionLevel = dataUsedBits <= getNumDataCodewords(version, newEcl) * 8; + if (boostEcl && canIncreaseErrorCorrectionLevel) + errorCorrectionLevel = newEcl; + } + return errorCorrectionLevel; + } + + + //Returns the minimal version number to use + private static int findMinimalVersion(List segments, Ecc errorCorrectionLevel, int minVersion, + int maxVersion) { int version, dataUsedBits; for (version = minVersion; ; version++) { int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; // Number of data bits available @@ -170,41 +248,7 @@ public final class QrCode { } } assert dataUsedBits != -1; - - // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : Ecc.values()) { // From low to high - final boolean canIncreaseErrorCorrectionLevel = dataUsedBits <= getNumDataCodewords(version, newEcl) * 8; - if (boostEcl && canIncreaseErrorCorrectionLevel) - errorCorrectionLevel = newEcl; - } - - // Concatenate all segments to create the data bit string - BitBuffer bitBuffer = new BitBuffer(); - for (QrSegment segment : segments) { - bitBuffer.appendBits(segment.mode.modeBits, 4); - bitBuffer.appendBits(segment.numChars, segment.mode.numCharCountBits(version)); - bitBuffer.appendData(segment.data); - } - assert bitBuffer.bitLength() == dataUsedBits; - - // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; - assert bitBuffer.bitLength() <= dataCapacityBits; - bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); - bitBuffer.appendBits(0, (8 - bitBuffer.bitLength() % 8) % 8); - assert bitBuffer.bitLength() % 8 == 0; - - // Pad with alternating bytes until data capacity is reached - for (int padByte = 0xEC; bitBuffer.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bitBuffer.appendBits(padByte, 8); - - // Pack bits into bytes in big endian - byte[] dataCodewords = new byte[bitBuffer.bitLength() / 8]; - for (int i = 0; i < bitBuffer.bitLength(); i++) - dataCodewords[i >>> 3] |= bitBuffer.getBit(i) << (7 - (i & 7)); - - // Create the QR Code object - return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); + return version; } /*---- Instance fields ----*/ From 5dc8f476f782ebc712f4d0a49a081cfb88b84d5b Mon Sep 17 00:00:00 2001 From: wslbal Date: Mon, 1 Jun 2020 10:25:18 +0900 Subject: [PATCH 14/28] 1) Command pattern 2) applyMask 3) switch statement --- .../main/java/io/nayuki/qrcodegen/Button.java | 17 +++++++++++++++++ .../main/java/io/nayuki/qrcodegen/Command.java | 5 +++++ .../src/main/java/io/nayuki/qrcodegen/Msk0.java | 7 +++++++ .../main/java/io/nayuki/qrcodegen/QrCode.java | 9 ++++++++- .../java/io/nayuki/qrcodegen/msk0Command.java | 13 +++++++++++++ 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Button.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk0.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk0Command.java diff --git a/java/src/main/java/io/nayuki/qrcodegen/Button.java b/java/src/main/java/io/nayuki/qrcodegen/Button.java new file mode 100644 index 0000000..66fca4e --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Button.java @@ -0,0 +1,17 @@ +package io.nayuki.qrcodegen; + +public class Button { + public Command theCommand; + + public Button(Command theCommand) { + setCommand(theCommand); + } + + public void setCommand(Command newCommand) { + this.theCommand = newCommand; + } + + public boolean pressed(int y, int x, int msk) { + return theCommand.excute(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Command.java b/java/src/main/java/io/nayuki/qrcodegen/Command.java new file mode 100644 index 0000000..b12845e --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Command.java @@ -0,0 +1,5 @@ +package io.nayuki.qrcodegen; + +public interface Command { + public abstract boolean excute(int y, int x, int msk); +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk0.java b/java/src/main/java/io/nayuki/qrcodegen/Msk0.java new file mode 100644 index 0000000..a125577 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk0.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk0 { + public boolean operation(int y, int x, int msk) { + return ((x + y) % 2 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 437a16c..a2cdbc2 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -546,9 +546,15 @@ public final class QrCode { private void applyMask(int msk) { if (msk < 0 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); - for (int y = 0; y < size; y++) { + for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { boolean invert; + Msk0 msk0 = new Msk0(); + Command mskCommand = new msk0Command(msk0); + + Button button = new Button(mskCommand); + invert = button.pressed(y, x, msk); + /* switch (msk) { case 0: invert = (x + y) % 2 == 0; break; case 1: invert = y % 2 == 0; break; @@ -560,6 +566,7 @@ public final class QrCode { case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; default: throw new AssertionError(); } + */ modules[y][x] ^= invert & !isFunction[y][x]; } } diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk0Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk0Command.java new file mode 100644 index 0000000..357bcdc --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk0Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk0Command implements Command{ + private Msk0 theMsk0; + + public msk0Command(Msk0 theMsk0) { + this.theMsk0 = theMsk0; + } + + public boolean excute(int y, int x, int msk) { + return theMsk0.operation(y, x, msk); + } +} From 6ca8ea40bc94e796b71fe1cbf6ce1b2da36bd384 Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 09:16:27 +0900 Subject: [PATCH 15/28] 1. Rename 2. encodeSegments in QrCode 3. variable name was wrong --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 2c65b16..1951bb1 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -151,8 +151,8 @@ public final class QrCode { Objects.requireNonNull(segments); Objects.requireNonNull(errorCorrectionLevel); final boolean isVersionInRange = MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION; - final boolean isMaskValid = mask < -1 || mask > 7; - if (!isVersionInRange || isMaskValid) + final boolean isMaskOutOfRange = mask < -1 || mask > 7; + if (!isVersionInRange || isMaskOutOfRange) throw new IllegalArgumentException("Invalid value"); @@ -181,6 +181,7 @@ public final class QrCode { return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); } + /*---- Private helper methods for encodeSegments ----*/ // Pack bits into bytes in big endian private static byte[] bitBufferToCodewords(BitBuffer bitBuffer) { @@ -197,9 +198,6 @@ public final class QrCode { bitBuffer.appendBits(padByte, 8); } - - /*---- Private helper methods for encodeSegments ----*/ - // Add terminator and pad up to a byte if applicable private static void addTerminator(BitBuffer bitBuffer, int dataCapacityBits) { bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); From 64581afd8c78bfef4525ca2990c22d69367ae57c Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 09:21:38 +0900 Subject: [PATCH 16/28] 1. Extract method 2. drawFunctionPatterns in Qrcode 3. Method drawFunctionPatterns was long --- .../src/main/java/io/nayuki/qrcodegen/QrCode.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 1951bb1..162aaf0 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -412,7 +412,16 @@ public final class QrCode { drawFinderPattern(size - 1 - FINDER_SIZE, FINDER_SIZE); drawFinderPattern(FINDER_SIZE, size - 1 - FINDER_SIZE); - // Draw numerous alignment patterns + drawAlignmentsPatterns(); + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); + } + + + // Draw numerous alignment patterns + private void drawAlignmentsPatterns() { int[] alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { @@ -425,10 +434,6 @@ public final class QrCode { drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } - - // Draw configuration data - drawFormatBits(0); // Dummy mask value; overwritten later in the constructor - drawVersion(); } From f8c7c8800be70f1fa6dd1e2efa0e40875d7e73fa Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 09:39:43 +0900 Subject: [PATCH 17/28] 1. Extract method 2. handleConstructorMasking in QrCode 3. To improve readability --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 162aaf0..f619deb 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -626,24 +626,32 @@ public final class QrCode { // A messy helper function for the constructor. This QR Code must be in an unmasked state when this // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. // This method applies and returns the actual mask chosen, from 0 to 7. - private int handleConstructorMasking(int msk) { - if (msk == -1) { // Automatically choose best mask - int minPenalty = Integer.MAX_VALUE; - for (int i = 0; i < 8; i++) { - applyMask(i); - drawFormatBits(i); - int penalty = getPenaltyScore(); - if (penalty < minPenalty) { - msk = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR + private int handleConstructorMasking(int mask) { + if (mask == -1) { + mask = findBestMask(); + } + assert 0 <= mask && mask <= 7; + applyMask(mask); // Apply the final choice of mask + drawFormatBits(mask); // Overwrite old format bits + return mask; // The caller shall assign this value to the final-declared field + } + + + // Automatically choose best mask + private int findBestMask() { + int mask = -1; + int minPenalty = Integer.MAX_VALUE; + for (int i = 0; i < 8; i++) { + applyMask(i); + drawFormatBits(i); + int penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; } + applyMask(i); // Undoes the mask due to XOR } - assert 0 <= msk && msk <= 7; - applyMask(msk); // Apply the final choice of mask - drawFormatBits(msk); // Overwrite old format bits - return msk; // The caller shall assign this value to the final-declared field + return mask; } From 33734a9821ebc56043e5e73789d1195a9b2997fa Mon Sep 17 00:00:00 2001 From: wslbal Date: Wed, 3 Jun 2020 10:10:49 +0900 Subject: [PATCH 18/28] 1) Factory Method Pattern 2) applyMask 3) dynamic object construct --- .../main/java/io/nayuki/qrcodegen/Msk1.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk2.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk3.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk4.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk5.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk6.java | 7 +++ .../main/java/io/nayuki/qrcodegen/Msk7.java | 7 +++ .../nayuki/qrcodegen/MskCommandFactory.java | 46 ++++++++++++++++ .../main/java/io/nayuki/qrcodegen/QrCode.java | 55 ++++++++----------- .../java/io/nayuki/qrcodegen/msk1Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk2Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk3Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk4Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk5Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk6Command.java | 13 +++++ .../java/io/nayuki/qrcodegen/msk7Command.java | 13 +++++ 16 files changed, 208 insertions(+), 33 deletions(-) create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk1.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk2.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk3.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk4.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk5.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk6.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Msk7.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/MskCommandFactory.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk1Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk2Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk3Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk4Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk5Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk6Command.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/msk7Command.java diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk1.java b/java/src/main/java/io/nayuki/qrcodegen/Msk1.java new file mode 100644 index 0000000..f9f92e3 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk1.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk1 { + public boolean operation(int y, int x, int msk) { + return (y % 2 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk2.java b/java/src/main/java/io/nayuki/qrcodegen/Msk2.java new file mode 100644 index 0000000..42c145e --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk2.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk2 { + public boolean operation(int y, int x, int msk) { + return (x % 3 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk3.java b/java/src/main/java/io/nayuki/qrcodegen/Msk3.java new file mode 100644 index 0000000..45064c0 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk3.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk3 { + public boolean operation(int y, int x, int msk) { + return ((x + y) % 3 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk4.java b/java/src/main/java/io/nayuki/qrcodegen/Msk4.java new file mode 100644 index 0000000..d0b13a1 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk4.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk4 { + public boolean operation(int y, int x, int msk) { + return ((x / 3 + y / 2) % 2 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk5.java b/java/src/main/java/io/nayuki/qrcodegen/Msk5.java new file mode 100644 index 0000000..decc5df --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk5.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk5 { + public boolean operation(int y, int x, int msk) { + return (x * y % 2 + x * y % 3 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk6.java b/java/src/main/java/io/nayuki/qrcodegen/Msk6.java new file mode 100644 index 0000000..555fb64 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk6.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk6 { + public boolean operation(int y, int x, int msk) { + return ((x * y % 2 + x * y % 3) % 2 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/Msk7.java b/java/src/main/java/io/nayuki/qrcodegen/Msk7.java new file mode 100644 index 0000000..7d98190 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Msk7.java @@ -0,0 +1,7 @@ +package io.nayuki.qrcodegen; + +public class Msk7 { + public boolean operation(int y, int x, int msk) { + return (((x + y) % 2 + x * y % 3) % 2 == 0); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/MskCommandFactory.java b/java/src/main/java/io/nayuki/qrcodegen/MskCommandFactory.java new file mode 100644 index 0000000..93bc177 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/MskCommandFactory.java @@ -0,0 +1,46 @@ +package io.nayuki.qrcodegen; + +public class MskCommandFactory { + public static Command getCommand(int msk) { + Command theCommand = null; + + Msk0 msk0 = new Msk0(); + Msk1 msk1 = new Msk1(); + Msk2 msk2 = new Msk2(); + Msk3 msk3 = new Msk3(); + Msk4 msk4 = new Msk4(); + Msk5 msk5 = new Msk5(); + Msk6 msk6 = new Msk6(); + Msk7 msk7 = new Msk7(); + + switch (msk) { + case 0: + theCommand = new msk0Command(msk0); + break; + case 1: + theCommand = new msk1Command(msk1); + break; + case 2: + theCommand = new msk2Command(msk2); + break; + case 3: + theCommand = new msk3Command(msk3); + break; + case 4: + theCommand = new msk4Command(msk4); + break; + case 5: + theCommand = new msk5Command(msk5); + break; + case 6: + theCommand = new msk6Command(msk6); + break; + case 7: + theCommand = new msk7Command(msk7); + break; + default: + throw new AssertionError(); + } + return theCommand; + } +} \ No newline at end of file diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index a2cdbc2..9d4002d 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -542,35 +542,24 @@ public final class QrCode { // The function modules must be marked and the codeword bits must be drawn // before masking. Due to the arithmetic of XOR, calling applyMask() with // the same mask value a second time will undo the mask. A final well-formed - // QR Code needs exactly one (not zero, two, etc.) mask applied. Command pattern + // QR Code needs exactly one (not zero, two, etc.) mask applied. private void applyMask(int msk) { + if (msk < 0 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); - for (int y = 0; y < size; y++) { + for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { boolean invert; - Msk0 msk0 = new Msk0(); - Command mskCommand = new msk0Command(msk0); - + + Command mskCommand = MskCommandFactory.getCommand(msk); Button button = new Button(mskCommand); invert = button.pressed(y, x, msk); - /* - switch (msk) { - case 0: invert = (x + y) % 2 == 0; break; - case 1: invert = y % 2 == 0; break; - case 2: invert = x % 3 == 0; break; - case 3: invert = (x + y) % 3 == 0; break; - case 4: invert = (x / 3 + y / 2) % 2 == 0; break; - case 5: invert = x * y % 2 + x * y % 3 == 0; break; - case 6: invert = (x * y % 2 + x * y % 3) % 2 == 0; break; - case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; - default: throw new AssertionError(); - } - */ + modules[y][x] ^= invert & !isFunction[y][x]; } } } + // A messy helper function for the constructor. This QR Code must be in an unmasked state when this @@ -619,21 +608,6 @@ public final class QrCode { return result; } - private int twobytwoHavingSameColor(boolean[][] modules) { - int result = 0; - // 2*2 blocks of modules having same color - for (int y = 0; y < size - 1; y++) { - for (int x = 0; x < size - 1; x++) { - boolean color = modules[y][x]; - if ( color == modules[y][x + 1] && - color == modules[y + 1][x] && - color == modules[y + 1][x + 1]) - result += PENALTY_N2; - } - } - return result; - } - // Calculates and returns the penalty score based on state of this QR Code's current modules. // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. private int getPenaltyScore() { @@ -679,6 +653,21 @@ public final class QrCode { return result; } + private int twobytwoHavingSameColor(boolean[][] modules) { + int result = 0; + // 2*2 blocks of modules having same color. + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + boolean color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) + result += PENALTY_N2; + } + } + return result; + } + /*---- Private helper functions ----*/ // Returns an ascending list of positions of alignment patterns for this version number. diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk1Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk1Command.java new file mode 100644 index 0000000..a503249 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk1Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk1Command implements Command { + private Msk1 theMsk1; + + public msk1Command(Msk1 theMsk1) { + this.theMsk1 = theMsk1; + } + + public boolean excute(int y, int x, int msk) { + return theMsk1.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk2Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk2Command.java new file mode 100644 index 0000000..d62f58e --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk2Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk2Command implements Command { + private Msk2 theMsk2; + + public msk2Command(Msk2 theMsk2) { + this.theMsk2 = theMsk2; + } + + public boolean excute(int y, int x, int msk) { + return theMsk2.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk3Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk3Command.java new file mode 100644 index 0000000..402f2a2 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk3Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk3Command implements Command { + private Msk3 theMsk3; + + public msk3Command(Msk3 theMsk3) { + this.theMsk3 = theMsk3; + } + + public boolean excute(int y, int x, int msk) { + return theMsk3.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk4Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk4Command.java new file mode 100644 index 0000000..ca755fc --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk4Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk4Command implements Command { + private Msk4 theMsk4; + + public msk4Command(Msk4 theMsk4) { + this.theMsk4 = theMsk4; + } + + public boolean excute(int y, int x, int msk) { + return theMsk4.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk5Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk5Command.java new file mode 100644 index 0000000..7a6f4b1 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk5Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk5Command implements Command { + private Msk5 theMsk5; + + public msk5Command(Msk5 theMsk5) { + this.theMsk5 = theMsk5; + } + + public boolean excute(int y, int x, int msk) { + return theMsk5.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk6Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk6Command.java new file mode 100644 index 0000000..078c270 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk6Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk6Command implements Command { + private Msk6 theMsk6; + + public msk6Command(Msk6 theMsk6) { + this.theMsk6 = theMsk6; + } + + public boolean excute(int y, int x, int msk) { + return theMsk6.operation(y, x, msk); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/msk7Command.java b/java/src/main/java/io/nayuki/qrcodegen/msk7Command.java new file mode 100644 index 0000000..b5c9e2c --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/msk7Command.java @@ -0,0 +1,13 @@ +package io.nayuki.qrcodegen; + +public class msk7Command implements Command { + private Msk7 theMsk7; + + public msk7Command(Msk7 theMsk7) { + this.theMsk7 = theMsk7; + } + + public boolean excute(int y, int x, int msk) { + return theMsk7.operation(y, x, msk); + } +} From 7047caa546b31a7e6f979403d3343efa5e6342f6 Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 10:23:45 +0900 Subject: [PATCH 19/28] 1. Move method 2. addPad in QrCode 3. Feature envy. addPad call only BitBuffer methods. --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index f619deb..c652ab6 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -172,7 +172,7 @@ public final class QrCode { addTerminator(bitBuffer, dataCapacityBits); - addPad(bitBuffer, dataCapacityBits); + bitBuffer.addPad(dataCapacityBits); byte[] dataCodewords = bitBufferToCodewords(bitBuffer); @@ -192,12 +192,6 @@ public final class QrCode { } - // Pad with alternating bytes until data capacity is reached - private static void addPad(BitBuffer bitBuffer, int dataCapacityBits) { - for (int padByte = 0xEC; bitBuffer.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bitBuffer.appendBits(padByte, 8); - } - // Add terminator and pad up to a byte if applicable private static void addTerminator(BitBuffer bitBuffer, int dataCapacityBits) { bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); From 4ddf3a02c727c757bbe8fcda8680cbe768b2d093 Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 10:25:59 +0900 Subject: [PATCH 20/28] 1. Move method 2. addPad to BitBuffer 3. Feature envy. addPad calls only BitBuffer methods. --- java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java index 624d129..807d988 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java @@ -125,5 +125,12 @@ public final class BitBuffer implements Cloneable { throw new AssertionError(e); } } + + + // Pad with alternating bytes until data capacity is reached + void addPad(int dataCapacityBits) { + for (int padByte = 0xEC; bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBits(padByte, 8); + } } From 889fc7d9d5ec65f7c7fe013e3e3483acb0fe2eb0 Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 10:30:39 +0900 Subject: [PATCH 21/28] 1. Move method, rename 2. bitBufferToCodewords in QrCode 3.Feature envy. It calls only BitBuffer method. Moved it to BitBuffer and renamed it. --- .../src/main/java/io/nayuki/qrcodegen/BitBuffer.java | 11 ++++++++++- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 12 ++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java index 807d988..ccebe37 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java @@ -128,9 +128,18 @@ public final class BitBuffer implements Cloneable { // Pad with alternating bytes until data capacity is reached - void addPad(int dataCapacityBits) { + public void addPad(int dataCapacityBits) { for (int padByte = 0xEC; bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) appendBits(padByte, 8); } + + + // Pack bits into bytes in big endian + public byte[] toCodewords() { + byte[] dataCodewords = new byte[bitLength() / 8]; + for (int i = 0; i < bitLength(); i++) + dataCodewords[i >>> 3] |= getBit(i) << (7 - (i & 7)); + return dataCodewords; + } } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index c652ab6..7b85539 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -175,7 +175,7 @@ public final class QrCode { bitBuffer.addPad(dataCapacityBits); - byte[] dataCodewords = bitBufferToCodewords(bitBuffer); + byte[] dataCodewords = bitBuffer.toCodewords(); // Create the QR Code object return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); @@ -183,15 +183,7 @@ public final class QrCode { /*---- Private helper methods for encodeSegments ----*/ - // Pack bits into bytes in big endian - private static byte[] bitBufferToCodewords(BitBuffer bitBuffer) { - byte[] dataCodewords = new byte[bitBuffer.bitLength() / 8]; - for (int i = 0; i < bitBuffer.bitLength(); i++) - dataCodewords[i >>> 3] |= bitBuffer.getBit(i) << (7 - (i & 7)); - return dataCodewords; - } - - + // Add terminator and pad up to a byte if applicable private static void addTerminator(BitBuffer bitBuffer, int dataCapacityBits) { bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); From 62ad71d6d72e25e0df04df268dddc68e76c7eb6a Mon Sep 17 00:00:00 2001 From: gerzees Date: Wed, 3 Jun 2020 10:37:53 +0900 Subject: [PATCH 22/28] 1. Move method 2. addTerminator in QrCode 3. Feature envy. It calls only BitBuffer methods. --- java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java | 8 ++++++++ java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 8 +------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java index ccebe37..8b4254e 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java @@ -141,5 +141,13 @@ public final class BitBuffer implements Cloneable { dataCodewords[i >>> 3] |= getBit(i) << (7 - (i & 7)); return dataCodewords; } + + + // Add terminator and pad up to a byte if applicable + public void addTerminator(int dataCapacityBits) { + appendBits(0, Math.min(4, dataCapacityBits - bitLength())); + appendBits(0, (8 - bitLength() % 8) % 8); + assert bitLength() % 8 == 0; + } } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 7b85539..cebfbea 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -169,7 +169,7 @@ public final class QrCode { int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; assert bitBuffer.bitLength() <= dataCapacityBits; - addTerminator(bitBuffer, dataCapacityBits); + bitBuffer.addTerminator(dataCapacityBits); bitBuffer.addPad(dataCapacityBits); @@ -184,12 +184,6 @@ public final class QrCode { /*---- Private helper methods for encodeSegments ----*/ - // Add terminator and pad up to a byte if applicable - private static void addTerminator(BitBuffer bitBuffer, int dataCapacityBits) { - bitBuffer.appendBits(0, Math.min(4, dataCapacityBits - bitBuffer.bitLength())); - bitBuffer.appendBits(0, (8 - bitBuffer.bitLength() % 8) % 8); - assert bitBuffer.bitLength() % 8 == 0; - } // Concatenate all segments to create the data bit string private static BitBuffer segmentsToBitBuffer(List segments, int version) { From b63fa83f32588dc046d96300b80d6552b960d060 Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Fri, 5 Jun 2020 16:49:40 +0900 Subject: [PATCH 23/28] 1. Strategy Pattern 2. QrSegmentsAdvanced 3. If i want to add new mode for making qrcode, I add many part of this code so, I using strategy pattern --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 222 ++++-------------- 1 file changed, 50 insertions(+), 172 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 19a32c1..a4f039c 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -112,16 +112,15 @@ public final class QrSegmentAdvanced { private static List makeSegmentsOptimally(int[] codePoints, int version) { if (codePoints.length == 0) return new ArrayList<>(); - Mode[] charModes = computeCharacterModes(codePoints, version); + QrMode[] charModes = computeCharacterModes(codePoints, version); return splitIntoSegments(codePoints, charModes); } - // Returns a new array representing the optimal mode per code point based on the given text and version. - private static Mode[] computeCharacterModes(int[] codePoints, int version) { + private static QrMode[] computeCharacterModes(int[] codePoints, int version) { if (codePoints.length == 0) throw new IllegalArgumentException(); - final Mode[] modeTypes = {Mode.BYTE, Mode.ALPHANUMERIC, Mode.NUMERIC, Mode.KANJI}; // Do not modify + final QrMode[] modeTypes = {new ByteMode(), new AlphanumericMode(), new NumericMode(), new KanjiMode()}; // Do not modify final int numModes = modeTypes.length; // Segment header sizes, measured in 1/6 bits @@ -132,7 +131,7 @@ public final class QrSegmentAdvanced { // charModes[i][j] represents the mode to encode the code point at // index i such that the final segment ends in modeTypes[j] and the // total number of bits is minimized over all possible choices - Mode[][] charModes = new Mode[codePoints.length][numModes]; + QrMode[][] charModes = new QrMode[codePoints.length][numModes]; // At the beginning of each iteration of the loop below, // prevCosts[j] is the exact minimum number of 1/6 bits needed to @@ -143,44 +142,44 @@ public final class QrSegmentAdvanced { prevCosts = calculate_cost(codePoints, modeTypes, numModes, headCosts, charModes, prevCosts); // Find optimal ending mode - Mode curMode = find_optimal(modeTypes, numModes, prevCosts); + QrMode curMode = null; + curMode = find_optimal(modeTypes, numModes, prevCosts, curMode); // Get optimal mode for each code point by tracing backwards - Mode[] result = new Mode[charModes.length]; + QrMode[] result = new QrMode[codePoints.length]; get_optimal(modeTypes, numModes, charModes, curMode, result); return result; } - private static int[] calculate_cost(int[] codePoints, final Mode[] modeTypes, final int numModes, - final int[] headCosts, Mode[][] charModes, int[] prevCosts) { + private static int[] calculate_cost(int[] codePoints, final QrMode[] modeTypes, final int numModes, + final int[] headCosts, QrMode[][] charModes, int[] prevCosts) { for (int i = 0; i < codePoints.length; i++) { - int cPoint = codePoints[i]; + int codePoint = codePoints[i]; int[] curCosts = new int[numModes]; { // Always extend a byte mode segment - curCosts[0] = prevCosts[0] + countUtf8Bytes(cPoint) * 8 * 6; - charModes[i][0] = modeTypes[0]; + charModes[i][0] = new ByteMode(); + curCosts[0] = charModes[i][0].getcost(prevCosts[0], codePoint); } // Extend a segment if possible - if (is_alphanumeric(cPoint)) { // Is alphanumeric - curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char - charModes[i][1] = modeTypes[1]; + if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(codePoint) != -1) { // Is alphanumeric + charModes[i][1] = new AlphanumericMode(); + curCosts[1] = charModes[i][1].getcost(prevCosts[1], codePoint); // 5.5 bits per alphanumeric char } - if (is_numeric(cPoint)) { // Is numeric - curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit - charModes[i][2] = modeTypes[2]; + if ('0' <= codePoint && codePoint <= '9') { // Is numeric + charModes[i][2] = new NumericMode(); + curCosts[2] = charModes[i][2].getcost(prevCosts[2], codePoint); // 3.33 bits per digit } - if (isKanji(cPoint)) { - curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char - charModes[i][3] = modeTypes[3]; + if (isKanji(codePoint)) { + charModes[i][3] = new KanjiMode(); + curCosts[3] = charModes[i][3].getcost(prevCosts[3], codePoint);; // 13 bits per Shift JIS char } - + // Start new segment at the end to switch modes for (int j = 0; j < numModes; j++) { // To mode for (int k = 0; k < numModes; k++) { // From mode int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]; - boolean is_lower = charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j]); - if (is_lower) { + if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) { curCosts[j] = newCost; charModes[i][j] = modeTypes[k]; } @@ -193,8 +192,8 @@ public final class QrSegmentAdvanced { } - private static void get_optimal(final Mode[] modeTypes, final int numModes, Mode[][] charModes, Mode curMode, - Mode[] result) { + private static void get_optimal(final QrMode[] modeTypes, final int numModes, QrMode[][] charModes, QrMode curMode, + QrMode[] result) { for (int i = result.length - 1; i >= 0; i--) { for (int j = 0; j < numModes; j++) { if (modeTypes[j] == curMode) { @@ -207,8 +206,7 @@ public final class QrSegmentAdvanced { } - private static Mode find_optimal(final Mode[] modeTypes, final int numModes, int[] prevCosts) { - Mode curMode = null; + private static QrMode find_optimal(final QrMode[] modeTypes, final int numModes, int[] prevCosts, QrMode curMode) { for (int i = 0, minCost = 0; i < numModes; i++) { if (curMode == null || prevCosts[i] < minCost) { minCost = prevCosts[i]; @@ -219,40 +217,31 @@ public final class QrSegmentAdvanced { } - private static boolean is_numeric(int c) { - return '0' <= c && c <= '9'; + private static boolean is_numeric(int convertedPoint) { + return '0' <= convertedPoint && convertedPoint <= '9'; } - private static boolean is_alphanumeric(int c) { - return QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1; + private static boolean is_alphanumeric(int convertedPoint) { + return QrSegment.ALPHANUMERIC_CHARSET.indexOf(convertedPoint) != -1; } // Returns a new list of segments based on the given text and modes, such that // consecutive code points in the same mode are put into the same segment. - private static List splitIntoSegments(int[] codePoints, Mode[] charModes) { + private static List splitIntoSegments(int[] codePoints, QrMode[] charModes) { if (codePoints.length == 0) throw new IllegalArgumentException(); List result = new ArrayList<>(); // Accumulate run of modes - Mode curMode = charModes[0]; + QrMode curMode = charModes[0]; int start = 0; for (int i = 1; ; i++) { if (i < codePoints.length && charModes[i] == curMode) continue; String s = new String(codePoints, start, i - start); - if (curMode == Mode.BYTE) - result.add(QrSegment.makeBytes(s.getBytes(StandardCharsets.UTF_8))); - else if (curMode == Mode.NUMERIC) - result.add(QrSegment.makeNumeric(s)); - else if (curMode == Mode.ALPHANUMERIC) - result.add(QrSegment.makeAlphanumeric(s)); - else if (curMode == Mode.KANJI) - result.add(makeKanji(s)); - else - throw new AssertionError(); + result.add(curMode.making(s)); if (i >= codePoints.length) return result; curMode = charModes[i]; @@ -263,8 +252,8 @@ public final class QrSegmentAdvanced { // Returns a new array of Unicode code points (effectively // UTF-32 / UCS-4) representing the given UTF-16 string. - private static int[] toCodePoints(String s) { - int[] result = s.codePoints().toArray(); + private static int[] toCodePoints(String str) { + int[] result = str.codePoints().toArray(); for (int c : result) { if (Character.isSurrogate((char)c)) throw new IllegalArgumentException("Invalid UTF-16 string"); @@ -274,7 +263,7 @@ public final class QrSegmentAdvanced { // Returns the number of UTF-8 bytes needed to encode the given Unicode code point. - private static int countUtf8Bytes(int currentPoint) { + public static int countUtf8Bytes(int currentPoint) { if (currentPoint < 0) throw new IllegalArgumentException("Invalid code point"); else if (currentPoint < 0x80) return 1; else if (currentPoint < 0x800) return 2; @@ -334,118 +323,7 @@ public final class QrSegmentAdvanced { } // Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT -// private static final String PACKED_QR_KANJI_TO_UNICODE = readPacked_KANJI(); - private static final String PACKED_QR_KANJI_TO_UNICODE = - "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZA" + - "ALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA///////////////////" + - "//////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h" + - "/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBN" + - "ME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////" + - "////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDh" + - "MOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+" + - "A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQm" + - "BCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQ" + - "JRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32t" + - "fe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYg" + - "ZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2" + - "cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5" + - "////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLs" + - "bTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CE" + - "lFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKg" + - "dZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/" + - "U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6" + - "UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo" + - "//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6r" + - "XydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOn" + - "ZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtk" + - "mANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiV" + - "nfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/X" + - "YB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6luf" + - "hQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/S" + - "ge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZ" + - "WXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJac" + - "l5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066" + - "TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bb" + - "fL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6" + - "ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333P" + - "fZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7e" + - "gM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bj" + - "WMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0" + - "gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oC" + - "iuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhY" + - "WVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLr" + - "W8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/D" + - "fQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGro" + - "XmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/" + - "l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16c" + - "YBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7" + - "WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851s" + - "Tk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04H" + - "YWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5f" + - "UbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+K" + - "gACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHM" + - "W+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3v" + - "lzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/l" + - "UBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGT" + - "UZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQ" + - "Uw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////" + - "VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaP" + - "VqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4" + - "WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrj" + - "Wtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yr" + - "XLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/" + - "XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQ" + - "YClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FY" + - "YVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KU" + - "Ytdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTH" + - "ZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2Zo" + - "Zl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGge" + - "aEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammy" + - "aa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7" + - "awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzX" + - "bMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7J" + - "brdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3Aw" + - "cD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKy" + - "csNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTy" + - "dPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYw" + - "djt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7" + - "d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXog" + - "eh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuc" + - "e5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G" + - "////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5a" + - "fnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAh" + - "gCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHC" + - "gbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMv" + - "gyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UY" + - "hSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs" + - "//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZ" + - "iF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9" + - "ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tf" + - "i2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3b" + - "jcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485" + - "j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEy" + - "kTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNW" + - "k7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYu" + - "li+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJem" + - "l6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnY" + - "mdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uD" + - "m5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1k" + - "nVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+" + - "n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////////////////////w=="; + private static final String PACKED_QR_KANJI_TO_UNICODE = readPacked_KANJI(); private static short[] UNICODE_TO_QR_KANJI = new short[1 << 16]; @@ -462,19 +340,19 @@ public final class QrSegmentAdvanced { } } -// private static String readPacked_KANJI() { -// String str = ""; -// try { -// File file = new File("./packet_KanJI.txt"); -// Scanner scan = new Scanner(file); -// str +=scan.nextLine(); -// scan.close(); -// } -// catch(FileNotFoundException e){ -// } -// -// return str; -// } + private static String readPacked_KANJI() { + String str = ""; + try { + File file = new File("./packet_KanJI.txt"); + Scanner scan = new Scanner(file); + str +=scan.nextLine(); + scan.close(); + } + catch(FileNotFoundException e){ + } + + return str; + } /*---- Miscellaneous ----*/ From 3383e1b34fee8666bc2d05547f87748539bec82d Mon Sep 17 00:00:00 2001 From: wslbal Date: Fri, 5 Jun 2020 23:20:32 +0900 Subject: [PATCH 24/28] =?UTF-8?q?merge=20=EC=98=A4=EB=A5=98=EC=97=90=20?= =?UTF-8?q?=EB=8C=80=ED=95=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/nayuki/qrcodegen/BitBuffer.java | 26 +- .../main/java/io/nayuki/qrcodegen/QrCode.java | 352 ++++++++++-------- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 284 ++++++-------- 3 files changed, 332 insertions(+), 330 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java index 624d129..4ff3e42 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java @@ -125,5 +125,29 @@ public final class BitBuffer implements Cloneable { throw new AssertionError(e); } } + + + // Pad with alternating bytes until data capacity is reached + public void addPad(int dataCapacityBits) { + for (int padByte = 0xEC; bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBits(padByte, 8); + } + + + // Pack bits into bytes in big endian + public byte[] toCodewords() { + byte[] dataCodewords = new byte[bitLength() / 8]; + for (int i = 0; i < bitLength(); i++) + dataCodewords[i >>> 3] |= getBit(i) << (7 - (i & 7)); + return dataCodewords; + } + + + // Add terminator and pad up to a byte if applicable + public void addTerminator(int dataCapacityBits) { + appendBits(0, Math.min(4, dataCapacityBits - bitLength())); + appendBits(0, (8 - bitLength() % 8) % 8); + assert bitLength() % 8 == 0; + } -} +} \ No newline at end of file diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 9d4002d..733f10d 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -51,9 +51,15 @@ import java.util.Objects; * @see QrSegment */ public final class QrCode { + + private static final int FINDER_SIZE = 3; + + private static final int TIMING_COORDINATE = 6; - /*---- Static factory functions (high level) ----*/ + + /*---- Static factory functions (high level) ----*/ + /** * Returns a QR Code representing the specified Unicode text string at the specified error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer @@ -73,8 +79,8 @@ public final class QrCode { List segs = QrSegment.makeSegments(text); return encodeSegments(segs, ecl); } - - + + /** * Returns a QR Code representing the specified binary data at the specified error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of @@ -93,10 +99,10 @@ public final class QrCode { QrSegment seg = QrSegment.makeBytes(data); return encodeSegments(Arrays.asList(seg), ecl); } - - + + /*---- Static factory functions (mid level) ----*/ - + /** * Returns a QR Code representing the specified segments at the specified error correction * level. The smallest possible QR Code version is automatically chosen for the output. The ECC level @@ -115,8 +121,8 @@ public final class QrCode { public static QrCode encodeSegments(List segs, Ecc ecl) { return encodeSegments(segs, ecl, MIN_VERSION, MAX_VERSION, -1, true); } - - + + /** * Returns a QR Code representing the specified segments with the specified encoding parameters. * The smallest possible QR Code version within the specified range is automatically @@ -128,8 +134,8 @@ public final class QrCode { * between modes (such as alphanumeric and byte) to encode text in less space. * This is a mid-level API; the high-level API is {@link #encodeText(String,Ecc)} * and {@link #encodeBinary(byte[],Ecc)}.

- * @param segs the segments to encode - * @param ecl the error correction level to use (not {@code null}) (boostable) + * @param segments the segments to encode + * @param errorCorrectionLevel the error correction level to use (not {@code null}) (boostable) * @param minVersion the minimum allowed version of the QR Code (at least 1) * @param maxVersion the maximum allowed version of the QR Code (at most 40) * @param mask the mask number to use (between 0 and 7 (inclusive)), or −1 for automatic mask @@ -141,136 +147,159 @@ public final class QrCode { * @throws DataTooLongException if the segments fail to fit in * the maxVersion QR Code at the ECL, which means they are too long */ - public static QrCode encodeSegments(List segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { - Objects.requireNonNull(segs); - Objects.requireNonNull(ecl); - if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) + public static QrCode encodeSegments(List segments, Ecc errorCorrectionLevel, int minVersion, int maxVersion, int mask, boolean boostEcl) { + Objects.requireNonNull(segments); + Objects.requireNonNull(errorCorrectionLevel); + final boolean isVersionInRange = MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION; + final boolean isMaskOutOfRange = mask < -1 || mask > 7; + if (!isVersionInRange || isMaskOutOfRange) throw new IllegalArgumentException("Invalid value"); + + + int version = findMinimalVersion(segments, errorCorrectionLevel, minVersion, maxVersion); + + int dataUsedBits = QrSegment.getTotalBits(segments, version); + + errorCorrectionLevel = findMaximalErrorCorrectionLevel(errorCorrectionLevel, boostEcl, version, dataUsedBits); + + + BitBuffer bitBuffer = segmentsToBitBuffer(segments, version); + assert bitBuffer.bitLength() == dataUsedBits; + + + int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; + assert bitBuffer.bitLength() <= dataCapacityBits; + bitBuffer.addTerminator(dataCapacityBits); + + + bitBuffer.addPad(dataCapacityBits); + + + byte[] dataCodewords = bitBuffer.toCodewords(); + + // Create the QR Code object + return new QrCode(version, errorCorrectionLevel, dataCodewords, mask); + } + + /*---- Private helper methods for encodeSegments ----*/ + + + + // Concatenate all segments to create the data bit string + private static BitBuffer segmentsToBitBuffer(List segments, int version) { + BitBuffer bitBuffer = new BitBuffer(); + for (QrSegment segment : segments) { + bitBuffer.appendBits(segment.mode.modeBits, 4); + bitBuffer.appendBits(segment.numChars, segment.mode.numCharCountBits(version)); + bitBuffer.appendData(segment.data); + } + return bitBuffer; + } + + + // Increase the error correction level while the data still fits in the current version number + private static Ecc findMaximalErrorCorrectionLevel(Ecc errorCorrectionLevel, boolean boostEcl, int version, + int dataUsedBits) { + for (Ecc newEcl : Ecc.values()) { // From low to high + final boolean canIncreaseErrorCorrectionLevel = dataUsedBits <= getNumDataCodewords(version, newEcl) * 8; + if (boostEcl && canIncreaseErrorCorrectionLevel) + errorCorrectionLevel = newEcl; + } + return errorCorrectionLevel; + } + - // Find the minimal version number to use + //Returns the minimal version number to use + private static int findMinimalVersion(List segments, Ecc errorCorrectionLevel, int minVersion, + int maxVersion) { int version, dataUsedBits; for (version = minVersion; ; version++) { - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = QrSegment.getTotalBits(segs, version); + int dataCapacityBits = getNumDataCodewords(version, errorCorrectionLevel) * 8; // Number of data bits available + dataUsedBits = QrSegment.getTotalBits(segments, version); if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) break; // This version number is found to be suitable if (version >= maxVersion) { // All versions in the range could not fit the given data - String msg = "Segment too long"; + String message = "Segment too long"; if (dataUsedBits != -1) - msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); - throw new DataTooLongException(msg); + message = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(message); } } assert dataUsedBits != -1; - - // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : Ecc.values()) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; - } - - // Concatenate all segments to create the data bit string - BitBuffer bb = new BitBuffer(); - for (QrSegment seg : segs) { - bb.appendBits(seg.mode.modeBits, 4); - bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - bb.appendData(seg.data); - } - assert bb.bitLength() == dataUsedBits; - - // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; - assert bb.bitLength() <= dataCapacityBits; - bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); - bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); - assert bb.bitLength() % 8 == 0; - - // Pad with alternating bytes until data capacity is reached - for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - - // Pack bits into bytes in big endian - byte[] dataCodewords = new byte[bb.bitLength() / 8]; - for (int i = 0; i < bb.bitLength(); i++) - dataCodewords[i >>> 3] |= bb.getBit(i) << (7 - (i & 7)); - - // Create the QR Code object - return new QrCode(version, ecl, dataCodewords, mask); + return version; } - - - + /*---- Instance fields ----*/ - + // Public immutable scalar parameters: - + /** The version number of this QR Code, which is between 1 and 40 (inclusive). * This determines the size of this barcode. */ public final int version; - + /** The width and height of this QR Code, measured in modules, between * 21 and 177 (inclusive). This is equal to version × 4 + 17. */ public final int size; - + /** The error correction level used in this QR Code, which is not {@code null}. */ public final Ecc errorCorrectionLevel; - + /** The index of the mask pattern used in this QR Code, which is between 0 and 7 (inclusive). *

Even if a QR Code is created with automatic masking requested (mask = * −1), the resulting object still has a mask value between 0 and 7. */ public final int mask; - + // Private grids of modules/pixels, with dimensions of size*size: - + // The modules of this QR Code (false = white, true = black). // Immutable after constructor finishes. Accessed through getModule(). private boolean[][] modules; - + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. private boolean[][] isFunction; - - - + + + /*---- Constructor (low level) ----*/ - + /** * Constructs a QR Code with the specified version number, * error correction level, data codeword bytes, and mask number. *

This is a low-level API that most users should not use directly. A mid-level * API is the {@link #encodeSegments(List,Ecc,int,int,int,boolean)} function.

- * @param ver the version number to use, which must be in the range 1 to 40 (inclusive) - * @param ecl the error correction level to use + * @param version the version number to use, which must be in the range 1 to 40 (inclusive) + * @param errorCorrectionLevel the error correction level to use * @param dataCodewords the bytes representing segments to encode (without ECC) - * @param msk the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice + * @param mask the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice * @throws NullPointerException if the byte array or error correction level is {@code null} * @throws IllegalArgumentException if the version or mask value is out of range, * or if the data is the wrong length for the specified version and error correction level */ - public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int msk) { + public QrCode(int version, Ecc errorCorrectionLevel, byte[] dataCodewords, int mask) { // Check arguments and initialize fields - if (ver < MIN_VERSION || ver > MAX_VERSION) + if (version < MIN_VERSION || version > MAX_VERSION) throw new IllegalArgumentException("Version value out of range"); - if (msk < -1 || msk > 7) + if (mask < -1 || mask > 7) throw new IllegalArgumentException("Mask value out of range"); - version = ver; - size = ver * 4 + 17; - errorCorrectionLevel = Objects.requireNonNull(ecl); + this.version = version; + this.size = version * 4 + 17; + this.errorCorrectionLevel = Objects.requireNonNull(errorCorrectionLevel); Objects.requireNonNull(dataCodewords); - modules = new boolean[size][size]; // Initially all white - isFunction = new boolean[size][size]; - + this.modules = new boolean[size][size]; // Initially all white + this.isFunction = new boolean[size][size]; + // Compute ECC, draw modules, do masking drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this.mask = handleConstructorMasking(msk); + this.mask = handleConstructorMasking(mask); isFunction = null; } - - - + + + /*---- Public instance methods ----*/ - + /** * Returns the color of the module (pixel) at the specified coordinates, which is {@code false} * for white or {@code true} for black. The top left corner has the coordinates (x=0, y=0). @@ -283,8 +312,8 @@ public final class QrCode { public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; } - - + + /** * Returns a raster image depicting this QR Code, with the specified module scale and border modules. *

For example, toImage(scale=10, border=4) means to pad the QR Code with 4 white @@ -301,7 +330,7 @@ public final class QrCode { throw new IllegalArgumentException("Value out of range"); if (border > Integer.MAX_VALUE / 2 || size + border * 2L > Integer.MAX_VALUE / scale) throw new IllegalArgumentException("Scale or border too large"); - + BufferedImage result = new BufferedImage((size + border * 2) * scale, (size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); for (int y = 0; y < result.getHeight(); y++) { for (int x = 0; x < result.getWidth(); x++) { @@ -311,8 +340,8 @@ public final class QrCode { } return result; } - - + + /** * Returns a string of SVG code for an image depicting this QR Code, with the specified number * of border modules. The string always uses Unix newlines (\n), regardless of the platform. @@ -325,12 +354,12 @@ public final class QrCode { throw new IllegalArgumentException("Border must be non-negative"); long brd = border; StringBuilder sb = new StringBuilder() - .append("\n") - .append("\n") - .append(String.format("\n", - size + brd * 2)) - .append("\t\n") - .append("\t\n") + .append("\n") + .append(String.format("\n", + size + brd * 2)) + .append("\t\n") + .append("\t\n") - .append("\n") - .toString(); + .append("\" fill=\"#000000\"/>\n") + .append("\n") + .toString(); } - - - + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ - + // Reads this object's version field, and draws and marks all function modules. private void drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (int i = 0; i < size; i++) { - setFunctionModule(6, i, i % 2 == 0); - setFunctionModule(i, 6, i % 2 == 0); + setFunctionModule(TIMING_COORDINATE, i, i % 2 == 0); + setFunctionModule(i, TIMING_COORDINATE, i % 2 == 0); } - + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - drawFinderPattern(3, 3); - drawFinderPattern(size - 4, 3); - drawFinderPattern(3, size - 4); + drawFinderPattern(FINDER_SIZE, FINDER_SIZE); + drawFinderPattern(size - 1 - FINDER_SIZE, FINDER_SIZE); + drawFinderPattern(FINDER_SIZE, size - 1 - FINDER_SIZE); + + drawAlignmentsPatterns(); + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); + } + - // Draw numerous alignment patterns + // Draw numerous alignment patterns + private void drawAlignmentsPatterns() { int[] alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - // Don't draw on the three finder corners - if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) + final boolean isLeftTop = i == 0 && j == 0; + final boolean isLeftBottom = i == 0 && j == numAlign - 1; + final boolean isRightTop = i == numAlign - 1 && j == 0; + final boolean onThreeFinderCorners = isLeftTop || isLeftBottom || isRightTop; + if (!onThreeFinderCorners) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } - - // Draw configuration data - drawFormatBits(0); // Dummy mask value; overwritten later in the constructor - drawVersion(); } - - + + // Draws two copies of the format bits (with its own error correction code) // based on the given mask and this object's error correction level field. private void drawFormatBits(int msk) { @@ -390,7 +427,7 @@ public final class QrCode { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); int bits = (data << 10 | rem) ^ 0x5412; // uint15 assert bits >>> 15 == 0; - + // Draw first copy for (int i = 0; i <= 5; i++) setFunctionModule(8, i, getBit(bits, i)); @@ -399,7 +436,7 @@ public final class QrCode { setFunctionModule(7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) setFunctionModule(14 - i, 8, getBit(bits, i)); - + // Draw second copy for (int i = 0; i < 8; i++) setFunctionModule(size - 1 - i, 8, getBit(bits, i)); @@ -407,21 +444,21 @@ public final class QrCode { setFunctionModule(8, size - 15 + i, getBit(bits, i)); setFunctionModule(8, size - 8, true); // Always black } - - + + // Draws two copies of the version bits (with its own error correction code), // based on this object's version field, iff 7 <= version <= 40. private void drawVersion() { if (version < 7) return; - + // Calculate error correction code and pack bits int rem = version; // version is uint6, in the range [7, 40] for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); int bits = version << 12 | rem; // uint18 assert bits >>> 18 == 0; - + // Draw two copies for (int i = 0; i < 18; i++) { boolean bit = getBit(bits, i); @@ -431,8 +468,8 @@ public final class QrCode { setFunctionModule(b, a, bit); } } - - + + // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. private void drawFinderPattern(int x, int y) { @@ -445,8 +482,8 @@ public final class QrCode { } } } - - + + // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. private void drawAlignmentPattern(int x, int y) { @@ -455,32 +492,32 @@ public final class QrCode { setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1); } } - - + + // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. private void setFunctionModule(int x, int y, boolean isBlack) { modules[y][x] = isBlack; isFunction[y][x] = true; } - - + + /*---- Private helper methods for constructor: Codewords and masking ----*/ - + // Returns a new byte string representing the given data with the appropriate error correction // codewords appended to it, based on this object's version and error correction level. private byte[] addEccAndInterleave(byte[] data) { Objects.requireNonNull(data); if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) 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 rawCodewords = getNumRawDataModules(version) / 8; int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockLen = rawCodewords / numBlocks; - + // Split data into blocks and append ECC to each block byte[][] blocks = new byte[numBlocks][]; byte[] rsDiv = reedSolomonComputeDivisor(blockEccLen); @@ -492,7 +529,7 @@ public final class QrCode { System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); blocks[i] = block; } - + // Interleave (not concatenate) the bytes from every block into a single sequence byte[] result = new byte[rawCodewords]; for (int i = 0, k = 0; i < blocks[0].length; i++) { @@ -506,15 +543,15 @@ public final class QrCode { } return result; } - - + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire // data area of this QR Code. Function modules need to be marked off before this is called. private void drawCodewords(byte[] data) { Objects.requireNonNull(data); if (data.length != getNumRawDataModules(version) / 8) throw new IllegalArgumentException(); - + int i = 0; // Bit index into the data // Do the funny zigzag scan for (int right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair @@ -536,8 +573,8 @@ public final class QrCode { } assert i == data.length * 8; } - - + + // XORs the codeword modules in this QR Code with the given mask pattern. // The function modules must be marked and the codeword bits must be drawn // before masking. Due to the arithmetic of XOR, calling applyMask() with @@ -560,36 +597,39 @@ public final class QrCode { } } - - + // A messy helper function for the constructor. This QR Code must be in an unmasked state when this // method is called. The given argument is the requested mask, which is -1 for auto or 0 to 7 for fixed. // This method applies and returns the actual mask chosen, from 0 to 7. - private int handleConstructorMasking(int msk) { - if (msk == -1) { // Automatically choose best mask - msk = chooseBestMask(msk); + private int handleConstructorMasking(int mask) { + if (mask == -1) { + mask = findBestMask(); } - assert 0 <= msk && msk <= 7; - applyMask(msk); // Apply the final choice of mask - drawFormatBits(msk); // Overwrite old format bits - return msk; // The caller shall assign this value to the final-declared field + assert 0 <= mask && mask <= 7; + applyMask(mask); // Apply the final choice of mask + drawFormatBits(mask); // Overwrite old format bits + return mask; // The caller shall assign this value to the final-declared field } - - private int chooseBestMask(int msk) { + + + // Automatically choose best mask + private int findBestMask() { + int mask = -1; int minPenalty = Integer.MAX_VALUE; for (int i = 0; i < 8; i++) { applyMask(i); drawFormatBits(i); int penalty = getPenaltyScore(); if (penalty < minPenalty) { - msk = i; + mask = i; minPenalty = penalty; } applyMask(i); // Undoes the mask due to XOR } - return msk; + return mask; } + private int havingSameColor(int run, boolean runColor, int[] runHistory, int result, int y, int x) { if (modules[y][x] == runColor) { run++; @@ -887,4 +927,4 @@ public final class QrCode { } } -} +} \ No newline at end of file diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 1efe7b5..b125f18 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -23,12 +23,20 @@ package io.nayuki.qrcodegen; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Objects; +import java.util.Scanner; + import io.nayuki.qrcodegen.QrSegment.Mode; @@ -64,14 +72,14 @@ public final class QrSegmentAdvanced { // Check arguments Objects.requireNonNull(text); Objects.requireNonNull(ecl); - if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) + if (not_Valid_Version(minVersion, maxVersion)) throw new IllegalArgumentException("Invalid value"); // Iterate through version numbers, and make tentative segments List segs = null; int[] codePoints = toCodePoints(text); for (int version = minVersion; ; version++) { - if (version == minVersion || version == 10 || version == 27) + if (is_valid_version(minVersion, version)) segs = makeSegmentsOptimally(codePoints, version); assert segs != null; @@ -88,22 +96,31 @@ public final class QrSegmentAdvanced { } } } + + + private static boolean is_valid_version(int minVersion, int version) { + return version == minVersion || version == 10 || version == 27; + } + + + private static boolean not_Valid_Version(int minVersion, int maxVersion) { + return !(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION); + } // Returns a new list of segments that is optimal for the given text at the given version number. private static List makeSegmentsOptimally(int[] codePoints, int version) { if (codePoints.length == 0) return new ArrayList<>(); - Mode[] charModes = computeCharacterModes(codePoints, version); + QrMode[] charModes = computeCharacterModes(codePoints, version); return splitIntoSegments(codePoints, charModes); } - // Returns a new array representing the optimal mode per code point based on the given text and version. - private static Mode[] computeCharacterModes(int[] codePoints, int version) { + private static QrMode[] computeCharacterModes(int[] codePoints, int version) { if (codePoints.length == 0) throw new IllegalArgumentException(); - final Mode[] modeTypes = {Mode.BYTE, Mode.ALPHANUMERIC, Mode.NUMERIC, Mode.KANJI}; // Do not modify + final QrMode[] modeTypes = {new ByteMode(), new AlphanumericMode(), new NumericMode(), new KanjiMode()}; // Do not modify final int numModes = modeTypes.length; // Segment header sizes, measured in 1/6 bits @@ -114,7 +131,7 @@ public final class QrSegmentAdvanced { // charModes[i][j] represents the mode to encode the code point at // index i such that the final segment ends in modeTypes[j] and the // total number of bits is minimized over all possible choices - Mode[][] charModes = new Mode[codePoints.length][numModes]; + QrMode[][] charModes = new QrMode[codePoints.length][numModes]; // At the beginning of each iteration of the loop below, // prevCosts[j] is the exact minimum number of 1/6 bits needed to @@ -122,27 +139,42 @@ public final class QrSegmentAdvanced { int[] prevCosts = headCosts.clone(); // Calculate costs using dynamic programming + prevCosts = calculate_cost(codePoints, modeTypes, numModes, headCosts, charModes, prevCosts); + + // Find optimal ending mode + QrMode curMode = null; + curMode = find_optimal(modeTypes, numModes, prevCosts, curMode); + + // Get optimal mode for each code point by tracing backwards + QrMode[] result = new QrMode[codePoints.length]; + get_optimal(modeTypes, numModes, charModes, curMode, result); + return result; + } + + + private static int[] calculate_cost(int[] codePoints, final QrMode[] modeTypes, final int numModes, + final int[] headCosts, QrMode[][] charModes, int[] prevCosts) { for (int i = 0; i < codePoints.length; i++) { - int c = codePoints[i]; + int codePoint = codePoints[i]; int[] curCosts = new int[numModes]; { // Always extend a byte mode segment - curCosts[0] = prevCosts[0] + countUtf8Bytes(c) * 8 * 6; - charModes[i][0] = modeTypes[0]; + charModes[i][0] = new ByteMode(); + curCosts[0] = charModes[i][0].getcost(prevCosts[0], codePoint); } // Extend a segment if possible - if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1) { // Is alphanumeric - curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char - charModes[i][1] = modeTypes[1]; + if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(codePoint) != -1) { // Is alphanumeric + charModes[i][1] = new AlphanumericMode(); + curCosts[1] = charModes[i][1].getcost(prevCosts[1], codePoint); // 5.5 bits per alphanumeric char } - if ('0' <= c && c <= '9') { // Is numeric - curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit - charModes[i][2] = modeTypes[2]; + if ('0' <= codePoint && codePoint <= '9') { // Is numeric + charModes[i][2] = new NumericMode(); + curCosts[2] = charModes[i][2].getcost(prevCosts[2], codePoint); // 3.33 bits per digit } - if (isKanji(c)) { - curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char - charModes[i][3] = modeTypes[3]; + if (isKanji(codePoint)) { + charModes[i][3] = new KanjiMode(); + curCosts[3] = charModes[i][3].getcost(prevCosts[3], codePoint);; // 13 bits per Shift JIS char } - + // Start new segment at the end to switch modes for (int j = 0; j < numModes; j++) { // To mode for (int k = 0; k < numModes; k++) { // From mode @@ -156,18 +188,12 @@ public final class QrSegmentAdvanced { prevCosts = curCosts; } - - // Find optimal ending mode - Mode curMode = null; - for (int i = 0, minCost = 0; i < numModes; i++) { - if (curMode == null || prevCosts[i] < minCost) { - minCost = prevCosts[i]; - curMode = modeTypes[i]; - } - } - - // Get optimal mode for each code point by tracing backwards - Mode[] result = new Mode[charModes.length]; + return prevCosts; + } + + + private static void get_optimal(final QrMode[] modeTypes, final int numModes, QrMode[][] charModes, QrMode curMode, + QrMode[] result) { for (int i = result.length - 1; i >= 0; i--) { for (int j = 0; j < numModes; j++) { if (modeTypes[j] == curMode) { @@ -177,34 +203,45 @@ public final class QrSegmentAdvanced { } } } - return result; + } + + + private static QrMode find_optimal(final QrMode[] modeTypes, final int numModes, int[] prevCosts, QrMode curMode) { + for (int i = 0, minCost = 0; i < numModes; i++) { + if (curMode == null || prevCosts[i] < minCost) { + minCost = prevCosts[i]; + curMode = modeTypes[i]; + } + } + return curMode; + } + + + private static boolean is_numeric(int convertedPoint) { + return '0' <= convertedPoint && convertedPoint <= '9'; + } + + + private static boolean is_alphanumeric(int convertedPoint) { + return QrSegment.ALPHANUMERIC_CHARSET.indexOf(convertedPoint) != -1; } // Returns a new list of segments based on the given text and modes, such that // consecutive code points in the same mode are put into the same segment. - private static List splitIntoSegments(int[] codePoints, Mode[] charModes) { + private static List splitIntoSegments(int[] codePoints, QrMode[] charModes) { if (codePoints.length == 0) throw new IllegalArgumentException(); List result = new ArrayList<>(); // Accumulate run of modes - Mode curMode = charModes[0]; + QrMode curMode = charModes[0]; int start = 0; for (int i = 1; ; i++) { if (i < codePoints.length && charModes[i] == curMode) continue; String s = new String(codePoints, start, i - start); - if (curMode == Mode.BYTE) - result.add(QrSegment.makeBytes(s.getBytes(StandardCharsets.UTF_8))); - else if (curMode == Mode.NUMERIC) - result.add(QrSegment.makeNumeric(s)); - else if (curMode == Mode.ALPHANUMERIC) - result.add(QrSegment.makeAlphanumeric(s)); - else if (curMode == Mode.KANJI) - result.add(makeKanji(s)); - else - throw new AssertionError(); + result.add(curMode.making(s)); if (i >= codePoints.length) return result; curMode = charModes[i]; @@ -215,8 +252,8 @@ public final class QrSegmentAdvanced { // Returns a new array of Unicode code points (effectively // UTF-32 / UCS-4) representing the given UTF-16 string. - private static int[] toCodePoints(String s) { - int[] result = s.codePoints().toArray(); + private static int[] toCodePoints(String str) { + int[] result = str.codePoints().toArray(); for (int c : result) { if (Character.isSurrogate((char)c)) throw new IllegalArgumentException("Invalid UTF-16 string"); @@ -226,12 +263,12 @@ public final class QrSegmentAdvanced { // Returns the number of UTF-8 bytes needed to encode the given Unicode code point. - private static int countUtf8Bytes(int cp) { - if (cp < 0) throw new IllegalArgumentException("Invalid code point"); - else if (cp < 0x80) return 1; - else if (cp < 0x800) return 2; - else if (cp < 0x10000) return 3; - else if (cp < 0x110000) return 4; + public static int countUtf8Bytes(int currentPoint) { + if (currentPoint < 0) throw new IllegalArgumentException("Invalid code point"); + else if (currentPoint < 0x80) return 1; + else if (currentPoint < 0x800) return 2; + else if (currentPoint < 0x10000) return 3; + else if (currentPoint < 0x110000) return 4; else throw new IllegalArgumentException("Invalid code point"); } @@ -277,8 +314,7 @@ public final class QrSegmentAdvanced { */ public static boolean isEncodableAsKanji(String text) { Objects.requireNonNull(text); - return text.chars().allMatch( - c -> isKanji((char)c)); + return text.chars().allMatch(c -> isKanji((char)c)); } @@ -286,119 +322,8 @@ public final class QrSegmentAdvanced { return c < UNICODE_TO_QR_KANJI.length && UNICODE_TO_QR_KANJI[c] != -1; } - - // Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT - private static final String PACKED_QR_KANJI_TO_UNICODE = - "MAAwATAC/wz/DjD7/xr/G/8f/wEwmzCcALT/QACo/z7/4/8/MP0w/jCdMJ4wA07dMAUwBjAHMPwgFSAQ/w8AXDAcIBb/XCAmICUgGCAZIBwgHf8I/wkwFDAV/zv/Pf9b/10wCDAJMAowCzAMMA0wDjAPMBAwEf8LIhIAsQDX//8A9/8dImD/HP8eImYiZyIeIjQmQiZA" + - "ALAgMiAzIQP/5f8EAKIAo/8F/wP/Bv8K/yAApyYGJgUlyyXPJc4lxyXGJaEloCWzJbIlvSW8IDswEiGSIZAhkSGTMBP/////////////////////////////IggiCyKGIocigiKDIioiKf////////////////////8iJyIoAKwh0iHUIgAiA///////////////////" + - "//////////8iICKlIxIiAiIHImEiUiJqImsiGiI9Ih0iNSIrIiz//////////////////yErIDAmbyZtJmogICAhALb//////////yXv/////////////////////////////////////////////////xD/Ef8S/xP/FP8V/xb/F/8Y/xn///////////////////8h" + - "/yL/I/8k/yX/Jv8n/yj/Kf8q/yv/LP8t/y7/L/8w/zH/Mv8z/zT/Nf82/zf/OP85/zr///////////////////9B/0L/Q/9E/0X/Rv9H/0j/Sf9K/0v/TP9N/07/T/9Q/1H/Uv9T/1T/Vf9W/1f/WP9Z/1r//////////zBBMEIwQzBEMEUwRjBHMEgwSTBKMEswTDBN" + - "ME4wTzBQMFEwUjBTMFQwVTBWMFcwWDBZMFowWzBcMF0wXjBfMGAwYTBiMGMwZDBlMGYwZzBoMGkwajBrMGwwbTBuMG8wcDBxMHIwczB0MHUwdjB3MHgweTB6MHswfDB9MH4wfzCAMIEwgjCDMIQwhTCGMIcwiDCJMIowizCMMI0wjjCPMJAwkTCSMJP/////////////" + - "////////////////////////MKEwojCjMKQwpTCmMKcwqDCpMKowqzCsMK0wrjCvMLAwsTCyMLMwtDC1MLYwtzC4MLkwujC7MLwwvTC+ML8wwDDBMMIwwzDEMMUwxjDHMMgwyTDKMMswzDDNMM4wzzDQMNEw0jDTMNQw1TDWMNcw2DDZMNow2zDcMN0w3jDf//8w4DDh" + - "MOIw4zDkMOUw5jDnMOgw6TDqMOsw7DDtMO4w7zDwMPEw8jDzMPQw9TD2/////////////////////wORA5IDkwOUA5UDlgOXA5gDmQOaA5sDnAOdA54DnwOgA6EDowOkA6UDpgOnA6gDqf////////////////////8DsQOyA7MDtAO1A7YDtwO4A7kDugO7A7wDvQO+" + - "A78DwAPBA8MDxAPFA8YDxwPIA8n/////////////////////////////////////////////////////////////////////////////////////////////////////////////BBAEEQQSBBMEFAQVBAEEFgQXBBgEGQQaBBsEHAQdBB4EHwQgBCEEIgQjBCQEJQQm" + - "BCcEKAQpBCoEKwQsBC0ELgQv////////////////////////////////////////BDAEMQQyBDMENAQ1BFEENgQ3BDgEOQQ6BDsEPAQ9//8EPgQ/BEAEQQRCBEMERARFBEYERwRIBEkESgRLBEwETQROBE///////////////////////////////////yUAJQIlDCUQ" + - "JRglFCUcJSwlJCU0JTwlASUDJQ8lEyUbJRclIyUzJSslOyVLJSAlLyUoJTclPyUdJTAlJSU4JUL/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////////////06cVRZaA5Y/VMBhG2MoWfaQIoR1gxx6UGCqY+FuJWXthGaCppv1aJNXJ2WhYnFbm1nQhnuY9H1ifb6bjmIWfJ+It1uJXrVjCWaXaEiVx5eNZ09O5U8KT01PnVBJVvJZN1nUWgFcCWDfYQ9hcGYTaQVwunVPdXB5+32t" + - "fe+Aw4QOiGOLApBVkHpTO06VTqVX34CykMF4704AWPFuopA4ejKDKIKLnC9RQVNwVL1U4VbgWftfFZjybeuA5IUt////////lmKWcJagl/tUC1PzW4dwz3+9j8KW6FNvnVx6uk4ReJOB/G4mVhhVBGsdhRqcO1nlU6ltZnTclY9WQk6RkEuW8oNPmQxT4VW2WzBfcWYg" + - "ZvNoBGw4bPNtKXRbdsh6Tpg0gvGIW4pgku1tsnWrdsqZxWCmiwGNipWyaY5TrVGG//9XElgwWURbtF72YChjqWP0bL9vFHCOcRRxWXHVcz9+AYJ2gtGFl5BgkludG1hpZbxsWnUlUflZLlllX4Bf3GK8ZfpqKmsna7Rzi3/BiVadLJ0OnsRcoWyWg3tRBFxLYbaBxmh2" + - "cmFOWU/6U3hgaW4pek+X804LUxZO7k9VTz1PoU9zUqBT71YJWQ9awVu2W+F50WaHZ5xntmtMbLNwa3PCeY15vno8e4eCsYLbgwSDd4Pvg9OHZoqyVimMqI/mkE6XHoaKT8Rc6GIRcll1O4Hlgr2G/ozAlsWZE5nVTstPGonjVt5YSljKXvtf62AqYJRgYmHQYhJi0GU5" + - "////////m0FmZmiwbXdwcHVMdoZ9dYKlh/mVi5aOjJ1R8VK+WRZUs1uzXRZhaGmCba94jYTLiFeKcpOnmrhtbJmohtlXo2f/hs6SDlKDVodUBF7TYuFkuWg8aDhru3NyeLp6a4maidKNa48DkO2Vo5aUl2lbZlyzaX2YTZhOY5t7IGor//9qf2i2nA1vX1JyVZ1gcGLs" + - "bTtuB27RhFuJEI9EThScOVP2aRtqOpeEaCpRXHrDhLKR3JOMVludKGgigwWEMXylUgiCxXTmTn5Pg1GgW9JSClLYUudd+1WaWCpZ5luMW5hb215yXnlgo2EfYWNhvmPbZWJn0WhTaPprPmtTbFdvIm+Xb0V0sHUYduN3C3r/e6F8IX3pfzZ/8ICdgmaDnomzisyMq5CE" + - "lFGVk5WRlaKWZZfTmSiCGE44VCtcuF3Mc6l2THc8XKl/640LlsGYEZhUmFhPAU8OU3FVnFZoV/pZR1sJW8RckF4MXn5fzGPuZzpl12XiZx9oy2jE////////al9eMGvFbBdsfXV/eUhbY3oAfQBfvYmPihiMtI13jsyPHZjimg6bPE6AUH1RAFmTW5xiL2KAZOxrOnKg" + - "dZF5R3+ph/uKvItwY6yDypegVAlUA1WraFRqWIpweCdndZ7NU3RbooEahlCQBk4YTkVOx08RU8pUOFuuXxNgJWVR//9nPWxCbHJs43B4dAN6dnquewh9Gnz+fWZl53JbU7tcRV3oYtJi4GMZbiCGWooxjd2S+G8BeaabWk6oTqtOrE+bT6BQ0VFHevZRcVH2U1RTIVN/" + - "U+tVrFiDXOFfN19KYC9gUGBtYx9lWWpLbMFywnLtd++A+IEFggiFTpD3k+GX/5lXmlpO8FHdXC1mgWltXEBm8ml1c4loUHyBUMVS5FdHXf6TJmWkayNrPXQ0eYF5vXtLfcqCuYPMiH+JX4s5j9GR0VQfkoBOXVA2U+VTOnLXc5Z36YLmjq+ZxpnImdJRd2Eahl5VsHp6" + - "UHZb05BHloVOMmrbkedcUVxI////////Y5h6n2yTl3SPYXqqcYqWiHyCaBd+cGhRk2xS8lQbhauKE3+kjs2Q4VNmiIh5QU/CUL5SEVFEVVNXLXPqV4tZUV9iX4RgdWF2YWdhqWOyZDplbGZvaEJuE3Vmej18+31MfZl+S39rgw6DSobNigiKY4tmjv2YGp2PgriPzpvo" + - "//9Sh2IfZINvwJaZaEFQkWsgbHpvVHp0fVCIQIojZwhO9lA5UCZQZVF8UjhSY1WnVw9YBVrMXvphsmH4YvNjcmkcailyfXKscy54FHhvfXl3DICpiYuLGYzijtKQY5N1lnqYVZoTnnhRQ1OfU7Nee18mbhtukHOEc/59Q4I3igCK+pZQTk5QC1PkVHxW+lnRW2Rd8V6r" + - "XydiOGVFZ69uVnLQfMqItIChgOGD8IZOioeN6JI3lseYZ58TTpROkk8NU0hUSVQ+Wi9fjF+hYJ9op2qOdFp4gYqeiqSLd5GQTl6byU6kT3xPr1AZUBZRSVFsUp9SuVL+U5pT41QR////////VA5ViVdRV6JZfVtUW11bj13lXedd9154XoNeml63XxhgUmFMYpdi2GOn" + - "ZTtmAmZDZvRnbWghaJdpy2xfbSptaW4vbp11MnaHeGx6P3zgfQV9GH1efbGAFYADgK+AsYFUgY+CKoNSiEyIYYsbjKKM/JDKkXWScXg/kvyVpJZN//+YBZmZmtidO1JbUqtT91QIWNVi92/gjGqPX565UUtSO1RKVv16QJF3nWCe0nNEbwmBcHURX/1g2pqoctuPvGtk" + - "mANOylbwV2RYvlpaYGhhx2YPZgZoOWixbfd11X06gm6bQk6bT1BTyVUGXW9d5l3uZ/tsmXRzeAKKUJOWiN9XUF6nYytQtVCsUY1nAFTJWF5Zu1uwX2liTWOhaD1rc24IcH2Rx3KAeBV4JnltZY59MIPciMGPCZabUmRXKGdQf2qMoVG0V0KWKlg6aYqAtFSyXQ5X/HiV" + - "nfpPXFJKVItkPmYoZxRn9XqEe1Z9IpMvaFybrXs5UxlRilI3////////W99i9mSuZOZnLWu6hamW0XaQm9ZjTJMGm6t2v2ZSTglQmFPCXHFg6GSSZWNoX3Hmc8p1I3uXfoKGlYuDjNuReJkQZaxmq2uLTtVO1E86T39SOlP4U/JV41bbWOtZy1nJWf9bUFxNXgJeK1/X" + - "YB1jB2UvW1xlr2W9ZehnnWti//9re2wPc0V5SXnBfPh9GX0rgKKBAoHziZaKXoppimaKjIrujMeM3JbMmPxrb06LTzxPjVFQW1db+mFIYwFmQmshbstsu3I+dL111HjBeTqADIAzgeqElI+ebFCef18Pi1idK3r6jvhbjZbrTgNT8Vf3WTFayVukYIluf28Gdb6M6luf" + - "hQB74FByZ/SCnVxhhUp+HoIOUZlcBGNojWZlnHFueT59F4AFix2OypBuhseQqlAfUvpcOmdTcHxyNZFMkciTK4LlW8JfMWD5TjtT1luIYktnMWuKculz4HougWuNo5FSmZZRElPXVGpb/2OIajl9rJcAVtpTzlRo////////W5dcMV3eT+5hAWL+bTJ5wHnLfUJ+TX/S" + - "ge2CH4SQiEaJcouQjnSPL5AxkUuRbJbGkZxOwE9PUUVTQV+TYg5n1GxBbgtzY34mkc2Sg1PUWRlbv23ReV1+LnybWH5xn1H6iFOP8E/KXPtmJXeseuOCHJn/UcZfqmXsaW9riW3z//9ulm9kdv59FF3hkHWRh5gGUeZSHWJAZpFm2W4aXrZ90n9yZviFr4X3ivhSqVPZ" + - "WXNej1+QYFWS5JZkULdRH1LdUyBTR1PsVOhVRlUxVhdZaFm+WjxbtVwGXA9cEVwaXoReil7gX3Bif2KEYttjjGN3ZgdmDGYtZnZnfmiiah9qNWy8bYhuCW5YcTxxJnFndcd3AXhdeQF5ZXnweuB7EXynfTmAloPWhIuFSYhdiPOKH4o8ilSKc4xhjN6RpJJmk36UGJac" + - "l5hOCk4ITh5OV1GXUnBXzlg0WMxbIl44YMVk/mdhZ1ZtRHK2dXN6Y4S4i3KRuJMgVjFX9Jj+////////Yu1pDWuWce1+VIB3gnKJ5pjfh1WPsVw7TzhP4U+1VQdaIFvdW+lfw2FOYy9lsGZLaO5pm214bfF1M3W5dx95XnnmfTOB44KvhaqJqoo6jquPm5Aykd2XB066" + - "TsFSA1h1WOxcC3UaXD2BTooKj8WWY5dteyWKz5gIkWJW81Oo//+QF1Q5V4JeJWOobDRwindhfIt/4IhwkEKRVJMQkxiWj3RemsRdB11pZXBnoo2olttjbmdJaRmDxZgXlsCI/m+EZHpb+E4WcCx1XWYvUcRSNlLiWdNfgWAnYhBlP2V0Zh9mdGjyaBZrY24FcnJ1H3bb" + - "fL6AVljwiP2Jf4qgipOKy5AdkZKXUpdZZYl6DoEGlrteLWDcYhplpWYUZ5B383pNfE1+PoEKjKyNZI3hjl94qVIHYtljpWRCYpiKLXqDe8CKrJbqfXaCDIdJTtlRSFNDU2Bbo1wCXBZd3WImYkdksGgTaDRsyW1FbRdn029ccU5xfWXLen97rX3a////////fkp/qIF6" + - "ghuCOYWmim6Mzo31kHiQd5KtkpGVg5uuUk1VhG84cTZRaHmFflWBs3zOVkxYUVyoY6pm/mb9aVpy2XWPdY55DnlWed98l30gfUSGB4o0ljuQYZ8gUOdSdVPMU+JQCVWqWO5ZT3I9W4tcZFMdYONg82NcY4NjP2O7//9kzWXpZvld42nNaf1vFXHlTol16Xb4epN8333P" + - "fZyAYYNJg1iEbIS8hfuIxY1wkAGQbZOXlxyaElDPWJdhjoHThTWNCJAgT8NQdFJHU3Ngb2NJZ19uLI2zkB9P11xejMplz32aU1KIllF2Y8NbWFtrXApkDWdRkFxO1lkaWSpscIpRVT5YFVmlYPBiU2fBgjVpVZZAmcSaKE9TWAZb/oAQXLFeL1+FYCBhS2I0Zv9s8G7e" + - "gM6Bf4LUiIuMuJAAkC6Wip7bm9tO41PwWSd7LJGNmEyd+W7dcCdTU1VEW4ViWGKeYtNsom/vdCKKF5Q4b8GK/oM4UeeG+FPq////////U+lPRpBUj7BZaoExXf166o+/aNqMN3L4nEhqPYqwTjlTWFYGV2ZixWOiZeZrTm3hbltwrXfteu97qn27gD2AxobLipWTW1bj" + - "WMdfPmWtZpZqgGu1dTeKx1Akd+VXMF8bYGVmemxgdfR6Gn9ugfSHGJBFmbN7yXVcevl7UYTE//+QEHnpepKDNlrhd0BOLU7yW5lf4GK9Zjxn8WzohmuId4o7kU6S85nQahdwJnMqgueEV4yvTgFRRlHLVYtb9V4WXjNegV8UXzVfa1+0YfJjEWaiZx1vbnJSdTp3OoB0" + - "gTmBeId2ir+K3I2FjfOSmpV3mAKc5VLFY1d29GcVbIhzzYzDk66Wc20lWJxpDmnMj/2TmnXbkBpYWmgCY7Rp+09Dbyxn2I+7hSZ9tJNUaT9vcFdqWPdbLH0scipUCpHjnbROrU9OUFxQdVJDjJ5USFgkW5peHV6VXq1e918fYIxitWM6Y9Bor2xAeId5jnoLfeCCR4oC" + - "iuaORJAT////////kLiRLZHYnw5s5WRYZOJldW70doR7G5Bpk9FuulTyX7lkpI9Nj+2SRFF4WGtZKVxVXpdt+36PdRyMvI7imFtwuU8da79vsXUwlvtRTlQQWDVYV1msXGBfkmWXZ1xuIXZ7g9+M7ZAUkP2TTXgleDpSql6mVx9ZdGASUBJRWlGs//9RzVIAVRBYVFhY" + - "WVdblVz2XYtgvGKVZC1ncWhDaLxo33bXbdhub22bcG9xyF9Tddh5d3tJe1R7UnzWfXFSMIRjhWmF5IoOiwSMRo4PkAOQD5QZlnaYLZowldhQzVLVVAxYAlwOYadknm0ed7N65YD0hASQU5KFXOCdB1M/X5dfs22ccnl3Y3m/e+Rr0nLsiq1oA2phUfh6gWk0XEqc9oLr" + - "W8WRSXAeVnhcb2DHZWZsjIxakEGYE1RRZseSDVlIkKNRhU5NUeqFmYsOcFhjepNLaWKZtH4EdXdTV2lgjt+W42xdToxcPF8Qj+lTAozRgImGeV7/ZeVOc1Fl////////WYJcP5fuTvtZil/Nio1v4XmweWJb54RxcytxsV50X/Vje2SaccN8mE5DXvxOS1fcVqJgqW/D" + - "fQ2A/YEzgb+PsomXhqRd9GKKZK2Jh2d3bOJtPnQ2eDRaRn91gq2ZrE/zXsNi3WOSZVdnb3bDckyAzIC6jymRTVANV/lakmiF//9pc3Fkcv2Mt1jyjOCWapAZh3955HfnhClPL1JlU1pizWfPbMp2fXuUfJWCNoWEj+tm3W8gcgZ+G4OrmcGeplH9e7F4cnu4gId7SGro" + - "XmGAjHVRdWBRa5Jibox2epGXmupPEH9wYpx7T5WlnOlWelhZhuSWvE80UiRTSlPNU9teBmQsZZFnf2w+bE5ySHKvc+11VH5BgiyF6Yype8SRxnFpmBKY72M9Zml1anbkeNCFQ4buUypTUVQmWYNeh198YLJiSWJ5YqtlkGvUbMx1snaueJF52H3Lf3eApYirirmMu5B/" + - "l16Y22oLfDhQmVw+X65nh2vYdDV3CX+O////////nztnynoXUzl1i5rtX2aBnYPxgJhfPF/FdWJ7RpA8aGdZ61qbfRB2fossT/VfamoZbDdvAnTieWiIaIpVjHle32PPdcV50oLXkyiS8oSchu2cLVTBX2xljG1ccBWMp4zTmDtlT3T2Tg1O2FfgWStaZlvMUaheA16c" + - "YBZidmV3//9lp2ZubW5yNnsmgVCBmoKZi1yMoIzmjXSWHJZET65kq2tmgh6EYYVqkOhcAWlTmKiEeoVXTw9Sb1+pXkVnDXmPgXmJB4mGbfVfF2JVbLhOz3Jpm5JSBlQ7VnRYs2GkYm5xGllufIl83n0blvBlh4BeThlPdVF1WEBeY15zXwpnxE4mhT2ViZZbfHOYAVD7" + - "WMF2VninUiV3pYURe4ZQT1kJckd7x33oj7qP1JBNT79SyVopXwGXrU/dgheS6lcDY1VraXUriNyPFHpCUt9Yk2FVYgpmrmvNfD+D6VAjT/hTBVRGWDFZSVudXPBc710pXpZisWNnZT5luWcL////////bNVs4XD5eDJ+K4DegrOEDITshwKJEooqjEqQppLSmP2c851s" + - "Tk9OoVCNUlZXSlmoXj1f2F/ZYj9mtGcbZ9Bo0lGSfSGAqoGoiwCMjIy/kn6WMlQgmCxTF1DVU1xYqGSyZzRyZ3dmekaR5lLDbKFrhlgAXkxZVGcsf/tR4XbG//9kaXjom1Seu1fLWblmJ2eaa85U6WnZXlWBnGeVm6pn/pxSaF1Opk/jU8hiuWcrbKuPxE+tfm2ev04H" + - "YWJugG8rhRNUc2cqm0Vd83uVXKxbxoccbkqE0XoUgQhZmXyNbBF3IFLZWSJxIXJfd9uXJ51haQtaf1oYUaVUDVR9Zg5234/3kpic9Fnqcl1uxVFNaMl9v33sl2KeumR4aiGDAlmEW19r23MbdvJ9soAXhJlRMmcontl27mdiUv+ZBVwkYjt8foywVU9gtn0LlYBTAU5f" + - "UbZZHHI6gDaRzl8ld+JThF95fQSFrIozjo2XVmfzha6UU2EJYQhsuXZS////////iu2POFUvT1FRKlLHU8tbpV59YKBhgmPWZwln2m5nbYxzNnM3dTF5UIjVipiQSpCRkPWWxIeNWRVOiE9ZTg6KiY8/mBBQrV58WZZbuV64Y9pj+mTBZtxpSmnYbQtutnGUdSh6r3+K" + - "gACESYTJiYGLIY4KkGWWfZkKYX5ikWsy//9sg210f8x//G3Af4WHuoj4Z2WDsZg8lvdtG31hhD2Rak5xU3VdUGsEb+uFzYYtiadSKVQPXGVnTmiodAZ0g3XiiM+I4ZHMluKWeF+Lc4d6y4ROY6B1ZVKJbUFunHQJdVl4a3ySloZ63J+NT7ZhbmXFhlxOhk6uUNpOIVHM" + - "W+5lmWiBbbxzH3ZCd616HHzngm+K0pB8kc+WdZgYUpt90VArU5hnl23LcdB0M4HojyqWo5xXnp90YFhBbZl9L5heTuRPNk+LUbdSsV26YBxzsnk8gtOSNJa3lvaXCp6Xn2Jmpmt0UhdSo3DIiMJeyWBLYZBvI3FJfD599IBv////////hO6QI5MsVEKbb2rTcImMwo3v" + - "lzJStFpBXspfBGcXaXxplG1qbw9yYnL8e+2AAYB+h0uQzlFtnpN5hICLkzKK1lAtVIyKcWtqjMSBB2DRZ6Cd8k6ZTpicEIprhcGFaGkAbn54l4FV////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////18MThBOFU4qTjFONk48Tj9OQk5WTlhOgk6FjGtOioISXw1Ojk6eTp9OoE6iTrBOs062Ts5OzU7ETsZOwk7XTt5O7U7fTvdPCU9aTzBPW09dT1dPR092T4hPj0+YT3tPaU9wT5FPb0+GT5ZRGE/UT99Pzk/YT9tP0U/aT9BP5E/l" + - "UBpQKFAUUCpQJVAFTxxP9lAhUClQLE/+T+9QEVAGUENQR2cDUFVQUFBIUFpQVlBsUHhQgFCaUIVQtFCy////////UMlQylCzUMJQ1lDeUOVQ7VDjUO5Q+VD1UQlRAVECURZRFVEUURpRIVE6UTdRPFE7UT9RQFFSUUxRVFFievhRaVFqUW5RgFGCVthRjFGJUY9RkVGT" + - "UZVRllGkUaZRolGpUapRq1GzUbFRslGwUbVRvVHFUclR21HghlVR6VHt//9R8FH1Uf5SBFILUhRSDlInUipSLlIzUjlST1JEUktSTFJeUlRSalJ0UmlSc1J/Un1SjVKUUpJScVKIUpGPqI+nUqxSrVK8UrVSwVLNUtdS3lLjUuaY7VLgUvNS9VL4UvlTBlMIdThTDVMQ" + - "Uw9TFVMaUyNTL1MxUzNTOFNAU0ZTRU4XU0lTTVHWU15TaVNuWRhTe1N3U4JTllOgU6ZTpVOuU7BTtlPDfBKW2VPfZvxx7lPuU+hT7VP6VAFUPVRAVCxULVQ8VC5UNlQpVB1UTlSPVHVUjlRfVHFUd1RwVJJUe1SAVHZUhFSQVIZUx1SiVLhUpVSsVMRUyFSo////////" + - "VKtUwlSkVL5UvFTYVOVU5lUPVRRU/VTuVO1U+lTiVTlVQFVjVUxVLlVcVUVVVlVXVThVM1VdVZlVgFSvVYpVn1V7VX5VmFWeVa5VfFWDValVh1WoVdpVxVXfVcRV3FXkVdRWFFX3VhZV/lX9VhtV+VZOVlBx31Y0VjZWMlY4//9Wa1ZkVi9WbFZqVoZWgFaKVqBWlFaP" + - "VqVWrla2VrRWwla8VsFWw1bAVshWzlbRVtNW11buVvlXAFb/VwRXCVcIVwtXDVcTVxhXFlXHVxxXJlc3VzhXTlc7V0BXT1dpV8BXiFdhV39XiVeTV6BXs1ekV6pXsFfDV8ZX1FfSV9NYClfWV+NYC1gZWB1YclghWGJYS1hwa8BYUlg9WHlYhVi5WJ9Yq1i6WN5Yu1i4" + - "WK5YxVjTWNFY11jZWNhY5VjcWORY31jvWPpY+Vj7WPxY/VkCWQpZEFkbaKZZJVksWS1ZMlk4WT560llVWVBZTllaWVhZYllgWWdZbFlp////////WXhZgVmdT15Pq1mjWbJZxlnoWdxZjVnZWdpaJVofWhFaHFoJWhpaQFpsWklaNVo2WmJaalqaWrxavlrLWsJavVrj" + - "Wtda5lrpWtZa+lr7WwxbC1sWWzJa0FsqWzZbPltDW0VbQFtRW1VbWltbW2VbaVtwW3NbdVt4ZYhbeluA//9bg1umW7hbw1vHW8lb1FvQW+Rb5lviW95b5VvrW/Bb9lvzXAVcB1wIXA1cE1wgXCJcKFw4XDlcQVxGXE5cU1xQXE9bcVxsXG5OYlx2XHlcjFyRXJRZm1yr" + - "XLtctly8XLdcxVy+XMdc2VzpXP1c+lztXYxc6l0LXRVdF11cXR9dG10RXRRdIl0aXRldGF1MXVJdTl1LXWxdc112XYddhF2CXaJdnV2sXa5dvV2QXbddvF3JXc1d013SXdZd213rXfJd9V4LXhpeGV4RXhteNl43XkReQ15AXk5eV15UXl9eYl5kXkdedV52XnqevF5/" + - "XqBewV7CXshe0F7P////////XtZe417dXtpe217iXuFe6F7pXuxe8V7zXvBe9F74Xv5fA18JX11fXF8LXxFfFl8pXy1fOF9BX0hfTF9OXy9fUV9WX1dfWV9hX21fc193X4Nfgl9/X4pfiF+RX4dfnl+ZX5hfoF+oX61fvF/WX/tf5F/4X/Ff3WCzX/9gIWBg//9gGWAQ" + - "YClgDmAxYBtgFWArYCZgD2A6YFpgQWBqYHdgX2BKYEZgTWBjYENgZGBCYGxga2BZYIFgjWDnYINgmmCEYJtglmCXYJJgp2CLYOFguGDgYNNgtF/wYL1gxmC1YNhhTWEVYQZg9mD3YQBg9GD6YQNhIWD7YPFhDWEOYUdhPmEoYSdhSmE/YTxhLGE0YT1hQmFEYXNhd2FY" + - "YVlhWmFrYXRhb2FlYXFhX2FdYVNhdWGZYZZhh2GsYZRhmmGKYZFhq2GuYcxhymHJYfdhyGHDYcZhumHLf3lhzWHmYeNh9mH6YfRh/2H9Yfxh/mIAYghiCWINYgxiFGIb////////Yh5iIWIqYi5iMGIyYjNiQWJOYl5iY2JbYmBiaGJ8YoJiiWJ+YpJik2KWYtRig2KU" + - "Ytdi0WK7Ys9i/2LGZNRiyGLcYsxiymLCYsdim2LJYwxi7mLxYydjAmMIYu9i9WNQYz5jTWQcY09jlmOOY4Bjq2N2Y6Njj2OJY59jtWNr//9jaWO+Y+ljwGPGY+NjyWPSY/ZjxGQWZDRkBmQTZCZkNmUdZBdkKGQPZGdkb2R2ZE5lKmSVZJNkpWSpZIhkvGTaZNJkxWTH" + - "ZLtk2GTCZPFk54IJZOBk4WKsZONk72UsZPZk9GTyZPplAGT9ZRhlHGUFZSRlI2UrZTRlNWU3ZTZlOHVLZUhlVmVVZU1lWGVeZV1lcmV4ZYJlg4uKZZtln2WrZbdlw2XGZcFlxGXMZdJl22XZZeBl4WXxZ3JmCmYDZftnc2Y1ZjZmNGYcZk9mRGZJZkFmXmZdZmRmZ2Zo" + - "Zl9mYmZwZoNmiGaOZolmhGaYZp1mwWa5Zslmvma8////////ZsRmuGbWZtpm4GY/ZuZm6WbwZvVm92cPZxZnHmcmZyeXOGcuZz9nNmdBZzhnN2dGZ15nYGdZZ2NnZGeJZ3BnqWd8Z2pnjGeLZ6ZnoWeFZ7dn72e0Z+xns2fpZ7hn5GfeZ91n4mfuZ7lnzmfGZ+dqnGge" + - "aEZoKWhAaE1oMmhO//9os2graFloY2h3aH9on2iPaK1olGidaJtog2quaLlodGi1aKBoumkPaI1ofmkBaMppCGjYaSJpJmjhaQxozWjUaOdo1Wk2aRJpBGjXaONpJWj5aOBo72koaSppGmkjaSFoxml5aXdpXGl4aWtpVGl+aW5pOWl0aT1pWWkwaWFpXmldaYFpammy" + - "aa5p0Gm/acFp02m+ac5b6GnKad1pu2nDaadqLmmRaaBpnGmVabRp3mnoagJqG2n/awpp+WnyaedqBWmxah5p7WoUaetqCmoSasFqI2oTakRqDGpyajZqeGpHamJqWWpmakhqOGoiapBqjWqgaoRqomqj////////apeGF2q7asNqwmq4arNqrGreatFq32qqatpq6mr7" + - "awWGFmr6axJrFpsxax9rOGs3dtxrOZjua0drQ2tJa1BrWWtUa1trX2tha3hreWt/a4BrhGuDa41rmGuVa55rpGuqa6trr2uya7Frs2u3a7xrxmvLa9Nr32vsa+tr82vv//+evmwIbBNsFGwbbCRsI2xebFVsYmxqbIJsjWyabIFsm2x+bGhsc2ySbJBsxGzxbNNsvWzX" + - "bMVs3WyubLFsvmy6bNts72zZbOptH4hNbTZtK209bThtGW01bTNtEm0MbWNtk21kbVpteW1ZbY5tlW/kbYVt+W4VbgpttW3HbeZtuG3Gbext3m3Mbeht0m3Fbfpt2W3kbdVt6m3ubi1ubm4ubhlucm5fbj5uI25rbitudm5Nbh9uQ246bk5uJG7/bh1uOG6CbqpumG7J" + - "brdu0269bq9uxG6ybtRu1W6PbqVuwm6fb0FvEXBMbuxu+G7+bz9u8m8xbu9vMm7M////////bz5vE273b4Zvem94b4FvgG9vb1tv829tb4JvfG9Yb45vkW/Cb2Zvs2+jb6FvpG+5b8Zvqm/fb9Vv7G/Ub9hv8W/ub9twCXALb/pwEXABcA9v/nAbcBpvdHAdcBhwH3Aw" + - "cD5wMnBRcGNwmXCScK9w8XCscLhws3CucN9wy3Dd//9w2XEJcP1xHHEZcWVxVXGIcWZxYnFMcVZxbHGPcftxhHGVcahxrHHXcblxvnHScclx1HHOceBx7HHncfVx/HH5cf9yDXIQchtyKHItcixyMHIycjtyPHI/ckByRnJLclhydHJ+coJygXKHcpJylnKicqdyuXKy" + - "csNyxnLEcs5y0nLicuBy4XL5cvdQD3MXcwpzHHMWcx1zNHMvcylzJXM+c05zT57Yc1dzanNoc3BzeHN1c3tzenPIc7NzznO7c8Bz5XPuc950onQFdG90JXP4dDJ0OnRVdD90X3RZdEF0XHRpdHB0Y3RqdHZ0fnSLdJ50p3TKdM901HPx////////dOB043TndOl07nTy" + - "dPB08XT4dPd1BHUDdQV1DHUOdQ11FXUTdR51JnUsdTx1RHVNdUp1SXVbdUZ1WnVpdWR1Z3VrdW11eHV2dYZ1h3V0dYp1iXWCdZR1mnWddaV1o3XCdbN1w3W1db11uHW8dbF1zXXKddJ12XXjdd51/nX///91/HYBdfB1+nXydfN2C3YNdgl2H3YndiB2IXYidiR2NHYw" + - "djt2R3ZIdkZ2XHZYdmF2YnZodml2anZndmx2cHZydnZ2eHZ8doB2g3aIdot2jnaWdpN2mXaadrB2tHa4drl2unbCds121nbSdt524Xbldud26oYvdvt3CHcHdwR3KXckdx53JXcmdxt3N3c4d0d3Wndod2t3W3dld393fnd5d453i3eRd6B3nnewd7Z3uXe/d7x3vXe7" + - "d8d3zXfXd9p33Hfjd+53/HgMeBJ5JnggeSp4RXiOeHR4hnh8eJp4jHijeLV4qniveNF4xnjLeNR4vni8eMV4ynjs////////eOd42nj9ePR5B3kSeRF5GXkseSt5QHlgeVd5X3laeVV5U3l6eX95inmdeaefS3mqea55s3m5ebp5yXnVeed57HnheeN6CHoNehh6GXog" + - "eh95gHoxejt6Pno3ekN6V3pJemF6Ynppn516cHp5en16iHqXepV6mHqWeql6yHqw//96tnrFesR6v5CDesd6ynrNes961XrTetl62nrdeuF64nrmeu168HsCew97CnsGezN7GHsZex57NXsoezZ7UHt6ewR7TXsLe0x7RXt1e2V7dHtne3B7cXtse257nXuYe597jXuc" + - "e5p7i3uSe497XXuZe8t7wXvMe897tHvGe9176XwRfBR75nvlfGB8AHwHfBN783v3fBd8DXv2fCN8J3wqfB98N3wrfD18THxDfFR8T3xAfFB8WHxffGR8VnxlfGx8dXyDfJB8pHytfKJ8q3yhfKh8s3yyfLF8rny5fL18wHzFfMJ82HzSfNx84ps7fO988nz0fPZ8+n0G" + - "////////fQJ9HH0VfQp9RX1LfS59Mn0/fTV9Rn1zfVZ9Tn1yfWh9bn1PfWN9k32JfVt9j319fZt9un2ufaN9tX3Hfb19q349faJ9r33cfbh9n32wfdh93X3kfd59+33yfeF+BX4KfiN+IX4SfjF+H34Jfgt+In5GfmZ+O341fjl+Q343//9+Mn46fmd+XX5Wfl5+WX5a" + - "fnl+an5pfnx+e36DfdV+fY+ufn9+iH6Jfox+kn6QfpN+lH6Wfo5+m36cfzh/On9Ff0x/TX9Of1B/UX9Vf1R/WH9ff2B/aH9pf2d/eH+Cf4Z/g3+If4d/jH+Uf55/nX+af6N/r3+yf7l/rn+2f7iLcX/Ff8Z/yn/Vf9R/4X/mf+l/83/5mNyABoAEgAuAEoAYgBmAHIAh" + - "gCiAP4A7gEqARoBSgFiAWoBfgGKAaIBzgHKAcIB2gHmAfYB/gISAhoCFgJuAk4CagK1RkICsgNuA5YDZgN2AxIDagNaBCYDvgPGBG4EpgSOBL4FL////////louBRoE+gVOBUYD8gXGBboFlgWaBdIGDgYiBioGAgYKBoIGVgaSBo4FfgZOBqYGwgbWBvoG4gb2BwIHC" + - "gbqByYHNgdGB2YHYgciB2oHfgeCB54H6gfuB/oIBggKCBYIHggqCDYIQghaCKYIrgjiCM4JAglmCWIJdglqCX4Jk//+CYoJogmqCa4IugnGCd4J4gn6CjYKSgquCn4K7gqyC4YLjgt+C0oL0gvOC+oOTgwOC+4L5gt6DBoLcgwmC2YM1gzSDFoMygzGDQIM5g1CDRYMv" + - "gyuDF4MYg4WDmoOqg5+DooOWgyODjoOHg4qDfIO1g3ODdYOgg4mDqIP0hBOD64POg/2EA4PYhAuDwYP3hAeD4IPyhA2EIoQgg72EOIUGg/uEbYQqhDyFWoSEhHeEa4SthG6EgoRphEaELIRvhHmENYTKhGKEuYS/hJ+E2YTNhLuE2oTQhMGExoTWhKGFIYT/hPSFF4UY" + - "hSyFH4UVhRSE/IVAhWOFWIVI////////hUGGAoVLhVWFgIWkhYiFkYWKhaiFbYWUhZuF6oWHhZyFd4V+hZCFyYW6hc+FuYXQhdWF3YXlhdyF+YYKhhOGC4X+hfqGBoYihhqGMIY/hk1OVYZUhl+GZ4ZxhpOGo4aphqqGi4aMhraGr4bEhsaGsIbJiCOGq4bUht6G6Ybs" + - "//+G34bbhu+HEocGhwiHAIcDhvuHEYcJhw2G+YcKhzSHP4c3hzuHJYcphxqHYIdfh3iHTIdOh3SHV4doh26HWYdTh2OHaogFh6KHn4eCh6+Hy4e9h8CH0JbWh6uHxIezh8eHxoe7h++H8ofgiA+IDYf+h/aH94gOh9KIEYgWiBWIIoghiDGINog5iCeIO4hEiEKIUohZ" + - "iF6IYohriIGIfoieiHWIfYi1iHKIgoiXiJKIroiZiKKIjYikiLCIv4ixiMOIxIjUiNiI2YjdiPmJAoj8iPSI6IjyiQSJDIkKiROJQ4keiSWJKokriUGJRIk7iTaJOIlMiR2JYIle////////iWaJZIltiWqJb4l0iXeJfomDiYiJiomTiZiJoYmpiaaJrImvibKJuom9" + - "ib+JwInaidyJ3YnnifSJ+IoDihaKEIoMihuKHYolijaKQYpbilKKRopIinyKbYpsimKKhYqCioSKqIqhipGKpYqmipqKo4rEis2KworaiuuK84rn//+K5IrxixSK4IriiveK3orbiwyLB4saiuGLFosQixeLIIszl6uLJosriz6LKItBi0yLT4tOi0mLVotbi1qLa4tf" + - "i2yLb4t0i32LgIuMi46LkouTi5aLmYuajDqMQYw/jEiMTIxOjFCMVYxijGyMeIx6jIKMiYyFjIqMjYyOjJSMfIyYYh2MrYyqjL2MsoyzjK6MtozIjMGM5IzjjNqM/Yz6jPuNBI0FjQqNB40PjQ2NEJ9OjROMzY0UjRaNZ41tjXGNc42BjZmNwo2+jbqNz43ajdaNzI3b" + - "jcuN6o3rjd+N4438jgiOCY3/jh2OHo4Qjh+OQo41jjCONI5K////////jkeOSY5MjlCOSI5ZjmSOYI4qjmOOVY52jnKOfI6BjoeOhY6EjouOio6TjpGOlI6ZjqqOoY6sjrCOxo6xjr6OxY7IjsuO247jjvyO+47rjv6PCo8FjxWPEo8ZjxOPHI8fjxuPDI8mjzOPO485" + - "j0WPQo8+j0yPSY9Gj06PV49c//+PYo9jj2SPnI+fj6OPrY+vj7eP2o/lj+KP6o/vkIeP9JAFj/mP+pARkBWQIZANkB6QFpALkCeQNpA1kDmP+JBPkFCQUZBSkA6QSZA+kFaQWJBekGiQb5B2lqiQcpCCkH2QgZCAkIqQiZCPkKiQr5CxkLWQ4pDkYkiQ25ECkRKRGZEy" + - "kTCRSpFWkViRY5FlkWmRc5FykYuRiZGCkaKRq5GvkaqRtZG0kbqRwJHBkcmRy5HQkdaR35HhkduR/JH1kfaSHpH/khSSLJIVkhGSXpJXkkWSSZJkkkiSlZI/kkuSUJKckpaSk5KbklqSz5K5kreS6ZMPkvqTRJMu////////kxmTIpMakyOTOpM1kzuTXJNgk3yTbpNW" + - "k7CTrJOtk5STuZPWk9eT6JPlk9iTw5Pdk9CTyJPklBqUFJQTlAOUB5QQlDaUK5Q1lCGUOpRBlFKURJRblGCUYpRelGqSKZRwlHWUd5R9lFqUfJR+lIGUf5WClYeVipWUlZaVmJWZ//+VoJWolaeVrZW8lbuVuZW+lcpv9pXDlc2VzJXVldSV1pXcleGV5ZXiliGWKJYu" + - "li+WQpZMlk+WS5Z3llyWXpZdll+WZpZylmyWjZaYlpWWl5aqlqeWsZaylrCWtJa2lriWuZbOlsuWyZbNiU2W3JcNltWW+ZcElwaXCJcTlw6XEZcPlxaXGZcklyqXMJc5lz2XPpdEl0aXSJdCl0mXXJdgl2SXZpdoUtKXa5dxl3mXhZd8l4GXepeGl4uXj5eQl5yXqJem" + - "l6OXs5e0l8OXxpfIl8uX3Jftn0+X8nrfl/aX9ZgPmAyYOJgkmCGYN5g9mEaYT5hLmGuYb5hw////////mHGYdJhzmKqYr5ixmLaYxJjDmMaY6ZjrmQOZCZkSmRSZGJkhmR2ZHpkkmSCZLJkumT2ZPplCmUmZRZlQmUuZUZlSmUyZVZmXmZiZpZmtma6ZvJnfmduZ3ZnY" + - "mdGZ7ZnumfGZ8pn7mfiaAZoPmgWZ4poZmiuaN5pFmkKaQJpD//+aPppVmk2aW5pXml+aYpplmmSaaZprmmqarZqwmryawJrPmtGa05rUmt6a35rimuOa5prvmuua7pr0mvGa95r7mwabGJsamx+bIpsjmyWbJ5somymbKpsumy+bMptEm0ObT5tNm06bUZtYm3Sbk5uD" + - "m5GblpuXm5+boJuom7SbwJvKm7mbxpvPm9Gb0pvjm+Kb5JvUm+GcOpvym/Gb8JwVnBScCZwTnAycBpwInBKcCpwEnC6cG5wlnCScIZwwnEecMpxGnD6cWpxgnGecdpx4nOec7JzwnQmdCJzrnQOdBp0qnSadr50jnR+dRJ0VnRKdQZ0/nT6dRp1I////////nV2dXp1k" + - "nVGdUJ1ZnXKdiZ2Hnaudb516nZqdpJ2pnbKdxJ3BnbuduJ26ncadz53Cndmd0534nead7Z3vnf2eGp4bnh6edZ55nn2egZ6InouejJ6SnpWekZ6dnqWeqZ64nqqerZdhnsyezp7PntCe1J7cnt6e3Z7gnuWe6J7v//+e9J72nvee+Z77nvye/Z8Hnwh2t58VnyGfLJ8+" + - "n0qfUp9Un2OfX59gn2GfZp9nn2yfap93n3Kfdp+Vn5yfoFgvaceQWXRkUdxxmf//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////" + - "/////////////////////////////////////////////w=="; +// Data derived from ftp://ftp.unicode.org/Public/MAPPINGS/OBSOLETE/EASTASIA/JIS/SHIFTJIS.TXT + private static final String PACKED_QR_KANJI_TO_UNICODE = readPacked_KANJI(); private static short[] UNICODE_TO_QR_KANJI = new short[1 << 16]; @@ -407,18 +332,31 @@ public final class QrSegmentAdvanced { Arrays.fill(UNICODE_TO_QR_KANJI, (short)-1); byte[] bytes = Base64.getDecoder().decode(PACKED_QR_KANJI_TO_UNICODE); for (int i = 0; i < bytes.length; i += 2) { - char c = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF)); - if (c == 0xFFFF) + char convertChar = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF)); + if (convertChar == 0xFFFF) continue; - assert UNICODE_TO_QR_KANJI[c] == -1; - UNICODE_TO_QR_KANJI[c] = (short)(i / 2); + assert UNICODE_TO_QR_KANJI[convertChar] == -1; + UNICODE_TO_QR_KANJI[convertChar] = (short)(i / 2); } } + private static String readPacked_KANJI() { + String str = ""; + try { + File file = new File("./packet_KanJI.txt"); + Scanner scan = new Scanner(file); + str +=scan.nextLine(); + scan.close(); + } + catch(FileNotFoundException e){ + } + + return str; + } /*---- Miscellaneous ----*/ private QrSegmentAdvanced() {} // Not instantiable -} +} \ No newline at end of file From b65dad6f4179f3950ecdd5d8b6d09c5844e4e410 Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Fri, 5 Jun 2020 23:45:52 +0900 Subject: [PATCH 25/28] 1.Strategy Pattern 2. AlphanumericMode, ByteMode, EciMode, NumericMode, QrMode 3. Add java class for using Strategy Pattern --- .../io/nayuki/qrcodegen/AlphanumericMode.java | 25 +++++++++++ .../java/io/nayuki/qrcodegen/ByteMode.java | 25 +++++++++++ .../java/io/nayuki/qrcodegen/EciMode.java | 23 ++++++++++ .../java/io/nayuki/qrcodegen/NumericMode.java | 23 ++++++++++ .../main/java/io/nayuki/qrcodegen/QrMode.java | 43 +++++++++++++++++++ 5 files changed, 139 insertions(+) create mode 100644 java/src/main/java/io/nayuki/qrcodegen/AlphanumericMode.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/ByteMode.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/EciMode.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/NumericMode.java create mode 100644 java/src/main/java/io/nayuki/qrcodegen/QrMode.java diff --git a/java/src/main/java/io/nayuki/qrcodegen/AlphanumericMode.java b/java/src/main/java/io/nayuki/qrcodegen/AlphanumericMode.java new file mode 100644 index 0000000..f0d59ba --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/AlphanumericMode.java @@ -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); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/ByteMode.java b/java/src/main/java/io/nayuki/qrcodegen/ByteMode.java new file mode 100644 index 0000000..1270381 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/ByteMode.java @@ -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)); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/EciMode.java b/java/src/main/java/io/nayuki/qrcodegen/EciMode.java new file mode 100644 index 0000000..ce33fa9 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/EciMode.java @@ -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; + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/NumericMode.java b/java/src/main/java/io/nayuki/qrcodegen/NumericMode.java new file mode 100644 index 0000000..a8af039 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/NumericMode.java @@ -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); + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrMode.java b/java/src/main/java/io/nayuki/qrcodegen/QrMode.java new file mode 100644 index 0000000..3f0127f --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/QrMode.java @@ -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; + } +} \ No newline at end of file From fe6c3980afb29637e981a6d3185709e0a0c790f9 Mon Sep 17 00:00:00 2001 From: gerzees Date: Sat, 6 Jun 2020 18:01:16 +0900 Subject: [PATCH 26/28] 1. Extract class 2. Ecc from QrCode 3. QrCode is too big --- .../main/java/io/nayuki/qrcodegen/Ecc.java | 61 +++++++++++++++++++ .../main/java/io/nayuki/qrcodegen/QrCode.java | 55 ++--------------- .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 34 +++++------ .../qrcodegen/QrCodeGeneratorWorker.java | 2 +- 4 files changed, 85 insertions(+), 67 deletions(-) create mode 100644 java/src/main/java/io/nayuki/qrcodegen/Ecc.java diff --git a/java/src/main/java/io/nayuki/qrcodegen/Ecc.java b/java/src/main/java/io/nayuki/qrcodegen/Ecc.java new file mode 100644 index 0000000..1ce9752 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/Ecc.java @@ -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 }; + } +} diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index dcb8b6b..5ade16c 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -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; @@ -830,9 +830,9 @@ public final class QrCode { // QR Code of the given version number and error correction level, with remainder bits discarded. // 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]; + return getNumRawDataModules(ver) / 8 + - 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; - } - } - } \ No newline at end of file diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 3c50bda..f9316d7 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -53,7 +53,7 @@ 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 @@ -73,15 +73,15 @@ public final class QrCodeGeneratorDemo { QrCode qr; // Numeric mode encoding (3.33 bits per digit) - qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); + qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", Ecc.MEDIUM); writePng(qr.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); + qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", Ecc.HIGH); writePng(qr.toImage(10, 2), "alphanumeric-QR.png"); // Unicode text as UTF-8 - qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); + qr = QrCode.encodeText("こんにちwa、世界! αβγδ", Ecc.QUARTILE); writePng(qr.toImage(10, 3), "unicode-QR.png"); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) @@ -92,7 +92,7 @@ public final class QrCodeGeneratorDemo { + "'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); + + "a White Rabbit with pink eyes ran close by her.", Ecc.HIGH); writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); } @@ -105,36 +105,36 @@ public final class QrCodeGeneratorDemo { // Illustration "silver" String silver0 = "THE SQUARE ROOT OF 2 IS 1."; String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; - qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); + qr = QrCode.encodeText(silver0 + silver1, Ecc.LOW); writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png"); segs = Arrays.asList( QrSegment.makeAlphanumeric(silver0), QrSegment.makeNumeric(silver1)); - qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr = QrCode.encodeSegments(segs, Ecc.LOW); writePng(qr.toImage(10, 3), "sqrt2-segmented-QR.png"); // Illustration "golden" String golden0 = "Golden ratio φ = 1."; String golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; String golden2 = "......"; - qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); + qr = QrCode.encodeText(golden0 + golden1 + golden2, Ecc.LOW); writePng(qr.toImage(8, 5), "phi-monolithic-QR.png"); segs = Arrays.asList( QrSegment.makeBytes(golden0.getBytes(StandardCharsets.UTF_8)), QrSegment.makeNumeric(golden1), QrSegment.makeAlphanumeric(golden2)); - qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr = QrCode.encodeSegments(segs, Ecc.LOW); writePng(qr.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); + qr = QrCode.encodeText(madoka, Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); - qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr = QrCode.encodeSegments(segs, Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-kanji-QR.png"); } @@ -146,20 +146,20 @@ public final class QrCodeGeneratorDemo { // 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 + qr = QrCode.encodeSegments(segs, 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 + qr = QrCode.encodeSegments(segs, Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 writePng(qr.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 + qr = QrCode.encodeSegments(segs, 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 + qr = QrCode.encodeSegments(segs, 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 + qr = QrCode.encodeSegments(segs, 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 + qr = QrCode.encodeSegments(segs, Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 writePng(qr.toImage(10, 3), "unicode-mask7-QR.png"); } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java index 4cf9de3..54f3dbf 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java @@ -82,7 +82,7 @@ public final class QrCodeGeneratorWorker { segs = 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(segs, 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++) { From 1980ace59524357baa69bb4f89362945774aa856 Mon Sep 17 00:00:00 2001 From: minsu4107 <32637512+minsu4107@users.noreply.github.com> Date: Sat, 6 Jun 2020 18:18:11 +0900 Subject: [PATCH 27/28] 1. Strategy Pattern 2. QrSemgmentAdvanced.java 3. Remain Concrete Strategy upload --- .../java/io/nayuki/qrcodegen/KanjiMode.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 java/src/main/java/io/nayuki/qrcodegen/KanjiMode.java diff --git a/java/src/main/java/io/nayuki/qrcodegen/KanjiMode.java b/java/src/main/java/io/nayuki/qrcodegen/KanjiMode.java new file mode 100644 index 0000000..9729f93 --- /dev/null +++ b/java/src/main/java/io/nayuki/qrcodegen/KanjiMode.java @@ -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); + } +} From 419466168efdc50c02e9ea979b1739739be17925 Mon Sep 17 00:00:00 2001 From: gerzees Date: Sat, 6 Jun 2020 18:23:26 +0900 Subject: [PATCH 28/28] 1. Extract method 2. Ecc from QrCode 3. I did not changed QrCode.Ecc to Ecc in signature of makeSegmentsOptimally() --- java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index b125f18..50066e8 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -68,7 +68,7 @@ public final class QrSegmentAdvanced { * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated * @throws DataTooLongException if the text fails to fit in the maxVersion QR Code at the ECL */ - public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { + public static List makeSegmentsOptimally(String text, Ecc ecl, int minVersion, int maxVersion) { // Check arguments Objects.requireNonNull(text); Objects.requireNonNull(ecl);