From 72921ece2999bd9a5dd6987743280a12a58d2242 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sun, 10 Apr 2016 06:11:57 +0000 Subject: [PATCH 001/810] Initial commit of QR Code generator library, in Java and JavaScript. --- BitBuffer.java | 95 ++++ QrCode.java | 848 ++++++++++++++++++++++++++++++++++ QrCodeGeneratorDemo.java | 147 ++++++ QrSegment.java | 222 +++++++++ qrcodegen-demo.js | 107 +++++ qrcodegen-js-demo.html | 81 ++++ qrcodegen.js | 975 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 2475 insertions(+) create mode 100644 BitBuffer.java create mode 100644 QrCode.java create mode 100644 QrCodeGeneratorDemo.java create mode 100644 QrSegment.java create mode 100644 qrcodegen-demo.js create mode 100644 qrcodegen-js-demo.html create mode 100644 qrcodegen.js diff --git a/BitBuffer.java b/BitBuffer.java new file mode 100644 index 0000000..e9a3dd1 --- /dev/null +++ b/BitBuffer.java @@ -0,0 +1,95 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.util.Arrays; + + +/** + * An appendable sequence of bits. Bits are packed in big endian within a byte. + */ +final class BitBuffer { + + /*---- Fields ----*/ + + private byte[] data; + private int bitLength; + + + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public BitBuffer() { + data = new byte[16]; + bitLength = 0; + } + + + + /*---- Methods ----*/ + + // Returns the number of bits in the buffer, which is a non-negative value. + public int bitLength() { + return bitLength; + } + + + // Returns a copy of all bytes, padding up to the nearest byte. + public byte[] getBytes() { + return Arrays.copyOf(data, (bitLength + 7) / 8); + } + + + // Appends the given number of bits of the given value to this sequence. + // If 0 <= len <= 31, then this requires 0 <= val < 2^len. + public void appendBits(int val, int len) { + if (len < 0 || len > 32 || len < 32 && (val & ((1 << len) - 1)) != val) + throw new IllegalArgumentException("Value out of range"); + ensureCapacity(bitLength + len); + for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit + data[bitLength >>> 3] |= ((val >>> i) & 1) << (7 - (bitLength & 7)); + } + + + // Appends the data of the given segment to this bit buffer. + public void appendData(QrSegment seg) { + if (seg == null) + throw new NullPointerException(); + ensureCapacity(bitLength + seg.bitLength); + for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit + int bit = (seg.getByte(i >>> 3) >>> (7 - (i & 7))) & 1; + data[bitLength >>> 3] |= bit << (7 - (bitLength & 7)); + } + } + + + // Expands the buffer if necessary, so that it can hold at least the given bit length. + private void ensureCapacity(int newBitLen) { + while (data.length < newBitLen) + data = Arrays.copyOf(data, data.length * 2); + } + +} diff --git a/QrCode.java b/QrCode.java new file mode 100644 index 0000000..16ec193 --- /dev/null +++ b/QrCode.java @@ -0,0 +1,848 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.awt.image.BufferedImage; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; + + +/** + * Represents an immutable square grid of black and white cells for a QR Code symbol, and + * provides static functions to create a QR Code from user-supplied textual or binary data. + *

This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes.

+ */ +public final class QrCode { + + /*---- Public static factory functions ----*/ + + /** + * Returns a QR Code symbol 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 Unicode + * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + * @param text the text to be encoded, which can be any Unicode string + * @param ecl the error correction level to use + * @return a QR Code representing the text + * @throws NullPointerException if the text or error correction level is {@code null} + * @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long + */ + public static QrCode encodeText(String text, Ecc ecl) { + if (text == null || ecl == null) + throw new NullPointerException(); + QrSegment seg; // Select the most efficient segment encoding automatically + if (QrSegment.NUMERIC_REGEX.matcher(text).matches()) + seg = QrSegment.makeNumeric(text); + else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches()) + seg = QrSegment.makeAlphanumeric(text); + else + seg = QrSegment.makeBytes(text.getBytes(StandardCharsets.UTF_8)); + return encodeSegments(Arrays.asList(seg), ecl); + } + + + /** + * Returns a QR Code symbol representing the specified binary data string at the specified error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * @param data the binary data to encode + * @param ecl the error correction level to use + * @return a QR Code representing the binary data + * @throws NullPointerException if the data or error correction level is {@code null} + * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long + */ + public static QrCode encodeBinary(byte[] data, Ecc ecl) { + if (data == null || ecl == null) + throw new NullPointerException(); + QrSegment seg = QrSegment.makeBytes(data); + return encodeSegments(Arrays.asList(seg), ecl); + } + + + /** + * Returns a QR Code symbol representing the specified data segments at the specified error + * correction level. The smallest possible QR Code version is automatically chosen for the output. + *

This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. This + * function is considered to be lower level than simply encoding text or binary data.

+ * @param segs the segments to encode + * @param ecl the error correction level to use + * @return a QR Code representing the segments + * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long + */ + public static QrCode encodeSegments(List segs, Ecc ecl) { + if (segs == null || ecl == null) + throw new NullPointerException(); + + // Find the minimal version number to use + int version, dataCapacityBits; + outer: + for (version = 1; ; version++) { // Increment until the data fits in the QR Code + if (version > 40) // All versions could not fit the given data + throw new IllegalArgumentException("Data too long"); + dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + + // Calculate the total number of bits needed at this version number + // to encode all the segments (i.e. segment metadata and payloads) + int dataUsedBits = 0; + for (QrSegment seg : segs) { + if (seg == null) + throw new NullPointerException(); + if (seg.numChars < 0) + throw new AssertionError(); + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1 << ccbits)) { + // Segment length value doesn't fit in the length field's bit-width, so fail immediately + continue outer; + } + dataUsedBits += 4 + ccbits + seg.bitLength; + } + if (dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + } + + // Create the data bit string by concatenating all segments + 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); + } + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); + bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.bitLength() % 8 != 0) + throw new AssertionError(); + + // Create the QR Code symbol + return new QrCode(version, ecl, bb.getBytes(), -1); + } + + + + /*---- Instance fields ----*/ + + // Public immutable scalar parameters + + /** This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + public final int version; + + /** The width and height of this QR Code symbol, measured in modules. + * Always equal to version × 4 + 17, in the range 21 to 177. */ + public final int size; + + /** The error correction level used in this QR Code symbol. Never {@code null}. */ + public final Ecc errorCorrectionLevel; + + /** The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + * Note that even if a constructor was called with automatic masking requested + * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ + public final int mask; + + // Private grids of modules/pixels (conceptually immutable) + private boolean[][] modules; // The modules of this QR Code symbol (false = white, true = black) + private boolean[][] isFunction; // Indicates function modules that are not subjected to masking + + + + /*---- Constructors ----*/ + + /** + * Creates a new QR Code symbol with the specified version number, error correction level, binary data string, and mask number. + *

This cumbersome constructor can be invoked directly by the user, but is considered + * to be even lower level than {@link #encodeSegments(List,Ecc)}.

+ * @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 dataCodewords the raw binary user data to encode + * @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 + */ + public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { + // Check arguments + if (ecl == null) + throw new NullPointerException(); + if (ver < 1 || ver > 40 || mask < -1 || mask > 7) + throw new IllegalArgumentException("Value out of range"); + if (dataCodewords == null) + throw new NullPointerException(); + + // Initialize fields + version = ver; + size = ver * 4 + 17; + errorCorrectionLevel = ecl; + modules = new boolean[size][size]; // Entirely white grid + isFunction = new boolean[size][size]; + + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(); + byte[] allCodewords = appendErrorCorrection(dataCodewords); + drawCodewords(allCodewords); + this.mask = handleConstructorMasking(mask); + } + + + /** + * Creates a new QR Code symbol based on the specified existing object, but with a potentially + * different mask pattern. The version, error correction level, codewords, etc. of the newly + * created object are all identical to the argument object; only the mask may differ. + * @param qr the existing QR Code to copy and modify + * @param mask the new mask pattern, 0 to 7 to force a fixed choice or -1 for an automatic choice + * @throws NullPointerException if the QR Code is {@code null} + * @throws IllegalArgumentException if the mask value is out of range + */ + public QrCode(QrCode qr, int mask) { + // Check arguments + if (qr == null) + throw new NullPointerException(); + if (mask < -1 || mask > 7) + throw new IllegalArgumentException("Mask value out of range"); + + // Copy scalar fields + version = qr.version; + size = qr.size; + errorCorrectionLevel = qr.errorCorrectionLevel; + + // Handle grid fields + isFunction = qr.isFunction; // Shallow copy because the data is read-only + modules = qr.modules.clone(); // Deep copy + for (int i = 0; i < modules.length; i++) + modules[i] = modules[i].clone(); + + // Handle masking + applyMask(qr.mask); // Undo old mask + this.mask = handleConstructorMasking(mask); + } + + + + /*---- Public instance methods ----*/ + + /** + * Returns the color of the module (pixel) at the specified coordinates, which is either 0 for white or 1 for black. The top + * left corner has the coordinates (x=0, y=0). If the specified coordinates are out of bounds, then 0 (white) is returned. + * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge + * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge + * @return the module's color, which is either 0 (white) or 1 (black) + */ + public int getModule(int x, int y) { + if (x < 0 || x >= size || y < 0 || y >= size) + return 0; // Infinite white border + else + return modules[y][x] ? 1 : 0; + } + + + /** + * Returns a new image object representing this QR Code, with the specified module scale and number + * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code symbol + * with 4 white border modules on all four edges, then use 10*10 pixels to represent each module. + * The resulting image only contains the hex colors 000000 and FFFFFF. + * @param scale the module scale factor, which must be positive + * @param border the number of border modules to add, which must be non-negative + * @return an image representing this QR Code, with padding and scaling + * @throws IllegalArgumentException if the scale or border is out of range + */ + public BufferedImage toImage(int scale, int border) { + if (scale <= 0 || border < 0) + throw new IllegalArgumentException("Value out of range"); + 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++) { + int val = getModule(x / scale - border, y / scale - border); // 0 or 1 + result.setRGB(x, y, val == 1 ? 0x000000 : 0xFFFFFF); + } + } + return result; + } + + + /** + * Based on the specified number of border modules to add as padding, this returns a + * string whose contents represents an SVG XML file that depicts this QR Code symbol. + * Note that Unix newlines (\n) are always used, regardless of the platform. + * @param border the number of border modules to add, which must be non-negative + * @return a string representing this QR Code as an SVG document + */ + public String toSvgString(int border) { + if (border < 0) + throw new IllegalArgumentException("Border must be non-negative"); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append(String.format("\n", size + border * 2)); + sb.append("\t\n"); + sb.append("\n"); + return sb.toString(); + } + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ + + private void drawFunctionPatterns() { + // Draw the horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + 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 the numerous alignment patterns + int[] alignPatPos = getAlignmentPatternPositions(version); + int numAlign = alignPatPos.length; + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) + continue; // Skip the three finder corners + else + 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 this object's error correction level and mask fields. + private void drawFormatBits(int mask) { + // Calculate error correction code and pack bits + int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if ((data & ((1 << 15) - 1)) != data) + throw new AssertionError(); + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >>> i) & 1) != 0); + setFunctionModule(8, 7, ((data >>> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >>> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >>> 8) & 1) != 0); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >>> i) & 1) != 0); + + // Draw second copy + for (int i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >>> i) & 1) != 0); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >>> i) & 1) != 0); + setFunctionModule(8, size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 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 data = version << 12 | rem; // uint18 + if ((data & ((1 << 18) - 1)) != data) + throw new AssertionError(); + + // Draw two copies + for (int i = 0; i < 18; i++) { + boolean bit = ((data >>> i) & 1) != 0; + setFunctionModule(size - 11 + i % 3, i / 3, bit); + setFunctionModule(i / 3, size - 11 + i % 3, bit); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + private void drawFinderPattern(int x, int y) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm + int xx = x + j; + int yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + private void drawAlignmentPattern(int x, int y) { + for (int i = -2; i <= 2; i++) { + for (int j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + 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[] appendErrorCorrection(byte[] data) { + if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) + throw new IllegalArgumentException(); + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; + int numEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal()][version]; + if (numEcc % numBlocks != 0) + throw new AssertionError(); + int eccLen = numEcc / numBlocks; + int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; + int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; + + byte[][] blocks = new byte[numBlocks][]; + ReedSolomonGenerator rs = new ReedSolomonGenerator(eccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1)); + byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); + k += dat.length; + byte[] ecc = rs.getRemainder(dat); + System.arraycopy(ecc, 0, block, block.length - eccLen, ecc.length); + blocks[i] = block; + } + + byte[] result = new byte[getNumRawDataModules(version) / 8]; + for (int i = 0, k = 0; i < blocks[0].length; i++) { + for (int j = 0; j < blocks.length; j++) { + if (i != shortBlockLen - eccLen || j >= numShortBlocks) { + result[k] = blocks[j][i]; + k++; + } + } + } + return result; + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + private void drawCodewords(byte[] data) { + if (data == null) + throw new NullPointerException(); + 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 + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + boolean upwards = ((right & 2) == 0) ^ (x < 6); + int y = upwards ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction[y][x] && i < data.length * 8) { + modules[y][x] = (data[i >>> 3] >>> (7 - (i & 7)) & 1) != 0; + i++; + } + } + } + } + if (i != data.length * 8) + throw new AssertionError(); + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + private void applyMask(int mask) { + if (mask < 0 || mask > 7) + throw new IllegalArgumentException("Mask value out of range"); + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + boolean invert; + switch (mask) { + 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 constructors. 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 mask) { + if (mask == -1) { // Automatically choose best mask + int minPenalty = Integer.MAX_VALUE; + for (int i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + int penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw new AssertionError(); + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + return mask; // 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. + private int getPenaltyScore() { + int result = 0; + + // Adjacent modules in row having same color + for (int y = 0; y < size; y++) { + boolean colorX = modules[y][0]; + for (int x = 1, runX = 1; x < size; x++) { + if (modules[y][x] != colorX) { + colorX = modules[y][x]; + runX = 1; + } else { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (int x = 0; x < size; x++) { + boolean colorY = modules[0][x]; + for (int y = 1, runY = 1; y < size; y++) { + if (modules[y][x] != colorY) { + colorY = modules[y][x]; + runY = 1; + } else { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 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; + } + } + + // Finder-like pattern in rows + for (int y = 0; y < size; y++) { + for (int x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + // Finder-like pattern in columns + for (int x = 0; x < size; x++) { + for (int y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + + // Balance of black and white modules + int black = 0; + for (boolean[] row : modules) { + for (boolean color : row) { + if (color) + black++; + } + } + int total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += PENALTY_N4; + return result; + } + + + + /*---- Static helper functions ----*/ + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + private static int[] getAlignmentPatternPositions(int ver) { + if (ver < 1 || ver > 40) + throw new IllegalArgumentException("Version number out of range"); + else if (ver == 1) + return new int[]{}; + else { + int numAlign = ver / 7 + 2; + int step; + if (ver != 32) + step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 + else // C-C-C-Combo breaker! + step = 26; + + int[] result = new int[numAlign]; + int size = ver * 4 + 17; + result[0] = 6; + for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) + result[i] = pos; + return result; + } + } + + + // Returns the number of raw data modules (bits) available at the given version number. + // These data modules are used for both user data codewords and error correction codewords. + // This stateless pure function could be implemented as a 40-entry lookup table. + private static int getNumRawDataModules(int ver) { + if (ver < 1 || ver > 40) + throw new IllegalArgumentException("Version number out of range"); + + int size = ver * 4 + 17; + int result = size * size; // Number of modules in the whole QR symbol square + result -= 64 * 3; // Subtract the three finders with separators + result -= 15 * 2 + 1; // Subtract the format information and black module + result -= (size - 16) * 2; // Subtract the timing patterns + // The four lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (numAlign - 1) * (numAlign - 1) * 25; // Subtract alignment patterns not overlapping with timing patterns + result -= (numAlign - 2) * 2 * 20; // Subtract alignment patterns that overlap with timing patterns + // The two lines above are equivalent to: result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; + } + + + // 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. + private static int getNumDataCodewords(int ver, Ecc ecl) { + return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal()][ver]; + } + + + /*---- Tables of constants ----*/ + + // 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 short[][] NUM_ERROR_CORRECTION_CODEWORDS = { + // 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, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low + {-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium + {-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile + {-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // 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 ----*/ + + /** + * Represents the error correction level used in a QR Code symbol. + */ + public enum Ecc { + // Constants declared in ascending order of error protection. + LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); + + // In the range 0 to 3 (unsigned 2-bit integer). + public final int formatBits; + + // Constructor. + private Ecc(int fb) { + formatBits = fb; + } + } + + + + /*---- Private helper class ----*/ + + /** + * Computes the Reed-Solomon error correction codewords for a sequence of data codewords + * at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because the divisor polynomial does not need to be recalculated for every input. + */ + private static final class ReedSolomonGenerator { + + /*-- Immutable field --*/ + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + private final byte[] coefficients; + + + /*-- Constructor --*/ + + /** + * Creates a Reed-Solomon ECC generator for the specified degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + * @param degree the divisor polynomial degree, which must be between 1 and 255 + * @throws IllegalArgumentException if degree < 1 or degree > 255 + */ + public ReedSolomonGenerator(int degree) { + if (degree < 1 || degree > 255) + throw new IllegalArgumentException("Degree out of range"); + + // Start with the monomial x^0 + coefficients = new byte[degree]; + coefficients[degree - 1] = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + int root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < coefficients.length; j++) { + coefficients[j] = (byte)multiply(coefficients[j] & 0xFF, root); + if (j + 1 < coefficients.length) + coefficients[j] ^= coefficients[j + 1]; + } + root = (root << 1) ^ ((root >>> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) + } + } + + + /*-- Method --*/ + + /** + * Computes and returns the Reed-Solomon error correction codewords for the specified sequence of data codewords. + * The returned object is always a new byte array. This method does not alter this object's state (because it is immutable). + * @param data the sequence of data codewords + * @return the Reed-Solomon error correction codewords + * @throws NullPointerException if the data is {@code null} + */ + public byte[] getRemainder(byte[] data) { + if (data == null) + throw new NullPointerException(); + + // Compute the remainder by performing polynomial division + byte[] result = new byte[coefficients.length]; + for (byte b : data) { + int factor = (b ^ result[0]) & 0xFF; + System.arraycopy(result, 1, result, 0, result.length - 1); + result[result.length - 1] = 0; + for (int j = 0; j < result.length; j++) + result[j] ^= multiply(coefficients[j] & 0xFF, factor); + } + return result; + } + + + /*-- Static function --*/ + + // 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 multiply(int x, int y) { + if ((x & 0xFF) != x || (y & 0xFF) != y) + throw new IllegalArgumentException("Byte out of range"); + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if ((z & 0xFF) != z) + throw new AssertionError(); + return z; + } + + } + +} diff --git a/QrCodeGeneratorDemo.java b/QrCodeGeneratorDemo.java new file mode 100644 index 0000000..e644f9a --- /dev/null +++ b/QrCodeGeneratorDemo.java @@ -0,0 +1,147 @@ +/* + * QR Code generator demo (Java) + * + * Run this command-line program with no arguments. The program creates/overwrites a bunch of + * PNG and SVG files in the current working directory to demonstrate the creation of QR Codes. + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.List; +import javax.imageio.ImageIO; + + +public final class QrCodeGeneratorDemo { + + // The main application program. + public static void main(String[] args) throws IOException { + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + } + + + // 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 + + QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol + + BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + File imgFile = new File("hello-world-QR.png"); // File path for output + ImageIO.write(img, "png", imgFile); // Write image to file + + String svg = qr.toSvgString(4); // Convert to SVG XML code + try (Writer out = new OutputStreamWriter( + new FileOutputStream("hello-world-QR.svg"), + StandardCharsets.UTF_8)) { + out.write(svg); // Create/overwrite file and write SVG data + } + } + + + // Creates a variety of QR Codes that exercise different features of the library, and writes each one to file. + private static void doVarietyDemo() throws IOException { + QrCode qr; + + // Project Nayuki URL + qr = QrCode.encodeText("https://www.nayuki.io/", QrCode.Ecc.HIGH); + qr = new QrCode(qr, 3); // Change mask, forcing to mask #3 + writePng(qr.toImage(8, 6), "project-nayuki-QR.png"); + + // Numeric mode encoding (3.33 bits per digit) + qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.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); + writePng(qr.toImage(10, 2), "alphanumeric-QR.png"); + + // Unicode text as UTF-8, and different masks + qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); + writePng(new QrCode(qr, 0).toImage(10, 3), "unicode-mask0-QR.png"); + writePng(new QrCode(qr, 1).toImage(10, 3), "unicode-mask1-QR.png"); + writePng(new QrCode(qr, 5).toImage(10, 3), "unicode-mask5-QR.png"); + writePng(new QrCode(qr, 7).toImage(10, 3), "unicode-mask7-QR.png"); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + qr = QrCode.encodeText("Alice was beginning to get very tired of sitting by her sister on the bank, " + + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + + "a White Rabbit with pink eyes ran close by her.", + QrCode.Ecc.HIGH); + writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); + } + + + // Creates QR Codes with manually specified segments for better compactness. + private static void doSegmentDemo() throws IOException { + QrCode qr; + List segs; + + // Illustration "silver" + String silver0 = "THE SQUARE ROOT OF 2 IS 1."; + String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); + writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png"); + + segs = Arrays.asList( + QrSegment.makeAlphanumeric(silver0), + QrSegment.makeNumeric(silver1)); + qr = QrCode.encodeSegments(segs, QrCode.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); + 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); + writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); + } + + + // Helper function to reduce code duplication. + private static void writePng(BufferedImage img, String filepath) throws IOException { + ImageIO.write(img, "png", new File(filepath)); + } + +} diff --git a/QrSegment.java b/QrSegment.java new file mode 100644 index 0000000..20c0609 --- /dev/null +++ b/QrSegment.java @@ -0,0 +1,222 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import java.util.Arrays; +import java.util.regex.Pattern; + + +/** + * Represents a character string to be encoded in a QR Code symbol. Each segment has + * a mode, and a sequence of characters that is already encoded as a sequence of bits. + * Instances of this class are immutable. + *

This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes.

+ */ +public final class QrSegment { + + /*---- Static factory functions ----*/ + + /** + * Returns a segment representing the specified binary data encoded in byte mode. + * @param data the binary data + * @return a segment containing the data + * @throws NullPointerException if the array is {@code null} + */ + public static QrSegment makeBytes(byte[] data) { + if (data == null) + throw new NullPointerException(); + return new QrSegment(Mode.BYTE, data.length, data, data.length * 8); + } + + + /** + * Returns a segment representing the specified string of decimal digits encoded in numeric mode. + * @param digits a string consisting of digits from 0 to 9 + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-digit characters + */ + public static QrSegment makeNumeric(String digits) { + if (digits == null) + throw new NullPointerException(); + if (!NUMERIC_REGEX.matcher(digits).matches()) + throw new IllegalArgumentException("String contains non-numeric characters"); + + BitBuffer bb = new BitBuffer(); + int i; + for (i = 0; i + 3 <= digits.length(); i += 3) // Process groups of 3 + bb.appendBits(Integer.parseInt(digits.substring(i, i + 3)), 10); + int rem = digits.length() - i; + if (rem > 0) // 1 or 2 digits remaining + bb.appendBits(Integer.parseInt(digits.substring(i)), rem * 3 + 1); + return new QrSegment(Mode.NUMERIC, digits.length(), bb.getBytes(), bb.bitLength()); + } + + + /** + * Returns a segment representing the specified text string encoded in alphanumeric mode. The characters allowed are: + * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + * @param text a string of text, with only certain characters allowed + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-encodable characters + */ + public static QrSegment makeAlphanumeric(String text) { + if (text == null) + throw new NullPointerException(); + if (!ALPHANUMERIC_REGEX.matcher(text).matches()) + throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode"); + + BitBuffer bb = new BitBuffer(); + int i; + for (i = 0; i + 2 <= text.length(); i += 2) { // Process groups of 2 + int temp = ALPHANUMERIC_ENCODING_TABLE[text.charAt(i) - ' '] * 45; + temp += ALPHANUMERIC_ENCODING_TABLE[text.charAt(i + 1) - ' ']; + bb.appendBits(temp, 11); + } + if (i < text.length()) // 1 character remaining + bb.appendBits(ALPHANUMERIC_ENCODING_TABLE[text.charAt(i) - ' '], 6); + return new QrSegment(Mode.ALPHANUMERIC, text.length(), bb.getBytes(), bb.bitLength()); + } + + + + /*---- Instance fields ----*/ + + /** The mode indicator for this segment. Never {@code null}. */ + public final Mode mode; + + /** The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + public final int numChars; + + /** The bits of this segment packed into a byte array in big endian. Accessed through {@link getByte(int)}. Not {@code null}. */ + private final byte[] data; + + /** The length of this segment's encoded data, measured in bits. Satisfies 0 ≤ {@code bitLength} ≤ {@code data.length} × 8. */ + public final int bitLength; + + + /*---- Constructor ----*/ + + /** + * Creates a new QR Code data segment with the specified parameters and data. + * @param md the mode, which is not {@code null} + * @param numCh the data length in characters, which is non-negative + * @param bitLen the data length in bits, which is non-negative + * @param b the bits packed into bytes, which is not {@code null} + * @throws NullPointerException if the mode or array is {@code null} + * @throws IllegalArgumentException if the character count or bit length are negative or invalid + */ + public QrSegment(Mode md, int numCh, byte[] b, int bitLen) { + if (md == null || b == null) + throw new NullPointerException(); + if (numCh < 0 || bitLen < 0 || bitLen > b.length * 8L) + throw new IllegalArgumentException("Invalid value"); + mode = md; + numChars = numCh; + data = Arrays.copyOf(b, (bitLen + 7) / 8); // Trim to precise length and also make defensive copy + bitLength = bitLen; + } + + + /*---- Method ----*/ + + /** + * Returns the data byte at the specified index. + * @param index the index to retrieve from, satisfying 0 ≤ {@code index} < ceil({@code bitLength} ÷ 8) + * @return the data byte at the specified index + * @throws IndexOutOfBoundsException if the index is out of bounds + */ + public byte getByte(int index) { + if (index < 0 || index > data.length) + throw new IndexOutOfBoundsException(); + return data[index]; + } + + + /*---- Constants ----*/ + + /** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */ + public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); + + /** Can test whether a string is encodable in alphanumeric mode (such as by using {@link #makeAlphanumeric(String)}). */ + public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); + + private static final byte[] ALPHANUMERIC_ENCODING_TABLE = { + // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 + 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, // Array indices 33 to 58 + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 + }; + + + + /*---- Public helper enumeration ----*/ + + /** + * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + */ + public enum Mode { + // Constants. + NUMERIC (0x1, 10, 12, 14), + ALPHANUMERIC(0x2, 9, 11, 13), + BYTE (0x4, 8, 16, 16), + KANJI (0x8, 8, 10, 12); + + + /*-- Fields --*/ + + /** An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ + public final int modeBits; + + private final int[] numBitsCharCount; + + + // Constructor. + private Mode(int mode, int... ccbits) { + this.modeBits = mode; + numBitsCharCount = ccbits; + } + + + /*-- Method --*/ + + /** + * Returns the bit width of the segment character count field for this mode object at the specified version number. + * @param ver the version number, which is between 1 to 40, inclusive + * @return the number of bits for the character count, which is between 8 to 16, inclusive + * @throws IllegalArgumentException if the version number is out of range + */ + public int numCharCountBits(int ver) { + if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; + else throw new IllegalArgumentException("Version number out of range"); + } + } + +} diff --git a/qrcodegen-demo.js b/qrcodegen-demo.js new file mode 100644 index 0000000..b48d505 --- /dev/null +++ b/qrcodegen-demo.js @@ -0,0 +1,107 @@ +/* + * QR Code generator demo (JavaScript) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +function redrawQrCode() { + // Get error correction level + var ecl; + if (document.getElementById("errcorlvl-medium").checked) + ecl = qrcodegen.QrCode.Ecc.MEDIUM; + else if (document.getElementById("errcorlvl-quartile").checked) + ecl = qrcodegen.QrCode.Ecc.QUARTILE; + else if (document.getElementById("errcorlvl-high").checked) + ecl = qrcodegen.QrCode.Ecc.HIGH; + else // In case no radio button is depressed + ecl = qrcodegen.QrCode.Ecc.LOW; + + // Get text and compute QR Code + var text = document.getElementById("text-input").value; + var qr = qrcodegen.encodeText(text, ecl); + + // Get scale and border + var scale = parseInt(document.getElementById("scale-input").value, 10); + var border = parseInt(document.getElementById("border-input").value, 10); + if (scale <= 0 || border < 0 || scale > 30 || border > 100) + return; + + // Draw QR Code onto canvas + var canvas = document.getElementById("qrcode-canvas"); + var width = (qr.getSize() + border * 2) * scale; + if (canvas.width != width) { + canvas.width = width; + canvas.height = width; + } + var ctx = canvas.getContext("2d"); + for (var y = -border; y < qr.getSize() + border; y++) { + for (var x = -border; x < qr.getSize() + border; x++) { + ctx.fillStyle = qr.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + + // Show statistics + var stats = "QR Code version = " + qr.getVersion() + ", "; + stats += "mask pattern = " + qr.getMask() + ", "; + stats += "character count = " + countUnicodeChars(text) + ",\n"; + stats += "encoding mode = "; + var seg = qrcodegen.encodeTextToSegment(text); + if (seg.getMode() == qrcodegen.QrSegment.Mode.NUMERIC) + stats += "numeric"; + else if (seg.getMode() == qrcodegen.QrSegment.Mode.ALPHANUMERIC) + stats += "alphanumeric"; + else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE) + stats += "byte"; + else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE) + stats += "kanji"; + else + stats += "unknown"; + stats += ", data bits = " + (4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length) + "."; + var elem = document.getElementById("statistics-output"); + while (elem.firstChild != null) + elem.removeChild(elem.firstChild); + elem.appendChild(document.createTextNode(stats)); +} + + +function countUnicodeChars(str) { + var result = 0; + for (var i = 0; i < str.length; i++, result++) { + var c = str.charCodeAt(i); + if (c < 0xD800 || c >= 0xE000) + continue; + else if (0xD800 <= c && c < 0xDC00) { // High surrogate + i++; + var d = str.charCodeAt(i); + if (0xDC00 <= d && d < 0xE000) // Low surrogate + continue; + } + throw "Invalid UTF-16 string"; + } + return result; +} + + +redrawQrCode(); diff --git a/qrcodegen-js-demo.html b/qrcodegen-js-demo.html new file mode 100644 index 0000000..1fea5e8 --- /dev/null +++ b/qrcodegen-js-demo.html @@ -0,0 +1,81 @@ + + + + + + QR Code generator library demo (JavaScript) + + + + +

QR Code generator demo library (JavaScript)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Text string:
Error correction: + + + + +
Scale: pixels per module
Border: modules
Statistics:
QR Code: + +
+
+ + + +
+

Copyright © 2016 Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

+ + diff --git a/qrcodegen.js b/qrcodegen.js new file mode 100644 index 0000000..8dacbe1 --- /dev/null +++ b/qrcodegen.js @@ -0,0 +1,975 @@ +/* + * QR Code generator library (JavaScript) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +/* + * Module "qrcodegen". Public members inside this namespace: + * - Function encodeText(str text, Ecc ecl) -> QrCode + * - Function encodeTextToSegment(str text) -> QrSegment + * - Function encodeBinary(list data, Ecc ecl) -> QrCode + * - Function encodeSegments(list segs, Ecc ecl) -> QrCode + * - Class QrCode: + * - Constructor QrCode(QrCode qr, int mask) + * - Constructor QrCode(list bytes, int mask, int version, Ecc ecl) + * - Method getVersion() -> int + * - Method getSize() -> int + * - Method getErrorCorrectionLevel() -> Ecc + * - Method getMask() -> int + * - Method getModule(int x, int y) -> int + * - Method isFunctionModule(int x, int y) -> bool + * - Method toSvgString(int border) -> str + * - Enum Ecc: + * - Constants LOW, MEDIUM, QUARTILE, HIGH + * - Fields int ordinal, formatBits + * - Class QrSegment: + * - Function makeBytes(list data) -> QrSegment + * - Function makeNumeric(str data) -> QrSegment + * - Function makeAlphanumeric(str data) -> QrSegment + * - Constructor QrSegment(Mode mode, int numChars, list bitData) + * - Method getMode() -> Mode + * - Method getNumChars() -> int + * - Method getBits() -> list + * - Enum Mode: + * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI + * - Method getModeBits() -> int + * - Method numCharCountBits(int ver) -> int + */ +var qrcodegen = new function() { + + /*---- Public static factory functions for QrCode ----*/ + + /* + * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + */ + this.encodeText = function(text, ecl) { + var seg = this.encodeTextToSegment(text); + return this.encodeSegments([seg], ecl); + }; + + + /* + * Returns a single QR segment representing the given Unicode text string. + */ + this.encodeTextToSegment = function(text) { + // Select the most efficient segment encoding automatically + if (QrSegment.NUMERIC_REGEX.test(text)) + return this.QrSegment.makeNumeric(text); + else if (QrSegment.ALPHANUMERIC_REGEX.test(text)) + return this.QrSegment.makeAlphanumeric(text); + else + return this.QrSegment.makeBytes(toUtf8ByteArray(text)); + }; + + + /* + * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + */ + this.encodeBinary = function(data, ecl) { + var seg = this.QrSegment.makeBytes(data); + return this.encodeSegments([seg], ecl); + }; + + + /* + * Returns a QR Code symbol representing the given data segments at the given error + * correction level. The smallest possible QR Code version is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. This + * function is considered to be lower level than simply encoding text or binary data. + */ + this.encodeSegments = function(segs, ecl) { + // Find the minimal version number to use + var version, dataCapacityBits; + outer: + for (version = 1; ; version++) { // Increment until the data fits in the QR Code + if (version > 40) // All versions could not fit the given data + throw "Data too long"; + dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + + // Calculate the total number of bits needed at this version number + // to encode all the segments (i.e. segment metadata and payloads) + var dataUsedBits = 0; + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; + if (seg.numChars < 0) + throw "Assertion error"; + var ccbits = seg.getMode().numCharCountBits(version); + if (seg.getNumChars() >= (1 << ccbits)) { + // Segment length value doesn't fit in the length field's bit-width, so fail immediately + continue outer; + } + dataUsedBits += 4 + ccbits + seg.getBits().length; + } + if (dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + } + + // Create the data bit string by concatenating all segments + var bb = new BitBuffer(); + segs.forEach(function(seg) { + bb.appendBits(seg.getMode().getModeBits(), 4); + bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); + bb.appendData(seg); + }); + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); + bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (var padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.bitLength() % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return new this.QrCode(bb.getBytes(), -1, version, ecl); + }; + + + + /* + * A class that represents an immutable square grid of black and white cells for a QR Code symbol, + * with associated static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. + * + * This constructor can be called in one of two ways: + * - new QrCode(bytes, mask, version, errCorLvl): + * Creates a new QR Code symbol with the given version number, error correction level, binary data array, + * and mask number. This cumbersome constructor can be invoked directly by the user, but is considered + * to be even lower level than qrcodegen.encodeSegments(). + * - new QrCode(qr, mask): + * Creates a new QR Code symbol based on the given existing object, but with a potentially different + * mask pattern. The version, error correction level, codewords, etc. of the newly created object are + * all identical to the argument object; only the mask may differ. + * In both cases, mask = -1 is for automatic choice or 0 to 7 for fixed choice. + */ + this.QrCode = function(initData, mask, version, errCorLvl) { + + /*-- Constructor --*/ + + // Handle simple scalar fields + if (mask < -1 || mask > 7) + throw "Mask value out of range"; + var size; + if (initData instanceof Array) { + if (version < 1 || version > 40) + throw "Version value out of range"; + size = version * 4 + 17; + } else if (initData instanceof qrcodegen.QrCode) { + version = initData.getVersion(); + size = initData.getSize(); + errCorLvl = initData.getErrorCorrectionLevel(); + } else + throw "Invalid initial data"; + + // Initialize both grids to be size*size arrays of Boolean false + var row = []; + for (var i = 0; i < size; i++) + row.push(false); + var modules = []; + var isFunction = []; + for (var i = 0; i < size; i++) { + modules.push(row.slice()); + isFunction.push(row.slice()); + } + + // Handle grid fields + if (initData instanceof Array) { + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(); + var allCodewords = appendErrorCorrection(initData); + drawCodewords(allCodewords); + } else if (initData instanceof qrcodegen.QrCode) { + for (var y = 0; y < size; y++) { + for (var x = 0; x < size; x++) { + modules[y][x] = initData.getModule(x, y) == 1; + isFunction[y][x] = initData.isFunctionModule(x, y); + } + } + applyMask(initData.getMask()); // Undo old mask + } else + throw "Invalid initial data"; + + // Handle masking + if (mask == -1) { // Automatically choose best mask + var minPenalty = Infinity; + for (var i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + var penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw "Assertion error"; + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + + + /*-- Accessor methods --*/ + + // Returns this QR Code symbol's version number, which is always between 1 and 40 (inclusive). + this.getVersion = function() { + return version; + }; + + // Returns the width and height of this QR Code symbol, measured in modules. + // Always equal to version * 4 + 17, in the range 21 to 177. + this.getSize = function() { + return size; + }; + + // Returns the error correction level used in this QR Code symbol. + this.getErrorCorrectionLevel = function() { + return errCorLvl; + }; + + // Returns the mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if a constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + this.getMask = function() { + return mask; + }; + + // Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top + // left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. + this.getModule = function(x, y) { + if (x < 0 || x >= size || y < 0 || y >= size) + return 0; // Infinite white border + else + return modules[y][x] ? 1 : 0; + }; + + // Tests whether the module at the given coordinates is a function module (true) or not (false). The top left + // corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. + this.isFunctionModule = function(x, y) { + if (x < 0 || x >= size || y < 0 || y >= size) + return false; // Infinite border + else + return isFunction[y][x]; + }; + + + /*-- Public instance methods --*/ + + // Based on the given number of border modules to add as padding, this returns a + // string whose contents represents an SVG XML file that depicts this QR Code symbol. + this.toSvgString = function(border) { + if (border < 0) + throw "Border must be non-negative"; + var result = "\n"; + result += "\n"; + result += "\n"; + result += "\t\n"; + result += "\n"; + return result; + }; + + + /*-- Private helper methods for constructor: Drawing function modules --*/ + + function drawFunctionPatterns() { + // Draw the horizontal and vertical timing patterns + for (var i = 0; i < size; i++) { + 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 the numerous alignment patterns + var alignPatPos = QrCode.getAlignmentPatternPositions(version); + var numAlign = alignPatPos.length; + for (var i = 0; i < numAlign; i++) { + for (var j = 0; j < numAlign; j++) { + if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) + continue; // Skip the three finder corners + else + 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 this object's error correction level and mask fields. + function drawFormatBits(mask) { + // Calculate error correction code and pack bits + var data = errCorLvl.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + var rem = data; + for (var i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if ((data & ((1 << 15) - 1)) != data) + throw "Assertion error"; + + // Draw first copy + for (var i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >>> i) & 1) != 0); + setFunctionModule(8, 7, ((data >>> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >>> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >>> 8) & 1) != 0); + for (var i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >>> i) & 1) != 0); + + // Draw second copy + for (var i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >>> i) & 1) != 0); + for (var i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >>> i) & 1) != 0); + setFunctionModule(8, size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + function drawVersion() { + if (version < 7) + return; + + // Calculate error correction code and pack bits + var rem = version; // version is uint6, in the range [7, 40] + for (var i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); + var data = version << 12 | rem; // uint18 + if ((data & ((1 << 18) - 1)) != data) + throw "Assertion error"; + + // Draw two copies + for (var i = 0; i < 18; i++) { + var bit = ((data >>> i) & 1) != 0; + var a = size - 11 + i % 3; + var b = Math.floor(i / 3); + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + function drawFinderPattern(x, y) { + for (var i = -4; i <= 4; i++) { + for (var j = -4; j <= 4; j++) { + var dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm + var xx = x + j; + var yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + function drawAlignmentPattern(x, y) { + for (var i = -2; i <= 2; i++) { + for (var j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + function setFunctionModule(x, y, 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. + function appendErrorCorrection(data) { + if (data.length != QrCode.getNumDataCodewords(version, errCorLvl)) + throw "Invalid argument"; + var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version]; + var numEcc = QrCode.NUM_ERROR_CORRECTION_CODEWORDS[errCorLvl.ordinal][version]; + if (numEcc % numBlocks != 0) + throw "Assertion error"; + var eccLen = Math.floor(numEcc / numBlocks); + var numShortBlocks = numBlocks - Math.floor(QrCode.getNumRawDataModules(version) / 8) % numBlocks; + var shortBlockLen = Math.floor(QrCode.getNumRawDataModules(version) / (numBlocks * 8)); + + var blocks = []; + var rs = new ReedSolomonGenerator(eccLen); + for (var i = 0, k = 0; i < numBlocks; i++) { + var dat = data.slice(k, k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1)); + k += dat.length; + var ecc = rs.getRemainder(dat); + if (i < numShortBlocks) + dat.push(0); + ecc.forEach(function(b) { + dat.push(b); + }); + blocks.push(dat); + } + + var result = []; + for (var i = 0; i < blocks[0].length; i++) { + for (var j = 0; j < blocks.length; j++) { + if (i != shortBlockLen - eccLen || j >= numShortBlocks) + result.push(blocks[j][i]); + } + } + if (result.length != Math.floor(QrCode.getNumRawDataModules(version) / 8)) + throw "Assertion error"; + return result; + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + function drawCodewords(data) { + if (data.length != Math.floor(QrCode.getNumRawDataModules(version) / 8)) + throw "Invalid argument"; + var i = 0; // Bit index into the data + // Do the funny zigzag scan + for (var right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (var vert = 0; vert < size; vert++) { // Vertical counter + for (var j = 0; j < 2; j++) { + var x = right - j; // Actual x coordinate + var upwards = ((right & 2) == 0) ^ (x < 6); + var y = upwards ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction[y][x] && i < data.length * 8) { + modules[y][x] = (data[i >>> 3] >>> (7 - (i & 7)) & 1) != 0; + i++; + } + } + } + } + if (i != data.length * 8) + throw "Assertion error"; + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + function applyMask(mask) { + if (mask < 0 || mask > 7) + throw "Mask value out of range"; + for (var y = 0; y < size; y++) { + for (var x = 0; x < size; x++) { + var invert; + switch (mask) { + 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 = (Math.floor(x / 3) + Math.floor(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 "Assertion error"; + } + modules[y][x] ^= invert & !isFunction[y][x]; + } + } + } + + + // 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. + function getPenaltyScore() { + var result = 0; + + // Adjacent modules in row having same color + for (var y = 0; y < size; y++) { + var colorX = modules[y][0]; + for (var x = 1, runX = 1; x < size; x++) { + if (modules[y][x] != colorX) { + colorX = modules[y][x]; + runX = 1; + } else { + runX++; + if (runX == 5) + result += QrCode.PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (var x = 0; x < size; x++) { + var colorY = modules[0][x]; + for (var y = 1, runY = 1; y < size; y++) { + if (modules[y][x] != colorY) { + colorY = modules[y][x]; + runY = 1; + } else { + runY++; + if (runY == 5) + result += QrCode.PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (var y = 0; y < size - 1; y++) { + for (var x = 0; x < size - 1; x++) { + var color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) + result += QrCode.PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (var y = 0; y < size; y++) { + for (var x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + // Finder-like pattern in columns + for (var x = 0; x < size; x++) { + for (var y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + + // Balance of black and white modules + var black = 0; + modules.forEach(function(row) { + row.forEach(function(color) { + if (color) + black++; + }); + }); + var total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (var k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += QrCode.PENALTY_N4; + return result; + } + }; + + + /*---- Private static helper functions ----*/ + + var QrCode = {}; // Private object to assign properties to + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + QrCode.getAlignmentPatternPositions = function(ver) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; + else if (ver == 1) + return []; + else { + var size = ver * 4 + 17; + var numAlign = Math.floor(ver / 7) + 2; + var step; + if (ver != 32) + step = Math.ceil((size - 13) / (2 * numAlign - 2)) * 2; + else // C-C-C-Combo breaker! + step = 26; + + var result = []; + for (var i = numAlign - 1, pos = size - 7; i >= 1; i--, pos -= step) + result.push(pos); + result.push(6); + result.reverse(); + return result; + } + }; + + // Returns the number of raw data modules (bits) available at the given version number. + // These data modules are used for both user data codewords and error correction codewords. + // This stateless pure function could be implemented as a 40-entry lookup table. + QrCode.getNumRawDataModules = function(ver) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; + var result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + var numAlign = Math.floor(ver / 7) + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; + }; + + // 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. + QrCode.getNumDataCodewords = function(ver, ecl) { + return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - QrCode.NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver]; + }; + + + /*---- Tables of constants ----*/ + + // For use in getPenaltyScore(), when evaluating which mask is best. + QrCode.PENALTY_N1 = 3; + QrCode.PENALTY_N2 = 3; + QrCode.PENALTY_N3 = 40; + QrCode.PENALTY_N4 = 10; + + QrCode.NUM_ERROR_CORRECTION_CODEWORDS = [ + // 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 + [null, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750], // Low + [null, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372], // Medium + [null, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040], // Quartile + [null, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430], // High + ]; + + QrCode.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 + [null, 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 + [null, 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 + [null, 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 + [null, 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 + ]; + + + /* + * A public helper enumeration that represents the error correction level used in a QR Code symbol. + * The fields 'ordinal' and 'formatBits' are in the range 0 to 3 (unsigned 2-bit integer). + */ + this.QrCode.Ecc = { + // Constants declared in ascending order of error protection + LOW : {ordinal: 0, formatBits: 1}, + MEDIUM : {ordinal: 1, formatBits: 0}, + QUARTILE: {ordinal: 2, formatBits: 3}, + HIGH : {ordinal: 3, formatBits: 2}, + }; + + + + /* + * A public class that represents a character string to be encoded in a QR Code symbol. + * Each segment has a mode, and a sequence of characters that is already encoded as + * a sequence of bits. Instances of this class are immutable. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ + this.QrSegment = function(mode, numChars, bitData) { + /*-- Accessor methods --*/ + this.getMode = function() { + return mode; + }; + this.getNumChars = function() { + return numChars; + }; + this.getBits = function() { + return bitData.slice(); + }; + }; + + /*-- Static factory functions --*/ + + // Returns a segment representing the given binary data encoded in byte mode. + this.QrSegment.makeBytes = function(data) { + var bb = new BitBuffer(); + data.forEach(function(b) { + bb.appendBits(b, 8); + }); + return new this(this.Mode.BYTE, data.length, bb.getBits()); + }; + + // Returns a segment representing the given string of decimal digits encoded in numeric mode. + this.QrSegment.makeNumeric = function(digits) { + if (!QrSegment.NUMERIC_REGEX.test(digits)) + throw "String contains non-numeric characters"; + var bb = new BitBuffer(); + var i; + for (i = 0; i + 3 <= digits.length; i += 3) // Process groups of 3 + bb.appendBits(parseInt(digits.substr(i, 3), 10), 10); + var rem = digits.length - i; + if (rem > 0) // 1 or 2 digits remaining + bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); + return new this(this.Mode.NUMERIC, digits.length, bb.getBits()); + }; + + // Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: + // 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + this.QrSegment.makeAlphanumeric = function(text) { + if (!QrSegment.ALPHANUMERIC_REGEX.test(text)) + throw "String contains unencodable characters in alphanumeric mode"; + var bb = new BitBuffer(); + var i; + for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2 + var temp = QrSegment.ALPHANUMERIC_ENCODING_TABLE[text.charCodeAt(i) - 32] * 45; + temp += QrSegment.ALPHANUMERIC_ENCODING_TABLE[text.charCodeAt(i + 1) - 32]; + bb.appendBits(temp, 11); + } + if (i < text.length) // 1 character remaining + bb.appendBits(QrSegment.ALPHANUMERIC_ENCODING_TABLE[text.charCodeAt(i) - 32], 6); + return new this(this.Mode.ALPHANUMERIC, text.length, bb.getBits()); + }; + + /*-- Constants --*/ + + var QrSegment = {}; // Private object to assign properties to + + // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + QrSegment.NUMERIC_REGEX = /^[0-9]*$/; + + // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; + + QrSegment.ALPHANUMERIC_ENCODING_TABLE = [ + // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 + 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, // Array indices 33 to 58 + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 + ]; + + + /* + * A public helper enumeration that represents the mode field of a segment. + * Objects are immutable. Provides methods to retrieve closely related values. + */ + this.QrSegment.Mode = { // Constants + NUMERIC : new Mode(0x1, [10, 12, 14]), + ALPHANUMERIC: new Mode(0x2, [ 9, 11, 13]), + BYTE : new Mode(0x4, [ 8, 16, 16]), + KANJI : new Mode(0x8, [ 8, 10, 12]), + }; + + // Private constructor for the enum. + function Mode(mode, ccbits) { + // Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + this.getModeBits = function() { + return mode; + }; + + // Returns the bit width of the segment character count field for this mode object at the given version number. + this.numCharCountBits = function(ver) { + if ( 1 <= ver && ver <= 9) return ccbits[0]; + else if (10 <= ver && ver <= 26) return ccbits[1]; + else if (27 <= ver && ver <= 40) return ccbits[2]; + else throw "Version number out of range"; + }; + } + + + + /*---- Private helper functions and classes ----*/ + + // Returns a new array of bytes representing the given string encoded in UTF-8. + function toUtf8ByteArray(str) { + var result = []; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c < 0x80) + result.push(c); + else if (c < 0x800) { + result.push(0xC0 | ((c >>> 6) & 0x1F)); + result.push(0x80 | ((c >>> 0) & 0x3F)); + } else if (0xD800 <= c && c < 0xDC00) { // High surrogate + i++; + if (i < str.length) { + var d = str.charCodeAt(i); + if (0xDC00 <= d && d < 0xE000) { // Low surrogate + c = ((c & 0x3FF) << 10 | (d & 0x3FF)) + 0x10000; + result.push(0xF0 | ((c >>> 18) & 0x07)); + result.push(0x80 | ((c >>> 12) & 0x3F)); + result.push(0x80 | ((c >>> 6) & 0x3F)); + result.push(0x80 | ((c >>> 0) & 0x3F)); + continue; + } + } + throw "Invalid UTF-16 string"; + } else if (0xDC00 <= c && c < 0xE000) // Low surrogate + throw "Invalid UTF-16 string"; + else if (c < 0x10000) { + result.push(0xE0 | ((c >>> 12) & 0x0F)); + result.push(0x80 | ((c >>> 6) & 0x3F)); + result.push(0x80 | ((c >>> 0) & 0x3F)); + } else + throw "Assertion error"; + } + return result; + } + + + + /* + * A private helper class that computes the Reed-Solomon error correction codewords for a sequence of + * data codewords at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because the divisor polynomial does not need to be recalculated for every input. + * This constructor creates a Reed-Solomon ECC generator for the given degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + */ + function ReedSolomonGenerator(degree) { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + var coefficients = []; + + // Start with the monomial x^0 + for (var i = 0; i < degree - 1; i++) + coefficients.push(0); + coefficients.push(1); + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + var root = 1; + for (var i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (var j = 0; j < coefficients.length; j++) { + coefficients[j] = ReedSolomonGenerator.multiply(coefficients[j], root); + if (j + 1 < coefficients.length) + coefficients[j] ^= coefficients[j + 1]; + } + root = (root << 1) ^ ((root >>> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) + } + + // Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. + // The returned object is always a new byte array. This method does not alter this object's state (because it is immutable). + this.getRemainder = function(data) { + // Compute the remainder by performing polynomial division + var result = coefficients.map(function() { return 0; }); + data.forEach(function(b) { + var factor = b ^ result[0]; + result.shift(); + result.push(0); + for (var j = 0; j < result.length; j++) + result[j] ^= ReedSolomonGenerator.multiply(coefficients[j], factor); + }); + return result; + }; + } + + // This static function 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. + ReedSolomonGenerator.multiply = function(x, y) { + if ((x & 0xFF) != x || (y & 0xFF) != y) + throw "Byte out of range"; + // Russian peasant multiplication + var z = 0; + for (var i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if ((z & 0xFF) != z) + throw "Assertion error"; + return z; + }; + + + + /* + * A private helper class that represents an appendable sequence of bits. + * This constructor creates an empty bit buffer (length 0). + */ + function BitBuffer() { + // Array of bits; each item is the integer 0 or 1 + var bitData = []; + + /*-- Methods --*/ + + // Returns the number of bits in the buffer, which is a non-negative value. + this.bitLength = function() { + return bitData.length; + }; + + // Returns a copy of all bits. + this.getBits = function() { + return bitData.slice(); + }; + + // Returns a copy of all bytes, padding up to the nearest byte. + this.getBytes = function() { + var result = []; + var numBytes = Math.ceil(bitData.length / 8); + for (var i = 0; i < numBytes; i++) + result.push(0); + bitData.forEach(function(bit, i) { + result[i >>> 3] |= bit << (7 - (i & 7)); + }); + return result; + }; + + // Appends the given number of bits of the given value to this sequence. + // If 0 <= len <= 31, then this requires 0 <= val < 2^len. + this.appendBits = function(val, len) { + if (len < 0 || len > 32 || len < 32 && (val & ((1 << len) - 1)) != val) + throw "Value out of range"; + for (var i = len - 1; i >= 0; i--) // Append bit by bit + bitData.push((val >>> i) & 1); + }; + + // Appends the bit data of the given segment to this bit buffer. + this.appendData = function(seg) { + seg.getBits().forEach(function(b) { // Append bit by bit + bitData.push(b); + }); + }; + } + +}; From 3c6cc5eabca6ad4b78fcfae9cca6e5c83d0337f6 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 11 Apr 2016 05:40:00 +0000 Subject: [PATCH 002/810] Added Python port of the library, updated comments and tweaked code in Java and JavaScript versions. --- QrCode.java | 45 ++- qrcodegen-demo.py | 125 +++++++ qrcodegen.js | 94 +++--- qrcodegen.py | 813 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1005 insertions(+), 72 deletions(-) create mode 100644 qrcodegen-demo.py create mode 100644 qrcodegen.py diff --git a/QrCode.java b/QrCode.java index 16ec193..14bc03d 100644 --- a/QrCode.java +++ b/QrCode.java @@ -53,17 +53,30 @@ public final class QrCode { public static QrCode encodeText(String text, Ecc ecl) { if (text == null || ecl == null) throw new NullPointerException(); - QrSegment seg; // Select the most efficient segment encoding automatically - if (QrSegment.NUMERIC_REGEX.matcher(text).matches()) - seg = QrSegment.makeNumeric(text); - else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches()) - seg = QrSegment.makeAlphanumeric(text); - else - seg = QrSegment.makeBytes(text.getBytes(StandardCharsets.UTF_8)); + QrSegment seg = encodeTextToSegment(text); return encodeSegments(Arrays.asList(seg), ecl); } + /** + * Returns a QR Code segment representing the specified Unicode text string. + * @param text the text to be encoded, which can be any Unicode string + * @return a QR Code representing the text + * @throws NullPointerException if the text is {@code null} + */ + public static QrSegment encodeTextToSegment(String text) { + if (text == null) + throw new NullPointerException(); + // Select the most efficient segment encoding automatically + if (QrSegment.NUMERIC_REGEX.matcher(text).matches()) + return QrSegment.makeNumeric(text); + else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches()) + return QrSegment.makeAlphanumeric(text); + else + return QrSegment.makeBytes(text.getBytes(StandardCharsets.UTF_8)); + } + + /** * Returns a QR Code symbol representing the specified binary data string at the specified error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of @@ -255,10 +268,10 @@ public final class QrCode { * @return the module's color, which is either 0 (white) or 1 (black) */ public int getModule(int x, int y) { - if (x < 0 || x >= size || y < 0 || y >= size) - return 0; // Infinite white border - else + if (0 <= x && x < size && 0 <= y && y < size) return modules[y][x] ? 1 : 0; + else + return 0; // Infinite white border } @@ -353,7 +366,7 @@ public final class QrCode { // Draws two copies of the format bits (with its own error correction code) - // based on this object's error correction level and mask fields. + // based on the given mask and this object's error correction level field. private void drawFormatBits(int mask) { // Calculate error correction code and pack bits int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 @@ -400,8 +413,9 @@ public final class QrCode { // Draw two copies for (int i = 0; i < 18; i++) { boolean bit = ((data >>> i) & 1) != 0; - setFunctionModule(size - 11 + i % 3, i / 3, bit); - setFunctionModule(i / 3, size - 11 + i % 3, bit); + int a = size - 11 + i % 3, b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); } } @@ -411,8 +425,7 @@ public final class QrCode { for (int i = -4; i <= 4; i++) { for (int j = -4; j <= 4; j++) { int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - int xx = x + j; - int yy = y + i; + int xx = x + j, yy = y + i; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -495,7 +508,7 @@ public final class QrCode { boolean upwards = ((right & 2) == 0) ^ (x < 6); int y = upwards ? size - 1 - vert : vert; // Actual y coordinate if (!isFunction[y][x] && i < data.length * 8) { - modules[y][x] = (data[i >>> 3] >>> (7 - (i & 7)) & 1) != 0; + modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; i++; } } diff --git a/qrcodegen-demo.py b/qrcodegen-demo.py new file mode 100644 index 0000000..7978c94 --- /dev/null +++ b/qrcodegen-demo.py @@ -0,0 +1,125 @@ +# +# QR Code generator demo (Python 2, 3) +# +# Copyright (c) 2016 Project Nayuki +# https://www.nayuki.io/page/qr-code-generator-library +# +# (MIT License) +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +from __future__ import print_function +import qrcodegen + + +# ---- Main program ---- + +def main(): + """The main application program.""" + do_basic_demo() + do_variety_demo() + do_segment_demo() + + +def do_basic_demo(): + """Creates a single QR Code, then prints it to the console.""" + text = u"Hello, world!" # User-supplied Unicode text + errcorlvl = qrcodegen.QrCode.Ecc.LOW # Error correction level + qr = qrcodegen.encode_text(text, errcorlvl) + print_qr(qr) + + +def do_variety_demo(): + """Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console.""" + + # Project Nayuki URL + qr = qrcodegen.encode_text("https://www.nayuki.io/", qrcodegen.QrCode.Ecc.HIGH) + qr = qrcodegen.QrCode(qrcode=qr, mask=3) # Change mask, forcing to mask #3 + print_qr(qr) + + # Numeric mode encoding (3.33 bits per digit) + qr = qrcodegen.encode_text("314159265358979323846264338327950288419716939937510", qrcodegen.QrCode.Ecc.MEDIUM) + print_qr(qr) + + # Alphanumeric mode encoding (5.5 bits per character) + qr = qrcodegen.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen.QrCode.Ecc.HIGH) + print_qr(qr) + + # Unicode text as UTF-8, and different masks + qr = qrcodegen.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", qrcodegen.QrCode.Ecc.QUARTILE) + print_qr(qrcodegen.QrCode(qrcode=qr, mask=0)) + print_qr(qrcodegen.QrCode(qrcode=qr, mask=1)) + print_qr(qrcodegen.QrCode(qrcode=qr, mask=5)) + print_qr(qrcodegen.QrCode(qrcode=qr, mask=7)) + + # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + qr = qrcodegen.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", qrcodegen.QrCode.Ecc.HIGH) + print_qr(qr) + + +def do_segment_demo(): + """Creates QR Codes with manually specified segments for better compactness.""" + + # Illustration "silver" + silver0 = "THE SQUARE ROOT OF 2 IS 1." + silver1 = "41421356237309504880168872420969807856967187537694807317667973799" + qr = qrcodegen.encode_text(silver0 + silver1, qrcodegen.QrCode.Ecc.LOW) + print_qr(qr) + + segs = [ + qrcodegen.QrSegment.make_alphanumeric(silver0), + qrcodegen.QrSegment.make_numeric(silver1)] + qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) + print_qr(qr) + + # Illustration "golden" + golden0 = u"Golden ratio \u03C6 = 1." + golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" + golden2 = u"......" + qr = qrcodegen.encode_text(golden0 + golden1 + golden2, qrcodegen.QrCode.Ecc.LOW) + print_qr(qr) + + segs = [ + qrcodegen.QrSegment.make_bytes(golden0.encode("UTF-8")), + qrcodegen.QrSegment.make_numeric(golden1), + qrcodegen.QrSegment.make_alphanumeric(golden2)] + qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) + print_qr(qr) + + + +# ---- Utilities ---- + +def print_qr(qrcode): + """Prints the given QrCode object to the console.""" + border = 4 + for y in range(-border, qrcode.get_size() + border): + for x in range(-border, qrcode.get_size() + border): + print(u"\u2588 "[qrcode.get_module(x,y)] * 2, end="") + print() + print() + + +# Run the main program +if __name__ == "__main__": + main() diff --git a/qrcodegen.js b/qrcodegen.js index 8dacbe1..81b6a6d 100644 --- a/qrcodegen.js +++ b/qrcodegen.js @@ -27,16 +27,16 @@ /* * Module "qrcodegen". Public members inside this namespace: - * - Function encodeText(str text, Ecc ecl) -> QrCode + * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode * - Function encodeTextToSegment(str text) -> QrSegment - * - Function encodeBinary(list data, Ecc ecl) -> QrCode - * - Function encodeSegments(list segs, Ecc ecl) -> QrCode + * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode + * - Function encodeSegments(list segs, QrCode.Ecc ecl) -> QrCode * - Class QrCode: * - Constructor QrCode(QrCode qr, int mask) - * - Constructor QrCode(list bytes, int mask, int version, Ecc ecl) + * - Constructor QrCode(list bytes, int mask, int version, QrCode.Ecc ecl) * - Method getVersion() -> int * - Method getSize() -> int - * - Method getErrorCorrectionLevel() -> Ecc + * - Method getErrorCorrectionLevel() -> QrCode.Ecc * - Method getMask() -> int * - Method getModule(int x, int y) -> int * - Method isFunctionModule(int x, int y) -> bool @@ -48,8 +48,8 @@ * - Function makeBytes(list data) -> QrSegment * - Function makeNumeric(str data) -> QrSegment * - Function makeAlphanumeric(str data) -> QrSegment - * - Constructor QrSegment(Mode mode, int numChars, list bitData) - * - Method getMode() -> Mode + * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) + * - Method getMode() -> QrSegment.Mode * - Method getNumChars() -> int * - Method getBits() -> list * - Enum Mode: @@ -155,11 +155,13 @@ var qrcodegen = new function() { + /*---- QR Code symbol class ----*/ + /* * A class that represents an immutable square grid of black and white cells for a QR Code symbol, * with associated static functions to create a QR Code from user-supplied textual or binary data. * This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. + * from 1 to 40, all 4 error correction levels. * * This constructor can be called in one of two ways: * - new QrCode(bytes, mask, version, errCorLvl): @@ -179,17 +181,15 @@ var qrcodegen = new function() { // Handle simple scalar fields if (mask < -1 || mask > 7) throw "Mask value out of range"; - var size; if (initData instanceof Array) { if (version < 1 || version > 40) throw "Version value out of range"; - size = version * 4 + 17; } else if (initData instanceof qrcodegen.QrCode) { version = initData.getVersion(); - size = initData.getSize(); errCorLvl = initData.getErrorCorrectionLevel(); } else throw "Invalid initial data"; + var size = version * 4 + 17; // Initialize both grids to be size*size arrays of Boolean false var row = []; @@ -204,7 +204,7 @@ var qrcodegen = new function() { // Handle grid fields if (initData instanceof Array) { - // Draw function patterns, draw all codewords, do masking + // Draw function patterns, draw all codewords drawFunctionPatterns(); var allCodewords = appendErrorCorrection(initData); drawCodewords(allCodewords); @@ -267,19 +267,19 @@ var qrcodegen = new function() { // Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top // left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. this.getModule = function(x, y) { - if (x < 0 || x >= size || y < 0 || y >= size) - return 0; // Infinite white border - else + if (0 <= x && x < size && 0 <= y && y < size) return modules[y][x] ? 1 : 0; + else + return 0; // Infinite white border }; // Tests whether the module at the given coordinates is a function module (true) or not (false). The top left // corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. this.isFunctionModule = function(x, y) { - if (x < 0 || x >= size || y < 0 || y >= size) - return false; // Infinite border - else + if (0 <= x && x < size && 0 <= y && y < size) return isFunction[y][x]; + else + return false; // Infinite border }; @@ -345,7 +345,7 @@ var qrcodegen = new function() { // Draws two copies of the format bits (with its own error correction code) - // based on this object's error correction level and mask fields. + // based on the given mask and this object's error correction level field. function drawFormatBits(mask) { // Calculate error correction code and pack bits var data = errCorLvl.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 @@ -392,8 +392,7 @@ var qrcodegen = new function() { // Draw two copies for (var i = 0; i < 18; i++) { var bit = ((data >>> i) & 1) != 0; - var a = size - 11 + i % 3; - var b = Math.floor(i / 3); + var a = size - 11 + i % 3, b = Math.floor(i / 3); setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); } @@ -405,8 +404,7 @@ var qrcodegen = new function() { for (var i = -4; i <= 4; i++) { for (var j = -4; j <= 4; j++) { var dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - var xx = x + j; - var yy = y + i; + var xx = x + j, yy = y + i; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -489,7 +487,7 @@ var qrcodegen = new function() { var upwards = ((right & 2) == 0) ^ (x < 6); var y = upwards ? size - 1 - vert : vert; // Actual y coordinate if (!isFunction[y][x] && i < data.length * 8) { - modules[y][x] = (data[i >>> 3] >>> (7 - (i & 7)) & 1) != 0; + modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; i++; } } @@ -614,9 +612,9 @@ var qrcodegen = new function() { var QrCode = {}; // Private object to assign properties to - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + // Returns a sequence of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of integers. QrCode.getAlignmentPatternPositions = function(ver) { if (ver < 1 || ver > 40) throw "Version number out of range"; @@ -705,6 +703,8 @@ var qrcodegen = new function() { + /*---- Data segment class ----*/ + /* * A public class that represents a character string to be encoded in a QR Code symbol. * Each segment has a mode, and a sequence of characters that is already encoded as @@ -714,6 +714,9 @@ var qrcodegen = new function() { * Any segment longer than this is meaningless for the purpose of generating QR Codes. */ this.QrSegment = function(mode, numChars, bitData) { + if (numChars < 0 || !(mode instanceof Mode)) + throw "Invalid argument"; + /*-- Accessor methods --*/ this.getMode = function() { return mode; @@ -726,7 +729,7 @@ var qrcodegen = new function() { }; }; - /*-- Static factory functions --*/ + /*-- Public static factory functions --*/ // Returns a segment representing the given binary data encoded in byte mode. this.QrSegment.makeBytes = function(data) { @@ -819,36 +822,15 @@ var qrcodegen = new function() { // Returns a new array of bytes representing the given string encoded in UTF-8. function toUtf8ByteArray(str) { + str = encodeURI(str); var result = []; for (var i = 0; i < str.length; i++) { - var c = str.charCodeAt(i); - if (c < 0x80) - result.push(c); - else if (c < 0x800) { - result.push(0xC0 | ((c >>> 6) & 0x1F)); - result.push(0x80 | ((c >>> 0) & 0x3F)); - } else if (0xD800 <= c && c < 0xDC00) { // High surrogate - i++; - if (i < str.length) { - var d = str.charCodeAt(i); - if (0xDC00 <= d && d < 0xE000) { // Low surrogate - c = ((c & 0x3FF) << 10 | (d & 0x3FF)) + 0x10000; - result.push(0xF0 | ((c >>> 18) & 0x07)); - result.push(0x80 | ((c >>> 12) & 0x3F)); - result.push(0x80 | ((c >>> 6) & 0x3F)); - result.push(0x80 | ((c >>> 0) & 0x3F)); - continue; - } - } - throw "Invalid UTF-16 string"; - } else if (0xDC00 <= c && c < 0xE000) // Low surrogate - throw "Invalid UTF-16 string"; - else if (c < 0x10000) { - result.push(0xE0 | ((c >>> 12) & 0x0F)); - result.push(0x80 | ((c >>> 6) & 0x3F)); - result.push(0x80 | ((c >>> 0) & 0x3F)); - } else - throw "Assertion error"; + if (str.charAt(i) != "%") + result.push(str.charCodeAt(i)); + else { + result.push(parseInt(str.substr(i + 1, 2), 16)); + i += 2; + } } return result; } diff --git a/qrcodegen.py b/qrcodegen.py new file mode 100644 index 0000000..5e858d0 --- /dev/null +++ b/qrcodegen.py @@ -0,0 +1,813 @@ +# +# QR Code generator library (Python 2, 3) +# +# Copyright (c) 2016 Project Nayuki +# https://www.nayuki.io/page/qr-code-generator-library +# +# (MIT License) +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +import itertools, re, sys + + +""" +Public members inside this module "qrcodegen": +- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode +- Function encode_text_to_segment(str text) -> QrSegment +- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode +- Function encode_segments(list segs, QrCode.Ecc ecl) -> QrCode +- Class QrCode: + - Constructor QrCode(QrCode qr, int mask) + - Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl) + - Method get_version() -> int + - Method get_size() -> int + - Method get_error_correction_level() -> QrCode.Ecc + - Method get_mask() -> int + - Method get_module(int x, int y) -> int + - Method to_svg_str(int border) -> str + - Enum Ecc: + - Constants LOW, MEDIUM, QUARTILE, HIGH + - Fields int ordinal, formatbits +- Class QrSegment: + - Function make_bytes(bytes data) -> QrSegment + - Function make_numeric(str digits) -> QrSegment + - Function make_alphanumeric(str text) -> QrSegment + - Constructor QrSegment(QrSegment.Mode mode, int numch, list bitdata) + - Method get_mode() -> QrSegment.Mode + - Method get_num_chars() -> int + - Method get_bits() -> list + - Enum Mode: + - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI + - Method get_mode_bits() -> int + - Method num_char_count_bits(int ver) -> int +""" + + +# ---- Public static factory functions for QrCode ---- + +def encode_text(text, ecl): + """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" + seg = encode_text_to_segment(text) + return encode_segments([seg], ecl) + + +def encode_text_to_segment(text): + """Returns a QR Code segment representing the given Unicode text string.""" + if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)): + raise TypeError("Text string expected") + # Select the most efficient segment encoding automatically + if QrSegment.NUMERIC_REGEX.match(text) is not None: + return QrSegment.make_numeric(text) + elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None: + return QrSegment.make_alphanumeric(text) + else: + return QrSegment.make_bytes(text.encode("UTF-8")) + + +def encode_binary(data, ecl): + """Returns a QR Code symbol representing the given binary data string at the given error correction level. + This function always encodes using the binary segment mode, not any text mode. The maximum number of + bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.""" + if not isinstance(data, bytes): + raise TypeError("Binary array expected") + return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) + + +def encode_segments(segs, ecl): + """Returns a QR Code symbol representing the given data segments at the given error + correction level. The smallest possible QR Code version is automatically chosen for the output. + This function allows the user to create a custom sequence of segments that switches + between modes (such as alphanumeric and binary) to encode text more efficiently. This + function is considered to be lower level than simply encoding text or binary data.""" + + # Find the minimal version number to use + for version in itertools.count(1): # Increment until the data fits in the QR Code + if version > 40: # All versions could not fit the given data + raise ValueError("Data too long") + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available + + # Calculate the total number of bits needed at this version number + # to encode all the segments (i.e. segment metadata and payloads) + datausedbits = 0 + for seg in segs: + if seg.get_num_chars() < 0: + raise AssertionError() + ccbits = seg.get_mode().num_char_count_bits(version) + if seg.get_num_chars() >= (1 << ccbits): + # Segment length value doesn't fit in the length field's bit-width, so fail immediately + break + datausedbits += 4 + ccbits + len(seg.get_bits()) + else: # If the loop above did not break + if datausedbits <= datacapacitybits: + break # This version number is found to be suitable + + # Create the data bit string by concatenating all segments + bb = _BitBuffer() + for seg in segs: + bb.append_bits(seg.get_mode().get_mode_bits(), 4) + bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version)) + bb.append_all(seg) + + # Add terminator and pad up to a byte if applicable + bb.append_bits(0, min(4, datacapacitybits - bb.bit_length())) + bb.append_bits(0, -bb.bit_length() % 8) + + # Pad with alternate bytes until data capacity is reached + for padbyte in itertools.cycle((0xEC, 0x11)): + if bb.bit_length() >= datacapacitybits: + break + bb.append_bits(padbyte, 8) + assert bb.bit_length() % 8 == 0 + + # Create the QR Code symbol + return QrCode(datacodewords=bb.get_bytes(), mask=-1, version=version, errcorlvl=ecl) + + + +# ---- QR Code symbol class ---- + +class QrCode(object): + """Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the + QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels.""" + + # ---- Constructor ---- + + def __init__(self, qrcode=None, datacodewords=None, mask=None, version=None, errcorlvl=None): + """This constructor can be called in one of two ways: + - QrCode(datacodewords=list, mask=int, version=int, errcorlvl=QrCode.Ecc): + Creates a new QR Code symbol with the given version number, error correction level, binary data array, + and mask number. This cumbersome constructor can be invoked directly by the user, but is considered + to be even lower level than qrcodegen.encode_segments(). + - QrCode(qrcode=QrCode, mask=int): + Creates a new QR Code symbol based on the given existing object, but with a potentially different + mask pattern. The version, error correction level, codewords, etc. of the newly created object are + all identical to the argument object; only the mask may differ. + In both cases, mask = -1 is for automatic choice or 0 to 7 for fixed choice.""" + + # Handle simple scalar fields + if not -1 <= mask <= 7: + raise ValueError("Mask value out of range") + if datacodewords is not None and qrcode is None: + if not 1 <= version <= 40: + raise ValueError("Version value out of range") + if not isinstance(errcorlvl, QrCode.Ecc): + raise TypeError("QrCode.Ecc expected") + elif qrcode is not None and datacodewords is None: + version = qrcode._version + errcorlvl = qrcode._errcorlvl + else: + raise ValueError("Exactly one of datacodewords or qrcode must be not None") + self._version = version + self._errcorlvl = errcorlvl + self._size = version * 4 + 17 + + if datacodewords is not None: # Render from scratch a QR Code based on data codewords + if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): + raise ValueError("Invalid array length") + # Initialize grids of modules + self._modules = [[False] * self._size for _ in range(self._size)] # The modules of the QR symbol; start with entirely white grid + self._isfunction = [[False] * self._size for _ in range(self._size)] # Indicates function modules that are not subjected to masking + # Draw function patterns, draw all codewords + self._draw_function_patterns() + allcodewords = self._append_error_correction(datacodewords) + self._draw_codewords(allcodewords) + elif qrcode is not None: # Modify the mask of an existing QR Code + self._modules = [list(row) for row in qrcode._modules] # Deep copy + self._isfunction = qrcode._isfunction # Shallow copy because the data is read-only + self._apply_mask(qrcode._mask) # Undo existing mask + + # Handle masking + if mask == -1: # Automatically choose best mask + minpenalty = 1 << 32 + for i in range(8): + self._draw_format_bits(i) + self._apply_mask(i) + penalty = self._get_penalty_score() + if penalty < minpenalty: + mask = i + minpenalty = penalty + self._apply_mask(i) # Undoes the mask due to XOR + assert 0 <= mask <= 7 + self._draw_format_bits(mask) # Overwrite old format bits + self._apply_mask(mask) # Apply the final choice of mask + self._mask = mask + + + # ---- Accessor methods ---- + + def get_version(self): + """Returns this QR Code symbol's version number, which is always between 1 and 40 (inclusive).""" + return self._version + + def get_size(self): + """Returns the width and height of this QR Code symbol, measured in modules. + Always equal to version * 4 + 17, in the range 21 to 177.""" + return self._size + + def get_error_correction_level(self): + """Returns the error correction level used in this QR Code symbol.""" + return self._errcorlvl + + def get_mask(self): + """Returns the mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + Note that even if a constructor was called with automatic masking requested + (mask = -1), the resulting object will still have a mask value between 0 and 7.""" + return self._mask + + def get_module(self, x, y): + """Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top + left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned.""" + return 1 if (0 <= x < self._size and 0 <= y < self._size and self._modules[y][x]) else 0 + + + # ---- Public instance methods ---- + + def to_svg_str(self, border): + """Based on the given number of border modules to add as padding, this returns a + string whose contents represents an SVG XML file that depicts this QR Code symbol.""" + if border < 0: + raise ValueError("Border must be non-negative") + parts = [] + for y in range(-border, self.size + border): + for x in range(-border, self.size + border): + if self.get_module(x, y) == 1: + parts.append("M{},{}h1v1h-1z".format(x + border, y + border)) + return """ + + + + +""".format(self.size + border * 2, " ".join(parts)) + + + # ---- Private helper methods for constructor: Drawing function modules ---- + + def _draw_function_patterns(self): + # Draw the horizontal and vertical timing patterns + for i in range(self._size): + self._set_function_module(6, i, i % 2 == 0) + self._set_function_module(i, 6, i % 2 == 0) + + # Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + self._draw_finder_pattern(3, 3) + self._draw_finder_pattern(self._size - 4, 3) + self._draw_finder_pattern(3, self._size - 4) + + # Draw the numerous alignment patterns + alignpatpos = QrCode._get_alignment_pattern_positions(self._version) + numalign = len(alignpatpos) + skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) # Skip the three finder corners + for i in range(numalign): + for j in range(numalign): + if (i, j) not in skips: + self._draw_alignment_pattern(alignpatpos[i], alignpatpos[j]) + + # Draw configuration data + self._draw_format_bits(0) # Dummy mask value; overwritten later in the constructor + self._draw_version() + + + def _draw_format_bits(self, mask): + """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.""" + # Calculate error correction code and pack bits + data = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3 + rem = data + for _ in range(10): + rem = (rem << 1) ^ ((rem >> 9) * 0x537) + data = data << 10 | rem + data ^= 0x5412 # uint15 + assert data & ((1 << 15) - 1) == data + + # Draw first copy + for i in range(0, 6): + self._set_function_module(8, i, ((data >> i) & 1) != 0) + self._set_function_module(8, 7, ((data >> 6) & 1) != 0) + self._set_function_module(8, 8, ((data >> 7) & 1) != 0) + self._set_function_module(7, 8, ((data >> 8) & 1) != 0) + for i in range(9, 15): + self._set_function_module(14 - i, 8, ((data >> i) & 1) != 0) + + # Draw second copy + for i in range(0, 8): + self._set_function_module(self._size - 1 - i, 8, ((data >> i) & 1) != 0) + for i in range(8, 15): + self._set_function_module(8, self._size - 15 + i, ((data >> i) & 1) != 0) + self._set_function_module(8, self._size - 8, True) + + + def _draw_version(self): + """Draws two copies of the version bits (with its own error correction code), + based on this object's version field (which only has an effect for 7 <= version <= 40).""" + if self._version < 7: + return + + # Calculate error correction code and pack bits + rem = self._version # version is uint6, in the range [7, 40] + for _ in range(12): + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) + data = self._version << 12 | rem # uint18 + assert data & ((1 << 18) - 1) == data + + # Draw two copies + for i in range(18): + bit = ((data >> i) & 1) != 0 + a, b = self._size - 11 + i % 3, i // 3 + self._set_function_module(a, b, bit) + self._set_function_module(b, a, bit) + + + def _draw_finder_pattern(self, x, y): + """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).""" + for i in range(-4, 5): + for j in range(-4, 5): + dist = max(abs(i), abs(j)) # Chebyshev/infinity norm + xx, yy = x + j, y + i + if 0 <= xx < self._size and 0 <= yy < self._size: + self._set_function_module(xx, yy, dist not in (2, 4)) + + + def _draw_alignment_pattern(self, x, y): + """Draws a 5*5 alignment pattern, with the center module at (x, y).""" + for i in range(-2, 3): + for j in range(-2, 3): + self._set_function_module(x + j, y + i, max(abs(i), abs(j)) != 1) + + + def _set_function_module(self, x, y, isblack): + """Sets the color of a module and marks it as a function module. + Only used by the constructor. Coordinates must be in range.""" + assert type(isblack) is bool + self._modules[y][x] = isblack + self._isfunction[y][x] = True + + + # ---- Private helper methods for constructor: Codewords and masking ---- + + def _append_error_correction(self, data): + """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.""" + version = self._version + assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl) + numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] + numecc = QrCode._NUM_ERROR_CORRECTION_CODEWORDS[self._errcorlvl.ordinal][version] + assert numecc % numblocks == 0 + ecclen = numecc // numblocks + numshortblocks = numblocks - QrCode._get_num_raw_data_modules(version) // 8 % numblocks + shortblocklen = self._get_num_raw_data_modules(version) // 8 // numblocks + + blocks = [] + rs = _ReedSolomonGenerator(ecclen) + k = 0 + for i in range(numblocks): + dat = data[k : k + shortblocklen - ecclen + (0 if i < numshortblocks else 1)] + k += len(dat) + ecc = rs.get_remainder(dat) + if i < numshortblocks: + dat.append(0) + dat.extend(ecc) + blocks.append(dat) + assert k == len(data) + + result = [] + for i in range(len(blocks[0])): + for (j, blk) in enumerate(blocks): + if i != shortblocklen - ecclen or j >= numshortblocks: + result.append(blk[i]) + assert len(result) == QrCode._get_num_raw_data_modules(version) // 8 + return result + + + def _draw_codewords(self, data): + """Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + data area of this QR Code symbol. Function modules need to be marked off before this is called.""" + assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 + + i = 0 # Bit index into the data + # Do the funny zigzag scan + for right in range(self._size - 1, 0, -2): # Index of right column in each column pair + if right <= 6: + right -= 1 + for vert in range(self._size): # Vertical counter + for j in range(2): + x = right - j # Actual x coordinate + upwards = ((right & 2) == 0) ^ (x < 6) + y = (self._size - 1 - vert) if upwards else vert # Actual y coordinate + if not self._isfunction[y][x] and i < len(data) * 8: + self._modules[y][x] = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0 + i += 1 + assert i == len(data) * 8 + + + def _apply_mask(self, mask): + """XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + This means it is possible to apply a mask, undo it, and try another mask. Note that a final + well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).""" + if not 0 <= mask <= 7: + raise ValueError("Mask value out of range") + masker = QrCode._MASK_PATTERNS[mask] + for y in range(self._size): + for x in range(self._size): + self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x]) + + + def _get_penalty_score(self): + """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.""" + result = 0 + size = self._size + modules = self._modules + + # Adjacent modules in row having same color + for y in range(size): + colorx = modules[y][0] + runx = 1 + for x in range(1, size): + if modules[y][x] != colorx: + colorx = modules[y][x] + runx = 1 + else: + runx += 1 + if runx == 5: + result += QrCode._PENALTY_N1 + elif runx > 5: + result += 1 + # Adjacent modules in column having same color + for x in range(size): + colory = modules[0][x] + runy = 1 + for y in range(1, size): + if modules[y][x] != colory: + colory = modules[y][x] + runy = 1 + else: + runy += 1 + if runy == 5: + result += QrCode._PENALTY_N1 + elif runy > 5: + result += 1 + + # 2*2 blocks of modules having same color + for y in range(size - 1): + for x in range(size - 1): + color = modules[y][x] + if color == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: + result += QrCode._PENALTY_N2 + + # Finder-like pattern in rows + for y in range(size): + bits = 0 + for x in range(size): + bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) + if x >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated + result += QrCode._PENALTY_N3 + # Finder-like pattern in columns + for x in range(size): + bits = 0 + for y in range(size): + bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) + if y >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated + result += QrCode._PENALTY_N3 + + # Balance of black and white modules + black = 0 + for row in modules: + for color in row: + if color: + black += 1 + total = size**2 + # Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for k in itertools.count(): + if (9-k)*total <= black*20 <= (11+k)*total: + break + result += QrCode._PENALTY_N4 + return result + + + # ---- Private static helper functions ---- + + @staticmethod + def _get_alignment_pattern_positions(ver): + """Returns a sequence of positions of the alignment patterns in ascending order. These positions are + used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). + This stateless pure function could be implemented as table of 40 variable-length lists of integers.""" + if not 1 <= ver <= 40: + raise ValueError("Version number out of range") + elif ver == 1: + return [] + else: + numalign = ver // 7 + 2 + if ver != 32: + step = (ver * 4 + numalign * 2 + 1) // (2 * numalign - 2) * 2 # ceil((size - 13) / (2*numalign - 2)) * 2 + else: # C-C-C-Combo breaker! + step = 26 + result = [6] + [None] * (numalign - 1) + pos = ver * 4 + 10 + for i in reversed(range(1, numalign)): + result[i] = pos + pos -= step + return result + + + @staticmethod + def _get_num_raw_data_modules(ver): + """Returns the number of raw data modules (bits) available at the given version number. + These data modules are used for both user data codewords and error correction codewords. + This stateless pure function could be implemented as a 40-entry lookup table.""" + if not 1 <= ver <= 40: + raise ValueError("Version number out of range") + result = (16 * ver + 128) * ver + 64 + if ver >= 2: + numalign = ver // 7 + 2 + result -= (25 * numalign - 10) * numalign - 55 + if ver >= 7: + result -= 18 * 2 # Subtract version information + return result + + + @staticmethod + def _get_num_data_codewords(ver, ecl): + """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.""" + return QrCode._get_num_raw_data_modules(ver) // 8 - QrCode._NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver] + + + # ---- Tables of constants ---- + + # For use in getPenaltyScore(), when evaluating which mask is best. + _PENALTY_N1 = 3 + _PENALTY_N2 = 3 + _PENALTY_N3 = 40 + _PENALTY_N4 = 10 + + _NUM_ERROR_CORRECTION_CODEWORDS = ( + # 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 + (None, 7, 10, 15, 20, 26, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750), # Low + (None, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372), # Medium + (None, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040), # Quartile + (None, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430)) # High + + _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 + (None, 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 + (None, 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 + (None, 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 + (None, 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 + + _MASK_PATTERNS = ( + (lambda x, y: (x + y) % 2 ), + (lambda x, y: y % 2 ), + (lambda x, y: x % 3 ), + (lambda x, y: (x + y) % 3 ), + (lambda x, y: (x // 3 + y // 2) % 2 ), + (lambda x, y: x * y % 2 + x * y % 3 ), + (lambda x, y: (x * y % 2 + x * y % 3) % 2 ), + (lambda x, y: ((x + y) % 2 + x * y % 3) % 2), + ) + + + # ---- Public helper enumeration ---- + + class Ecc(object): + """Represents the error correction level used in a QR Code symbol.""" + # Private constructor + def __init__(self, i, fb): + self.ordinal = i # In the range 0 to 3 (unsigned 2-bit integer) + self.formatbits = fb # In the range 0 to 3 (unsigned 2-bit integer) + + # Create the class constants outside the class + Ecc.LOW = Ecc(0, 1) + Ecc.MEDIUM = Ecc(1, 0) + Ecc.QUARTILE = Ecc(2, 3) + Ecc.HIGH = Ecc(3, 2) + + + +# ---- Data segment class ---- + +class QrSegment(object): + """Represents a character string to be encoded in a QR Code symbol. Each segment has + a mode, and a sequence of characters that is already encoded as a sequence of bits. + Instances of this class are immutable. + This segment class imposes no length restrictions, but QR Codes have restrictions. + Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + Any segment longer than this is meaningless for the purpose of generating QR Codes.""" + + # -- Public static factory functions -- + + @staticmethod + def make_bytes(data): + """Returns a segment representing the given binary data encoded in byte mode.""" + bb = _BitBuffer() + for b in data: + if sys.version_info[0] < 3: + b = ord(b) + bb.append_bits(b, 8) + return QrSegment(QrSegment.Mode.BYTE, len(data), bb.get_bits()) + + + @staticmethod + def make_numeric(digits): + """Returns a segment representing the given string of decimal digits encoded in numeric mode.""" + if QrSegment.NUMERIC_REGEX.match(digits) is None: + raise ValueError("String contains non-numeric characters") + bb = _BitBuffer() + for i in range(0, len(digits) - 2, 3): # Process groups of 3 + bb.append_bits(int(digits[i : i + 3]), 10) + rem = len(digits) % 3 + if rem > 0: # 1 or 2 digits remaining + bb.append_bits(int(digits[-rem : ]), rem * 3 + 1) + return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb.get_bits()) + + + @staticmethod + def make_alphanumeric(text): + """Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: + 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.""" + if QrSegment.ALPHANUMERIC_REGEX.match(text) is None: + raise ValueError("String contains unencodable characters in alphanumeric mode") + bb = _BitBuffer() + for i in range(0, len(text) - 1, 2): # Process groups of 2 + temp = QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 + temp += QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] + bb.append_bits(temp, 11) + if len(text) % 2 > 0: # 1 character remaining + bb.append_bits(QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[-1]], 6) + return QrSegment(QrSegment.Mode.ALPHANUMERIC, len(text), bb.get_bits()) + + + # -- Constructor -- + + def __init__(self, mode, numch, bitdata): + if numch < 0 or not isinstance(mode, QrSegment.Mode): + raise ValueError() + self._mode = mode + self._numchars = numch + self._bitdata = list(bitdata) # Defensive copy + + + # -- Accessor methods -- + + def get_mode(self): + return self._mode + + def get_num_chars(self): + return self._numchars + + def get_bits(self): + return list(self._bitdata) # Defensive copy + + + # -- Constants -- + + # Can test whether a string is encodable in numeric mode (such as by using make_numeric()) + NUMERIC_REGEX = re.compile("[0-9]*$") + + # Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) + ALPHANUMERIC_REGEX = re.compile("[A-Z0-9 $%*+./:-]*$") + + # Dictionary of "0"->0, "A"->10, "$"->37, etc. + ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} + + + # -- Helper enumeration -- + + class Mode(object): + """The mode field of a segment. Immutable. Provides methods to retrieve closely related values.""" + # Private constructor + def __init__(self, modebits, charcounts): + self._modebits = modebits + self._charcounts = charcounts + + def get_mode_bits(self): + return self._modebits + + def num_char_count_bits(self, ver): + """Returns the bit width of the segment character count field for this mode object at the given version number.""" + if 1 <= ver <= 9: return self._charcounts[0] + elif 10 <= ver <= 26: return self._charcounts[1] + elif 27 <= ver <= 40: return self._charcounts[2] + else: raise ValueError("Version number out of range") + + # Create the class constants outside the class + Mode.NUMERIC = Mode(0x1, (10, 12, 14)) + Mode.ALPHANUMERIC = Mode(0x2, ( 9, 11, 13)) + Mode.BYTE = Mode(0x4, ( 8, 16, 16)) + Mode.KANJI = Mode(0x8, ( 8, 10, 12)) + + + +# ---- Private helper classes ---- + +class _ReedSolomonGenerator(object): + """Computes the Reed-Solomon error correction codewords for a sequence of data codewords + at a given degree. Objects are immutable, and the state only depends on the degree. + This class exists because the divisor polynomial does not need to be recalculated for every input.""" + + def __init__(self, degree): + """Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + as a lookup table over all possible parameter values, instead of as an algorithm.""" + if degree < 1 or degree > 255: + raise ValueError("Degree out of range") + + # Start with the monomial x^0 + self.coefficients = [0] * (degree - 1) + [1] + + # Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + # drop the highest term, and store the rest of the coefficients in order of descending powers. + # Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + root = 1 + for i in range(degree): + # Multiply the current product by (x - r^i) + for j in range(degree): + self.coefficients[j] = _ReedSolomonGenerator.multiply(self.coefficients[j], root) + if j + 1 < degree: + self.coefficients[j] ^= self.coefficients[j + 1] + root = (root << 1) ^ ((root >> 7) * 0x11D) # Multiply by 0x02 mod GF(2^8/0x11D) + + + def get_remainder(self, data): + """Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. + The returned object is always a new byte list. This method does not alter this object's state (because it is immutable).""" + # Compute the remainder by performing polynomial division + result = [0] * len(self.coefficients) + for b in data: + factor = (b ^ result[0]) + del result[0] + result.append(0) + for j in range(len(result)): + result[j] ^= _ReedSolomonGenerator.multiply(self.coefficients[j], factor) + return result + + + @staticmethod + def multiply(x, y): + """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.""" + if x & 0xFF != x or y & 0xFF != y: + raise ValueError("Byte out of range") + # Russian peasant multiplication + z = 0 + for i in reversed(range(8)): + z = (z << 1) ^ ((z >> 7) * 0x11D) + z ^= ((y >> i) & 1) * x + assert z & 0xFF == z + return z + + + +class _BitBuffer(object): + """An appendable sequence of bits. Bits are packed in big endian within a byte.""" + + def __init__(self): + """Creates an empty bit buffer (length 0).""" + self.data = [] + + def bit_length(self): + """Returns the number of bits in the buffer, which is a non-negative value.""" + return len(self.data) + + def get_bits(self): + """Returns a copy of all bits.""" + return list(self.data) + + def get_bytes(self): + """Returns a copy of all bytes, padding up to the nearest byte.""" + result = [0] * ((len(self.data) + 7) // 8) + for (i, bit) in enumerate(self.data): + result[i >> 3] |= bit << (7 - (i & 7)) + return result + + def append_bits(self, val, n): + """Appends the given number of bits of the given value to this sequence. This requires 0 <= val < 2^n.""" + if n < 0 or not 0 <= val < (1 << n): + raise ValueError("Value out of range") + for i in reversed(range(n)): # Append bit by bit + self.data.append((val >> i) & 1) + + def append_all(self, seg): + """Appends the data of the given segment to this bit buffer.""" + if not isinstance(seg, QrSegment): + raise TypeError("QrSegment expected") + self.data.extend(seg.get_bits()) From 53454449c4f329f127a1ee6c321e6229b59c3305 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Tue, 12 Apr 2016 05:17:45 +0000 Subject: [PATCH 003/810] Tweaked and simplified Java, JavaScript, Python code. --- BitBuffer.java | 4 ++-- QrCode.java | 18 ++++++++++-------- qrcodegen.js | 16 +++++++++------- qrcodegen.py | 10 ++++++---- 4 files changed, 27 insertions(+), 21 deletions(-) diff --git a/BitBuffer.java b/BitBuffer.java index e9a3dd1..e759aeb 100644 --- a/BitBuffer.java +++ b/BitBuffer.java @@ -66,7 +66,7 @@ final class BitBuffer { // Appends the given number of bits of the given value to this sequence. // If 0 <= len <= 31, then this requires 0 <= val < 2^len. public void appendBits(int val, int len) { - if (len < 0 || len > 32 || len < 32 && (val & ((1 << len) - 1)) != val) + if (len < 0 || len > 32 || len < 32 && (val >>> len) != 0) throw new IllegalArgumentException("Value out of range"); ensureCapacity(bitLength + len); for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit @@ -88,7 +88,7 @@ final class BitBuffer { // Expands the buffer if necessary, so that it can hold at least the given bit length. private void ensureCapacity(int newBitLen) { - while (data.length < newBitLen) + while (data.length * 8 < newBitLen) data = Arrays.copyOf(data, data.length * 2); } diff --git a/QrCode.java b/QrCode.java index 14bc03d..82d20a7 100644 --- a/QrCode.java +++ b/QrCode.java @@ -613,9 +613,9 @@ public final class QrCode { 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]) + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) result += PENALTY_N2; } } @@ -694,7 +694,7 @@ public final class QrCode { result -= 64 * 3; // Subtract the three finders with separators result -= 15 * 2 + 1; // Subtract the format information and black module result -= (size - 16) * 2; // Subtract the timing patterns - // The four lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; + // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { int numAlign = ver / 7 + 2; result -= (numAlign - 1) * (numAlign - 1) * 25; // Subtract alignment patterns not overlapping with timing patterns @@ -711,6 +711,8 @@ 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. private static int getNumDataCodewords(int ver, Ecc ecl) { + if (ver < 1 || ver > 40) + throw new IllegalArgumentException("Version number out of range"); return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal()][ver]; } @@ -759,13 +761,13 @@ public final class QrCode { // Constructor. private Ecc(int fb) { formatBits = fb; - } + } } /*---- Private helper class ----*/ - + /** * Computes the Reed-Solomon error correction codewords for a sequence of data codewords * at a given degree. Objects are immutable, and the state only depends on the degree. @@ -843,7 +845,7 @@ public final class QrCode { // 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 multiply(int x, int y) { - if ((x & 0xFF) != x || (y & 0xFF) != y) + if (x >>> 8 != 0 || y >>> 8 != 0) throw new IllegalArgumentException("Byte out of range"); // Russian peasant multiplication int z = 0; @@ -851,7 +853,7 @@ public final class QrCode { z = (z << 1) ^ ((z >>> 7) * 0x11D); z ^= ((y >>> i) & 1) * x; } - if ((z & 0xFF) != z) + if (z >>> 8 != 0) throw new AssertionError(); return z; } diff --git a/qrcodegen.js b/qrcodegen.js index 81b6a6d..103610a 100644 --- a/qrcodegen.js +++ b/qrcodegen.js @@ -566,10 +566,10 @@ var qrcodegen = new function() { // 2*2 blocks of modules having same color for (var y = 0; y < size - 1; y++) { for (var x = 0; x < size - 1; x++) { - var color = modules[y][x]; - if ( color == modules[y][x + 1] && - color == modules[y + 1][x] && - color == modules[y + 1][x + 1]) + var color = modules[y][x]; + if ( color == modules[y][x + 1] && + color == modules[y + 1][x] && + color == modules[y + 1][x + 1]) result += QrCode.PENALTY_N2; } } @@ -658,6 +658,8 @@ var qrcodegen = new function() { // 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. QrCode.getNumDataCodewords = function(ver, ecl) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - QrCode.NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver]; }; @@ -890,7 +892,7 @@ var qrcodegen = new function() { // This static function 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. ReedSolomonGenerator.multiply = function(x, y) { - if ((x & 0xFF) != x || (y & 0xFF) != y) + if (x >>> 8 != 0 || y >>> 8 != 0) throw "Byte out of range"; // Russian peasant multiplication var z = 0; @@ -898,7 +900,7 @@ var qrcodegen = new function() { z = (z << 1) ^ ((z >>> 7) * 0x11D); z ^= ((y >>> i) & 1) * x; } - if ((z & 0xFF) != z) + if (z >>> 8 != 0) throw "Assertion error"; return z; }; @@ -940,7 +942,7 @@ var qrcodegen = new function() { // Appends the given number of bits of the given value to this sequence. // If 0 <= len <= 31, then this requires 0 <= val < 2^len. this.appendBits = function(val, len) { - if (len < 0 || len > 32 || len < 32 && (val & ((1 << len) - 1)) != val) + if (len < 0 || len > 32 || len < 32 && (val >>> len) != 0) throw "Value out of range"; for (var i = len - 1; i >= 0; i--) // Append bit by bit bitData.push((val >>> i) & 1); diff --git a/qrcodegen.py b/qrcodegen.py index 5e858d0..5f5e739 100644 --- a/qrcodegen.py +++ b/qrcodegen.py @@ -548,6 +548,8 @@ class QrCode(object): """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.""" + if not 1 <= ver <= 40: + raise ValueError("Version number out of range") return QrCode._get_num_raw_data_modules(ver) // 8 - QrCode._NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver] @@ -588,14 +590,14 @@ class QrCode(object): # ---- Public helper enumeration ---- - + class Ecc(object): """Represents the error correction level used in a QR Code symbol.""" # Private constructor def __init__(self, i, fb): self.ordinal = i # In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # In the range 0 to 3 (unsigned 2-bit integer) - + # Create the class constants outside the class Ecc.LOW = Ecc(0, 1) Ecc.MEDIUM = Ecc(1, 0) @@ -765,14 +767,14 @@ class _ReedSolomonGenerator(object): def multiply(x, y): """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.""" - if x & 0xFF != x or y & 0xFF != y: + if x >> 8 != 0 or y >> 8 != 0: raise ValueError("Byte out of range") # Russian peasant multiplication z = 0 for i in reversed(range(8)): z = (z << 1) ^ ((z >> 7) * 0x11D) z ^= ((y >> i) & 1) * x - assert z & 0xFF == z + assert z >> 8 == 0 return z From 7f01bbf4f31801a175ff2a83d990e9184f1e272a Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Tue, 12 Apr 2016 19:11:07 +0000 Subject: [PATCH 004/810] Simplified another pair of bit width checks using the right shift operator. --- QrCode.java | 4 ++-- qrcodegen.js | 4 ++-- qrcodegen.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/QrCode.java b/QrCode.java index 82d20a7..304e2df 100644 --- a/QrCode.java +++ b/QrCode.java @@ -375,7 +375,7 @@ public final class QrCode { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); data = data << 10 | rem; data ^= 0x5412; // uint15 - if ((data & ((1 << 15) - 1)) != data) + if (data >>> 15 != 0) throw new AssertionError(); // Draw first copy @@ -407,7 +407,7 @@ public final class QrCode { for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); int data = version << 12 | rem; // uint18 - if ((data & ((1 << 18) - 1)) != data) + if (data >>> 18 != 0) throw new AssertionError(); // Draw two copies diff --git a/qrcodegen.js b/qrcodegen.js index 103610a..898c38c 100644 --- a/qrcodegen.js +++ b/qrcodegen.js @@ -354,7 +354,7 @@ var qrcodegen = new function() { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); data = data << 10 | rem; data ^= 0x5412; // uint15 - if ((data & ((1 << 15) - 1)) != data) + if (data >>> 15 != 0) throw "Assertion error"; // Draw first copy @@ -386,7 +386,7 @@ var qrcodegen = new function() { for (var i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); var data = version << 12 | rem; // uint18 - if ((data & ((1 << 18) - 1)) != data) + if (data >>> 18 != 0) throw "Assertion error"; // Draw two copies diff --git a/qrcodegen.py b/qrcodegen.py index 5f5e739..bd674c8 100644 --- a/qrcodegen.py +++ b/qrcodegen.py @@ -294,7 +294,7 @@ class QrCode(object): rem = (rem << 1) ^ ((rem >> 9) * 0x537) data = data << 10 | rem data ^= 0x5412 # uint15 - assert data & ((1 << 15) - 1) == data + assert data >> 15 == 0 # Draw first copy for i in range(0, 6): @@ -324,7 +324,7 @@ class QrCode(object): for _ in range(12): rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) data = self._version << 12 | rem # uint18 - assert data & ((1 << 18) - 1) == data + assert data >> 18 == 0 # Draw two copies for i in range(18): From bc10146355a04a38c526d96a7a7c49c576fb0960 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Fri, 15 Apr 2016 02:58:02 +0000 Subject: [PATCH 005/810] Moved all code into new subdirectories per programming language. --- BitBuffer.java => java/BitBuffer.java | 0 QrCode.java => java/QrCode.java | 0 QrCodeGeneratorDemo.java => java/QrCodeGeneratorDemo.java | 0 QrSegment.java => java/QrSegment.java | 0 qrcodegen-demo.js => javascript/qrcodegen-demo.js | 0 qrcodegen-js-demo.html => javascript/qrcodegen-js-demo.html | 0 qrcodegen.js => javascript/qrcodegen.js | 0 qrcodegen-demo.py => python/qrcodegen-demo.py | 0 qrcodegen.py => python/qrcodegen.py | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename BitBuffer.java => java/BitBuffer.java (100%) rename QrCode.java => java/QrCode.java (100%) rename QrCodeGeneratorDemo.java => java/QrCodeGeneratorDemo.java (100%) rename QrSegment.java => java/QrSegment.java (100%) rename qrcodegen-demo.js => javascript/qrcodegen-demo.js (100%) rename qrcodegen-js-demo.html => javascript/qrcodegen-js-demo.html (100%) rename qrcodegen.js => javascript/qrcodegen.js (100%) rename qrcodegen-demo.py => python/qrcodegen-demo.py (100%) rename qrcodegen.py => python/qrcodegen.py (100%) diff --git a/BitBuffer.java b/java/BitBuffer.java similarity index 100% rename from BitBuffer.java rename to java/BitBuffer.java diff --git a/QrCode.java b/java/QrCode.java similarity index 100% rename from QrCode.java rename to java/QrCode.java diff --git a/QrCodeGeneratorDemo.java b/java/QrCodeGeneratorDemo.java similarity index 100% rename from QrCodeGeneratorDemo.java rename to java/QrCodeGeneratorDemo.java diff --git a/QrSegment.java b/java/QrSegment.java similarity index 100% rename from QrSegment.java rename to java/QrSegment.java diff --git a/qrcodegen-demo.js b/javascript/qrcodegen-demo.js similarity index 100% rename from qrcodegen-demo.js rename to javascript/qrcodegen-demo.js diff --git a/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html similarity index 100% rename from qrcodegen-js-demo.html rename to javascript/qrcodegen-js-demo.html diff --git a/qrcodegen.js b/javascript/qrcodegen.js similarity index 100% rename from qrcodegen.js rename to javascript/qrcodegen.js diff --git a/qrcodegen-demo.py b/python/qrcodegen-demo.py similarity index 100% rename from qrcodegen-demo.py rename to python/qrcodegen-demo.py diff --git a/qrcodegen.py b/python/qrcodegen.py similarity index 100% rename from qrcodegen.py rename to python/qrcodegen.py From 008fddf4be0bdd8c66a62166c18bed5d00e4ed95 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Fri, 15 Apr 2016 04:26:37 +0000 Subject: [PATCH 006/810] Added C++ port of the library. --- cpp/BitBuffer.cpp | 63 ++++ cpp/BitBuffer.hpp | 76 +++++ cpp/QrCode.cpp | 623 ++++++++++++++++++++++++++++++++++++ cpp/QrCode.hpp | 317 ++++++++++++++++++ cpp/QrCodeGeneratorDemo.cpp | 141 ++++++++ cpp/QrSegment.cpp | 138 ++++++++ cpp/QrSegment.hpp | 154 +++++++++ 7 files changed, 1512 insertions(+) create mode 100644 cpp/BitBuffer.cpp create mode 100644 cpp/BitBuffer.hpp create mode 100644 cpp/QrCode.cpp create mode 100644 cpp/QrCode.hpp create mode 100644 cpp/QrCodeGeneratorDemo.cpp create mode 100644 cpp/QrSegment.cpp create mode 100644 cpp/QrSegment.hpp diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp new file mode 100644 index 0000000..8028a21 --- /dev/null +++ b/cpp/BitBuffer.cpp @@ -0,0 +1,63 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include "BitBuffer.hpp" + + +qrcodegen::BitBuffer::BitBuffer() : + data(), + bitLength(0) {} + + +int qrcodegen::BitBuffer::getBitLength() const { + return bitLength; +} + + +std::vector qrcodegen::BitBuffer::getBytes() const { + return data; +} + + +void qrcodegen::BitBuffer::appendBits(uint32_t val, int len) { + if (len < 0 || len > 32 || (len < 32 && (val >> len) != 0)) + throw "Value out of range"; + size_t newBitLen = bitLength + len; + while (data.size() * 8 < newBitLen) + data.push_back(0); + for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit + data.at(bitLength >> 3) |= ((val >> i) & 1) << (7 - (bitLength & 7)); +} + + +void qrcodegen::BitBuffer::appendData(const QrSegment &seg) { + size_t newBitLen = bitLength + seg.bitLength; + while (data.size() * 8 < newBitLen) + data.push_back(0); + for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit + int bit = (seg.data.at(i >> 3) >> (7 - (i & 7))) & 1; + data.at(bitLength >> 3) |= bit << (7 - (bitLength & 7)); + } +} diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp new file mode 100644 index 0000000..47daebb --- /dev/null +++ b/cpp/BitBuffer.hpp @@ -0,0 +1,76 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include "QrSegment.hpp" + + +namespace qrcodegen { + +/** + * An appendable sequence of bits. Bits are packed in big endian within a byte. + */ +class BitBuffer final { + + /*---- Fields ----*/ +private: + + std::vector data; + int bitLength; + + + + /*---- Constructor ----*/ +public: + + // Creates an empty bit buffer (length 0). + BitBuffer(); + + + + /*---- Methods ----*/ +public: + + // Returns the number of bits in the buffer, which is a non-negative value. + int getBitLength() const; + + + // Returns a copy of all bytes, padding up to the nearest byte. + std::vector getBytes() const; + + + // Appends the given number of bits of the given value to this sequence. + // If 0 <= len <= 31, then this requires 0 <= val < 2^len. + void appendBits(uint32_t val, int len); + + + // Appends the data of the given segment to this bit buffer. + void appendData(const QrSegment &seg); + +}; + +} diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp new file mode 100644 index 0000000..96ab0a4 --- /dev/null +++ b/cpp/QrCode.cpp @@ -0,0 +1,623 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include +#include +#include +#include +#include "BitBuffer.hpp" +#include "QrCode.hpp" + + +qrcodegen::QrCode::Ecc::Ecc(int ord, int fb) : + ordinal(ord), + formatBits(fb) {} + + +const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::LOW (0, 1); +const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::MEDIUM (1, 0); +const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::QUARTILE(2, 3); +const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::HIGH (3, 2); + + +qrcodegen::QrCode qrcodegen::QrCode::encodeText(const char *text, const Ecc &ecl) { + std::vector segs; + segs.push_back(encodeTextToSegment(text)); + return encodeSegments(segs, ecl); +} + + +qrcodegen::QrSegment qrcodegen::QrCode::encodeTextToSegment(const char *text) { + // Select the most efficient segment encoding automatically + if (QrSegment::isNumeric(text)) + return QrSegment::makeNumeric(text); + else if (QrSegment::isAlphanumeric(text)) + return QrSegment::makeAlphanumeric(text); + else { + std::vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + return QrSegment::makeBytes(bytes); + } +} + + +qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &data, const Ecc &ecl) { + std::vector segs; + segs.push_back(QrSegment::makeBytes(data)); + return encodeSegments(segs, ecl); +} + + +qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector &segs, const Ecc &ecl) { + // Find the minimal version number to use + int version, dataCapacityBits; + for (version = 1; ; version++) { // Increment until the data fits in the QR Code + if (version > 40) // All versions could not fit the given data + throw "Data too long"; + dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + + // Calculate the total number of bits needed at this version number + // to encode all the segments (i.e. segment metadata and payloads) + int dataUsedBits = 0; + for (size_t i = 0; i < segs.size(); i++) { + const QrSegment &seg(segs.at(i)); + if (seg.numChars < 0) + throw "Assertion error"; + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1 << ccbits)) { + // Segment length value doesn't fit in the length field's bit-width, so fail immediately + goto continueOuter; + } + dataUsedBits += 4 + ccbits + seg.bitLength; + } + if (dataUsedBits <= dataCapacityBits) + break; // This version number is found to be suitable + continueOuter:; + } + + // Create the data bit string by concatenating all segments + BitBuffer bb; + for (size_t i = 0; i < segs.size(); i++) { + const QrSegment &seg(segs.at(i)); + bb.appendBits(seg.mode.modeBits, 4); + bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); + bb.appendData(seg); + } + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, std::min(4, dataCapacityBits - bb.getBitLength())); + bb.appendBits(0, (8 - bb.getBitLength() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.getBitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.getBitLength() % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return QrCode(version, ecl, bb.getBytes(), -1); +} + + +qrcodegen::QrCode::QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask) : + // Initialize scalar fields + version(ver), + size(1 <= ver && ver <= 40 ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior + errorCorrectionLevel(ecl) { + + // Check arguments + if (ver < 1 || ver > 40 || mask < -1 || mask > 7) + throw "Value out of range"; + + std::vector row(size); + for (int i = 0; i < size; i++) { + modules.push_back(row); + isFunction.push_back(row); + } + + // Draw function patterns, draw all codewords, do masking + drawFunctionPatterns(); + const std::vector allCodewords(appendErrorCorrection(dataCodewords)); + drawCodewords(allCodewords); + this->mask = handleConstructorMasking(mask); +} + + +qrcodegen::QrCode::QrCode(const QrCode &qr, int mask) : + // Copy scalar fields + version(qr.version), + size(qr.size), + errorCorrectionLevel(qr.errorCorrectionLevel) { + + // Check arguments + if (mask < -1 || mask > 7) + throw "Mask value out of range"; + + // Handle grid fields + modules = qr.modules; + isFunction = qr.isFunction; + + // Handle masking + applyMask(qr.mask); // Undo old mask + this->mask = handleConstructorMasking(mask); +} + + +int qrcodegen::QrCode::getMask() const { + return mask; +} + + +int qrcodegen::QrCode::getModule(int x, int y) const { + if (0 <= x && x < size && 0 <= y && y < size) + return modules.at(y).at(x) ? 1 : 0; + else + return 0; // Infinite white border +} + + +std::string qrcodegen::QrCode::toSvgString(int border) const { + if (border < 0) + throw "Border must be non-negative"; + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} + + +void qrcodegen::QrCode::drawFunctionPatterns() { + // Draw the horizontal and vertical timing patterns + for (int i = 0; i < size; i++) { + 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 the numerous alignment patterns + const std::vector alignPatPos(getAlignmentPatternPositions(version)); + int numAlign = alignPatPos.size(); + for (int i = 0; i < numAlign; i++) { + for (int j = 0; j < numAlign; j++) { + if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) + continue; // Skip the three finder corners + else + drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); + } + } + + // Draw configuration data + drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + drawVersion(); +} + + +void qrcodegen::QrCode::drawFormatBits(int mask) { + // Calculate error correction code and pack bits + int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int rem = data; + for (int i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if (data >> 15 != 0) + throw "Assertion error"; + + // Draw first copy + for (int i = 0; i <= 5; i++) + setFunctionModule(8, i, ((data >> i) & 1) != 0); + setFunctionModule(8, 7, ((data >> 6) & 1) != 0); + setFunctionModule(8, 8, ((data >> 7) & 1) != 0); + setFunctionModule(7, 8, ((data >> 8) & 1) != 0); + for (int i = 9; i < 15; i++) + setFunctionModule(14 - i, 8, ((data >> i) & 1) != 0); + + // Draw second copy + for (int i = 0; i <= 7; i++) + setFunctionModule(size - 1 - i, 8, ((data >> i) & 1) != 0); + for (int i = 8; i < 15; i++) + setFunctionModule(8, size - 15 + i, ((data >> i) & 1) != 0); + setFunctionModule(8, size - 8, true); +} + + +void qrcodegen::QrCode::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 data = version << 12 | rem; // uint18 + if (data >> 18 != 0) + throw "Assertion error"; + + // Draw two copies + for (int i = 0; i < 18; i++) { + bool bit = ((data >> i) & 1) != 0; + int a = size - 11 + i % 3, b = i / 3; + setFunctionModule(a, b, bit); + setFunctionModule(b, a, bit); + } +} + + +void qrcodegen::QrCode::drawFinderPattern(int x, int y) { + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + int dist = std::max(std::abs(i), std::abs(j)); // Chebyshev/infinity norm + int xx = x + j, yy = y + i; + if (0 <= xx && xx < size && 0 <= yy && yy < size) + setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } +} + + +void qrcodegen::QrCode::drawAlignmentPattern(int x, int y) { + for (int i = -2; i <= 2; i++) { + for (int j = -2; j <= 2; j++) + setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); + } +} + + +void qrcodegen::QrCode::setFunctionModule(int x, int y, bool isBlack) { + modules.at(y).at(x) = isBlack; + isFunction.at(y).at(x) = true; +} + + +std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector &data) const { + if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) + throw "Invalid argument"; + int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal][version]; + int numEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal][version]; + if (numEcc % numBlocks != 0) + throw "Assertion error"; + int eccLen = numEcc / numBlocks; + int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; + int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; + + std::vector> blocks; + const ReedSolomonGenerator rs(eccLen); + for (int i = 0, k = 0; i < numBlocks; i++) { + std::vector dat; + dat.insert(dat.begin(), data.begin() + k, data.begin() + (k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1))); + k += dat.size(); + const std::vector ecc(rs.getRemainder(dat)); + if (i < numShortBlocks) + dat.push_back(0); + dat.insert(dat.end(), ecc.begin(), ecc.end()); + blocks.push_back(dat); + } + + std::vector result; + for (int i = 0; static_cast(i) < blocks.at(0).size(); i++) { + for (int j = 0; static_cast(j) < blocks.size(); j++) { + if (i != shortBlockLen - eccLen || j >= numShortBlocks) + result.push_back(blocks.at(j).at(i)); + } + } + return result; +} + + +void qrcodegen::QrCode::drawCodewords(const std::vector &data) { + if (data.size() != static_cast(getNumRawDataModules(version) / 8)) + throw "Invalid argument"; + + size_t 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 + if (right == 6) + right = 5; + for (int vert = 0; vert < size; vert++) { // Vertical counter + for (int j = 0; j < 2; j++) { + int x = right - j; // Actual x coordinate + bool upwards = ((right & 2) == 0) ^ (x < 6); + int y = upwards ? size - 1 - vert : vert; // Actual y coordinate + if (!isFunction.at(y).at(x) && i < data.size() * 8) { + modules.at(y).at(x) = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0; + i++; + } + } + } + } + if (static_cast(i) != data.size() * 8) + throw "Assertion error"; +} + + +void qrcodegen::QrCode::applyMask(int mask) { + if (mask < 0 || mask > 7) + throw "Mask value out of range"; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + bool invert; + switch (mask) { + 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 "Assertion error"; + } + modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); + } + } +} + + +int qrcodegen::QrCode::handleConstructorMasking(int mask) { + if (mask == -1) { // Automatically choose best mask + int32_t minPenalty = INT32_MAX; + for (int i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + int penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw "Assertion error"; + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + return mask; // The caller shall assign this value to the final-declared field +} + + +int qrcodegen::QrCode::getPenaltyScore() const { + int result = 0; + + // Adjacent modules in row having same color + for (int y = 0; y < size; y++) { + bool colorX = modules.at(y).at(0); + for (int x = 1, runX = 1; x < size; x++) { + if (modules.at(y).at(x) != colorX) { + colorX = modules.at(y).at(x); + runX = 1; + } else { + runX++; + if (runX == 5) + result += PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (int x = 0; x < size; x++) { + bool colorY = modules.at(0).at(x); + for (int y = 1, runY = 1; y < size; y++) { + if (modules.at(y).at(x) != colorY) { + colorY = modules.at(y).at(x); + runY = 1; + } else { + runY++; + if (runY == 5) + result += PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (int y = 0; y < size - 1; y++) { + for (int x = 0; x < size - 1; x++) { + bool color = modules.at(y).at(x); + if ( color == modules.at(y).at(x + 1) && + color == modules.at(y + 1).at(x) && + color == modules.at(y + 1).at(x + 1)) + result += PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (int y = 0; y < size; y++) { + for (int x = 0, bits = 0; x < size; x++) { + bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + // Finder-like pattern in columns + for (int x = 0; x < size; x++) { + for (int y = 0, bits = 0; y < size; y++) { + bits = ((bits << 1) & 0x7FF) | (modules.at(y).at(x) ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += PENALTY_N3; + } + } + + // Balance of black and white modules + int black = 0; + for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + if (modules.at(y).at(x)) + black++; + } + } + int total = size * size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += PENALTY_N4; + return result; +} + + +std::vector qrcodegen::QrCode::getAlignmentPatternPositions(int ver) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; + else if (ver == 1) + return std::vector(); + else { + int numAlign = ver / 7 + 2; + int step; + if (ver != 32) + step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; // ceil((size - 13) / (2*numAlign - 2)) * 2 + else // C-C-C-Combo breaker! + step = 26; + + std::vector result(numAlign); + int size = ver * 4 + 17; + result.at(0) = 6; + for (int i = numAlign - 1, pos = size - 7; i >= 1; i--, pos -= step) + result.at(i) = pos; + return result; + } +} + + +int qrcodegen::QrCode::getNumRawDataModules(int ver) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; +} + + +int qrcodegen::QrCode::getNumDataCodewords(int ver, const Ecc &ecl) { + if (ver < 1 || ver > 40) + throw "Version number out of range"; + return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver]; +} + + +/*---- Tables of constants ----*/ + +// For use in getPenaltyScore(), when evaluating which mask is best. +const int qrcodegen::QrCode::PENALTY_N1 = 3; +const int qrcodegen::QrCode::PENALTY_N2 = 3; +const int qrcodegen::QrCode::PENALTY_N3 = 40; +const int qrcodegen::QrCode::PENALTY_N4 = 10; + + +const int16_t qrcodegen::QrCode::NUM_ERROR_CORRECTION_CODEWORDS[4][41] = { + // 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, 36, 40, 48, 60, 72, 80, 96, 104, 120, 132, 144, 168, 180, 196, 224, 224, 252, 270, 300, 312, 336, 360, 390, 420, 450, 480, 510, 540, 570, 570, 600, 630, 660, 720, 750}, // Low + {-1, 10, 16, 26, 36, 48, 64, 72, 88, 110, 130, 150, 176, 198, 216, 240, 280, 308, 338, 364, 416, 442, 476, 504, 560, 588, 644, 700, 728, 784, 812, 868, 924, 980, 1036, 1064, 1120, 1204, 1260, 1316, 1372}, // Medium + {-1, 13, 22, 36, 52, 72, 96, 108, 132, 160, 192, 224, 260, 288, 320, 360, 408, 448, 504, 546, 600, 644, 690, 750, 810, 870, 952, 1020, 1050, 1140, 1200, 1290, 1350, 1440, 1530, 1590, 1680, 1770, 1860, 1950, 2040}, // Quartile + {-1, 17, 28, 44, 64, 88, 112, 130, 156, 192, 224, 264, 308, 352, 384, 432, 480, 532, 588, 650, 700, 750, 816, 900, 960, 1050, 1110, 1200, 1260, 1350, 1440, 1530, 1620, 1710, 1800, 1890, 1980, 2100, 2220, 2310, 2430}, // High +}; + +const int8_t qrcodegen::QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { + // 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 +}; + + +qrcodegen::QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : + coefficients() { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + + // Start with the monomial x^0 + coefficients.resize(degree); + coefficients.at(degree - 1) = 1; + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + int root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < coefficients.size(); j++) { + coefficients.at(j) = multiply(coefficients.at(j), static_cast(root)); + if (j + 1 < coefficients.size()) + coefficients.at(j) ^= coefficients.at(j + 1); + } + root = (root << 1) ^ ((root >> 7) * 0x11D); // Multiply by 0x02 mod GF(2^8/0x11D) + } +} + + +std::vector qrcodegen::QrCode::ReedSolomonGenerator::getRemainder(const std::vector &data) const { + // Compute the remainder by performing polynomial division + std::vector result(coefficients.size()); + for (size_t i = 0; i < data.size(); i++) { + uint8_t factor = data.at(i) ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t j = 0; j < result.size(); j++) + result.at(j) ^= multiply(coefficients.at(j), factor); + } + return result; +} + + +uint8_t qrcodegen::QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw "Assertion error"; + return static_cast(z); +} diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp new file mode 100644 index 0000000..445170d --- /dev/null +++ b/cpp/QrCode.hpp @@ -0,0 +1,317 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include +#include +#include "QrSegment.hpp" + + +namespace qrcodegen { + +/* + * Represents an immutable square grid of black and white cells for a QR Code symbol, and + * provides static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. + */ +class QrCode final { + + /*---- Public helper enumeration ----*/ +public: + + /* + * Represents the error correction level used in a QR Code symbol. + */ + class Ecc final { + // Constants declared in ascending order of error protection. + public: + const static Ecc LOW, MEDIUM, QUARTILE, HIGH; + + // Fields. + public: + const int ordinal; // In the range 0 to 3 (unsigned 2-bit integer). + const int formatBits; // In the range 0 to 3 (unsigned 2-bit integer). + + // Constructor. + private: + Ecc(int ord, int fb); + }; + + + + /*---- Public static factory functions ----*/ +public: + + /* + * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + */ + static QrCode encodeText(const char *text, const Ecc &ecl); + + + /* + * Returns a QR Code segment representing the given Unicode text string. + */ + static QrSegment encodeTextToSegment(const char *text); + + + /* + * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + */ + static QrCode encodeBinary(const std::vector &data, const Ecc &ecl); + + + /* + * Returns a QR Code symbol representing the given data segments at the given error + * correction level. The smallest possible QR Code version is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. This + * function is considered to be lower level than simply encoding text or binary data. + */ + static QrCode encodeSegments(const std::vector &segs, const Ecc &ecl); + + + + /*---- Instance fields ----*/ + + // Public immutable scalar parameters +public: + + /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + const int version; + + /* The width and height of this QR Code symbol, measured in modules. + * Always equal to version × 4 + 17, in the range 21 to 177. */ + const int size; + + /* The error correction level used in this QR Code symbol. */ + const Ecc &errorCorrectionLevel; + + /* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + * Note that even if a constructor was called with automatic masking requested + * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ +private: + int mask; + + // Private grids of modules/pixels (conceptually immutable) +private: + std::vector> modules; // The modules of this QR Code symbol (false = white, true = black) + std::vector> isFunction; // Indicates function modules that are not subjected to masking + + + + /*---- Constructors ----*/ +public: + + /* + * Creates a new QR Code symbol with the given version number, error correction level, binary data string, and mask number. + * This cumbersome constructor can be invoked directly by the user, but is considered + * to be even lower level than encodeSegments(). + */ + QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask); + + + /* + * Creates a new QR Code symbol based on the given existing object, but with a potentially + * different mask pattern. The version, error correction level, codewords, etc. of the newly + * created object are all identical to the argument object; only the mask may differ. + */ + QrCode(const QrCode &qr, int mask); + + + + /*---- Public instance methods ----*/ +public: + + int getMask() const; + + + /* + * Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top + * left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. + */ + int getModule(int x, int y) const; + + + /* + * Based on the given number of border modules to add as padding, this returns a + * string whose contents represents an SVG XML file that depicts this QR Code symbol. + * Note that Unix newlines (\n) are always used, regardless of the platform. + */ + std::string toSvgString(int border) const; + + + + /*---- Private helper methods for constructor: Drawing function modules ----*/ +private: + + void drawFunctionPatterns(); + + + // 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. + void drawFormatBits(int mask); + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + void drawVersion(); + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + void drawFinderPattern(int x, int y); + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + void drawAlignmentPattern(int x, int y); + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + void setFunctionModule(int x, int y, bool isBlack); + + + /*---- Private helper methods for constructor: Codewords and masking ----*/ +private: + + // 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. + std::vector appendErrorCorrection(const std::vector &data) const; + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + void drawCodewords(const std::vector &data); + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + void applyMask(int mask); + + + // A messy helper function for the constructors. 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. + int handleConstructorMasking(int mask); + + + // 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. + int getPenaltyScore() const; + + + + /*---- Static helper functions ----*/ +private: + + // Returns a set of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. + static std::vector getAlignmentPatternPositions(int ver); + + + // Returns the number of raw data modules (bits) available at the given version number. + // These data modules are used for both user data codewords and error correction codewords. + // This stateless pure function could be implemented as a 40-entry lookup table. + static int getNumRawDataModules(int ver); + + + // 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, const Ecc &ecl); + + + /*---- Tables of constants ----*/ +private: + + // For use in getPenaltyScore(), when evaluating which mask is best. + static const int PENALTY_N1; + static const int PENALTY_N2; + static const int PENALTY_N3; + static const int PENALTY_N4; + + static const int16_t NUM_ERROR_CORRECTION_CODEWORDS[4][41]; + static const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; + + + + /*---- Private helper class ----*/ +private: + + /* + * Computes the Reed-Solomon error correction codewords for a sequence of data codewords + * at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because the divisor polynomial does not need to be recalculated for every input. + */ + class ReedSolomonGenerator final { + + /*-- Immutable field --*/ + private: + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + std::vector coefficients; + + + /*-- Constructor --*/ + public: + + /* + * Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + * as a lookup table over all possible parameter values, instead of as an algorithm. + */ + ReedSolomonGenerator(int degree); + + + /*-- Method --*/ + public: + + /* + * Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. + * The returned object is always a new byte array. This method does not alter this object's state (because it is immutable). + */ + std::vector getRemainder(const std::vector &data) const; + + + /*-- Static function --*/ + private: + + // 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. + static uint8_t multiply(uint8_t x, uint8_t y); + + }; + +}; + +} diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp new file mode 100644 index 0000000..f003a0c --- /dev/null +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -0,0 +1,141 @@ +/* + * QR Code generator demo (C++) + * + * Run this command-line program with no arguments. The program computes a bunch of demonstration + * QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include +#include +#include "QrCode.hpp" + + +// Function prototypes +static void doBasicDemo(); +static void doVarietyDemo(); +static void doSegmentDemo(); +static void printQr(const qrcodegen::QrCode &qr); + + + +int main(int argc, char **argv) { + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + return 0; +} + + +// Creates a single QR Code, then prints it to the console. +static void doBasicDemo() { + const char *text = "Hello, world!"; // User-supplied text + const qrcodegen::QrCode::Ecc &errCorLvl = qrcodegen::QrCode::Ecc::LOW; // Error correction level + + // Make and print the QR Code symbol + const qrcodegen::QrCode qr = qrcodegen::QrCode::encodeText(text, errCorLvl); + std::cout << qr.toSvgString(4) << std::endl; + printQr(qr); +} + + +// Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. +static void doVarietyDemo() { + // Project Nayuki URL + const qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText("https://www.nayuki.io/", qrcodegen::QrCode::Ecc::HIGH); + printQr(qrcodegen::QrCode(qr0, 3)); // Change mask, forcing to mask #3 + + // Numeric mode encoding (3.33 bits per digit) + const qrcodegen::QrCode qr1 = qrcodegen::QrCode::encodeText("314159265358979323846264338327950288419716939937510", qrcodegen::QrCode::Ecc::MEDIUM); + printQr(qr1); + + // Alphanumeric mode encoding (5.5 bits per character) + const qrcodegen::QrCode qr2 = qrcodegen::QrCode::encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen::QrCode::Ecc::HIGH); + printQr(qr2); + + // Unicode text as UTF-8, and different masks + const qrcodegen::QrCode qr3 = qrcodegen::QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", qrcodegen::QrCode::Ecc::QUARTILE); + printQr(qrcodegen::QrCode(qr3, 0)); + printQr(qrcodegen::QrCode(qr3, 1)); + printQr(qrcodegen::QrCode(qr3, 5)); + printQr(qrcodegen::QrCode(qr3, 7)); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + const qrcodegen::QrCode qr4 = qrcodegen::QrCode::encodeText("Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", + qrcodegen::QrCode::Ecc::HIGH); + printQr(qr4); +} + + +// Creates QR Codes with manually specified segments for better compactness. +static void doSegmentDemo() { + // Illustration "silver" + const char *silver0 = "THE SQUARE ROOT OF 2 IS 1."; + const char *silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; + const qrcodegen::QrCode qr0 = qrcodegen::QrCode::encodeText( + (std::string(silver0) + silver1).c_str(), + qrcodegen::QrCode::Ecc::LOW); + printQr(qr0); + + std::vector segs; + segs.push_back(qrcodegen::QrSegment::makeAlphanumeric(silver0)); + segs.push_back(qrcodegen::QrSegment::makeNumeric(silver1)); + const qrcodegen::QrCode qr1 = qrcodegen::QrCode::encodeSegments(segs, qrcodegen::QrCode::Ecc::LOW); + printQr(qr1); + + // Illustration "golden" + const char *golden0 = "Golden ratio \xCF\x86 = 1."; + const char *golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + const char *golden2 = "......"; + const qrcodegen::QrCode qr2 = qrcodegen::QrCode::encodeText( + (std::string(golden0) + golden1 + golden2).c_str(), + qrcodegen::QrCode::Ecc::LOW); + printQr(qr2); + + segs.clear(); + std::vector bytes; + for (const char *temp = golden0; *temp != '\0'; temp++) + bytes.push_back(static_cast(*temp)); + segs.push_back(qrcodegen::QrSegment::makeBytes(bytes)); + segs.push_back(qrcodegen::QrSegment::makeNumeric(golden1)); + segs.push_back(qrcodegen::QrSegment::makeAlphanumeric(golden2)); + const qrcodegen::QrCode qr3 = qrcodegen::QrCode::encodeSegments(segs, qrcodegen::QrCode::Ecc::LOW); + printQr(qr3); +} + + +// Prints the given QR Code to the console. +static void printQr(const qrcodegen::QrCode &qr) { + int border = 4; + for (int y = -border; y < qr.size + border; y++) { + for (int x = -border; x < qr.size + border; x++) { + std::cout << (qr.getModule(x, y) == 1 ? "##" : " "); + } + std::cout << std::endl; + } +} diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp new file mode 100644 index 0000000..debb41a --- /dev/null +++ b/cpp/QrSegment.cpp @@ -0,0 +1,138 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#include "BitBuffer.hpp" +#include "QrSegment.hpp" + + +qrcodegen::QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int qrcodegen::QrSegment::Mode::numCharCountBits(int ver) const { + if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; + else throw "Version number out of range"; +} + + +const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const qrcodegen::QrSegment::Mode qrcodegen::QrSegment::Mode::KANJI (0x8, 8, 10, 12); + + + +qrcodegen::QrSegment qrcodegen::QrSegment::makeBytes(const std::vector &data) { + return QrSegment(Mode::BYTE, data.size(), data, data.size() * 8); +} + + +qrcodegen::QrSegment qrcodegen::QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw "String contains non-numeric characters"; + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(accumData, 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(accumData, accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, bb.getBytes(), bb.getBitLength()); +} + + +qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + char c = *text; + if (c < ' ' || c > 'Z') + throw "String contains unencodable characters in alphanumeric mode"; + accumData = accumData * 45 + ALPHANUMERIC_ENCODING_TABLE[c - ' ']; + accumCount++; + if (accumCount == 2) { + bb.appendBits(accumData, 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(accumData, 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, bb.getBytes(), bb.getBitLength()); +} + + +qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen) : + mode(md), + numChars(numCh), + data(b), + bitLength(bitLen) { + if (numCh < 0 || bitLen < 0 || b.size() != static_cast((bitLen + 7) / 8)) + throw "Invalid value"; +} + + +bool qrcodegen::QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < ' ' || c > 'Z' || ALPHANUMERIC_ENCODING_TABLE[c - ' '] == -1) + return false; + } + return true; +} + + +bool qrcodegen::QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +const int8_t qrcodegen::QrSegment::ALPHANUMERIC_ENCODING_TABLE[59] = { + // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 + 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 + 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, // Array indices 33 to 58 + // A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, // ASCII codes 65 to 90 +}; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp new file mode 100644 index 0000000..20e5120 --- /dev/null +++ b/cpp/QrSegment.hpp @@ -0,0 +1,154 @@ +/* + * QR Code generator library (C++) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +#pragma once + +#include +#include + + +namespace qrcodegen { + +/* + * Represents a character string to be encoded in a QR Code symbol. Each segment has + * a mode, and a sequence of characters that is already encoded as a sequence of bits. + * Instances of this class are immutable. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + */ +public: + class Mode final { + // Constants. + public: + static const Mode NUMERIC; + static const Mode ALPHANUMERIC; + static const Mode BYTE; + static const Mode KANJI; + + + /*-- Fields --*/ + + /* An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ + public: + const int modeBits; + + private: + int numBitsCharCount[3]; + + + // Constructor. + private: + Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Method --*/ + public: + + /* + * Returns the bit width of the segment character count field for this mode object at the given version number. + */ + int numCharCountBits(int ver) const; + }; + + + + /*---- Static factory functions ----*/ +public: + + /* + * Returns a segment representing the given binary data encoded in byte mode. + */ + static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: + * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + static QrSegment makeAlphanumeric(const char *text); + + + /*---- Static helper functions ----*/ +public: + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + */ + static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + */ + static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ +public: + + /* The mode indicator for this segment. */ + const Mode mode; + + /* The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + const int numChars; + + /* The bits of this segment packed into a byte array in big endian. */ + const std::vector data; + + /* The length of this segment's encoded data, measured in bits. Satisfies ceil(bitLength / 8) = data.size(). */ + const int bitLength; + + + /*---- Constructor ----*/ +public: + + /* + * Creates a new QR Code data segment with the given parameters and data. + */ + QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen); + + + /*---- Constant ----*/ +private: + + static const int8_t ALPHANUMERIC_ENCODING_TABLE[59]; + +}; + +} From bd52bc32527c2f3c0d88eb65a5de7ed378b8da64 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Fri, 15 Apr 2016 23:59:53 +0000 Subject: [PATCH 007/810] Moved Java code into appropriate directory structure for packages. --- java/{ => io/nayuki/qrcodegen}/BitBuffer.java | 0 java/{ => io/nayuki/qrcodegen}/QrCode.java | 0 java/{ => io/nayuki/qrcodegen}/QrCodeGeneratorDemo.java | 0 java/{ => io/nayuki/qrcodegen}/QrSegment.java | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename java/{ => io/nayuki/qrcodegen}/BitBuffer.java (100%) rename java/{ => io/nayuki/qrcodegen}/QrCode.java (100%) rename java/{ => io/nayuki/qrcodegen}/QrCodeGeneratorDemo.java (100%) rename java/{ => io/nayuki/qrcodegen}/QrSegment.java (100%) diff --git a/java/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java similarity index 100% rename from java/BitBuffer.java rename to java/io/nayuki/qrcodegen/BitBuffer.java diff --git a/java/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java similarity index 100% rename from java/QrCode.java rename to java/io/nayuki/qrcodegen/QrCode.java diff --git a/java/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java similarity index 100% rename from java/QrCodeGeneratorDemo.java rename to java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java diff --git a/java/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java similarity index 100% rename from java/QrSegment.java rename to java/io/nayuki/qrcodegen/QrSegment.java From 3fda371340fa53a01228828d24e4f4ccad33fbb8 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 01:14:22 +0000 Subject: [PATCH 008/810] Tweaked the comment for QrCode.encodeTextToSegment() in most language versions. --- cpp/QrCode.hpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- python/qrcodegen.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 445170d..82107a5 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -75,7 +75,7 @@ public: /* - * Returns a QR Code segment representing the given Unicode text string. + * Returns a QR segment representing the given Unicode text string. */ static QrSegment encodeTextToSegment(const char *text); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 304e2df..d04cf7b 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -59,9 +59,9 @@ public final class QrCode { /** - * Returns a QR Code segment representing the specified Unicode text string. + * Returns a QR segment representing the specified Unicode text string. * @param text the text to be encoded, which can be any Unicode string - * @return a QR Code representing the text + * @return a QR segment containing the text * @throws NullPointerException if the text is {@code null} */ public static QrSegment encodeTextToSegment(String text) { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index bd674c8..eeda106 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -69,7 +69,7 @@ def encode_text(text, ecl): def encode_text_to_segment(text): - """Returns a QR Code segment representing the given Unicode text string.""" + """Returns a QR segment representing the given Unicode text string.""" if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)): raise TypeError("Text string expected") # Select the most efficient segment encoding automatically From 1b2a5541212ea25eb03014e95ea4326189e7a3fe Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 01:17:15 +0000 Subject: [PATCH 009/810] Tweaked minor comment and whitespace consistency in Python and C++ code. --- cpp/BitBuffer.hpp | 2 +- cpp/QrCode.cpp | 1 - python/qrcodegen-demo.py | 2 +- python/qrcodegen.py | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index 47daebb..89f032e 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -31,7 +31,7 @@ namespace qrcodegen { -/** +/* * An appendable sequence of bits. Bits are packed in big endian within a byte. */ class BitBuffer final { diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 96ab0a4..c0de087 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -545,7 +545,6 @@ int qrcodegen::QrCode::getNumDataCodewords(int ver, const Ecc &ecl) { /*---- Tables of constants ----*/ -// For use in getPenaltyScore(), when evaluating which mask is best. const int qrcodegen::QrCode::PENALTY_N1 = 3; const int qrcodegen::QrCode::PENALTY_N2 = 3; const int qrcodegen::QrCode::PENALTY_N3 = 40; diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index 7978c94..81be66d 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -110,7 +110,7 @@ def do_segment_demo(): # ---- Utilities ---- -def print_qr(qrcode): +def print_qr(qrcode): """Prints the given QrCode object to the console.""" border = 4 for y in range(-border, qrcode.get_size() + border): diff --git a/python/qrcodegen.py b/python/qrcodegen.py index eeda106..8de6d8e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -66,7 +66,7 @@ def encode_text(text, ecl): code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" seg = encode_text_to_segment(text) return encode_segments([seg], ecl) - + def encode_text_to_segment(text): """Returns a QR segment representing the given Unicode text string.""" From ca7e7a60a7ca4b1c14a686f79b958b4a1ec9a268 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 01:44:24 +0000 Subject: [PATCH 010/810] Moved QrCode.encodeTextToSegment() to QrSegment.makeSegments() and changed the function's code somewhat, in all language versions; updated JavaScript demo script to handle new semantics. --- cpp/QrCode.cpp | 18 +----------- cpp/QrCode.hpp | 6 ---- cpp/QrSegment.cpp | 18 ++++++++++++ cpp/QrSegment.hpp | 7 +++++ java/io/nayuki/qrcodegen/QrCode.java | 24 ++-------------- java/io/nayuki/qrcodegen/QrSegment.java | 28 +++++++++++++++++++ javascript/qrcodegen-demo.js | 36 +++++++++++++++--------- javascript/qrcodegen.js | 36 ++++++++++++------------ python/qrcodegen.py | 37 ++++++++++++++----------- 9 files changed, 119 insertions(+), 91 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index c0de087..273a688 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -43,27 +43,11 @@ const qrcodegen::QrCode::Ecc qrcodegen::QrCode::Ecc::HIGH (3, 2); qrcodegen::QrCode qrcodegen::QrCode::encodeText(const char *text, const Ecc &ecl) { - std::vector segs; - segs.push_back(encodeTextToSegment(text)); + std::vector segs(QrSegment::makeSegments(text)); return encodeSegments(segs, ecl); } -qrcodegen::QrSegment qrcodegen::QrCode::encodeTextToSegment(const char *text) { - // Select the most efficient segment encoding automatically - if (QrSegment::isNumeric(text)) - return QrSegment::makeNumeric(text); - else if (QrSegment::isAlphanumeric(text)) - return QrSegment::makeAlphanumeric(text); - else { - std::vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - return QrSegment::makeBytes(bytes); - } -} - - qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &data, const Ecc &ecl) { std::vector segs; segs.push_back(QrSegment::makeBytes(data)); diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 82107a5..9463279 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -74,12 +74,6 @@ public: static QrCode encodeText(const char *text, const Ecc &ecl); - /* - * Returns a QR segment representing the given Unicode text string. - */ - static QrSegment encodeTextToSegment(const char *text); - - /* * Returns a QR Code symbol representing the given binary data string at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index debb41a..0c48908 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -100,6 +100,24 @@ qrcodegen::QrSegment qrcodegen::QrSegment::makeAlphanumeric(const char *text) { } +std::vector qrcodegen::QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + std::vector result; + if (*text == '\0'); // Leave the vector empty + else if (QrSegment::isNumeric(text)) + result.push_back(QrSegment::makeNumeric(text)); + else if (QrSegment::isAlphanumeric(text)) + result.push_back(QrSegment::makeAlphanumeric(text)); + else { + std::vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(QrSegment::makeBytes(bytes)); + } + return result; +} + + qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen) : mode(md), numChars(numCh), diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 20e5120..18f7a51 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -103,6 +103,13 @@ public: static QrSegment makeAlphanumeric(const char *text); + /* + * Returns a list of zero or more segments to represent the given text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + */ + static std::vector makeSegments(const char *text); + + /*---- Static helper functions ----*/ public: diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d04cf7b..3fc5581 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -25,7 +25,6 @@ package io.nayuki.qrcodegen; import java.awt.image.BufferedImage; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.List; @@ -53,27 +52,8 @@ public final class QrCode { public static QrCode encodeText(String text, Ecc ecl) { if (text == null || ecl == null) throw new NullPointerException(); - QrSegment seg = encodeTextToSegment(text); - return encodeSegments(Arrays.asList(seg), ecl); - } - - - /** - * Returns a QR segment representing the specified Unicode text string. - * @param text the text to be encoded, which can be any Unicode string - * @return a QR segment containing the text - * @throws NullPointerException if the text is {@code null} - */ - public static QrSegment encodeTextToSegment(String text) { - if (text == null) - throw new NullPointerException(); - // Select the most efficient segment encoding automatically - if (QrSegment.NUMERIC_REGEX.matcher(text).matches()) - return QrSegment.makeNumeric(text); - else if (QrSegment.ALPHANUMERIC_REGEX.matcher(text).matches()) - return QrSegment.makeAlphanumeric(text); - else - return QrSegment.makeBytes(text.getBytes(StandardCharsets.UTF_8)); + List segs = QrSegment.makeSegments(text); + return encodeSegments(segs, ecl); } diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 20c0609..8ebc224 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -24,7 +24,10 @@ package io.nayuki.qrcodegen; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.regex.Pattern; @@ -104,6 +107,31 @@ public final class QrSegment { } + /** + * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + * @param text the text to be encoded, which can be any Unicode string + * @return a list of segments containing the text + * @throws NullPointerException if the text is {@code null} + */ + public static List makeSegments(String text) { + if (text == null) + throw new NullPointerException(); + + // Select the most efficient segment encoding automatically + if (text.equals("")) + return new ArrayList<>(); + QrSegment seg; + if (NUMERIC_REGEX.matcher(text).matches()) + seg = makeNumeric(text); + else if (ALPHANUMERIC_REGEX.matcher(text).matches()) + seg = makeAlphanumeric(text); + else + seg = makeBytes(text.getBytes(StandardCharsets.UTF_8)); + return Arrays.asList(seg); + } + + /*---- Instance fields ----*/ diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index b48d505..e4a21b6 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -39,7 +39,8 @@ function redrawQrCode() { // Get text and compute QR Code var text = document.getElementById("text-input").value; - var qr = qrcodegen.encodeText(text, ecl); + var segs = qrcodegen.QrSegment.makeSegments(text); + var qr = qrcodegen.encodeSegments(segs, ecl); // Get scale and border var scale = parseInt(document.getElementById("scale-input").value, 10); @@ -67,18 +68,27 @@ function redrawQrCode() { stats += "mask pattern = " + qr.getMask() + ", "; stats += "character count = " + countUnicodeChars(text) + ",\n"; stats += "encoding mode = "; - var seg = qrcodegen.encodeTextToSegment(text); - if (seg.getMode() == qrcodegen.QrSegment.Mode.NUMERIC) - stats += "numeric"; - else if (seg.getMode() == qrcodegen.QrSegment.Mode.ALPHANUMERIC) - stats += "alphanumeric"; - else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE) - stats += "byte"; - else if (seg.getMode() == qrcodegen.QrSegment.Mode.BYTE) - stats += "kanji"; - else - stats += "unknown"; - stats += ", data bits = " + (4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length) + "."; + if (segs.length == 0) + stats += "none"; + else if (segs.length == 1) { + var mode = segs[0].getMode(); + if (mode == qrcodegen.QrSegment.Mode.NUMERIC) + stats += "numeric"; + else if (mode == qrcodegen.QrSegment.Mode.ALPHANUMERIC) + stats += "alphanumeric"; + else if (mode == qrcodegen.QrSegment.Mode.BYTE) + stats += "byte"; + else if (mode == qrcodegen.QrSegment.Mode.BYTE) + stats += "kanji"; + else + stats += "unknown"; + } else + stats += "multiple"; + var databits = 0; + segs.forEach(function(seg) { + databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length; + }); + stats += ", data bits = " + databits + "."; var elem = document.getElementById("statistics-output"); while (elem.firstChild != null) elem.removeChild(elem.firstChild); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 898c38c..77da0c7 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -28,7 +28,6 @@ /* * Module "qrcodegen". Public members inside this namespace: * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode - * - Function encodeTextToSegment(str text) -> QrSegment * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode * - Function encodeSegments(list segs, QrCode.Ecc ecl) -> QrCode * - Class QrCode: @@ -48,6 +47,7 @@ * - Function makeBytes(list data) -> QrSegment * - Function makeNumeric(str data) -> QrSegment * - Function makeAlphanumeric(str data) -> QrSegment + * - Function makeSegments(str text) -> list * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) * - Method getMode() -> QrSegment.Mode * - Method getNumChars() -> int @@ -67,22 +67,8 @@ var qrcodegen = new function() { * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. */ this.encodeText = function(text, ecl) { - var seg = this.encodeTextToSegment(text); - return this.encodeSegments([seg], ecl); - }; - - - /* - * Returns a single QR segment representing the given Unicode text string. - */ - this.encodeTextToSegment = function(text) { - // Select the most efficient segment encoding automatically - if (QrSegment.NUMERIC_REGEX.test(text)) - return this.QrSegment.makeNumeric(text); - else if (QrSegment.ALPHANUMERIC_REGEX.test(text)) - return this.QrSegment.makeAlphanumeric(text); - else - return this.QrSegment.makeBytes(toUtf8ByteArray(text)); + var segs = this.QrSegment.makeSegments(text); + return this.encodeSegments(segs, ecl); }; @@ -773,6 +759,22 @@ var qrcodegen = new function() { return new this(this.Mode.ALPHANUMERIC, text.length, bb.getBits()); }; + /* + * Returns a new mutable list of zero or more segments to represent the given Unicode text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + */ + this.QrSegment.makeSegments = function(text) { + // Select the most efficient segment encoding automatically + if (text == "") + return []; + else if (QrSegment.NUMERIC_REGEX.test(text)) + return [this.makeNumeric(text)]; + else if (QrSegment.ALPHANUMERIC_REGEX.test(text)) + return [this.makeAlphanumeric(text)]; + else + return [this.makeBytes(toUtf8ByteArray(text))]; + }; + /*-- Constants --*/ var QrSegment = {}; // Private object to assign properties to diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8de6d8e..1aefcda 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -28,7 +28,6 @@ import itertools, re, sys """ Public members inside this module "qrcodegen": - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode -- Function encode_text_to_segment(str text) -> QrSegment - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode - Function encode_segments(list segs, QrCode.Ecc ecl) -> QrCode - Class QrCode: @@ -47,6 +46,7 @@ Public members inside this module "qrcodegen": - Function make_bytes(bytes data) -> QrSegment - Function make_numeric(str digits) -> QrSegment - Function make_alphanumeric(str text) -> QrSegment + - Function make_segments(str text) -> list - Constructor QrSegment(QrSegment.Mode mode, int numch, list bitdata) - Method get_mode() -> QrSegment.Mode - Method get_num_chars() -> int @@ -64,21 +64,8 @@ def encode_text(text, ecl): """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" - seg = encode_text_to_segment(text) - return encode_segments([seg], ecl) - - -def encode_text_to_segment(text): - """Returns a QR segment representing the given Unicode text string.""" - if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)): - raise TypeError("Text string expected") - # Select the most efficient segment encoding automatically - if QrSegment.NUMERIC_REGEX.match(text) is not None: - return QrSegment.make_numeric(text) - elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None: - return QrSegment.make_alphanumeric(text) - else: - return QrSegment.make_bytes(text.encode("UTF-8")) + segs = QrSegment.make_segments(text) + return encode_segments(segs, ecl) def encode_binary(data, ecl): @@ -659,6 +646,24 @@ class QrSegment(object): return QrSegment(QrSegment.Mode.ALPHANUMERIC, len(text), bb.get_bits()) + @staticmethod + def make_segments(text): + """Returns a new mutable list of zero or more segments to represent the given Unicode text string. + The result may use various segment modes and switch modes to optimize the length of the bit stream.""" + if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)): + raise TypeError("Text string expected") + + # Select the most efficient segment encoding automatically + if text == "": + return [] + elif QrSegment.NUMERIC_REGEX.match(text) is not None: + return [QrSegment.make_numeric(text)] + elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None: + return [QrSegment.make_alphanumeric(text)] + else: + return [QrSegment.make_bytes(text.encode("UTF-8"))] + + # -- Constructor -- def __init__(self, mode, numch, bitdata): From 5692e951ddebfb2f0df4fcf6747defbedab1036e Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 03:53:58 +0000 Subject: [PATCH 011/810] Revamped QrCode.encodeSegments() to add parameters to make a much richer API, in all language versions; updated JavaScript demo script to handle new semantics. --- cpp/QrCode.cpp | 48 ++++++++-------- cpp/QrCode.hpp | 11 ++-- cpp/QrSegment.cpp | 17 ++++++ cpp/QrSegment.hpp | 4 ++ java/io/nayuki/qrcodegen/QrCode.java | 76 +++++++++++++++---------- java/io/nayuki/qrcodegen/QrSegment.java | 21 +++++++ javascript/qrcodegen-demo.js | 3 +- javascript/qrcodegen.js | 73 ++++++++++++++---------- python/qrcodegen.py | 65 +++++++++++++-------- 9 files changed, 205 insertions(+), 113 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 273a688..866c33a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -55,34 +55,34 @@ qrcodegen::QrCode qrcodegen::QrCode::encodeBinary(const std::vector &da } -qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector &segs, const Ecc &ecl) { +qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector &segs, const Ecc &ecl, + int minVersion, int maxVersion, int mask, bool boostEcl) { + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) + throw "Invalid value"; + // Find the minimal version number to use - int version, dataCapacityBits; - for (version = 1; ; version++) { // Increment until the data fits in the QR Code - if (version > 40) // All versions could not fit the given data - throw "Data too long"; - dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - - // Calculate the total number of bits needed at this version number - // to encode all the segments (i.e. segment metadata and payloads) - int dataUsedBits = 0; - for (size_t i = 0; i < segs.size(); i++) { - const QrSegment &seg(segs.at(i)); - if (seg.numChars < 0) - throw "Assertion error"; - int ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1 << ccbits)) { - // Segment length value doesn't fit in the length field's bit-width, so fail immediately - goto continueOuter; - } - dataUsedBits += 4 + ccbits + seg.bitLength; - } - if (dataUsedBits <= dataCapacityBits) + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment::getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) break; // This version number is found to be suitable - continueOuter:; + if (version >= maxVersion) // All versions in the range could not fit the given data + throw "Data too long"; + } + if (dataUsedBits == -1) + throw "Assertion error"; + + // Increase the error correction level while the data still fits in the current version number + const Ecc *newEcl = &ecl; + if (boostEcl) { + if (dataUsedBits <= getNumDataCodewords(version, Ecc::MEDIUM ) * 8) newEcl = &Ecc::MEDIUM ; + if (dataUsedBits <= getNumDataCodewords(version, Ecc::QUARTILE) * 8) newEcl = &Ecc::QUARTILE; + if (dataUsedBits <= getNumDataCodewords(version, Ecc::HIGH ) * 8) newEcl = &Ecc::HIGH ; } // Create the data bit string by concatenating all segments + int dataCapacityBits = getNumDataCodewords(version, *newEcl) * 8; BitBuffer bb; for (size_t i = 0; i < segs.size(); i++) { const QrSegment &seg(segs.at(i)); @@ -102,7 +102,7 @@ qrcodegen::QrCode qrcodegen::QrCode::encodeSegments(const std::vector throw "Assertion error"; // Create the QR Code symbol - return QrCode(version, ecl, bb.getBytes(), -1); + return QrCode(version, *newEcl, bb.getBytes(), mask); } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 9463279..7302fd3 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -83,13 +83,14 @@ public: /* - * Returns a QR Code symbol representing the given data segments at the given error - * correction level. The smallest possible QR Code version is automatically chosen for the output. + * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * The smallest possible QR Code version within the specified range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. This - * function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. */ - static QrCode encodeSegments(const std::vector &segs, const Ecc &ecl); + static QrCode encodeSegments(const std::vector &segs, const Ecc &ecl, + int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 0c48908..2b76cf4 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -22,6 +22,7 @@ * Software. */ +#include #include "BitBuffer.hpp" #include "QrSegment.hpp" @@ -128,6 +129,22 @@ qrcodegen::QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &segs, int version) { + if (version < 1 || version > 40) + throw "Version number out of range"; + int result = 0; + for (size_t i = 0; i < segs.size(); i++) { + const QrSegment &seg(segs.at(i)); + int ccbits = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1 << ccbits)) + return -1; + result += 4 + ccbits + seg.bitLength; + } + return result; +} + + bool qrcodegen::QrSegment::isAlphanumeric(const char *text) { for (; *text != '\0'; text++) { char c = *text; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 18f7a51..54682ee 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -151,6 +151,10 @@ public: QrSegment(const Mode &md, int numCh, const std::vector &b, int bitLen); + // Package-private helper function. + static int getTotalBits(const std::vector &segs, int version); + + /*---- Constant ----*/ private: diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 3fc5581..2abdebe 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -76,49 +76,66 @@ public final class QrCode { /** - * Returns a QR Code symbol representing the specified data segments at the specified error - * correction level. The smallest possible QR Code version is automatically chosen for the output. + * Returns a QR Code symbol representing the specified data segments at the specified error correction + * level or higher. The smallest possible QR Code version is automatically chosen for the output. *

This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. This * function is considered to be lower level than simply encoding text or binary data.

* @param segs the segments to encode - * @param ecl the error correction level to use + * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the segments * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long + * @throws IllegalArgumentException if the data is too long to fit in the largest version QR Code at the ECL */ public static QrCode encodeSegments(List segs, Ecc ecl) { + return encodeSegments(segs, ecl, 1, 40, -1, true); + } + + + /** + * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * The smallest possible QR Code version within the specified range is automatically chosen for the output. + *

This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data.

+ * @param segs the segments to encode + * @param ecl the error correction level to use (may be boosted) + * @param minVersion the minimum allowed version of the QR symbol (at least 1) + * @param maxVersion the maximum allowed version of the QR symbol (at most 40) + * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice + * @param boostEcl increases the error correction level if it can be done without increasing the version number + * @return a QR Code representing the segments + * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, or if mask + * < −1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL + */ + public static QrCode encodeSegments(List segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { if (segs == null || ecl == null) throw new NullPointerException(); + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) + throw new IllegalArgumentException("Invalid value"); // Find the minimal version number to use - int version, dataCapacityBits; - outer: - for (version = 1; ; version++) { // Increment until the data fits in the QR Code - if (version > 40) // All versions could not fit the given data - throw new IllegalArgumentException("Data too long"); - dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available - - // Calculate the total number of bits needed at this version number - // to encode all the segments (i.e. segment metadata and payloads) - int dataUsedBits = 0; - for (QrSegment seg : segs) { - if (seg == null) - throw new NullPointerException(); - if (seg.numChars < 0) - throw new AssertionError(); - int ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1 << ccbits)) { - // Segment length value doesn't fit in the length field's bit-width, so fail immediately - continue outer; - } - dataUsedBits += 4 + ccbits + seg.bitLength; - } - if (dataUsedBits <= dataCapacityBits) + int version, dataUsedBits; + for (version = minVersion; ; version++) { + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = QrSegment.getTotalBits(segs, 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 + throw new IllegalArgumentException("Data too long"); + } + if (dataUsedBits == -1) + throw new AssertionError(); + + // Increase the error correction level while the data still fits in the current version number + for (Ecc newEcl : Ecc.values()) { + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; } // Create the data bit string by concatenating all segments + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { bb.appendBits(seg.mode.modeBits, 4); @@ -137,7 +154,7 @@ public final class QrCode { throw new AssertionError(); // Create the QR Code symbol - return new QrCode(version, ecl, bb.getBytes(), -1); + return new QrCode(version, ecl, bb.getBytes(), mask); } @@ -732,7 +749,8 @@ public final class QrCode { * Represents the error correction level used in a QR Code symbol. */ public enum Ecc { - // Constants declared in ascending order of error protection. + // These enum constants must be declared in ascending order of error protection, + // for the sake of the implicit ordinal() method and values() function. LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); // In the range 0 to 3 (unsigned 2-bit integer). diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 8ebc224..1315a48 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -186,6 +186,27 @@ public final class QrSegment { } + // Package-private helper function. + static int getTotalBits(List segs, int version) { + if (segs == null) + throw new NullPointerException(); + if (version < 1 || version > 40) + throw new IllegalArgumentException("Version number out of range"); + + int result = 0; + for (QrSegment seg : segs) { + if (seg == null) + throw new NullPointerException(); + int ccbits = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1 << ccbits)) + return -1; + result += 4 + ccbits + seg.bitLength; + } + return result; + } + + /*---- Constants ----*/ /** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */ diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index e4a21b6..f5b304b 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -88,7 +88,8 @@ function redrawQrCode() { segs.forEach(function(seg) { databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length; }); - stats += ", data bits = " + databits + "."; + stats += ", error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; + stats += "data bits = " + databits + "."; var elem = document.getElementById("statistics-output"); while (elem.firstChild != null) elem.removeChild(elem.firstChild); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 77da0c7..1aae9ee 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -29,7 +29,8 @@ * Module "qrcodegen". Public members inside this namespace: * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode - * - Function encodeSegments(list segs, QrCode.Ecc ecl) -> QrCode + * - Function encodeSegments(list segs, QrCode.Ecc ecl, + * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Class QrCode: * - Constructor QrCode(QrCode qr, int mask) * - Constructor QrCode(list bytes, int mask, int version, QrCode.Ecc ecl) @@ -84,40 +85,39 @@ var qrcodegen = new function() { /* - * Returns a QR Code symbol representing the given data segments at the given error - * correction level. The smallest possible QR Code version is automatically chosen for the output. + * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * The smallest possible QR Code version within the specified range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. This - * function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. */ - this.encodeSegments = function(segs, ecl) { + this.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { + if (minVersion == undefined) minVersion = 1; + if (maxVersion == undefined) maxVersion = 40; + if (mask == undefined) mask = -1; + if (boostEcl == undefined) boostEcl = true; + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) + throw "Invalid value"; + // Find the minimal version number to use - var version, dataCapacityBits; - outer: - for (version = 1; ; version++) { // Increment until the data fits in the QR Code - if (version > 40) // All versions could not fit the given data - throw "Data too long"; - dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available - - // Calculate the total number of bits needed at this version number - // to encode all the segments (i.e. segment metadata and payloads) - var dataUsedBits = 0; - for (var i = 0; i < segs.length; i++) { - var seg = segs[i]; - if (seg.numChars < 0) - throw "Assertion error"; - var ccbits = seg.getMode().numCharCountBits(version); - if (seg.getNumChars() >= (1 << ccbits)) { - // Segment length value doesn't fit in the length field's bit-width, so fail immediately - continue outer; - } - dataUsedBits += 4 + ccbits + seg.getBits().length; - } - if (dataUsedBits <= dataCapacityBits) + var version, dataUsedBits; + for (version = minVersion; ; version++) { + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = this.QrSegment.getTotalBits(segs, version); + if (dataUsedBits != null && 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 + throw "Data too long"; } + // Increase the error correction level while the data still fits in the current version number + [this.QrCode.Ecc.MEDIUM, this.QrCode.Ecc.QUARTILE, this.QrCode.Ecc.HIGH].forEach(function(newEcl) { + if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + }); + // Create the data bit string by concatenating all segments + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; var bb = new BitBuffer(); segs.forEach(function(seg) { bb.appendBits(seg.getMode().getModeBits(), 4); @@ -136,7 +136,7 @@ var qrcodegen = new function() { throw "Assertion error"; // Create the QR Code symbol - return new this.QrCode(bb.getBytes(), -1, version, ecl); + return new this.QrCode(bb.getBytes(), mask, version, ecl); }; @@ -775,6 +775,21 @@ var qrcodegen = new function() { return [this.makeBytes(toUtf8ByteArray(text))]; }; + // Package-private helper function. + this.QrSegment.getTotalBits = function(segs, version) { + if (version < 1 || version > 40) + throw "Version number out of range"; + var result = 0; + segs.forEach(function(seg) { + var ccbits = seg.getMode().numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.getNumChars() >= (1 << ccbits)) + return null; + result += 4 + ccbits + seg.getBits().length; + }); + return result; + }; + /*-- Constants --*/ var QrSegment = {}; // Private object to assign properties to diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 1aefcda..ab7f467 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -29,7 +29,8 @@ import itertools, re, sys Public members inside this module "qrcodegen": - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode -- Function encode_segments(list segs, QrCode.Ecc ecl) -> QrCode +- Function encode_segments(list segs, QrCode.Ecc ecl, + int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - Class QrCode: - Constructor QrCode(QrCode qr, int mask) - Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl) @@ -77,35 +78,34 @@ def encode_binary(data, ecl): return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) -def encode_segments(segs, ecl): - """Returns a QR Code symbol representing the given data segments at the given error - correction level. The smallest possible QR Code version is automatically chosen for the output. +def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): + """Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + The smallest possible QR Code version within the specified range is automatically chosen for the output. This function allows the user to create a custom sequence of segments that switches - between modes (such as alphanumeric and binary) to encode text more efficiently. This - function is considered to be lower level than simply encoding text or binary data.""" + between modes (such as alphanumeric and binary) to encode text more efficiently. + This function is considered to be lower level than simply encoding text or binary data.""" + + if not 1 <= minversion <= maxversion <= 40 or not -1 <= mask <= 7: + raise ValueError("Invalid value") # Find the minimal version number to use - for version in itertools.count(1): # Increment until the data fits in the QR Code - if version > 40: # All versions could not fit the given data - raise ValueError("Data too long") + for version in range(minversion, maxversion + 1): datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available - - # Calculate the total number of bits needed at this version number - # to encode all the segments (i.e. segment metadata and payloads) - datausedbits = 0 - for seg in segs: - if seg.get_num_chars() < 0: - raise AssertionError() - ccbits = seg.get_mode().num_char_count_bits(version) - if seg.get_num_chars() >= (1 << ccbits): - # Segment length value doesn't fit in the length field's bit-width, so fail immediately - break - datausedbits += 4 + ccbits + len(seg.get_bits()) - else: # If the loop above did not break - if datausedbits <= datacapacitybits: - break # This version number is found to be suitable + datausedbits = QrSegment.get_total_bits(segs, version) + if datausedbits is not None and 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 + raise ValueError("Data too long") + if datausedbits is None: + raise AssertionError() + + # Increase the error correction level while the data still fits in the current version number + for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): + if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: + ecl = newecl # Create the data bit string by concatenating all segments + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 bb = _BitBuffer() for seg in segs: bb.append_bits(seg.get_mode().get_mode_bits(), 4) @@ -124,7 +124,7 @@ def encode_segments(segs, ecl): assert bb.bit_length() % 8 == 0 # Create the QR Code symbol - return QrCode(datacodewords=bb.get_bytes(), mask=-1, version=version, errcorlvl=ecl) + return QrCode(None, bb.get_bytes(), mask, version, ecl) @@ -686,6 +686,21 @@ class QrSegment(object): return list(self._bitdata) # Defensive copy + # Package-private helper function. + @staticmethod + def get_total_bits(segs, version): + if not 1 <= version <= 40: + raise ValueError("Version number out of range") + result = 0 + for seg in segs: + ccbits = seg.get_mode().num_char_count_bits(version) + # Fail if segment length value doesn't fit in the length field's bit-width + if seg.get_num_chars() >= (1 << ccbits): + return None + result += 4 + ccbits + len(seg.get_bits()) + return result + + # -- Constants -- # Can test whether a string is encodable in numeric mode (such as by using make_numeric()) From ee88ce819c9e89cb4b798e4d9c3b39ca760d0add Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 04:04:02 +0000 Subject: [PATCH 012/810] Fixed a bug and reordered a line of code in the JavaScript demo script. --- javascript/qrcodegen-demo.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index f5b304b..9abf067 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -78,17 +78,17 @@ function redrawQrCode() { stats += "alphanumeric"; else if (mode == qrcodegen.QrSegment.Mode.BYTE) stats += "byte"; - else if (mode == qrcodegen.QrSegment.Mode.BYTE) + else if (mode == qrcodegen.QrSegment.Mode.KANJI) stats += "kanji"; else stats += "unknown"; } else stats += "multiple"; + stats += ", error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; var databits = 0; segs.forEach(function(seg) { databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length; }); - stats += ", error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; stats += "data bits = " + databits + "."; var elem = document.getElementById("statistics-output"); while (elem.firstChild != null) From de261ca62d05c91becb8657fa1f1926c4949d341 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 16 Apr 2016 04:06:14 +0000 Subject: [PATCH 013/810] Reorganized the JavaScript demo code for modularity and clarity without changing behavior. --- javascript/qrcodegen-demo.js | 105 +++++++++++++++++------------------ 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 9abf067..46c501f 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -26,18 +26,20 @@ function redrawQrCode() { - // Get error correction level - var ecl; - if (document.getElementById("errcorlvl-medium").checked) - ecl = qrcodegen.QrCode.Ecc.MEDIUM; - else if (document.getElementById("errcorlvl-quartile").checked) - ecl = qrcodegen.QrCode.Ecc.QUARTILE; - else if (document.getElementById("errcorlvl-high").checked) - ecl = qrcodegen.QrCode.Ecc.HIGH; - else // In case no radio button is depressed - ecl = qrcodegen.QrCode.Ecc.LOW; + // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. + function getInputErrorCorrectionLevel() { + if (document.getElementById("errcorlvl-medium").checked) + return qrcodegen.QrCode.Ecc.MEDIUM; + else if (document.getElementById("errcorlvl-quartile").checked) + return qrcodegen.QrCode.Ecc.QUARTILE; + else if (document.getElementById("errcorlvl-high").checked) + return qrcodegen.QrCode.Ecc.HIGH; + else // In case no radio button is depressed + return qrcodegen.QrCode.Ecc.LOW; + } - // Get text and compute QR Code + // Get form inputs and compute QR Code + var ecl = getInputErrorCorrectionLevel(); var text = document.getElementById("text-input").value; var segs = qrcodegen.QrSegment.makeSegments(text); var qr = qrcodegen.encodeSegments(segs, ecl); @@ -63,33 +65,48 @@ function redrawQrCode() { } } - // Show statistics + + // Returns a string to describe the given list of segments. + function describeSegments(segs) { + if (segs.length == 0) + return "none"; + else if (segs.length == 1) { + var mode = segs[0].getMode(); + var Mode = qrcodegen.QrSegment.Mode; + if (mode == Mode.NUMERIC ) return "numeric"; + if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; + if (mode == Mode.BYTE ) return "byte"; + if (mode == Mode.KANJI ) return "kanji"; + return "unknown"; + } else + return "multiple"; + } + + // Returns the number of Unicode code points in the given UTF-16 string. + function countUnicodeChars(str) { + var result = 0; + for (var i = 0; i < str.length; i++, result++) { + var c = str.charCodeAt(i); + if (c < 0xD800 || c >= 0xE000) + continue; + else if (0xD800 <= c && c < 0xDC00) { // High surrogate + i++; + var d = str.charCodeAt(i); + if (0xDC00 <= d && d < 0xE000) // Low surrogate + continue; + } + throw "Invalid UTF-16 string"; + } + return result; + } + + // Show the QR Code symbol's statistics as a string var stats = "QR Code version = " + qr.getVersion() + ", "; stats += "mask pattern = " + qr.getMask() + ", "; stats += "character count = " + countUnicodeChars(text) + ",\n"; - stats += "encoding mode = "; - if (segs.length == 0) - stats += "none"; - else if (segs.length == 1) { - var mode = segs[0].getMode(); - if (mode == qrcodegen.QrSegment.Mode.NUMERIC) - stats += "numeric"; - else if (mode == qrcodegen.QrSegment.Mode.ALPHANUMERIC) - stats += "alphanumeric"; - else if (mode == qrcodegen.QrSegment.Mode.BYTE) - stats += "byte"; - else if (mode == qrcodegen.QrSegment.Mode.KANJI) - stats += "kanji"; - else - stats += "unknown"; - } else - stats += "multiple"; - stats += ", error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; - var databits = 0; - segs.forEach(function(seg) { - databits += 4 + seg.getMode().numCharCountBits(qr.getVersion()) + seg.getBits().length; - }); - stats += "data bits = " + databits + "."; + stats += "encoding mode = " + describeSegments(segs) + ", "; + stats += "error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; + stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.getVersion()) + "."; var elem = document.getElementById("statistics-output"); while (elem.firstChild != null) elem.removeChild(elem.firstChild); @@ -97,22 +114,4 @@ function redrawQrCode() { } -function countUnicodeChars(str) { - var result = 0; - for (var i = 0; i < str.length; i++, result++) { - var c = str.charCodeAt(i); - if (c < 0xD800 || c >= 0xE000) - continue; - else if (0xD800 <= c && c < 0xDC00) { // High surrogate - i++; - var d = str.charCodeAt(i); - if (0xDC00 <= d && d < 0xE000) // Low surrogate - continue; - } - throw "Invalid UTF-16 string"; - } - return result; -} - - redrawQrCode(); From f325bfe638a6c88b0294de31dc225fd391311519 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 18:40:14 +0000 Subject: [PATCH 014/810] Moved {encodeText(), encodeBinary(), encodeSegments()} into class QrCode in Python and JavaScript implementations, for consistency with Java implementation. --- javascript/qrcodegen-demo.js | 2 +- javascript/qrcodegen.js | 168 +++++++++++++++++------------------ python/qrcodegen-demo.py | 20 ++--- python/qrcodegen.py | 146 +++++++++++++++--------------- 4 files changed, 167 insertions(+), 169 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 46c501f..94b911d 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -42,7 +42,7 @@ function redrawQrCode() { var ecl = getInputErrorCorrectionLevel(); var text = document.getElementById("text-input").value; var segs = qrcodegen.QrSegment.makeSegments(text); - var qr = qrcodegen.encodeSegments(segs, ecl); + var qr = qrcodegen.QrCode.encodeSegments(segs, ecl); // Get scale and border var scale = parseInt(document.getElementById("scale-input").value, 10); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 1aae9ee..7254be3 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -27,11 +27,11 @@ /* * Module "qrcodegen". Public members inside this namespace: - * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode - * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode - * - Function encodeSegments(list segs, QrCode.Ecc ecl, - * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Class QrCode: + * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode + * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode + * - Function encodeSegments(list segs, QrCode.Ecc ecl, + * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constructor QrCode(QrCode qr, int mask) * - Constructor QrCode(list bytes, int mask, int version, QrCode.Ecc ecl) * - Method getVersion() -> int @@ -60,87 +60,6 @@ */ var qrcodegen = new function() { - /*---- Public static factory functions for QrCode ----*/ - - /* - * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. - * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode - * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. - */ - this.encodeText = function(text, ecl) { - var segs = this.QrSegment.makeSegments(text); - return this.encodeSegments(segs, ecl); - }; - - - /* - * Returns a QR Code symbol representing the given binary data string at the given error correction level. - * This function always encodes using the binary segment mode, not any text mode. The maximum number of - * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - */ - this.encodeBinary = function(data, ecl) { - var seg = this.QrSegment.makeBytes(data); - return this.encodeSegments([seg], ecl); - }; - - - /* - * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. - * The smallest possible QR Code version within the specified range is automatically chosen for the output. - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. - */ - this.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { - if (minVersion == undefined) minVersion = 1; - if (maxVersion == undefined) maxVersion = 40; - if (mask == undefined) mask = -1; - if (boostEcl == undefined) boostEcl = true; - if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) - throw "Invalid value"; - - // Find the minimal version number to use - var version, dataUsedBits; - for (version = minVersion; ; version++) { - var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = this.QrSegment.getTotalBits(segs, version); - if (dataUsedBits != null && 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 - throw "Data too long"; - } - - // Increase the error correction level while the data still fits in the current version number - [this.QrCode.Ecc.MEDIUM, this.QrCode.Ecc.QUARTILE, this.QrCode.Ecc.HIGH].forEach(function(newEcl) { - if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; - }); - - // Create the data bit string by concatenating all segments - var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; - var bb = new BitBuffer(); - segs.forEach(function(seg) { - bb.appendBits(seg.getMode().getModeBits(), 4); - bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); - bb.appendData(seg); - }); - - // Add terminator and pad up to a byte if applicable - bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); - bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for (var padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - if (bb.bitLength() % 8 != 0) - throw "Assertion error"; - - // Create the QR Code symbol - return new this.QrCode(bb.getBytes(), mask, version, ecl); - }; - - - /*---- QR Code symbol class ----*/ /* @@ -594,6 +513,85 @@ var qrcodegen = new function() { }; + /*---- Public static factory functions for QrCode ----*/ + + /* + * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + */ + this.QrCode.encodeText = function(text, ecl) { + var segs = qrcodegen.QrSegment.makeSegments(text); + return this.encodeSegments(segs, ecl); + }; + + + /* + * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * This function always encodes using the binary segment mode, not any text mode. The maximum number of + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + */ + this.QrCode.encodeBinary = function(data, ecl) { + var seg = qrcodegen.QrSegment.makeBytes(data); + return this.encodeSegments([seg], ecl); + }; + + /* + * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * The smallest possible QR Code version within the specified range is automatically chosen for the output. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and binary) to encode text more efficiently. + * This function is considered to be lower level than simply encoding text or binary data. + */ + this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { + if (minVersion == undefined) minVersion = 1; + if (maxVersion == undefined) maxVersion = 40; + if (mask == undefined) mask = -1; + if (boostEcl == undefined) boostEcl = true; + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) + throw "Invalid value"; + + // Find the minimal version number to use + var version, dataUsedBits; + for (version = minVersion; ; version++) { + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version); + if (dataUsedBits != null && 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 + throw "Data too long"; + } + + // Increase the error correction level while the data still fits in the current version number + [this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { + if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + }); + + // Create the data bit string by concatenating all segments + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + var bb = new BitBuffer(); + segs.forEach(function(seg) { + bb.appendBits(seg.getMode().getModeBits(), 4); + bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); + bb.appendData(seg); + }); + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength())); + bb.appendBits(0, (8 - bb.bitLength() % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (var padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.bitLength() % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return new this(bb.getBytes(), mask, version, ecl); + }; + + /*---- Private static helper functions ----*/ var QrCode = {}; // Private object to assign properties to diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index 81be66d..b8a1542 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -39,7 +39,7 @@ def do_basic_demo(): """Creates a single QR Code, then prints it to the console.""" text = u"Hello, world!" # User-supplied Unicode text errcorlvl = qrcodegen.QrCode.Ecc.LOW # Error correction level - qr = qrcodegen.encode_text(text, errcorlvl) + qr = qrcodegen.QrCode.encode_text(text, errcorlvl) print_qr(qr) @@ -47,27 +47,27 @@ def do_variety_demo(): """Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console.""" # Project Nayuki URL - qr = qrcodegen.encode_text("https://www.nayuki.io/", qrcodegen.QrCode.Ecc.HIGH) + qr = qrcodegen.QrCode.encode_text("https://www.nayuki.io/", qrcodegen.QrCode.Ecc.HIGH) qr = qrcodegen.QrCode(qrcode=qr, mask=3) # Change mask, forcing to mask #3 print_qr(qr) # Numeric mode encoding (3.33 bits per digit) - qr = qrcodegen.encode_text("314159265358979323846264338327950288419716939937510", qrcodegen.QrCode.Ecc.MEDIUM) + qr = qrcodegen.QrCode.encode_text("314159265358979323846264338327950288419716939937510", qrcodegen.QrCode.Ecc.MEDIUM) print_qr(qr) # Alphanumeric mode encoding (5.5 bits per character) - qr = qrcodegen.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen.QrCode.Ecc.HIGH) + qr = qrcodegen.QrCode.encode_text("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", qrcodegen.QrCode.Ecc.HIGH) print_qr(qr) # Unicode text as UTF-8, and different masks - qr = qrcodegen.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", qrcodegen.QrCode.Ecc.QUARTILE) + qr = qrcodegen.QrCode.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", qrcodegen.QrCode.Ecc.QUARTILE) print_qr(qrcodegen.QrCode(qrcode=qr, mask=0)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=1)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=5)) print_qr(qrcodegen.QrCode(qrcode=qr, mask=7)) # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - qr = qrcodegen.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, " + qr = qrcodegen.QrCode.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, " "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " @@ -83,27 +83,27 @@ def do_segment_demo(): # Illustration "silver" silver0 = "THE SQUARE ROOT OF 2 IS 1." silver1 = "41421356237309504880168872420969807856967187537694807317667973799" - qr = qrcodegen.encode_text(silver0 + silver1, qrcodegen.QrCode.Ecc.LOW) + qr = qrcodegen.QrCode.encode_text(silver0 + silver1, qrcodegen.QrCode.Ecc.LOW) print_qr(qr) segs = [ qrcodegen.QrSegment.make_alphanumeric(silver0), qrcodegen.QrSegment.make_numeric(silver1)] - qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) + qr = qrcodegen.QrCode.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) print_qr(qr) # Illustration "golden" golden0 = u"Golden ratio \u03C6 = 1." golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" golden2 = u"......" - qr = qrcodegen.encode_text(golden0 + golden1 + golden2, qrcodegen.QrCode.Ecc.LOW) + qr = qrcodegen.QrCode.encode_text(golden0 + golden1 + golden2, qrcodegen.QrCode.Ecc.LOW) print_qr(qr) segs = [ qrcodegen.QrSegment.make_bytes(golden0.encode("UTF-8")), qrcodegen.QrSegment.make_numeric(golden1), qrcodegen.QrSegment.make_alphanumeric(golden2)] - qr = qrcodegen.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) + qr = qrcodegen.QrCode.encode_segments(segs, qrcodegen.QrCode.Ecc.LOW) print_qr(qr) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index ab7f467..bd44b27 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -27,11 +27,11 @@ import itertools, re, sys """ Public members inside this module "qrcodegen": -- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode -- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode -- Function encode_segments(list segs, QrCode.Ecc ecl, - int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - Class QrCode: +- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode +- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode + - Function encode_segments(list segs, QrCode.Ecc ecl, + int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - Constructor QrCode(QrCode qr, int mask) - Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl) - Method get_version() -> int @@ -59,81 +59,81 @@ Public members inside this module "qrcodegen": """ -# ---- Public static factory functions for QrCode ---- - -def encode_text(text, ecl): - """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. - As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode - code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" - segs = QrSegment.make_segments(text) - return encode_segments(segs, ecl) - - -def encode_binary(data, ecl): - """Returns a QR Code symbol representing the given binary data string at the given error correction level. - This function always encodes using the binary segment mode, not any text mode. The maximum number of - bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.""" - if not isinstance(data, bytes): - raise TypeError("Binary array expected") - return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) - - -def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): - """Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. - The smallest possible QR Code version within the specified range is automatically chosen for the output. - This function allows the user to create a custom sequence of segments that switches - between modes (such as alphanumeric and binary) to encode text more efficiently. - This function is considered to be lower level than simply encoding text or binary data.""" - - if not 1 <= minversion <= maxversion <= 40 or not -1 <= mask <= 7: - raise ValueError("Invalid value") - - # Find the minimal version number to use - for version in range(minversion, maxversion + 1): - datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available - datausedbits = QrSegment.get_total_bits(segs, version) - if datausedbits is not None and 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 - raise ValueError("Data too long") - if datausedbits is None: - raise AssertionError() - - # Increase the error correction level while the data still fits in the current version number - for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): - if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: - ecl = newecl - - # Create the data bit string by concatenating all segments - datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 - bb = _BitBuffer() - for seg in segs: - bb.append_bits(seg.get_mode().get_mode_bits(), 4) - bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version)) - bb.append_all(seg) - - # Add terminator and pad up to a byte if applicable - bb.append_bits(0, min(4, datacapacitybits - bb.bit_length())) - bb.append_bits(0, -bb.bit_length() % 8) - - # Pad with alternate bytes until data capacity is reached - for padbyte in itertools.cycle((0xEC, 0x11)): - if bb.bit_length() >= datacapacitybits: - break - bb.append_bits(padbyte, 8) - assert bb.bit_length() % 8 == 0 - - # Create the QR Code symbol - return QrCode(None, bb.get_bytes(), mask, version, ecl) - - - # ---- QR Code symbol class ---- class QrCode(object): """Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels.""" + # ---- Public static factory functions ---- + + def encode_text(text, ecl): + """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" + segs = QrSegment.make_segments(text) + return QrCode.encode_segments(segs, ecl) + + + def encode_binary(data, ecl): + """Returns a QR Code symbol representing the given binary data string at the given error correction level. + This function always encodes using the binary segment mode, not any text mode. The maximum number of + bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.""" + if not isinstance(data, bytes): + raise TypeError("Binary array expected") + return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) + + + @staticmethod + def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): + """Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + The smallest possible QR Code version within the specified range is automatically chosen for the output. + This function allows the user to create a custom sequence of segments that switches + between modes (such as alphanumeric and binary) to encode text more efficiently. + This function is considered to be lower level than simply encoding text or binary data.""" + + if not 1 <= minversion <= maxversion <= 40 or not -1 <= mask <= 7: + raise ValueError("Invalid value") + + # Find the minimal version number to use + for version in range(minversion, maxversion + 1): + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available + datausedbits = QrSegment.get_total_bits(segs, version) + if datausedbits is not None and 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 + raise ValueError("Data too long") + if datausedbits is None: + raise AssertionError() + + # Increase the error correction level while the data still fits in the current version number + for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): + if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: + ecl = newecl + + # Create the data bit string by concatenating all segments + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 + bb = _BitBuffer() + for seg in segs: + bb.append_bits(seg.get_mode().get_mode_bits(), 4) + bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version)) + bb.append_all(seg) + + # Add terminator and pad up to a byte if applicable + bb.append_bits(0, min(4, datacapacitybits - bb.bit_length())) + bb.append_bits(0, -bb.bit_length() % 8) + + # Pad with alternate bytes until data capacity is reached + for padbyte in itertools.cycle((0xEC, 0x11)): + if bb.bit_length() >= datacapacitybits: + break + bb.append_bits(padbyte, 8) + assert bb.bit_length() % 8 == 0 + + # Create the QR Code symbol + return QrCode(None, bb.get_bytes(), mask, version, ecl) + + # ---- Constructor ---- def __init__(self, qrcode=None, datacodewords=None, mask=None, version=None, errcorlvl=None): From 3878c12a81bbadb1160422cdc39e45fd08208090 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 19:30:53 +0000 Subject: [PATCH 015/810] Added HTML and JavaScript code to support advanced QR Code generation parameters. --- javascript/qrcodegen-demo.js | 23 ++++++++++++++++++++++- javascript/qrcodegen-js-demo.html | 8 ++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 94b911d..d468aaa 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -42,7 +42,11 @@ function redrawQrCode() { var ecl = getInputErrorCorrectionLevel(); var text = document.getElementById("text-input").value; var segs = qrcodegen.QrSegment.makeSegments(text); - var qr = qrcodegen.QrCode.encodeSegments(segs, ecl); + var minVer = parseInt(document.getElementById("version-min-input").value, 10); + var maxVer = parseInt(document.getElementById("version-max-input").value, 10); + var mask = parseInt(document.getElementById("mask-input").value, 10); + var boostEcc = document.getElementById("boost-ecc-input").checked; + var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); // Get scale and border var scale = parseInt(document.getElementById("scale-input").value, 10); @@ -114,4 +118,21 @@ function redrawQrCode() { } +function handleVersionMinMax(which) { + var minElem = document.getElementById("version-min-input"); + var maxElem = document.getElementById("version-max-input"); + var minVal = parseInt(minElem.value, 10); + var maxVal = parseInt(maxElem.value, 10); + minVal = Math.max(Math.min(minVal, 40), 1); + maxVal = Math.max(Math.min(maxVal, 40), 1); + if (which == "min" && minVal > maxVal) + maxVal = minVal; + else if (which == "max" && maxVal < minVal) + minVal = maxVal; + minElem.value = minVal.toString(); + maxElem.value = maxVal.toString(); + redrawQrCode(); +} + + redrawQrCode(); diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index 1fea5e8..e3f0733 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -51,6 +51,14 @@ + + Advanced: + + Version min/max: to
+ Mask pattern: (−1 for automatic)
+ + + Scale: pixels per module From c55751e7bd09a546b8ef64d1c31a3ac0f36939b5 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 19:39:32 +0000 Subject: [PATCH 016/810] Fixed JavaScript code for detecting the need for a larger QR Code version, due to incorrect code simplification in commit 5692e951ddeb. --- javascript/qrcodegen.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 7254be3..270278f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -778,13 +778,14 @@ var qrcodegen = new function() { if (version < 1 || version > 40) throw "Version number out of range"; var result = 0; - segs.forEach(function(seg) { + for (var i = 0; i < segs.length; i++) { + var seg = segs[i]; var ccbits = seg.getMode().numCharCountBits(version); // Fail if segment length value doesn't fit in the length field's bit-width if (seg.getNumChars() >= (1 << ccbits)) return null; result += 4 + ccbits + seg.getBits().length; - }); + } return result; }; From 1d272114978663547157a9edfbdecbff79fc04d2 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 19:40:21 +0000 Subject: [PATCH 017/810] Updated JavaScript demo code to show no canvas if QR Code generation failed. --- javascript/qrcodegen-demo.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index d468aaa..83ce9d3 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -38,6 +38,11 @@ function redrawQrCode() { return qrcodegen.QrCode.Ecc.LOW; } + // Reset canvas in case of early termination + var canvas = document.getElementById("qrcode-canvas"); + canvas.width = 0; + canvas.height = 0; + // Get form inputs and compute QR Code var ecl = getInputErrorCorrectionLevel(); var text = document.getElementById("text-input").value; @@ -55,12 +60,9 @@ function redrawQrCode() { return; // Draw QR Code onto canvas - var canvas = document.getElementById("qrcode-canvas"); var width = (qr.getSize() + border * 2) * scale; - if (canvas.width != width) { - canvas.width = width; - canvas.height = width; - } + canvas.width = width; + canvas.height = width; var ctx = canvas.getContext("2d"); for (var y = -border; y < qr.getSize() + border; y++) { for (var x = -border; x < qr.getSize() + border; x++) { From fc705efac35303cd281f41d36a13757b6d9e1486 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 20:44:02 +0000 Subject: [PATCH 018/810] Added white background rectangle to SVG XML code output, in all language versions. --- cpp/QrCode.cpp | 1 + java/io/nayuki/qrcodegen/QrCode.java | 1 + javascript/qrcodegen.js | 1 + python/qrcodegen.py | 1 + 4 files changed, 4 insertions(+) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 866c33a..3e3e5ab 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -171,6 +171,7 @@ std::string qrcodegen::QrCode::toSvgString(int border) const { sb << "\n"; sb << "\n"; + sb << "\t\n" sb << "\t\n"); sb.append("\n"); sb.append(String.format("\n", size + border * 2)); + sb.append("\t\n"); sb.append("\t\n"; result += "\n"; result += "\n"; + result += "\t\n"; result += "\t + """.format(self.size + border * 2, " ".join(parts)) From 549f4bb38b999a6fbcf9517ecb0a9d9452349f5d Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 18 Apr 2016 20:48:40 +0000 Subject: [PATCH 019/810] Added HTML elements and JavaScript demo code to support QR Code output as an SVG vector image. --- javascript/qrcodegen-demo.js | 61 ++++++++++++++++++++++--------- javascript/qrcodegen-js-demo.html | 21 ++++++++++- 2 files changed, 62 insertions(+), 20 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 83ce9d3..b3a65de 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -26,6 +26,26 @@ function redrawQrCode() { + // Show/hide rows based on bitmap/vector image output + var bitmapOutput = document.getElementById("output-format-bitmap").checked; + var scaleRow = document.getElementById("scale-row"); + var svgXmlRow = document.getElementById("svg-xml-row"); + if (bitmapOutput) { + scaleRow.style.removeProperty("display"); + svgXmlRow.style.display = "none"; + } else { + scaleRow.style.display = "none"; + svgXmlRow.style.removeProperty("display"); + } + var svgXml = document.getElementById("svg-xml-output"); + svgXml.value = ""; + + // Reset output images in case of early termination + var canvas = document.getElementById("qrcode-canvas"); + var svg = document.getElementById("qrcode-svg"); + canvas.style.display = "none"; + svg.style.display = "none"; + // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. function getInputErrorCorrectionLevel() { if (document.getElementById("errcorlvl-medium").checked) @@ -38,11 +58,6 @@ function redrawQrCode() { return qrcodegen.QrCode.Ecc.LOW; } - // Reset canvas in case of early termination - var canvas = document.getElementById("qrcode-canvas"); - canvas.width = 0; - canvas.height = 0; - // Get form inputs and compute QR Code var ecl = getInputErrorCorrectionLevel(); var text = document.getElementById("text-input").value; @@ -53,22 +68,32 @@ function redrawQrCode() { var boostEcc = document.getElementById("boost-ecc-input").checked; var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); - // Get scale and border - var scale = parseInt(document.getElementById("scale-input").value, 10); + // Draw image output var border = parseInt(document.getElementById("border-input").value, 10); - if (scale <= 0 || border < 0 || scale > 30 || border > 100) + if (border < 0 || border > 100) return; - - // Draw QR Code onto canvas - var width = (qr.getSize() + border * 2) * scale; - canvas.width = width; - canvas.height = width; - var ctx = canvas.getContext("2d"); - for (var y = -border; y < qr.getSize() + border; y++) { - for (var x = -border; x < qr.getSize() + border; x++) { - ctx.fillStyle = qr.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; - ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + if (bitmapOutput) { + var scale = parseInt(document.getElementById("scale-input").value, 10); + if (scale <= 0 || scale > 30) + return; + // Draw QR Code onto canvas + var width = (qr.getSize() + border * 2) * scale; + canvas.width = width; + canvas.height = width; + var ctx = canvas.getContext("2d"); + for (var y = -border; y < qr.getSize() + border; y++) { + for (var x = -border; x < qr.getSize() + border; x++) { + ctx.fillStyle = qr.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } } + canvas.style.removeProperty("display"); + } else { + var code = qr.toSvgString(border); + svg.setAttribute("viewBox", / viewBox="([^"]*)"/.exec(code)[1]); + svg.querySelector("path").setAttribute("d", / d="([^"]*)"/.exec(code)[1]); + svg.style.removeProperty("display"); + svgXml.value = qr.toSvgString(border); } diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index e3f0733..36d4849 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -60,13 +60,20 @@
- Scale: - + Output format: + +
Border:
+
+ Scale: + +
Statistics: @@ -75,6 +82,16 @@ QR Code: + + + + + +
+
+ SVG XML code: + +
From a8d8b6ddbe40e99d84f574f7d957da1f3d30d71f Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Tue, 19 Apr 2016 00:29:24 +0000 Subject: [PATCH 020/810] Refactored JavaScript code to replace get*() methods with read-only fields created by Object.defineProperty(). --- javascript/qrcodegen-demo.js | 16 +++--- javascript/qrcodegen.js | 101 +++++++++++++++++------------------ 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index b3a65de..ee07226 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -77,12 +77,12 @@ function redrawQrCode() { if (scale <= 0 || scale > 30) return; // Draw QR Code onto canvas - var width = (qr.getSize() + border * 2) * scale; + var width = (qr.size + border * 2) * scale; canvas.width = width; canvas.height = width; var ctx = canvas.getContext("2d"); - for (var y = -border; y < qr.getSize() + border; y++) { - for (var x = -border; x < qr.getSize() + border; x++) { + for (var y = -border; y < qr.size + border; y++) { + for (var x = -border; x < qr.size + border; x++) { ctx.fillStyle = qr.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); } @@ -102,7 +102,7 @@ function redrawQrCode() { if (segs.length == 0) return "none"; else if (segs.length == 1) { - var mode = segs[0].getMode(); + var mode = segs[0].mode; var Mode = qrcodegen.QrSegment.Mode; if (mode == Mode.NUMERIC ) return "numeric"; if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; @@ -132,12 +132,12 @@ function redrawQrCode() { } // Show the QR Code symbol's statistics as a string - var stats = "QR Code version = " + qr.getVersion() + ", "; - stats += "mask pattern = " + qr.getMask() + ", "; + var stats = "QR Code version = " + qr.version + ", "; + stats += "mask pattern = " + qr.mask + ", "; stats += "character count = " + countUnicodeChars(text) + ",\n"; stats += "encoding mode = " + describeSegments(segs) + ", "; - stats += "error correction = level " + "LMQH".charAt(qr.getErrorCorrectionLevel().ordinal) + ", "; - stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.getVersion()) + "."; + stats += "error correction = level " + "LMQH".charAt(qr.errorCorrectionLevel.ordinal) + ", "; + stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.version) + "."; var elem = document.getElementById("statistics-output"); while (elem.firstChild != null) elem.removeChild(elem.firstChild); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 57317c1..5dbafd8 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -34,10 +34,8 @@ * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constructor QrCode(QrCode qr, int mask) * - Constructor QrCode(list bytes, int mask, int version, QrCode.Ecc ecl) - * - Method getVersion() -> int - * - Method getSize() -> int - * - Method getErrorCorrectionLevel() -> QrCode.Ecc - * - Method getMask() -> int + * - Fields int version, size, mask + * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> int * - Method isFunctionModule(int x, int y) -> bool * - Method toSvgString(int border) -> str @@ -50,12 +48,12 @@ * - Function makeAlphanumeric(str data) -> QrSegment * - Function makeSegments(str text) -> list * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) - * - Method getMode() -> QrSegment.Mode - * - Method getNumChars() -> int + * - Field QrSegment.Mode mode + * - Field int numChars * - Method getBits() -> list * - Enum Mode: * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI - * - Method getModeBits() -> int + * - Field int modeBits * - Method numCharCountBits(int ver) -> int */ var qrcodegen = new function() { @@ -90,8 +88,8 @@ var qrcodegen = new function() { if (version < 1 || version > 40) throw "Version value out of range"; } else if (initData instanceof qrcodegen.QrCode) { - version = initData.getVersion(); - errCorLvl = initData.getErrorCorrectionLevel(); + version = initData.version; + errCorLvl = initData.errorCorrectionLevel; } else throw "Invalid initial data"; var size = version * 4 + 17; @@ -120,7 +118,7 @@ var qrcodegen = new function() { isFunction[y][x] = initData.isFunctionModule(x, y); } } - applyMask(initData.getMask()); // Undo old mask + applyMask(initData.mask); // Undo old mask } else throw "Invalid initial data"; @@ -143,32 +141,27 @@ var qrcodegen = new function() { drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask + // Define read-only properties + + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + Object.defineProperty(this, "version", {value:version}); + + // The width and height of this QR Code symbol, measured in modules. + // Always equal to version * 4 + 17, in the range 21 to 177. + Object.defineProperty(this, "size", {value:size}); + + // The error correction level used in this QR Code symbol. + + Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); + + // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if the constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + Object.defineProperty(this, "mask", {value:mask}); + /*-- Accessor methods --*/ - // Returns this QR Code symbol's version number, which is always between 1 and 40 (inclusive). - this.getVersion = function() { - return version; - }; - - // Returns the width and height of this QR Code symbol, measured in modules. - // Always equal to version * 4 + 17, in the range 21 to 177. - this.getSize = function() { - return size; - }; - - // Returns the error correction level used in this QR Code symbol. - this.getErrorCorrectionLevel = function() { - return errCorLvl; - }; - - // Returns the mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). - // Note that even if a constructor was called with automatic masking requested - // (mask = -1), the resulting object will still have a mask value between 0 and 7. - this.getMask = function() { - return mask; - }; - // Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top // left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. this.getModule = function(x, y) { @@ -573,8 +566,8 @@ var qrcodegen = new function() { var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; var bb = new BitBuffer(); segs.forEach(function(seg) { - bb.appendBits(seg.getMode().getModeBits(), 4); - bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); + bb.appendBits(seg.mode.modeBits, 4); + bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); bb.appendData(seg); }); @@ -676,16 +669,22 @@ var qrcodegen = new function() { ]; + // Private constructor for the enum. + function Ecc(ord, fb) { + Object.defineProperty(this, "ordinal", {value:ord}); + Object.defineProperty(this, "formatBits", {value:fb}); + } + /* * A public helper enumeration that represents the error correction level used in a QR Code symbol. * The fields 'ordinal' and 'formatBits' are in the range 0 to 3 (unsigned 2-bit integer). */ this.QrCode.Ecc = { // Constants declared in ascending order of error protection - LOW : {ordinal: 0, formatBits: 1}, - MEDIUM : {ordinal: 1, formatBits: 0}, - QUARTILE: {ordinal: 2, formatBits: 3}, - HIGH : {ordinal: 3, formatBits: 2}, + LOW : new Ecc(0, 1), + MEDIUM : new Ecc(1, 0), + QUARTILE: new Ecc(2, 3), + HIGH : new Ecc(3, 2), }; @@ -704,13 +703,13 @@ var qrcodegen = new function() { if (numChars < 0 || !(mode instanceof Mode)) throw "Invalid argument"; - /*-- Accessor methods --*/ - this.getMode = function() { - return mode; - }; - this.getNumChars = function() { - return numChars; - }; + // The mode indicator for this segment. + Object.defineProperty(this, "mode", {value:mode}); + + // The length of this segment's unencoded data, measured in characters. Always zero or positive. + Object.defineProperty(this, "numChars", {value:numChars}); + + // Returns a copy of all bits, which is an array of 0s and 1s. this.getBits = function() { return bitData.slice(); }; @@ -781,9 +780,9 @@ var qrcodegen = new function() { var result = 0; for (var i = 0; i < segs.length; i++) { var seg = segs[i]; - var ccbits = seg.getMode().numCharCountBits(version); + var ccbits = seg.mode.numCharCountBits(version); // Fail if segment length value doesn't fit in the length field's bit-width - if (seg.getNumChars() >= (1 << ccbits)) + if (seg.numChars >= (1 << ccbits)) return null; result += 4 + ccbits + seg.getBits().length; } @@ -821,10 +820,8 @@ var qrcodegen = new function() { // Private constructor for the enum. function Mode(mode, ccbits) { - // Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. - this.getModeBits = function() { - return mode; - }; + // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + Object.defineProperty(this, "modeBits", {value:mode}); // Returns the bit width of the segment character count field for this mode object at the given version number. this.numCharCountBits = function(ver) { From 1df147943ed63995c488934e7da6159ecef6d4f4 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:19:57 +0000 Subject: [PATCH 021/810] Tweaked section comments and blank lines for better uniformity across all language versions. --- cpp/QrCode.hpp | 4 +-- cpp/QrSegment.hpp | 16 +++++++---- java/io/nayuki/qrcodegen/QrCode.java | 4 +-- java/io/nayuki/qrcodegen/QrSegment.java | 8 ++++-- javascript/qrcodegen.js | 37 +++++++++++++++++-------- python/qrcodegen.py | 12 ++++---- 6 files changed, 51 insertions(+), 30 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 7302fd3..bcc822e 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -225,7 +225,7 @@ private: - /*---- Static helper functions ----*/ + /*---- Private static helper functions ----*/ private: // Returns a set of positions of the alignment patterns in ascending order. These positions are @@ -246,7 +246,7 @@ private: static int getNumDataCodewords(int ver, const Ecc &ecl); - /*---- Tables of constants ----*/ + /*---- Private tables of constants ----*/ private: // For use in getPenaltyScore(), when evaluating which mask is best. diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 54682ee..79274c4 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -47,8 +47,10 @@ class QrSegment final { */ public: class Mode final { - // Constants. + + /*-- Constants --*/ public: + static const Mode NUMERIC; static const Mode ALPHANUMERIC; static const Mode BYTE; @@ -65,23 +67,25 @@ public: int numBitsCharCount[3]; - // Constructor. + /*-- Constructor --*/ + private: Mode(int mode, int cc0, int cc1, int cc2); /*-- Method --*/ - public: /* * Returns the bit width of the segment character count field for this mode object at the given version number. */ + public: int numCharCountBits(int ver) const; + }; - /*---- Static factory functions ----*/ + /*---- Public static factory functions ----*/ public: /* @@ -110,7 +114,7 @@ public: static std::vector makeSegments(const char *text); - /*---- Static helper functions ----*/ + /*---- Public static helper functions ----*/ public: /* @@ -155,7 +159,7 @@ public: static int getTotalBits(const std::vector &segs, int version); - /*---- Constant ----*/ + /*---- Private constant ----*/ private: static const int8_t ALPHANUMERIC_ENCODING_TABLE[59]; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 663a4aa..fd62b68 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -652,7 +652,7 @@ public final class QrCode { - /*---- Static helper functions ----*/ + /*---- Private static helper functions ----*/ // Returns a set of positions of the alignment patterns in ascending order. These positions are // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). @@ -715,7 +715,7 @@ public final class QrCode { } - /*---- Tables of constants ----*/ + /*---- Private tables of constants ----*/ // For use in getPenaltyScore(), when evaluating which mask is best. private static final int PENALTY_N1 = 3; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 1315a48..4d441da 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -230,7 +230,9 @@ public final class QrSegment { * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. */ public enum Mode { - // Constants. + + /*-- Constants --*/ + NUMERIC (0x1, 10, 12, 14), ALPHANUMERIC(0x2, 9, 11, 13), BYTE (0x4, 8, 16, 16), @@ -245,7 +247,8 @@ public final class QrSegment { private final int[] numBitsCharCount; - // Constructor. + /*-- Constructor --*/ + private Mode(int mode, int... ccbits) { this.modeBits = mode; numBitsCharCount = ccbits; @@ -266,6 +269,7 @@ public final class QrSegment { else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; else throw new IllegalArgumentException("Version number out of range"); } + } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 5dbafd8..4e36ab7 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -79,7 +79,7 @@ var qrcodegen = new function() { */ this.QrCode = function(initData, mask, version, errCorLvl) { - /*-- Constructor --*/ + /*---- Constructor ----*/ // Handle simple scalar fields if (mask < -1 || mask > 7) @@ -141,7 +141,8 @@ var qrcodegen = new function() { drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask - // Define read-only properties + + /*---- Read-only instance properties ----*/ // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). Object.defineProperty(this, "version", {value:version}); @@ -160,7 +161,7 @@ var qrcodegen = new function() { Object.defineProperty(this, "mask", {value:mask}); - /*-- Accessor methods --*/ + /*---- Accessor methods ----*/ // Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top // left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. @@ -181,7 +182,7 @@ var qrcodegen = new function() { }; - /*-- Public instance methods --*/ + /*---- Public instance methods ----*/ // Based on the given number of border modules to add as padding, this returns a // string whose contents represents an SVG XML file that depicts this QR Code symbol. @@ -211,7 +212,7 @@ var qrcodegen = new function() { }; - /*-- Private helper methods for constructor: Drawing function modules --*/ + /*---- Private helper methods for constructor: Drawing function modules ----*/ function drawFunctionPatterns() { // Draw the horizontal and vertical timing patterns @@ -530,6 +531,7 @@ var qrcodegen = new function() { return this.encodeSegments([seg], ecl); }; + /* * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. * The smallest possible QR Code version within the specified range is automatically chosen for the output. @@ -586,10 +588,11 @@ var qrcodegen = new function() { }; - /*---- Private static helper functions ----*/ + /*---- Private static helper functions QrCode ----*/ var QrCode = {}; // Private object to assign properties to + // Returns a sequence of positions of the alignment patterns in ascending order. These positions are // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). // This stateless pure function could be implemented as table of 40 variable-length lists of integers. @@ -616,6 +619,7 @@ var qrcodegen = new function() { } }; + // Returns the number of raw data modules (bits) available at the given version number. // These data modules are used for both user data codewords and error correction codewords. // This stateless pure function could be implemented as a 40-entry lookup table. @@ -632,6 +636,7 @@ var qrcodegen = new function() { return result; }; + // 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. @@ -642,7 +647,7 @@ var qrcodegen = new function() { }; - /*---- Tables of constants ----*/ + /*---- Private tables of constants for QrCode ----*/ // For use in getPenaltyScore(), when evaluating which mask is best. QrCode.PENALTY_N1 = 3; @@ -669,12 +674,14 @@ var qrcodegen = new function() { ]; - // Private constructor for the enum. + /*---- Public helper enumeration ----*/ + function Ecc(ord, fb) { Object.defineProperty(this, "ordinal", {value:ord}); Object.defineProperty(this, "formatBits", {value:fb}); } + /* * A public helper enumeration that represents the error correction level used in a QR Code symbol. * The fields 'ordinal' and 'formatBits' are in the range 0 to 3 (unsigned 2-bit integer). @@ -715,7 +722,8 @@ var qrcodegen = new function() { }; }; - /*-- Public static factory functions --*/ + + /*---- Public static factory functions for QrSegment ----*/ // Returns a segment representing the given binary data encoded in byte mode. this.QrSegment.makeBytes = function(data) { @@ -726,6 +734,7 @@ var qrcodegen = new function() { return new this(this.Mode.BYTE, data.length, bb.getBits()); }; + // Returns a segment representing the given string of decimal digits encoded in numeric mode. this.QrSegment.makeNumeric = function(digits) { if (!QrSegment.NUMERIC_REGEX.test(digits)) @@ -757,6 +766,7 @@ var qrcodegen = new function() { return new this(this.Mode.ALPHANUMERIC, text.length, bb.getBits()); }; + /* * Returns a new mutable list of zero or more segments to represent the given Unicode text string. * The result may use various segment modes and switch modes to optimize the length of the bit stream. @@ -773,6 +783,7 @@ var qrcodegen = new function() { return [this.makeBytes(toUtf8ByteArray(text))]; }; + // Package-private helper function. this.QrSegment.getTotalBits = function(segs, version) { if (version < 1 || version > 40) @@ -789,7 +800,8 @@ var qrcodegen = new function() { return result; }; - /*-- Constants --*/ + + /*---- Constants for QrSegment ----*/ var QrSegment = {}; // Private object to assign properties to @@ -807,6 +819,8 @@ var qrcodegen = new function() { ]; + /*---- Public helper enumeration ----*/ + /* * A public helper enumeration that represents the mode field of a segment. * Objects are immutable. Provides methods to retrieve closely related values. @@ -818,6 +832,7 @@ var qrcodegen = new function() { KANJI : new Mode(0x8, [ 8, 10, 12]), }; + // Private constructor for the enum. function Mode(mode, ccbits) { // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. @@ -929,8 +944,6 @@ var qrcodegen = new function() { // Array of bits; each item is the integer 0 or 1 var bitData = []; - /*-- Methods --*/ - // Returns the number of bits in the buffer, which is a non-negative value. this.bitLength = function() { return bitData.length; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 9797d81..284f522 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -541,7 +541,7 @@ class QrCode(object): return QrCode._get_num_raw_data_modules(ver) // 8 - QrCode._NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal][ver] - # ---- Tables of constants ---- + # ---- Private tables of constants ---- # For use in getPenaltyScore(), when evaluating which mask is best. _PENALTY_N1 = 3 @@ -604,7 +604,7 @@ class QrSegment(object): Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. Any segment longer than this is meaningless for the purpose of generating QR Codes.""" - # -- Public static factory functions -- + # ---- Public static factory functions ---- @staticmethod def make_bytes(data): @@ -665,7 +665,7 @@ class QrSegment(object): return [QrSegment.make_bytes(text.encode("UTF-8"))] - # -- Constructor -- + # ---- Constructor ---- def __init__(self, mode, numch, bitdata): if numch < 0 or not isinstance(mode, QrSegment.Mode): @@ -675,7 +675,7 @@ class QrSegment(object): self._bitdata = list(bitdata) # Defensive copy - # -- Accessor methods -- + # ---- Accessor methods ---- def get_mode(self): return self._mode @@ -702,7 +702,7 @@ class QrSegment(object): return result - # -- Constants -- + # ---- Constants ---- # Can test whether a string is encodable in numeric mode (such as by using make_numeric()) NUMERIC_REGEX = re.compile("[0-9]*$") @@ -714,7 +714,7 @@ class QrSegment(object): ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} - # -- Helper enumeration -- + # ---- Public helper enumeration ---- class Mode(object): """The mode field of a segment. Immutable. Provides methods to retrieve closely related values.""" From be316029aa21fa7775a48ceb84957d85134763b1 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:27:41 +0000 Subject: [PATCH 022/810] Added and updated code comments for members (mostly) and within functions (a few), in all languages. --- cpp/QrCodeGeneratorDemo.cpp | 1 + cpp/QrSegment.hpp | 1 + java/io/nayuki/qrcodegen/QrSegment.java | 1 + javascript/qrcodegen.js | 41 +++++++++++++++---------- python/qrcodegen.py | 20 ++++++------ 5 files changed, 39 insertions(+), 25 deletions(-) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index f003a0c..569d205 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -38,6 +38,7 @@ static void printQr(const qrcodegen::QrCode &qr); +// The main application program. int main(int argc, char **argv) { doBasicDemo(); doVarietyDemo(); diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 79274c4..cc8db37 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -162,6 +162,7 @@ public: /*---- Private constant ----*/ private: + /* Maps shifted ASCII codes to alphanumeric mode character codes. */ static const int8_t ALPHANUMERIC_ENCODING_TABLE[59]; }; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 4d441da..c41792a 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -215,6 +215,7 @@ public final class QrSegment { /** Can test whether a string is encodable in alphanumeric mode (such as by using {@link #makeAlphanumeric(String)}). */ public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); + /** Maps shifted ASCII codes to alphanumeric mode character codes. */ private static final byte[] ALPHANUMERIC_ENCODING_TABLE = { // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 4e36ab7..0b3c9f6 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -26,14 +26,14 @@ /* - * Module "qrcodegen". Public members inside this namespace: + * Module "qrcodegen", public members: * - Class QrCode: * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode * - Function encodeSegments(list segs, QrCode.Ecc ecl, * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constructor QrCode(QrCode qr, int mask) - * - Constructor QrCode(list bytes, int mask, int version, QrCode.Ecc ecl) + * - Constructor QrCode(list datacodewords, int mask, int version, QrCode.Ecc ecl) * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> int @@ -67,10 +67,10 @@ var qrcodegen = new function() { * from 1 to 40, all 4 error correction levels. * * This constructor can be called in one of two ways: - * - new QrCode(bytes, mask, version, errCorLvl): + * - new QrCode(datacodewords, mask, version, errCorLvl): * Creates a new QR Code symbol with the given version number, error correction level, binary data array, * and mask number. This cumbersome constructor can be invoked directly by the user, but is considered - * to be even lower level than qrcodegen.encodeSegments(). + * to be even lower level than QrCode.encodeSegments(). * - new QrCode(qr, mask): * Creates a new QR Code symbol based on the given existing object, but with a potentially different * mask pattern. The version, error correction level, codewords, etc. of the newly created object are @@ -81,7 +81,7 @@ var qrcodegen = new function() { /*---- Constructor ----*/ - // Handle simple scalar fields + // Check arguments and handle simple scalar fields if (mask < -1 || mask > 7) throw "Mask value out of range"; if (initData instanceof Array) { @@ -590,7 +590,7 @@ var qrcodegen = new function() { /*---- Private static helper functions QrCode ----*/ - var QrCode = {}; // Private object to assign properties to + var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'. // Returns a sequence of positions of the alignment patterns in ascending order. These positions are @@ -676,15 +676,17 @@ var qrcodegen = new function() { /*---- Public helper enumeration ----*/ + // Private constructor. function Ecc(ord, fb) { + // (Public) In the range 0 to 3 (unsigned 2-bit integer) Object.defineProperty(this, "ordinal", {value:ord}); + Object.defineProperty(this, "formatBits", {value:fb}); } /* - * A public helper enumeration that represents the error correction level used in a QR Code symbol. - * The fields 'ordinal' and 'formatBits' are in the range 0 to 3 (unsigned 2-bit integer). + * Represents the error correction level used in a QR Code symbol. */ this.QrCode.Ecc = { // Constants declared in ascending order of error protection @@ -725,7 +727,9 @@ var qrcodegen = new function() { /*---- Public static factory functions for QrSegment ----*/ - // Returns a segment representing the given binary data encoded in byte mode. + /* + * Returns a segment representing the given binary data encoded in byte mode. + */ this.QrSegment.makeBytes = function(data) { var bb = new BitBuffer(); data.forEach(function(b) { @@ -735,7 +739,9 @@ var qrcodegen = new function() { }; - // Returns a segment representing the given string of decimal digits encoded in numeric mode. + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ this.QrSegment.makeNumeric = function(digits) { if (!QrSegment.NUMERIC_REGEX.test(digits)) throw "String contains non-numeric characters"; @@ -749,8 +755,11 @@ var qrcodegen = new function() { return new this(this.Mode.NUMERIC, digits.length, bb.getBits()); }; - // Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: - // 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: + * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ this.QrSegment.makeAlphanumeric = function(text) { if (!QrSegment.ALPHANUMERIC_REGEX.test(text)) throw "String contains unencodable characters in alphanumeric mode"; @@ -803,7 +812,7 @@ var qrcodegen = new function() { /*---- Constants for QrSegment ----*/ - var QrSegment = {}; // Private object to assign properties to + var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'. // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). QrSegment.NUMERIC_REGEX = /^[0-9]*$/; @@ -811,6 +820,7 @@ var qrcodegen = new function() { // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; + // Maps shifted ASCII codes to alphanumeric mode character codes. QrSegment.ALPHANUMERIC_ENCODING_TABLE = [ // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 @@ -822,8 +832,7 @@ var qrcodegen = new function() { /*---- Public helper enumeration ----*/ /* - * A public helper enumeration that represents the mode field of a segment. - * Objects are immutable. Provides methods to retrieve closely related values. + * Represents the mode field of a segment. Immutable. */ this.QrSegment.Mode = { // Constants NUMERIC : new Mode(0x1, [10, 12, 14]), @@ -833,7 +842,7 @@ var qrcodegen = new function() { }; - // Private constructor for the enum. + // Private constructor. function Mode(mode, ccbits) { // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. Object.defineProperty(this, "modeBits", {value:mode}); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 284f522..73ae851 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -26,14 +26,14 @@ import itertools, re, sys """ -Public members inside this module "qrcodegen": +This module "qrcodegen", public members: - Class QrCode: -- Function encode_text(str text, QrCode.Ecc ecl) -> QrCode -- Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode + - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode + - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode - Function encode_segments(list segs, QrCode.Ecc ecl, int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - Constructor QrCode(QrCode qr, int mask) - - Constructor QrCode(bytes bytes, int mask, int version, QrCode.Ecc ecl) + - Constructor QrCode(bytes datacodewords, int mask, int version, QrCode.Ecc ecl) - Method get_version() -> int - Method get_size() -> int - Method get_error_correction_level() -> QrCode.Ecc @@ -141,14 +141,14 @@ class QrCode(object): - QrCode(datacodewords=list, mask=int, version=int, errcorlvl=QrCode.Ecc): Creates a new QR Code symbol with the given version number, error correction level, binary data array, and mask number. This cumbersome constructor can be invoked directly by the user, but is considered - to be even lower level than qrcodegen.encode_segments(). + to be even lower level than QrCode.encode_segments(). - QrCode(qrcode=QrCode, mask=int): Creates a new QR Code symbol based on the given existing object, but with a potentially different mask pattern. The version, error correction level, codewords, etc. of the newly created object are all identical to the argument object; only the mask may differ. In both cases, mask = -1 is for automatic choice or 0 to 7 for fixed choice.""" - # Handle simple scalar fields + # Check arguments and handle simple scalar fields if not -1 <= mask <= 7: raise ValueError("Mask value out of range") if datacodewords is not None and qrcode is None: @@ -586,7 +586,7 @@ class QrCode(object): self.ordinal = i # In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # In the range 0 to 3 (unsigned 2-bit integer) - # Create the class constants outside the class + # Public constants. Create them outside the class. Ecc.LOW = Ecc(0, 1) Ecc.MEDIUM = Ecc(1, 0) Ecc.QUARTILE = Ecc(2, 3) @@ -717,13 +717,15 @@ class QrSegment(object): # ---- Public helper enumeration ---- class Mode(object): - """The mode field of a segment. Immutable. Provides methods to retrieve closely related values.""" + """The mode field of a segment. Immutable.""" + # Private constructor def __init__(self, modebits, charcounts): self._modebits = modebits self._charcounts = charcounts def get_mode_bits(self): + """Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object.""" return self._modebits def num_char_count_bits(self, ver): @@ -733,7 +735,7 @@ class QrSegment(object): elif 27 <= ver <= 40: return self._charcounts[2] else: raise ValueError("Version number out of range") - # Create the class constants outside the class + # Public constants. Create them outside the class. Mode.NUMERIC = Mode(0x1, (10, 12, 14)) Mode.ALPHANUMERIC = Mode(0x2, ( 9, 11, 13)) Mode.BYTE = Mode(0x4, ( 8, 16, 16)) From f63f890235727b517499252d3374ee95e5ba5a2b Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:40:49 +0000 Subject: [PATCH 023/810] Changed ~4 members to package-private in all language versions, updating code in Java version but only updating comments in JavaScript, Python, C++ versions. --- cpp/QrCode.hpp | 2 +- cpp/QrSegment.hpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 2 +- java/io/nayuki/qrcodegen/QrSegment.java | 4 ++-- javascript/qrcodegen.js | 15 +++++++-------- python/qrcodegen.py | 8 ++++---- 6 files changed, 17 insertions(+), 18 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index bcc822e..6e57c51 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -54,7 +54,7 @@ public: // Fields. public: const int ordinal; // In the range 0 to 3 (unsigned 2-bit integer). - const int formatBits; // In the range 0 to 3 (unsigned 2-bit integer). + const int formatBits; // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). // Constructor. private: diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index cc8db37..741a6dc 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -59,7 +59,7 @@ public: /*-- Fields --*/ - /* An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ + /* (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ public: const int modeBits; @@ -76,7 +76,7 @@ public: /*-- Method --*/ /* - * Returns the bit width of the segment character count field for this mode object at the given version number. + * (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. */ public: int numCharCountBits(int ver) const; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index fd62b68..e610ce3 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -755,7 +755,7 @@ public final class QrCode { LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); // In the range 0 to 3 (unsigned 2-bit integer). - public final int formatBits; + final int formatBits; // Constructor. private Ecc(int fb) { diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index c41792a..4fa7766 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -243,7 +243,7 @@ public final class QrSegment { /*-- Fields --*/ /** An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ - public final int modeBits; + final int modeBits; private final int[] numBitsCharCount; @@ -264,7 +264,7 @@ public final class QrSegment { * @return the number of bits for the character count, which is between 8 to 16, inclusive * @throws IllegalArgumentException if the version number is out of range */ - public int numCharCountBits(int ver) { + int numCharCountBits(int ver) { if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0b3c9f6..c7763cf 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -37,11 +37,10 @@ * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> int - * - Method isFunctionModule(int x, int y) -> bool * - Method toSvgString(int border) -> str * - Enum Ecc: * - Constants LOW, MEDIUM, QUARTILE, HIGH - * - Fields int ordinal, formatBits + * - Field int ordinal * - Class QrSegment: * - Function makeBytes(list data) -> QrSegment * - Function makeNumeric(str data) -> QrSegment @@ -53,8 +52,6 @@ * - Method getBits() -> list * - Enum Mode: * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI - * - Field int modeBits - * - Method numCharCountBits(int ver) -> int */ var qrcodegen = new function() { @@ -172,8 +169,9 @@ var qrcodegen = new function() { return 0; // Infinite white border }; - // Tests whether the module at the given coordinates is a function module (true) or not (false). The top left - // corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. + // (Package-private) Tests whether the module at the given coordinates is a function module (true) or not (false). + // The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. + // The JavaScript version of this library has this method because it is impossible to access private variables of another object. this.isFunctionModule = function(x, y) { if (0 <= x && x < size && 0 <= y && y < size) return isFunction[y][x]; @@ -681,6 +679,7 @@ var qrcodegen = new function() { // (Public) In the range 0 to 3 (unsigned 2-bit integer) Object.defineProperty(this, "ordinal", {value:ord}); + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer) Object.defineProperty(this, "formatBits", {value:fb}); } @@ -844,10 +843,10 @@ var qrcodegen = new function() { // Private constructor. function Mode(mode, ccbits) { - // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + // (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. Object.defineProperty(this, "modeBits", {value:mode}); - // Returns the bit width of the segment character count field for this mode object at the given version number. + // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. this.numCharCountBits = function(ver) { if ( 1 <= ver && ver <= 9) return ccbits[0]; else if (10 <= ver && ver <= 26) return ccbits[1]; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 73ae851..cc31111 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -42,7 +42,7 @@ This module "qrcodegen", public members: - Method to_svg_str(int border) -> str - Enum Ecc: - Constants LOW, MEDIUM, QUARTILE, HIGH - - Fields int ordinal, formatbits + - Field int ordinal - Class QrSegment: - Function make_bytes(bytes data) -> QrSegment - Function make_numeric(str digits) -> QrSegment @@ -54,8 +54,6 @@ This module "qrcodegen", public members: - Method get_bits() -> list - Enum Mode: - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI - - Method get_mode_bits() -> int - - Method num_char_count_bits(int ver) -> int """ @@ -584,7 +582,7 @@ class QrCode(object): # Private constructor def __init__(self, i, fb): self.ordinal = i # In the range 0 to 3 (unsigned 2-bit integer) - self.formatbits = fb # In the range 0 to 3 (unsigned 2-bit integer) + self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) # Public constants. Create them outside the class. Ecc.LOW = Ecc(0, 1) @@ -724,10 +722,12 @@ class QrSegment(object): self._modebits = modebits self._charcounts = charcounts + # Package-private method def get_mode_bits(self): """Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object.""" return self._modebits + # Package-private method def num_char_count_bits(self, ver): """Returns the bit width of the segment character count field for this mode object at the given version number.""" if 1 <= ver <= 9: return self._charcounts[0] From a4cccee0b347bbef18422576d4ec3967ca45a3a4 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:45:18 +0000 Subject: [PATCH 024/810] Changed 2 members to public in JavaScript code, changed 1 member to explicitly private in Python code. --- javascript/qrcodegen.js | 17 +++++++++-------- python/qrcodegen.py | 10 +++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index c7763cf..0327469 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -50,6 +50,7 @@ * - Field QrSegment.Mode mode * - Field int numChars * - Method getBits() -> list + * - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX * - Enum Mode: * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI */ @@ -742,7 +743,7 @@ var qrcodegen = new function() { * Returns a segment representing the given string of decimal digits encoded in numeric mode. */ this.QrSegment.makeNumeric = function(digits) { - if (!QrSegment.NUMERIC_REGEX.test(digits)) + if (!this.NUMERIC_REGEX.test(digits)) throw "String contains non-numeric characters"; var bb = new BitBuffer(); var i; @@ -760,7 +761,7 @@ var qrcodegen = new function() { * 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. */ this.QrSegment.makeAlphanumeric = function(text) { - if (!QrSegment.ALPHANUMERIC_REGEX.test(text)) + if (!this.ALPHANUMERIC_REGEX.test(text)) throw "String contains unencodable characters in alphanumeric mode"; var bb = new BitBuffer(); var i; @@ -783,9 +784,9 @@ var qrcodegen = new function() { // Select the most efficient segment encoding automatically if (text == "") return []; - else if (QrSegment.NUMERIC_REGEX.test(text)) + else if (this.NUMERIC_REGEX.test(text)) return [this.makeNumeric(text)]; - else if (QrSegment.ALPHANUMERIC_REGEX.test(text)) + else if (this.ALPHANUMERIC_REGEX.test(text)) return [this.makeAlphanumeric(text)]; else return [this.makeBytes(toUtf8ByteArray(text))]; @@ -813,11 +814,11 @@ var qrcodegen = new function() { var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'. - // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). - QrSegment.NUMERIC_REGEX = /^[0-9]*$/; + // (Public) Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + this.QrSegment.NUMERIC_REGEX = /^[0-9]*$/; - // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). - QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; + // (Public) Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; // Maps shifted ASCII codes to alphanumeric mode character codes. QrSegment.ALPHANUMERIC_ENCODING_TABLE = [ diff --git a/python/qrcodegen.py b/python/qrcodegen.py index cc31111..81c445b 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -637,11 +637,11 @@ class QrSegment(object): raise ValueError("String contains unencodable characters in alphanumeric mode") bb = _BitBuffer() for i in range(0, len(text) - 1, 2): # Process groups of 2 - temp = QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 - temp += QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] + temp = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 + temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] bb.append_bits(temp, 11) if len(text) % 2 > 0: # 1 character remaining - bb.append_bits(QrSegment.ALPHANUMERIC_ENCODING_TABLE[text[-1]], 6) + bb.append_bits(QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[-1]], 6) return QrSegment(QrSegment.Mode.ALPHANUMERIC, len(text), bb.get_bits()) @@ -708,8 +708,8 @@ class QrSegment(object): # Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) ALPHANUMERIC_REGEX = re.compile("[A-Z0-9 $%*+./:-]*$") - # Dictionary of "0"->0, "A"->10, "$"->37, etc. - ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} + # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. + _ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} # ---- Public helper enumeration ---- From 3c995fa0965e51cc232a99a674724d9e1b16e6ac Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:46:46 +0000 Subject: [PATCH 025/810] Updated some comments to note public and private members in JavaScript, Python, C++ code. --- cpp/QrCode.hpp | 2 +- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 7 ++++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 6e57c51..0d981c0 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -53,7 +53,7 @@ public: // Fields. public: - const int ordinal; // In the range 0 to 3 (unsigned 2-bit integer). + const int ordinal; // (Public) In the range 0 to 3 (unsigned 2-bit integer). const int formatBits; // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). // Constructor. diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0327469..6fbc6b1 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -161,7 +161,7 @@ var qrcodegen = new function() { /*---- Accessor methods ----*/ - // Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top + // (Public) Returns the color of the module (pixel) at the given coordinates, which is either 0 for white or 1 for black. The top // left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then 0 (white) is returned. this.getModule = function(x, y) { if (0 <= x && x < size && 0 <= y && y < size) @@ -820,7 +820,7 @@ var qrcodegen = new function() { // (Public) Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; - // Maps shifted ASCII codes to alphanumeric mode character codes. + // (Private) Maps shifted ASCII codes to alphanumeric mode character codes. QrSegment.ALPHANUMERIC_ENCODING_TABLE = [ // SP, !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, :, ;, <, =, >, ?, @, // ASCII codes 32 to 64 36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, -1, // Array indices 0 to 32 diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 81c445b..9c987c1 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -52,6 +52,7 @@ This module "qrcodegen", public members: - Method get_mode() -> QrSegment.Mode - Method get_num_chars() -> int - Method get_bits() -> list + - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX - Enum Mode: - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI """ @@ -581,7 +582,7 @@ class QrCode(object): """Represents the error correction level used in a QR Code symbol.""" # Private constructor def __init__(self, i, fb): - self.ordinal = i # In the range 0 to 3 (unsigned 2-bit integer) + self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) # Public constants. Create them outside the class. @@ -702,10 +703,10 @@ class QrSegment(object): # ---- Constants ---- - # Can test whether a string is encodable in numeric mode (such as by using make_numeric()) + # (Public) Can test whether a string is encodable in numeric mode (such as by using make_numeric()) NUMERIC_REGEX = re.compile("[0-9]*$") - # Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) + # (Public) Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) ALPHANUMERIC_REGEX = re.compile("[A-Z0-9 $%*+./:-]*$") # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. From fc5f11b3b2de6bc44a172f68f52e898220a74a3f Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:50:42 +0000 Subject: [PATCH 026/810] Tweaked line breaking, whitespace, and declaration ordering without changing behavior. --- cpp/QrCodeGeneratorDemo.cpp | 6 +++--- .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 6 +++--- javascript/qrcodegen.js | 20 +++++++++---------- python/qrcodegen-demo.py | 3 ++- python/qrcodegen.py | 2 +- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 569d205..ee2f89d 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -81,14 +81,14 @@ static void doVarietyDemo() { printQr(qrcodegen::QrCode(qr3, 7)); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - const qrcodegen::QrCode qr4 = qrcodegen::QrCode::encodeText("Alice was beginning to get very tired of sitting by her sister on the bank, " + const qrcodegen::QrCode qr4 = qrcodegen::QrCode::encodeText( + "Alice was beginning to get very tired of sitting by her sister on the bank, " "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " - "a White Rabbit with pink eyes ran close by her.", - qrcodegen::QrCode::Ecc::HIGH); + "a White Rabbit with pink eyes ran close by her.", qrcodegen::QrCode::Ecc::HIGH); printQr(qr4); } diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index e644f9a..c29b971 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -94,14 +94,14 @@ public final class QrCodeGeneratorDemo { writePng(new QrCode(qr, 7).toImage(10, 3), "unicode-mask7-QR.png"); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - qr = QrCode.encodeText("Alice was beginning to get very tired of sitting by her sister on the bank, " + qr = QrCode.encodeText( + "Alice was beginning to get very tired of sitting by her sister on the bank, " + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " - + "a White Rabbit with pink eyes ran close by her.", - QrCode.Ecc.HIGH); + + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 6fbc6b1..5b2af31 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -675,16 +675,6 @@ var qrcodegen = new function() { /*---- Public helper enumeration ----*/ - // Private constructor. - function Ecc(ord, fb) { - // (Public) In the range 0 to 3 (unsigned 2-bit integer) - Object.defineProperty(this, "ordinal", {value:ord}); - - // (Package-private) In the range 0 to 3 (unsigned 2-bit integer) - Object.defineProperty(this, "formatBits", {value:fb}); - } - - /* * Represents the error correction level used in a QR Code symbol. */ @@ -697,6 +687,16 @@ var qrcodegen = new function() { }; + // Private constructor. + function Ecc(ord, fb) { + // (Public) In the range 0 to 3 (unsigned 2-bit integer) + Object.defineProperty(this, "ordinal", {value:ord}); + + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer) + Object.defineProperty(this, "formatBits", {value:fb}); + } + + /*---- Data segment class ----*/ diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index b8a1542..cd72b53 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -67,7 +67,8 @@ def do_variety_demo(): print_qr(qrcodegen.QrCode(qrcode=qr, mask=7)) # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - qr = qrcodegen.QrCode.encode_text("Alice was beginning to get very tired of sitting by her sister on the bank, " + qr = qrcodegen.QrCode.encode_text( + "Alice was beginning to get very tired of sitting by her sister on the bank, " "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 9c987c1..59d2338 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -168,7 +168,7 @@ class QrCode(object): if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): raise ValueError("Invalid array length") # Initialize grids of modules - self._modules = [[False] * self._size for _ in range(self._size)] # The modules of the QR symbol; start with entirely white grid + self._modules = [[False] * self._size for _ in range(self._size)] # The modules of the QR symbol; start with entirely white grid self._isfunction = [[False] * self._size for _ in range(self._size)] # Indicates function modules that are not subjected to masking # Draw function patterns, draw all codewords self._draw_function_patterns() From 8bdecc51a38c04562498389e27bcdb73f2fe63b8 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Wed, 20 Apr 2016 20:59:31 +0000 Subject: [PATCH 027/810] Fixed simple errors in C++ and Python code. --- cpp/QrCode.cpp | 2 +- python/qrcodegen.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 3e3e5ab..deac175 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -171,7 +171,7 @@ std::string qrcodegen::QrCode::toSvgString(int border) const { sb << "\n"; sb << "\n"; - sb << "\t\n" + sb << "\t\n"; sb << "\t Date: Wed, 20 Apr 2016 21:00:23 +0000 Subject: [PATCH 028/810] Added robustness checks to JavaScript, Python, C++ code without changing functionality in normal operation. --- cpp/QrCode.cpp | 2 +- javascript/qrcodegen.js | 2 ++ python/qrcodegen.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index deac175..0bfbd35 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -348,7 +348,7 @@ void qrcodegen::QrCode::drawCodewords(const std::vector &data) { bool upwards = ((right & 2) == 0) ^ (x < 6); int y = upwards ? size - 1 - vert : vert; // Actual y coordinate if (!isFunction.at(y).at(x) && i < data.size() * 8) { - modules.at(y).at(x) = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0; + modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; i++; } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 5b2af31..ceea27c 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -86,6 +86,8 @@ var qrcodegen = new function() { if (version < 1 || version > 40) throw "Version value out of range"; } else if (initData instanceof qrcodegen.QrCode) { + if (version != undefined || errCorLvl != undefined) + throw "Values must be undefined"; version = initData.version; errCorLvl = initData.errorCorrectionLevel; } else diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 0464c0f..8dd0616 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -158,6 +158,8 @@ class QrCode(object): if not isinstance(errcorlvl, QrCode.Ecc): raise TypeError("QrCode.Ecc expected") elif qrcode is not None and datacodewords is None: + if version is not None or errcorlvl is not None: + raise ValueError("Values must be None") version = qrcode._version errcorlvl = qrcode._errcorlvl else: From 4fcf02d1f953f511ad70f81dddb14090f52d25d5 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 01:57:58 +0000 Subject: [PATCH 029/810] Added comments and renamed variables in QrCode.appendErrorCorrection() in all language versions. --- cpp/QrCode.cpp | 17 +++++++++++------ java/io/nayuki/qrcodegen/QrCode.java | 19 ++++++++++++------- javascript/qrcodegen.js | 17 +++++++++++------ python/qrcodegen.py | 17 +++++++++++------ 4 files changed, 45 insertions(+), 25 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 0bfbd35..ec701a7 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -301,19 +301,22 @@ void qrcodegen::QrCode::setFunctionModule(int x, int y, bool isBlack) { std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector &data) const { if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) throw "Invalid argument"; + + // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal][version]; - int numEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal][version]; - if (numEcc % numBlocks != 0) + int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal][version]; + if (totalEcc % numBlocks != 0) throw "Assertion error"; - int eccLen = numEcc / numBlocks; + int blockEccLen = totalEcc / numBlocks; int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; + // Split data into blocks and append ECC to each block std::vector> blocks; - const ReedSolomonGenerator rs(eccLen); + const ReedSolomonGenerator rs(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { std::vector dat; - dat.insert(dat.begin(), data.begin() + k, data.begin() + (k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1))); + dat.insert(dat.begin(), data.begin() + k, data.begin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); k += dat.size(); const std::vector ecc(rs.getRemainder(dat)); if (i < numShortBlocks) @@ -322,10 +325,12 @@ std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector< blocks.push_back(dat); } + // Interleave (not concatenate) the bytes from every block into a single sequence std::vector result; for (int i = 0; static_cast(i) < blocks.at(0).size(); i++) { for (int j = 0; static_cast(j) < blocks.size(); j++) { - if (i != shortBlockLen - eccLen || j >= numShortBlocks) + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) result.push_back(blocks.at(j).at(i)); } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index e610ce3..7fbbdc2 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -455,29 +455,34 @@ public final class QrCode { private byte[] appendErrorCorrection(byte[] data) { if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) throw new IllegalArgumentException(); + + // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; - int numEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal()][version]; - if (numEcc % numBlocks != 0) + int totalEcc = NUM_ERROR_CORRECTION_CODEWORDS[errorCorrectionLevel.ordinal()][version]; + if (totalEcc % numBlocks != 0) throw new AssertionError(); - int eccLen = numEcc / numBlocks; + int blockEccLen = totalEcc / numBlocks; int numShortBlocks = numBlocks - getNumRawDataModules(version) / 8 % numBlocks; int shortBlockLen = getNumRawDataModules(version) / 8 / numBlocks; + // Split data into blocks and append ECC to each block byte[][] blocks = new byte[numBlocks][]; - ReedSolomonGenerator rs = new ReedSolomonGenerator(eccLen); + ReedSolomonGenerator rs = new ReedSolomonGenerator(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { - byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1)); + byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); k += dat.length; byte[] ecc = rs.getRemainder(dat); - System.arraycopy(ecc, 0, block, block.length - eccLen, ecc.length); + 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[getNumRawDataModules(version) / 8]; for (int i = 0, k = 0; i < blocks[0].length; i++) { for (int j = 0; j < blocks.length; j++) { - if (i != shortBlockLen - eccLen || j >= numShortBlocks) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { result[k] = blocks[j][i]; k++; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index ceea27c..5a40e4e 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -337,18 +337,21 @@ var qrcodegen = new function() { function appendErrorCorrection(data) { if (data.length != QrCode.getNumDataCodewords(version, errCorLvl)) throw "Invalid argument"; + + // Calculate parameter numbers var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version]; - var numEcc = QrCode.NUM_ERROR_CORRECTION_CODEWORDS[errCorLvl.ordinal][version]; - if (numEcc % numBlocks != 0) + var totalEcc = QrCode.NUM_ERROR_CORRECTION_CODEWORDS[errCorLvl.ordinal][version]; + if (totalEcc % numBlocks != 0) throw "Assertion error"; - var eccLen = Math.floor(numEcc / numBlocks); + var blockEccLen = Math.floor(totalEcc / numBlocks); var numShortBlocks = numBlocks - Math.floor(QrCode.getNumRawDataModules(version) / 8) % numBlocks; var shortBlockLen = Math.floor(QrCode.getNumRawDataModules(version) / (numBlocks * 8)); + // Split data into blocks and append ECC to each block var blocks = []; - var rs = new ReedSolomonGenerator(eccLen); + var rs = new ReedSolomonGenerator(blockEccLen); for (var i = 0, k = 0; i < numBlocks; i++) { - var dat = data.slice(k, k + shortBlockLen - eccLen + (i < numShortBlocks ? 0 : 1)); + var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); k += dat.length; var ecc = rs.getRemainder(dat); if (i < numShortBlocks) @@ -359,10 +362,12 @@ var qrcodegen = new function() { blocks.push(dat); } + // Interleave (not concatenate) the bytes from every block into a single sequence var result = []; for (var i = 0; i < blocks[0].length; i++) { for (var j = 0; j < blocks.length; j++) { - if (i != shortBlockLen - eccLen || j >= numShortBlocks) + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) result.push(blocks[j][i]); } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8dd0616..7fbdde9 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -357,18 +357,21 @@ class QrCode(object): codewords appended to it, based on this object's version and error correction level.""" version = self._version assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl) + + # Calculate parameter numbers numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] - numecc = QrCode._NUM_ERROR_CORRECTION_CODEWORDS[self._errcorlvl.ordinal][version] - assert numecc % numblocks == 0 - ecclen = numecc // numblocks + totalecc = QrCode._NUM_ERROR_CORRECTION_CODEWORDS[self._errcorlvl.ordinal][version] + assert totalecc % numblocks == 0 + blockecclen = totalecc // numblocks numshortblocks = numblocks - QrCode._get_num_raw_data_modules(version) // 8 % numblocks shortblocklen = self._get_num_raw_data_modules(version) // 8 // numblocks + # Split data into blocks and append ECC to each block blocks = [] - rs = _ReedSolomonGenerator(ecclen) + rs = _ReedSolomonGenerator(blockecclen) k = 0 for i in range(numblocks): - dat = data[k : k + shortblocklen - ecclen + (0 if i < numshortblocks else 1)] + dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] k += len(dat) ecc = rs.get_remainder(dat) if i < numshortblocks: @@ -377,10 +380,12 @@ class QrCode(object): blocks.append(dat) assert k == len(data) + # Interleave (not concatenate) the bytes from every block into a single sequence result = [] for i in range(len(blocks[0])): for (j, blk) in enumerate(blocks): - if i != shortblocklen - ecclen or j >= numshortblocks: + # Skip the padding byte in short blocks + if i != shortblocklen - blockecclen or j >= numshortblocks: result.append(blk[i]) assert len(result) == QrCode._get_num_raw_data_modules(version) // 8 return result From 7622a9d799626bc36aceea2c3b35867937f79ad7 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 02:19:50 +0000 Subject: [PATCH 030/810] Fixed field reference errors in Python code. --- python/qrcodegen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 7fbdde9..97d5c35 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -235,8 +235,8 @@ class QrCode(object): if border < 0: raise ValueError("Border must be non-negative") parts = [] - for y in range(-border, self.size + border): - for x in range(-border, self.size + border): + for y in range(-border, self._size + border): + for x in range(-border, self._size + border): if self.get_module(x, y) == 1: parts.append("M{},{}h1v1h-1z".format(x + border, y + border)) return """ @@ -245,7 +245,7 @@ class QrCode(object): -""".format(self.size + border * 2, " ".join(parts)) +""".format(self._size + border * 2, " ".join(parts)) # ---- Private helper methods for constructor: Drawing function modules ---- From def528043eacbd96f369cf129af1dc8a54615bc5 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 02:31:09 +0000 Subject: [PATCH 031/810] Added an assertion check to C++ code. --- cpp/QrCode.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index ec701a7..450c4cc 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -334,6 +334,8 @@ std::vector qrcodegen::QrCode::appendErrorCorrection(const std::vector< result.push_back(blocks.at(j).at(i)); } } + if (result.size() != static_cast(getNumRawDataModules(version) / 8)) + throw "Assertion error"; return result; } From 85703de600d084cba1bda99c73e22b68615fa914 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 18:39:47 +0000 Subject: [PATCH 032/810] Fixed subtle logic bug in QrSegment.java to match the API promise of a fully mutable list being returned. --- java/io/nayuki/qrcodegen/QrSegment.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 4fa7766..35ce18e 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -119,16 +119,16 @@ public final class QrSegment { throw new NullPointerException(); // Select the most efficient segment encoding automatically + List result = new ArrayList<>(); if (text.equals("")) - return new ArrayList<>(); - QrSegment seg; - if (NUMERIC_REGEX.matcher(text).matches()) - seg = makeNumeric(text); + return result; + else if (NUMERIC_REGEX.matcher(text).matches()) + result.add(makeNumeric(text)); else if (ALPHANUMERIC_REGEX.matcher(text).matches()) - seg = makeAlphanumeric(text); + result.add(makeAlphanumeric(text)); else - seg = makeBytes(text.getBytes(StandardCharsets.UTF_8)); - return Arrays.asList(seg); + result.add(makeBytes(text.getBytes(StandardCharsets.UTF_8))); + return result; } From 0f71a9f818b32a214e4febbb6d5ddac7fee67a9f Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 21:42:35 +0000 Subject: [PATCH 033/810] Added an experimental advanced QR segment encoder class in Java for kanji mode encoding and optimal segment mode switching, changed a statistics helper function in QrCode.java to package-private to support the new class. --- java/io/nayuki/qrcodegen/QrCode.java | 2 +- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 404 ++++++++++++++++++ 2 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 java/io/nayuki/qrcodegen/QrSegmentAdvanced.java diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 7fbbdc2..e00a120 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -713,7 +713,7 @@ public final class QrCode { // 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. - private static int getNumDataCodewords(int ver, Ecc ecl) { + static int getNumDataCodewords(int ver, Ecc ecl) { if (ver < 1 || ver > 40) throw new IllegalArgumentException("Version number out of range"); return getNumRawDataModules(ver) / 8 - NUM_ERROR_CORRECTION_CODEWORDS[ecl.ordinal()][ver]; diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java new file mode 100644 index 0000000..b636689 --- /dev/null +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -0,0 +1,404 @@ +/* + * QR Code generator library - Optional advanced logic (Java) + * + * Copyright (c) 2016 Project Nayuki + * https://www.nayuki.io/page/qr-code-generator-library + * + * (MIT License) + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + +import static io.nayuki.qrcodegen.QrSegment.Mode.ALPHANUMERIC; +import static io.nayuki.qrcodegen.QrSegment.Mode.BYTE; +import static io.nayuki.qrcodegen.QrSegment.Mode.NUMERIC; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; + + +public final class QrSegmentAdvanced { + + /*---- Optimal list of segments encoder ----*/ + + /** + * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. + * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints given + * by the specified {error correction level, minimum version number, maximum version number}, plus the additional + * constraint that the segment modes {NUMERIC, ALPHANUMERIC, BYTE} can be used but KANJI cannot be used. + *

This function can be viewed as a significantly more sophisticated and slower replacement + * for {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way + * that overlaps with {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

+ * @param text the text to be encoded, which can be any Unicode string + * @param ecl the error correction level to use + * @param minVersion the minimum allowed version of the QR symbol (at least 1) + * @param maxVersion the maximum allowed version of the QR symbol (at most 40) + * @return a list of segments containing the text, minimizing the bit length with respect to the constraints + * @throws NullPointerException if the data or error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, + * or if the data is too long to fit in a QR Code at maxVersion at the ECL + */ + public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { + // Check arguments + if (text == null || ecl == null) + throw new NullPointerException(); + if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40)) + throw new IllegalArgumentException("Invalid value"); + + // Iterate through version numbers, and make tentative segments + List segs = null; + for (int version = minVersion; version <= maxVersion; version++) { + if (version == minVersion || version == 10 || version == 27) + segs = makeSegmentsOptimally(text, version); + + // Check if the segments fit + int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + int dataUsedBits = QrSegment.getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + return segs; + } + throw new IllegalArgumentException("Data too long"); + } + + + // Returns a list of segments that is optimal for the given text at the given version number. + private static List makeSegmentsOptimally(String text, int version) { + byte[] data = text.getBytes(StandardCharsets.UTF_8); + int[][] bitCosts = computeBitCosts(data, version); + QrSegment.Mode[] charModes = computeCharacterModes(data, version, bitCosts); + return splitIntoSegments(data, charModes); + } + + + private static int[][] computeBitCosts(byte[] data, int version) { + // Segment header sizes, measured in 1/6 bits + int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; + int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; + int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; + + // result[mode][len] is the number of 1/6 bits to encode the first len characters of the text, ending in the mode + int[][] result = new int[3][data.length + 1]; + Arrays.fill(result[1], Integer.MAX_VALUE / 2); + Arrays.fill(result[2], Integer.MAX_VALUE / 2); + result[0][0] = bytesCost; + result[1][0] = alphnumCost; + result[2][0] = numberCost; + + // Calculate the cost table using dynamic programming + for (int i = 0; i < data.length; i++) { + // Encode a character + int j = i + 1; + char c = (char)data[i]; + result[0][j] = result[0][i] + 48; // 8 bits per byte + if (isAlphanumeric(c)) + result[1][j] = result[1][i] + 39; // 6.5 bits per alphanumeric char + if (isNumeric(c)) + result[2][j] = result[2][i] + 20; // 3.33 bits per alphanumeric char + + // Switch modes, rounding up fractional bits + result[0][j] = Math.min((Math.min(result[1][j], result[2][j]) + 5) / 6 * 6 + bytesCost , result[0][j]); + result[1][j] = Math.min((Math.min(result[2][j], result[0][j]) + 5) / 6 * 6 + alphnumCost, result[1][j]); + result[2][j] = Math.min((Math.min(result[0][j], result[1][j]) + 5) / 6 * 6 + numberCost , result[2][j]); + } + return result; + } + + + private static QrSegment.Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { + // Segment header sizes, measured in 1/6 bits + int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; + int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; + int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; + + // Infer the mode used for last character by taking the minimum + QrSegment.Mode curMode; + int end = bitCosts[0].length - 1; + if (bitCosts[0][end] <= Math.min(bitCosts[1][end], bitCosts[2][end])) + curMode = BYTE; + else if (bitCosts[1][end] <= bitCosts[2][end]) + curMode = ALPHANUMERIC; + else + curMode = NUMERIC; + + // Work backwards to calculate optimal encoding mode for each character + QrSegment.Mode[] result = new QrSegment.Mode[data.length]; + if (data.length == 0) + return result; + result[data.length - 1] = curMode; + for (int i = data.length - 2; i >= 0; i--) { + char c = (char)data[i]; + if (curMode == NUMERIC) { + if (isNumeric(c)) + curMode = NUMERIC; + else if (isAlphanumeric(c) && (bitCosts[1][i] + 39 + 5) / 6 * 6 + numberCost == bitCosts[2][i + 1]) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else if (curMode == ALPHANUMERIC) { + if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + alphnumCost == bitCosts[1][i + 1]) + curMode = NUMERIC; + else if (isAlphanumeric(c)) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else if (curMode == BYTE) { + if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + curMode = NUMERIC; + else if (isAlphanumeric(c) && (bitCosts[1][i] + 39 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + curMode = ALPHANUMERIC; + else + curMode = BYTE; + } else + throw new AssertionError(); + result[i] = curMode; + } + return result; + } + + + private static List splitIntoSegments(byte[] data, QrSegment.Mode[] charModes) { + List result = new ArrayList<>(); + if (data.length == 0) + return result; + + // Accumulate run of modes + QrSegment.Mode curMode = charModes[0]; + int start = 0; + for (int i = 1; i < data.length; i++) { + if (charModes[i] != curMode) { + if (curMode == BYTE) + result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); + else { + String temp = new String(data, start, i - start, StandardCharsets.US_ASCII); + if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(temp)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(temp)); + else + throw new AssertionError(); + } + curMode = charModes[i]; + start = i; + } + } + + // Final segment + if (curMode == BYTE) + result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, data.length))); + else { + String temp = new String(data, start, data.length - start, StandardCharsets.US_ASCII); + if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(temp)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(temp)); + else + throw new AssertionError(); + } + return result; + } + + + private static boolean isAlphanumeric(char c) { + return isNumeric(c) || 'A' <= c && c <= 'Z' || " $%*+./:-".indexOf(c) != -1; + } + + private static boolean isNumeric(char c) { + return '0' <= c && c <= '9'; + } + + + /*---- Kanji mode segment encoder ----*/ + + /** + * Returns a segment representing the specified string encoded in kanji mode. + *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, + * Asian punctuation, full-width ASCII}.
+ * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + * @param text the text to be encoded, which must fall in the kanji mode subset of characters + * @return a segment containing the data + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-kanji-mode characters + * @see #isEncodableAsKanji(String) + */ + public static QrSegment makeKanjiSegment(String text) { + if (text == null) + throw new NullPointerException(); + BitBuffer bb = new BitBuffer(); + for (int i = 0; i < text.length(); i++) { + int val = UNICODE_TO_QR_KANJI[text.charAt(i)]; + if (val == -1) + throw new IllegalArgumentException("String contains non-kanji-mode characters"); + bb.appendBits(val, 13); + } + return new QrSegment(QrSegment.Mode.KANJI, text.length(), bb.getBytes(), bb.bitLength()); + } + + + /** + * Tests whether the specified text string can be encoded as a segment in kanji mode. + *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, + * Asian punctuation, full-width ASCII}.
+ * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + * @param text the string to test for encodability + * @return {@code true} if and only if the string can be encoded in kanji mode + * @throws NullPointerException if the string is {@code null} + * @see #makeKanjiSegment(String) + */ + public static boolean isEncodableAsKanji(String text) { + if (text == null) + throw new NullPointerException(); + for (int i = 0; i < text.length(); i++) { + if (UNICODE_TO_QR_KANJI[text.charAt(i)] == -1) + return false; + } + return true; + } + + + // 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=="; + + + private static short[] UNICODE_TO_QR_KANJI = new short[65536]; + + static { + 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) { + int j = ((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF); + if (j == 0xFFFF) + continue; + if (UNICODE_TO_QR_KANJI[j] != -1) + throw new AssertionError(); + UNICODE_TO_QR_KANJI[j] = (short)(i / 2); + } + } + +} From 671237fff22b288ddc465ceb71cf92f579113b3e Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Thu, 21 Apr 2016 23:08:42 +0000 Subject: [PATCH 034/810] Added project readme markdown document. --- Readme.markdown | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 Readme.markdown diff --git a/Readme.markdown b/Readme.markdown new file mode 100644 index 0000000..e339598 --- /dev/null +++ b/Readme.markdown @@ -0,0 +1,118 @@ +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to provide the best and clearest QR Code generator library. The primary goals are flexible options and absolute correctness. The secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo and extensive description: [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + + +Features +-------- + +Core features: + +* Available in 4 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++ +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages), BufferedImage raster bitmap (Java only) +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number + +Optional advanced features (Java only): + +* Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes +* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general parts + + +Examples +-------- + +Java language: + + import io.nayuki.qrcodegen.*; + + // Simple operation + QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); + BufferedImage img = qr0.toImage(4, 10); + ImageIO.write(img, "png", new File("qr-code.png")); + + // Manual operation + List segs = QrSegment.makeSegments("3141592653589793238462643383"); + QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); + +JavaScript language: + + // Name abbreviated for the sake of these examples here + var QRC = qrcodegen.QrCode; + + // Simple operation + var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); + var svg = qr0.toSvgString(4); + + // Manual operation + var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); + var qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); + +Python language: + + from qrcodegen import * + + # Simple operation + qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM); + svg = qr0.to_svg_str(4) + + # Manual operation + segs = QrSegment.make_segments("3141592653589793238462643383"); + qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False); + +C++ language: + + #include + #include "QrCode.hpp" + using namespace qrcodegen; + + // Simple operation + QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); + std::string svg = qr0.toSvgString(4); + + // Manual operation + std::vector segs = + QrSegment::makeSegments("3141592653589793238462643383"); + QrCode qr1 = QrCode::encodeSegments(segs, QrCode::Ecc::HIGH, 5, 5, 2, false); + + +License +------- + +Copyright © 2016 Project Nayuki +[https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + +(MIT License) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +* The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall the + authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising from, + out of or in connection with the Software or the use or other dealings in the + Software. From 9a832a200d270c3f79e5220f69a2498d559ad010 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Fri, 22 Apr 2016 16:10:35 +0000 Subject: [PATCH 035/810] Added QrCode.drawCanvas() to JavaScript version of the library, by moving it from the demo program. --- javascript/qrcodegen-demo.js | 12 +----------- javascript/qrcodegen.js | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index ee07226..e4fedf3 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -76,17 +76,7 @@ function redrawQrCode() { var scale = parseInt(document.getElementById("scale-input").value, 10); if (scale <= 0 || scale > 30) return; - // Draw QR Code onto canvas - var width = (qr.size + border * 2) * scale; - canvas.width = width; - canvas.height = width; - var ctx = canvas.getContext("2d"); - for (var y = -border; y < qr.size + border; y++) { - for (var x = -border; x < qr.size + border; x++) { - ctx.fillStyle = qr.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; - ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); - } - } + qr.drawCanvas(scale, border, canvas); canvas.style.removeProperty("display"); } else { var code = qr.toSvgString(border); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 5a40e4e..10d46a6 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -37,6 +37,7 @@ * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> int + * - Method drawCanvas(int scale, int border, HTMLCanvasElement canvas) -> void * - Method toSvgString(int border) -> str * - Enum Ecc: * - Constants LOW, MEDIUM, QUARTILE, HIGH @@ -185,6 +186,24 @@ var qrcodegen = new function() { /*---- Public instance methods ----*/ + // Draws this QR Code symbol with the given module scale and number of modules onto the given HTML canvas element. + // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely + // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. + this.drawCanvas = function(scale, border, canvas) { + if (scale <= 0 || border < 0) + throw "Value out of range"; + var width = (size + border * 2) * scale; + canvas.width = width; + canvas.height = width; + var ctx = canvas.getContext("2d"); + for (var y = -border; y < size + border; y++) { + for (var x = -border; x < size + border; x++) { + ctx.fillStyle = this.getModule(x, y) == 1 ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + }; + // Based on the given number of border modules to add as padding, this returns a // string whose contents represents an SVG XML file that depicts this QR Code symbol. this.toSvgString = function(border) { From 2e485a3808fe2ffc25c76f9daa7703f33fd09f15 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sun, 24 Apr 2016 05:01:04 +0000 Subject: [PATCH 036/810] Updated readme to remove semicolons from example Python code. --- Readme.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index e339598..13d7825 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -68,12 +68,12 @@ Python language: from qrcodegen import * # Simple operation - qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM); + qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) svg = qr0.to_svg_str(4) # Manual operation - segs = QrSegment.make_segments("3141592653589793238462643383"); - qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False); + segs = QrSegment.make_segments("3141592653589793238462643383") + qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) C++ language: From 83aae7263ff6b5bc59bcee5442aba8295890305e Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sun, 24 Apr 2016 05:34:49 +0000 Subject: [PATCH 037/810] Added setup.py script for Distutils packaging. --- python/setup.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 python/setup.py diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..e212a20 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,113 @@ +# +# QR Code generator Distutils script (Python 2, 3) +# +# Copyright (c) 2016 Project Nayuki +# https://www.nayuki.io/page/qr-code-generator-library +# +# (MIT License) +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +import distutils.core + + +distutils.core.setup( + name = "qrcodegen", + description = "High quality QR Code generator library for Python 2 and 3", + version = "1.0.0", + platforms = "OS Independent", + license = "MIT License", + + author = "Project Nayuki", + author_email = "nayuki@eigenstate.org", + url = "https://www.nayuki.io/page/qr-code-generator-library", + + classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Information Technology", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Topic :: Multimedia :: Graphics", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + + long_description = """========================= +QR Code generator library +========================= + + +Introduction +------------ + +This project aims to provide the best and clearest QR Code generator library. The primary goals are flexible options and absolute correctness. The secondary goals are compact implementation size and good documentation comments. + +Home page with live JavaScript demo and extensive description: https://www.nayuki.io/page/qr-code-generator-library + + +Features +-------- + +Core features: + +* Available in 4 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++ +* Significantly shorter code but more documentation comments compared to competing libraries +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number + + +Usage +----- + +Install this package by downloading the source code ZIP file from PyPI_, or by running ``pip install qrcodegen``. + +Examples:: + + from qrcodegen import * + + # Simple operation + qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) + svg = qr0.to_svg_str(4) + + # Manual operation + segs = QrSegment.make_segments("3141592653589793238462643383") + qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) + border = 4 + for y in range(-border, qr1.get_size() + border): + for x in range(-border, qr1.get_size() + border): + color = qr1.get_module(x, y) # 0 for white, 1 for black + # (... paint the module onto pixels ...) + +More complete set of examples: https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen-demo.py . + +API documentation is in the source file itself, with a summary comment at the top: https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen.py . + +.. _PyPI: https://pypi.python.org/pypi/qrcodegen""", + + py_modules = ["qrcodegen"], +) From 0cc355344d6f8109c3c7358bfe7a90dede4efd36 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Mon, 25 Apr 2016 22:11:47 +0000 Subject: [PATCH 038/810] Updated Python packaging code to create Wheel packages - changed distribution script to use setuptools instead of distutils, added setup.cfg. --- python/setup.cfg | 2 ++ python/setup.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 python/setup.cfg diff --git a/python/setup.cfg b/python/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/python/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/python/setup.py b/python/setup.py index e212a20..032e106 100644 --- a/python/setup.py +++ b/python/setup.py @@ -22,10 +22,10 @@ # Software. # -import distutils.core +import setuptools -distutils.core.setup( +setuptools.setup( name = "qrcodegen", description = "High quality QR Code generator library for Python 2 and 3", version = "1.0.0", From 2de9610756945f23ec52aef829ea40a7aee7bc43 Mon Sep 17 00:00:00 2001 From: Nayuki Minase Date: Sat, 7 May 2016 19:02:11 +0000 Subject: [PATCH 039/810] Added comment about remainder bits in QR symbol in all language versions. --- cpp/QrCode.cpp | 2 ++ java/io/nayuki/qrcodegen/QrCode.java | 2 ++ javascript/qrcodegen.js | 2 ++ python/qrcodegen.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 450c4cc..51e6dad 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -358,6 +358,8 @@ void qrcodegen::QrCode::drawCodewords(const std::vector &data) { modules.at(y).at(x) = ((data.at(i >> 3) >> (7 - (i & 7))) & 1) != 0; i++; } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized } } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index e00a120..3cad230 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -514,6 +514,8 @@ public final class QrCode { modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; i++; } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized } } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 10d46a6..0b59066 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -415,6 +415,8 @@ var qrcodegen = new function() { modules[y][x] = ((data[i >>> 3] >>> (7 - (i & 7))) & 1) != 0; i++; } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized } } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 97d5c35..f77b9a5 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -409,6 +409,8 @@ class QrCode(object): if not self._isfunction[y][x] and i < len(data) * 8: self._modules[y][x] = ((data[i >> 3] >> (7 - (i & 7))) & 1) != 0 i += 1 + # If there are any remainder bits (0 to 7), they are already + # set to 0/false/white when the grid of modules was initialized assert i == len(data) * 8 From 924b590c327c51336ef99eed03fa0c1fc240a8ce Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 24 May 2016 18:27:17 +0000 Subject: [PATCH 040/810] Tweaked readme document to make the C++ code example more correct. --- Readme.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.markdown b/Readme.markdown index 13d7825..0c7698a 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -77,6 +77,7 @@ Python language: C++ language: + #include #include #include "QrCode.hpp" using namespace qrcodegen; From a9a5cdbb58ff549053060af39aa7bed2a434edaf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 24 May 2016 18:41:34 +0000 Subject: [PATCH 041/810] Updated documentation comments for 3 functions in all language versions. --- cpp/QrCode.hpp | 8 +++++--- java/io/nayuki/qrcodegen/QrCode.java | 12 +++++++----- javascript/qrcodegen.js | 6 ++++-- python/qrcodegen.py | 10 ++++++---- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 0d981c0..5b23776 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -70,6 +70,7 @@ public: * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. */ static QrCode encodeText(const char *text, const Ecc &ecl); @@ -78,6 +79,7 @@ public: * Returns a QR Code symbol representing the given binary data string at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. */ static QrCode encodeBinary(const std::vector &data, const Ecc &ecl); @@ -126,9 +128,9 @@ private: public: /* - * Creates a new QR Code symbol with the given version number, error correction level, binary data string, and mask number. - * This cumbersome constructor can be invoked directly by the user, but is considered - * to be even lower level than encodeSegments(). + * Creates a new QR Code symbol with the given version number, error correction level, binary data array, + * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. + * To go one level up, see the encodeSegments() function. */ QrCode(int ver, const Ecc &ecl, const std::vector &dataCodewords, int mask); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 3cad230..bf9c760 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -43,8 +43,9 @@ public final class QrCode { * Returns a QR Code symbol 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 Unicode * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. * @param text the text to be encoded, which can be any Unicode string - * @param ecl the error correction level to use + * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the text * @throws NullPointerException if the text or error correction level is {@code null} * @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long @@ -61,8 +62,9 @@ public final class QrCode { * Returns a QR Code symbol representing the specified binary data string at the specified error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. * @param data the binary data to encode - * @param ecl the error correction level to use + * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the binary data * @throws NullPointerException if the data or error correction level is {@code null} * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long @@ -187,9 +189,9 @@ public final class QrCode { /*---- Constructors ----*/ /** - * Creates a new QR Code symbol with the specified version number, error correction level, binary data string, and mask number. - *

This cumbersome constructor can be invoked directly by the user, but is considered - * to be even lower level than {@link #encodeSegments(List,Ecc)}.

+ * Creates a new QR Code symbol with the specified version number, error correction level, binary data array, and mask number. + *

This is a cumbersome low-level constructor that should not be invoked directly by the user. + * To go one level up, see the {@link #encodeSegments(List,Ecc)} 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 dataCodewords the raw binary user data to encode diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0b59066..a6fc652 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -68,8 +68,8 @@ var qrcodegen = new function() { * This constructor can be called in one of two ways: * - new QrCode(datacodewords, mask, version, errCorLvl): * Creates a new QR Code symbol with the given version number, error correction level, binary data array, - * and mask number. This cumbersome constructor can be invoked directly by the user, but is considered - * to be even lower level than QrCode.encodeSegments(). + * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. + * To go one level up, see the QrCode.encodeSegments() function. * - new QrCode(qr, mask): * Creates a new QR Code symbol based on the given existing object, but with a potentially different * mask pattern. The version, error correction level, codewords, etc. of the newly created object are @@ -541,6 +541,7 @@ var qrcodegen = new function() { * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode * code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. */ this.QrCode.encodeText = function(text, ecl) { var segs = qrcodegen.QrSegment.makeSegments(text); @@ -552,6 +553,7 @@ var qrcodegen = new function() { * Returns a QR Code symbol representing the given binary data string at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. */ this.QrCode.encodeBinary = function(data, ecl) { var seg = qrcodegen.QrSegment.makeBytes(data); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index f77b9a5..5cb51ac 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -70,7 +70,8 @@ class QrCode(object): def encode_text(text, ecl): """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode - code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output.""" + code points (not UTF-16 code units). The smallest possible QR Code version is automatically chosen for the output. + The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" segs = QrSegment.make_segments(text) return QrCode.encode_segments(segs, ecl) @@ -79,7 +80,8 @@ class QrCode(object): def encode_binary(data, ecl): """Returns a QR Code symbol representing the given binary data string at the given error correction level. This function always encodes using the binary segment mode, not any text mode. The maximum number of - bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output.""" + bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" if not isinstance(data, bytes): raise TypeError("Binary array expected") return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) @@ -141,8 +143,8 @@ class QrCode(object): """This constructor can be called in one of two ways: - QrCode(datacodewords=list, mask=int, version=int, errcorlvl=QrCode.Ecc): Creates a new QR Code symbol with the given version number, error correction level, binary data array, - and mask number. This cumbersome constructor can be invoked directly by the user, but is considered - to be even lower level than QrCode.encode_segments(). + and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. + To go one level up, see the QrCode.encode_segments() function. - QrCode(qrcode=QrCode, mask=int): Creates a new QR Code symbol based on the given existing object, but with a potentially different mask pattern. The version, error correction level, codewords, etc. of the newly created object are From fb702fd46e7eb4610646606d98786f2917e90065 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 24 May 2016 18:50:22 +0000 Subject: [PATCH 042/810] Added some explicit header #includes to C++ demo code for clarity. --- cpp/QrCodeGeneratorDemo.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index ee2f89d..f126ca4 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -25,8 +25,10 @@ * Software. */ +#include #include #include +#include #include "QrCode.hpp" From ff9fbba9cfd5406aa23a02b9c68c9db61b5474ec Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 14 Jun 2016 17:10:55 +0000 Subject: [PATCH 043/810] Renamed loop variable from j to i in ReedSolomonGenerator.getRemainder() in most language versions. --- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index bf9c760..669c09e 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -841,8 +841,8 @@ public final class QrCode { int factor = (b ^ result[0]) & 0xFF; System.arraycopy(result, 1, result, 0, result.length - 1); result[result.length - 1] = 0; - for (int j = 0; j < result.length; j++) - result[j] ^= multiply(coefficients[j] & 0xFF, factor); + for (int i = 0; i < result.length; i++) + result[i] ^= multiply(coefficients[i] & 0xFF, factor); } return result; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index a6fc652..e1dda5c 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -950,8 +950,8 @@ var qrcodegen = new function() { var factor = b ^ result[0]; result.shift(); result.push(0); - for (var j = 0; j < result.length; j++) - result[j] ^= ReedSolomonGenerator.multiply(coefficients[j], factor); + for (var i = 0; i < result.length; i++) + result[i] ^= ReedSolomonGenerator.multiply(coefficients[i], factor); }); return result; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 5cb51ac..7113cf6 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -795,8 +795,8 @@ class _ReedSolomonGenerator(object): factor = (b ^ result[0]) del result[0] result.append(0) - for j in range(len(result)): - result[j] ^= _ReedSolomonGenerator.multiply(self.coefficients[j], factor) + for i in range(len(result)): + result[i] ^= _ReedSolomonGenerator.multiply(self.coefficients[i], factor) return result From 1d5e77d4fc40062c30b60398272f7e2bd60ca154 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 14 Jun 2016 17:28:32 +0000 Subject: [PATCH 044/810] Simplified some code in QrCode.getAlignmentPatternPositions() in most language versions. --- cpp/QrCode.cpp | 8 ++++---- javascript/qrcodegen.js | 8 +++----- python/qrcodegen.py | 6 +++--- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 51e6dad..cab7433 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -506,11 +506,11 @@ std::vector qrcodegen::QrCode::getAlignmentPatternPositions(int ver) { else // C-C-C-Combo breaker! step = 26; - std::vector result(numAlign); + std::vector result; int size = ver * 4 + 17; - result.at(0) = 6; - for (int i = numAlign - 1, pos = size - 7; i >= 1; i--, pos -= step) - result.at(i) = pos; + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.insert(result.begin(), pos); + result.insert(result.begin(), 6); return result; } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index e1dda5c..298349e 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -639,11 +639,9 @@ var qrcodegen = new function() { else // C-C-C-Combo breaker! step = 26; - var result = []; - for (var i = numAlign - 1, pos = size - 7; i >= 1; i--, pos -= step) - result.push(pos); - result.push(6); - result.reverse(); + var result = [6]; + for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.splice(1, 0, pos); return result; } }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 7113cf6..b0d618a 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -519,10 +519,10 @@ class QrCode(object): step = (ver * 4 + numalign * 2 + 1) // (2 * numalign - 2) * 2 # ceil((size - 13) / (2*numalign - 2)) * 2 else: # C-C-C-Combo breaker! step = 26 - result = [6] + [None] * (numalign - 1) + result = [6] pos = ver * 4 + 10 - for i in reversed(range(1, numalign)): - result[i] = pos + for i in range(numalign - 1): + result.insert(1, pos) pos -= step return result From 18834e548b9e3ac1d7f8b993882a9ea372bf884b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 5 Oct 2016 17:36:45 +0000 Subject: [PATCH 045/810] Deleted a trailing whitespace. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index b636689..fe7be0e 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -234,7 +234,7 @@ public final class QrSegmentAdvanced { * @param text the text to be encoded, which must fall in the kanji mode subset of characters * @return a segment containing the data * @throws NullPointerException if the string is {@code null} - * @throws IllegalArgumentException if the string contains non-kanji-mode characters + * @throws IllegalArgumentException if the string contains non-kanji-mode characters * @see #isEncodableAsKanji(String) */ public static QrSegment makeKanjiSegment(String text) { From 941dd14cc7df4709e963999f87a3cf28b5e5e04f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 24 Oct 2016 07:27:48 +0000 Subject: [PATCH 046/810] Simplified Python code, without changing behavior. --- python/qrcodegen.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index b0d618a..2304f72 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -468,8 +468,7 @@ class QrCode(object): # 2*2 blocks of modules having same color for y in range(size - 1): for x in range(size - 1): - color = modules[y][x] - if color == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: + if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: result += QrCode._PENALTY_N2 # Finder-like pattern in rows @@ -488,11 +487,7 @@ class QrCode(object): result += QrCode._PENALTY_N3 # Balance of black and white modules - black = 0 - for row in modules: - for color in row: - if color: - black += 1 + black = sum(1 for x in range(size) for y in range(size) if modules[y][x]) total = size**2 # Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% for k in itertools.count(): @@ -622,10 +617,9 @@ class QrSegment(object): def make_bytes(data): """Returns a segment representing the given binary data encoded in byte mode.""" bb = _BitBuffer() + py3 = sys.version_info.major >= 3 for b in data: - if sys.version_info[0] < 3: - b = ord(b) - bb.append_bits(b, 8) + bb.append_bits((b if py3 else ord(b)), 8) return QrSegment(QrSegment.Mode.BYTE, len(data), bb.get_bits()) @@ -663,7 +657,7 @@ class QrSegment(object): def make_segments(text): """Returns a new mutable list of zero or more segments to represent the given Unicode text string. The result may use various segment modes and switch modes to optimize the length of the bit stream.""" - if not isinstance(text, str) and (sys.version_info[0] >= 3 or not isinstance(text, unicode)): + if not (isinstance(text, str) or (sys.version_info.major < 3 and isinstance(text, unicode))): raise TypeError("Text string expected") # Select the most efficient segment encoding automatically From 26caba357297057eda78edddca01a80ee006c264 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 24 Oct 2016 07:28:30 +0000 Subject: [PATCH 047/810] Tweaked documentation comment and blank line in JavaScript code. --- javascript/qrcodegen.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 298349e..03018e1 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -29,7 +29,7 @@ * Module "qrcodegen", public members: * - Class QrCode: * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode - * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode + * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode * - Function encodeSegments(list segs, QrCode.Ecc ecl, * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constructor QrCode(QrCode qr, int mask) @@ -153,7 +153,6 @@ var qrcodegen = new function() { Object.defineProperty(this, "size", {value:size}); // The error correction level used in this QR Code symbol. - Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). From b2e7844a94f4c67b5b6f9d20c796b37d43ec73b5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 3 Jan 2017 18:31:49 +0000 Subject: [PATCH 048/810] Updated wording of documentation comments in {C++, JavaScript, Python} versions - changed the word "specified" to "given" for consistency. --- cpp/QrCode.hpp | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 5b23776..314025e 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -85,8 +85,8 @@ public: /* - * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. - * The smallest possible QR Code version within the specified range is automatically chosen for the output. + * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. * This function is considered to be lower level than simply encoding text or binary data. diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 03018e1..d579577 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -561,8 +561,8 @@ var qrcodegen = new function() { /* - * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. - * The smallest possible QR Code version within the specified range is automatically chosen for the output. + * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. * This function is considered to be lower level than simply encoding text or binary data. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 2304f72..d7e4462 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -89,8 +89,8 @@ class QrCode(object): @staticmethod def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): - """Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. - The smallest possible QR Code version within the specified range is automatically chosen for the output. + """Returns a QR Code symbol representing the given data segments with the given encoding parameters. + The smallest possible QR Code version within the given range is automatically chosen for the output. This function allows the user to create a custom sequence of segments that switches between modes (such as alphanumeric and binary) to encode text more efficiently. This function is considered to be lower level than simply encoding text or binary data.""" From bd470926cad43aab7c586d41ad0ad83f0fba0b79 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 25 Jan 2017 01:13:57 +0000 Subject: [PATCH 049/810] Tweaked all Java code to replace explicit null checks with calls to Objects.requireNonNull() (requires Java SE 7+). --- java/io/nayuki/qrcodegen/BitBuffer.java | 4 +-- java/io/nayuki/qrcodegen/QrCode.java | 28 ++++++++----------- java/io/nayuki/qrcodegen/QrSegment.java | 23 ++++++--------- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 11 ++++---- 4 files changed, 28 insertions(+), 38 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index e759aeb..d918570 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -25,6 +25,7 @@ package io.nayuki.qrcodegen; import java.util.Arrays; +import java.util.Objects; /** @@ -76,8 +77,7 @@ final class BitBuffer { // Appends the data of the given segment to this bit buffer. public void appendData(QrSegment seg) { - if (seg == null) - throw new NullPointerException(); + Objects.requireNonNull(seg); ensureCapacity(bitLength + seg.bitLength); for (int i = 0; i < seg.bitLength; i++, bitLength++) { // Append bit by bit int bit = (seg.getByte(i >>> 3) >>> (7 - (i & 7))) & 1; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 669c09e..69bf1ee 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -27,6 +27,7 @@ package io.nayuki.qrcodegen; import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** @@ -51,8 +52,8 @@ public final class QrCode { * @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long */ public static QrCode encodeText(String text, Ecc ecl) { - if (text == null || ecl == null) - throw new NullPointerException(); + Objects.requireNonNull(text); + Objects.requireNonNull(ecl); List segs = QrSegment.makeSegments(text); return encodeSegments(segs, ecl); } @@ -70,8 +71,8 @@ public final class QrCode { * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long */ public static QrCode encodeBinary(byte[] data, Ecc ecl) { - if (data == null || ecl == null) - throw new NullPointerException(); + Objects.requireNonNull(data); + Objects.requireNonNull(ecl); QrSegment seg = QrSegment.makeBytes(data); return encodeSegments(Arrays.asList(seg), ecl); } @@ -112,8 +113,8 @@ public final class QrCode { * < −1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL */ public static QrCode encodeSegments(List segs, Ecc ecl, int minVersion, int maxVersion, int mask, boolean boostEcl) { - if (segs == null || ecl == null) - throw new NullPointerException(); + Objects.requireNonNull(segs); + Objects.requireNonNull(ecl); if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40) || mask < -1 || mask > 7) throw new IllegalArgumentException("Invalid value"); @@ -201,12 +202,10 @@ public final class QrCode { */ public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments - if (ecl == null) - throw new NullPointerException(); + Objects.requireNonNull(ecl); if (ver < 1 || ver > 40 || mask < -1 || mask > 7) throw new IllegalArgumentException("Value out of range"); - if (dataCodewords == null) - throw new NullPointerException(); + Objects.requireNonNull(dataCodewords); // Initialize fields version = ver; @@ -234,8 +233,7 @@ public final class QrCode { */ public QrCode(QrCode qr, int mask) { // Check arguments - if (qr == null) - throw new NullPointerException(); + Objects.requireNonNull(qr); if (mask < -1 || mask > 7) throw new IllegalArgumentException("Mask value out of range"); @@ -497,8 +495,7 @@ public final class QrCode { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire // data area of this QR Code symbol. Function modules need to be marked off before this is called. private void drawCodewords(byte[] data) { - if (data == null) - throw new NullPointerException(); + Objects.requireNonNull(data); if (data.length != getNumRawDataModules(version) / 8) throw new IllegalArgumentException(); @@ -832,8 +829,7 @@ public final class QrCode { * @throws NullPointerException if the data is {@code null} */ public byte[] getRemainder(byte[] data) { - if (data == null) - throw new NullPointerException(); + Objects.requireNonNull(data); // Compute the remainder by performing polynomial division byte[] result = new byte[coefficients.length]; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 35ce18e..b7abb7c 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -28,6 +28,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.regex.Pattern; @@ -50,8 +51,7 @@ public final class QrSegment { * @throws NullPointerException if the array is {@code null} */ public static QrSegment makeBytes(byte[] data) { - if (data == null) - throw new NullPointerException(); + Objects.requireNonNull(data); return new QrSegment(Mode.BYTE, data.length, data, data.length * 8); } @@ -64,8 +64,7 @@ public final class QrSegment { * @throws IllegalArgumentException if the string contains non-digit characters */ public static QrSegment makeNumeric(String digits) { - if (digits == null) - throw new NullPointerException(); + Objects.requireNonNull(digits); if (!NUMERIC_REGEX.matcher(digits).matches()) throw new IllegalArgumentException("String contains non-numeric characters"); @@ -89,8 +88,7 @@ public final class QrSegment { * @throws IllegalArgumentException if the string contains non-encodable characters */ public static QrSegment makeAlphanumeric(String text) { - if (text == null) - throw new NullPointerException(); + Objects.requireNonNull(text); if (!ALPHANUMERIC_REGEX.matcher(text).matches()) throw new IllegalArgumentException("String contains unencodable characters in alphanumeric mode"); @@ -115,8 +113,7 @@ public final class QrSegment { * @throws NullPointerException if the text is {@code null} */ public static List makeSegments(String text) { - if (text == null) - throw new NullPointerException(); + Objects.requireNonNull(text); // Select the most efficient segment encoding automatically List result = new ArrayList<>(); @@ -160,8 +157,8 @@ public final class QrSegment { * @throws IllegalArgumentException if the character count or bit length are negative or invalid */ public QrSegment(Mode md, int numCh, byte[] b, int bitLen) { - if (md == null || b == null) - throw new NullPointerException(); + Objects.requireNonNull(md); + Objects.requireNonNull(b); if (numCh < 0 || bitLen < 0 || bitLen > b.length * 8L) throw new IllegalArgumentException("Invalid value"); mode = md; @@ -188,15 +185,13 @@ public final class QrSegment { // Package-private helper function. static int getTotalBits(List segs, int version) { - if (segs == null) - throw new NullPointerException(); + Objects.requireNonNull(segs); if (version < 1 || version > 40) throw new IllegalArgumentException("Version number out of range"); int result = 0; for (QrSegment seg : segs) { - if (seg == null) - throw new NullPointerException(); + Objects.requireNonNull(seg); int ccbits = seg.mode.numCharCountBits(version); // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index fe7be0e..ef813bd 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; +import java.util.Objects; public final class QrSegmentAdvanced { @@ -57,8 +58,8 @@ public final class QrSegmentAdvanced { */ public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { // Check arguments - if (text == null || ecl == null) - throw new NullPointerException(); + Objects.requireNonNull(text); + Objects.requireNonNull(ecl); if (!(1 <= minVersion && minVersion <= maxVersion && maxVersion <= 40)) throw new IllegalArgumentException("Invalid value"); @@ -238,8 +239,7 @@ public final class QrSegmentAdvanced { * @see #isEncodableAsKanji(String) */ public static QrSegment makeKanjiSegment(String text) { - if (text == null) - throw new NullPointerException(); + Objects.requireNonNull(text); BitBuffer bb = new BitBuffer(); for (int i = 0; i < text.length(); i++) { int val = UNICODE_TO_QR_KANJI[text.charAt(i)]; @@ -262,8 +262,7 @@ public final class QrSegmentAdvanced { * @see #makeKanjiSegment(String) */ public static boolean isEncodableAsKanji(String text) { - if (text == null) - throw new NullPointerException(); + Objects.requireNonNull(text); for (int i = 0; i < text.length(); i++) { if (UNICODE_TO_QR_KANJI[text.charAt(i)] == -1) return false; From 8033f9cec037639a84baffcc1c7b33dbcb5a8513 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 10 Feb 2017 03:17:18 +0000 Subject: [PATCH 050/810] Fixed a constant value in QrSegmentAdvanced, which affects how the optimal segment switching is selected. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index ef813bd..5caf956 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -109,7 +109,7 @@ public final class QrSegmentAdvanced { char c = (char)data[i]; result[0][j] = result[0][i] + 48; // 8 bits per byte if (isAlphanumeric(c)) - result[1][j] = result[1][i] + 39; // 6.5 bits per alphanumeric char + result[1][j] = result[1][i] + 33; // 5.5 bits per alphanumeric char if (isNumeric(c)) result[2][j] = result[2][i] + 20; // 3.33 bits per alphanumeric char @@ -148,7 +148,7 @@ public final class QrSegmentAdvanced { if (curMode == NUMERIC) { if (isNumeric(c)) curMode = NUMERIC; - else if (isAlphanumeric(c) && (bitCosts[1][i] + 39 + 5) / 6 * 6 + numberCost == bitCosts[2][i + 1]) + else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + numberCost == bitCosts[2][i + 1]) curMode = ALPHANUMERIC; else curMode = BYTE; @@ -162,7 +162,7 @@ public final class QrSegmentAdvanced { } else if (curMode == BYTE) { if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) curMode = NUMERIC; - else if (isAlphanumeric(c) && (bitCosts[1][i] + 39 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) curMode = ALPHANUMERIC; else curMode = BYTE; From e062a386d0385bf2e7b56435934870954609a599 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 10 Feb 2017 03:19:39 +0000 Subject: [PATCH 051/810] Deleted copyright year in all source files, updated year in readme document. --- Readme.markdown | 2 +- cpp/BitBuffer.cpp | 2 +- cpp/BitBuffer.hpp | 2 +- cpp/QrCode.cpp | 2 +- cpp/QrCode.hpp | 2 +- cpp/QrCodeGeneratorDemo.cpp | 2 +- cpp/QrSegment.cpp | 2 +- cpp/QrSegment.hpp | 2 +- java/io/nayuki/qrcodegen/BitBuffer.java | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 2 +- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 2 +- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 2 +- javascript/qrcodegen-demo.js | 2 +- javascript/qrcodegen-js-demo.html | 2 +- javascript/qrcodegen.js | 2 +- python/qrcodegen-demo.py | 2 +- python/qrcodegen.py | 2 +- python/setup.py | 2 +- 19 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 0c7698a..214a480 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -95,7 +95,7 @@ C++ language: License ------- -Copyright © 2016 Project Nayuki +Copyright © 2017 Project Nayuki [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) (MIT License) diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp index 8028a21..4b48cb7 100644 --- a/cpp/BitBuffer.cpp +++ b/cpp/BitBuffer.cpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index 89f032e..0a11a98 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index cab7433..ef37674 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 314025e..9ae0c11 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index f126ca4..8581278 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -4,7 +4,7 @@ * Run this command-line program with no arguments. The program computes a bunch of demonstration * QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 2b76cf4..2ae2242 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 741a6dc..d46e96d 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -1,7 +1,7 @@ /* * QR Code generator library (C++) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index d918570..4c5d0ee 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -1,7 +1,7 @@ /* * QR Code generator library (Java) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 69bf1ee..4642fe2 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -1,7 +1,7 @@ /* * QR Code generator library (Java) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index c29b971..509a126 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -4,7 +4,7 @@ * Run this command-line program with no arguments. The program creates/overwrites a bunch of * PNG and SVG files in the current working directory to demonstrate the creation of QR Codes. * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index b7abb7c..59aa5df 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -1,7 +1,7 @@ /* * QR Code generator library (Java) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 5caf956..93dd67c 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -1,7 +1,7 @@ /* * QR Code generator library - Optional advanced logic (Java) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index e4fedf3..b9caba1 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -1,7 +1,7 @@ /* * QR Code generator demo (JavaScript) * - * Copyright (c) 2016 Project Nayuki + * Copyright (c) Project Nayuki * https://www.nayuki.io/page/qr-code-generator-library * * (MIT License) diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index 36d4849..4cd757d 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -1,7 +1,7 @@ + + + + + QR Code generator library demo (TypeScript) + + + + +

QR Code generator demo library (TypeScript)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Text string:
QR Code: + + + + + +
Error correction: + + + + +
Output format: + + +
Border: modules
Scale: pixels per module
Version range:Minimum = , maximum =
Mask pattern: (−1 for automatic, 0 to 7 for manual)
Boost ECC:
Statistics:
SVG XML code: + +
+
+ + + +
+

Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

+ + diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts new file mode 100644 index 0000000..8702e73 --- /dev/null +++ b/typescript/qrcodegen.ts @@ -0,0 +1,1013 @@ +/* + * QR Code generator library (TypeScript) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +namespace qrcodegen { + + type bit = number; + type byte = number; + type int = number; + + + /*---- QR Code symbol class ----*/ + + /* + * A class that represents an immutable square grid of black and white cells for a QR Code symbol, + * with associated static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels. + * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, + * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor + * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. + */ + export class QrCode { + + /*-- Public static factory functions --*/ + + // Returns a QR Code symbol 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 + // Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible + // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + // ecl argument if it can be done without increasing the version. + public static encodeText(text: string, ecl: QrCode_Ecc): QrCode { + let segs: Array = qrcodegen.QrSegment.makeSegments(text); + return QrCode.encodeSegments(segs, ecl); + } + + + // Returns a QR Code symbol representing the given binary data string at the given error correction level. + // This function always encodes using the binary segment mode, not any text mode. The maximum number of + // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + public static encodeBinary(data: Array, ecl: QrCode_Ecc): QrCode { + let seg: QrSegment = qrcodegen.QrSegment.makeBytes(data); + return QrCode.encodeSegments([seg], ecl); + } + + + // Returns a QR Code symbol representing the given data segments with the given encoding parameters. + // The smallest possible QR Code version within the given range is automatically chosen for the output. + // This function allows the user to create a custom sequence of segments that switches + // between modes (such as alphanumeric and binary) to encode text more efficiently. + // This function is considered to be lower level than simply encoding text or binary data. + public static encodeSegments(segs: Array, ecl: QrCode_Ecc, + minVersion: int = 1, maxVersion: int = 1, + mask: int = -1, boostEcl: boolean = true): QrCode { + + if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION) + || mask < -1 || mask > 7) + throw "Invalid value"; + + // Find the minimal version number to use + let version: int; + let dataUsedBits: int; + for (version = minVersion; ; version++) { + let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + let usedBits: number|null = QrSegment.getTotalBits(segs, version); + if (usedBits != null && usedBits <= dataCapacityBits) { + dataUsedBits = usedBits; + break; // This version number is found to be suitable + } + if (version >= maxVersion) // All versions in the range could not fit the given data + throw "Data too long"; + } + + // Increase the error correction level while the data still fits in the current version number + [QrCode_Ecc.MEDIUM, QrCode_Ecc.QUARTILE, QrCode_Ecc.HIGH].forEach((newEcl: QrCode_Ecc) => { + if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) + ecl = newEcl; + }); + + // Create the data bit string by concatenating all segments + let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; + let bb = new BitBuffer(); + segs.forEach((seg: QrSegment) => { + bb.appendBits(seg.mode.modeBits, 4); + bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); + seg.getBits().forEach( + (b: bit) => bb.push(b)); + }); + + // Add terminator and pad up to a byte if applicable + bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); + bb.appendBits(0, (8 - bb.length % 8) % 8); + + // Pad with alternate bytes until data capacity is reached + for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + if (bb.length % 8 != 0) + throw "Assertion error"; + + // Create the QR Code symbol + return new QrCode(bb.getBytes(), mask, version, ecl); + } + + + /*-- Fields --*/ + + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + public readonly version: int; + + // The width and height of this QR Code symbol, measured in modules. + // Always equal to version * 4 + 17, in the range 21 to 177. + public readonly size: int; + + // The error correction level used in this QR Code symbol. + public readonly errorCorrectionLevel: QrCode_Ecc; + + // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if the constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + public readonly mask: int; + + // The modules of this QR Code symbol (false = white, true = black). + private readonly modules: Array> = []; + + // Indicates function modules that are not subjected to masking. + private readonly isFunction: Array> = []; + + + public constructor(datacodewords: Array, mask: int, version: int, errCorLvl: QrCode_Ecc) { + // Check arguments and handle simple scalar fields + if (mask < -1 || mask > 7) + throw "Mask value out of range"; + if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) + throw "Version value out of range"; + this.version = version; + this.size = version * 4 + 17; + this.errorCorrectionLevel = errCorLvl; + + // Initialize both grids to be size*size arrays of Boolean false + let row: Array = []; + for (let i = 0; i < this.size; i++) + row.push(false); + for (let i = 0; i < this.size; i++) { + this.modules.push(row.slice()); + this.isFunction.push(row.slice()); + } + + // Handle grid fields, draw function patterns, draw all codewords + this.drawFunctionPatterns(); + let allCodewords: Array = this.appendErrorCorrection(datacodewords); + this.drawCodewords(allCodewords); + + // Handle masking + if (mask == -1) { // Automatically choose best mask + let minPenalty: int = 1000000000; + for (let i = 0; i < 8; i++) { + this.drawFormatBits(i); + this.applyMask(i); + let penalty: int = this.getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + this.applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw "Assertion error"; + this.mask = mask; + this.drawFormatBits(mask); // Overwrite old format bits + this.applyMask(mask); // Apply the final choice of mask + } + + + /*-- Accessor methods --*/ + + // Returns the color of the module (pixel) at the given coordinates, which is either + // false for white or true for black. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then false (white) is returned. + public getModule(x: int, y: int): boolean { + return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x]; + } + + + /*-- Public instance methods --*/ + + // Draws this QR Code symbol with the given module scale and number of modules onto the given HTML canvas element. + // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely + // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. + public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { + if (scale <= 0 || border < 0) + throw "Value out of range"; + let width: int = (this.size + border * 2) * scale; + canvas.width = width; + canvas.height = width; + let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; + for (let y = -border; y < this.size + border; y++) { + for (let x = -border; x < this.size + border; x++) { + ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + } + + + // Based on the given number of border modules to add as padding, this returns a + // string whose contents represents an SVG XML file that depicts this QR Code symbol. + // Note that Unix newlines (\n) are always used, regardless of the platform. + public toSvgString(border: int): string { + if (border < 0) + throw "Border must be non-negative"; + let parts: Array = []; + for (let y = -border; y < this.size + border; y++) { + for (let x = -border; x < this.size + border; x++) { + if (this.getModule(x, y)) + parts.push(`M${x + border},${y + border}h1v1h-1z`); + } + } + return ` + + + + + +` + } + + + /*-- Private helper methods for constructor: Drawing function modules --*/ + + private drawFunctionPatterns(): void { + // Draw horizontal and vertical timing patterns + for (let i = 0; i < this.size; i++) { + this.setFunctionModule(6, i, i % 2 == 0); + this.setFunctionModule(i, 6, i % 2 == 0); + } + + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + this.drawFinderPattern(3, 3); + this.drawFinderPattern(this.size - 4, 3); + this.drawFinderPattern(3, this.size - 4); + + // Draw numerous alignment patterns + let alignPatPos: Array = QrCode.getAlignmentPatternPositions(this.version); + let numAlign: int = alignPatPos.length; + for (let i = 0; i < numAlign; i++) { + for (let j = 0; j < numAlign; j++) { + if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) + continue; // Skip the three finder corners + else + this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); + } + } + + // Draw configuration data + this.drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + this.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 drawFormatBits(mask: int): void { + // Calculate error correction code and pack bits + let data: int = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + let rem: int = data; + for (let i = 0; i < 10; i++) + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + data = data << 10 | rem; + data ^= 0x5412; // uint15 + if (data >>> 15 != 0) + throw "Assertion error"; + + // Draw first copy + for (let i = 0; i <= 5; i++) + this.setFunctionModule(8, i, getBit(data, i)); + this.setFunctionModule(8, 7, getBit(data, 6)); + this.setFunctionModule(8, 8, getBit(data, 7)); + this.setFunctionModule(7, 8, getBit(data, 8)); + for (let i = 9; i < 15; i++) + this.setFunctionModule(14 - i, 8, getBit(data, i)); + + // Draw second copy + for (let i = 0; i <= 7; i++) + this.setFunctionModule(this.size - 1 - i, 8, getBit(data, i)); + for (let i = 8; i < 15; i++) + this.setFunctionModule(8, this.size - 15 + i, getBit(data, i)); + this.setFunctionModule(8, this.size - 8, true); + } + + + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field (which only has an effect for 7 <= version <= 40). + private drawVersion(): void { + if (this.version < 7) + return; + + // Calculate error correction code and pack bits + let rem: int = this.version; // version is uint6, in the range [7, 40] + for (let i = 0; i < 12; i++) + rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); + let data: int = this.version << 12 | rem; // uint18 + if (data >>> 18 != 0) + throw "Assertion error"; + + // Draw two copies + for (let i = 0; i < 18; i++) { + let bt: boolean = getBit(data, i); + let a: int = this.size - 11 + i % 3; + let b: int = Math.floor(i / 3); + this.setFunctionModule(a, b, bt); + this.setFunctionModule(b, a, bt); + } + } + + + // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + private drawFinderPattern(x: int, y: int): void { + for (let i = -4; i <= 4; i++) { + for (let j = -4; j <= 4; j++) { + let dist: int = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm + let xx: int = x + j; + let yy: int = y + i; + if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size) + this.setFunctionModule(xx, yy, dist != 2 && dist != 4); + } + } + } + + + // Draws a 5*5 alignment pattern, with the center module at (x, y). + private drawAlignmentPattern(x: int, y: int): void { + for (let i = -2; i <= 2; i++) { + for (let j = -2; j <= 2; j++) + this.setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + } + } + + + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in range. + private setFunctionModule(x: int, y: int, isBlack: boolean): void { + this.modules[y][x] = isBlack; + this.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 appendErrorCorrection(data: Array): Array { + const ver: int = this.version; + const ecl: QrCode_Ecc = this.errorCorrectionLevel; + if (data.length != QrCode.getNumDataCodewords(ver, ecl)) + throw "Invalid argument"; + + // Calculate parameter numbers + let numBlocks: int = QrCode_NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + let blockEccLen: int = QrCode_ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver]; + let rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8); + let numShortBlocks: int = numBlocks - rawCodewords % numBlocks; + let shortBlockLen: int = Math.floor(rawCodewords / numBlocks); + + // Split data into blocks and append ECC to each block + let blocks: Array> = []; + let rs = new ReedSolomonGenerator(blockEccLen); + for (let i = 0, k = 0; i < numBlocks; i++) { + let dat: Array = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); + k += dat.length; + let ecc: Array = rs.getRemainder(dat); + if (i < numShortBlocks) + dat.push(0); + ecc.forEach( + (b: byte) => dat.push(b)); + blocks.push(dat); + } + + // Interleave (not concatenate) the bytes from every block into a single sequence + let result: Array = []; + for (let i = 0; i < blocks[0].length; i++) { + for (let j = 0; j < blocks.length; j++) { + // Skip the padding byte in short blocks + if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) + result.push(blocks[j][i]); + } + } + if (result.length != rawCodewords) + throw "Assertion error"; + return result; + } + + + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code symbol. Function modules need to be marked off before this is called. + private drawCodewords(data: Array): void { + if (data.length != Math.floor(QrCode.getNumRawDataModules(this.version) / 8)) + throw "Invalid argument"; + let i: int = 0; // Bit index into the data + // Do the funny zigzag scan + for (let right = this.size - 1; right >= 1; right -= 2) { // Index of right column in each column pair + if (right == 6) + right = 5; + for (let vert = 0; vert < this.size; vert++) { // Vertical counter + for (let j = 0; j < 2; j++) { + let x: int = right - j; // Actual x coordinate + let upward: boolean = ((right + 1) & 2) == 0; + let y: int = upward ? this.size - 1 - vert : vert; // Actual y coordinate + if (!this.isFunction[y][x] && i < data.length * 8) { + this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); + i++; + } + // If there are any remainder bits (0 to 7), they are already + // set to 0/false/white when the grid of modules was initialized + } + } + } + if (i != data.length * 8) + throw "Assertion error"; + } + + + // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical + // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. + // This means it is possible to apply a mask, undo it, and try another mask. Note that a final + // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + private applyMask(mask: int): void { + if (mask < 0 || mask > 7) + throw "Mask value out of range"; + for (let y = 0; y < this.size; y++) { + for (let x = 0; x < this.size; x++) { + let invert: boolean; + switch (mask) { + 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 = (Math.floor(x / 3) + Math.floor(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 "Assertion error"; + } + if (invert && !this.isFunction[y][x]) + this.modules[y][x] = !this.modules[y][x]; + } + } + } + + + // 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 getPenaltyScore(): int { + let result: int = 0; + + // Adjacent modules in row having same color + for (let y = 0; y < this.size; y++) { + for (let x = 0, runX = 0, colorX = false; x < this.size; x++) { + if (x == 0 || this.modules[y][x] != colorX) { + colorX = this.modules[y][x]; + runX = 1; + } else { + runX++; + if (runX == 5) + result += QrCode.PENALTY_N1; + else if (runX > 5) + result++; + } + } + } + // Adjacent modules in column having same color + for (let x = 0; x < this.size; x++) { + for (let y = 0, runY = 0, colorY = false; y < this.size; y++) { + if (y == 0 || this.modules[y][x] != colorY) { + colorY = this.modules[y][x]; + runY = 1; + } else { + runY++; + if (runY == 5) + result += QrCode.PENALTY_N1; + else if (runY > 5) + result++; + } + } + } + + // 2*2 blocks of modules having same color + for (let y = 0; y < this.size - 1; y++) { + for (let x = 0; x < this.size - 1; x++) { + let color: boolean = this.modules[y][x]; + if ( color == this.modules[y][x + 1] && + color == this.modules[y + 1][x] && + color == this.modules[y + 1][x + 1]) + result += QrCode.PENALTY_N2; + } + } + + // Finder-like pattern in rows + for (let y = 0; y < this.size; y++) { + for (let x = 0, bits = 0; x < this.size; x++) { + bits = ((bits << 1) & 0x7FF) | (this.modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + // Finder-like pattern in columns + for (let x = 0; x < this.size; x++) { + for (let y = 0, bits = 0; y < this.size; y++) { + bits = ((bits << 1) & 0x7FF) | (this.modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + result += QrCode.PENALTY_N3; + } + } + + // Balance of black and white modules + let black: int = 0; + this.modules.forEach((row: Array) => { + row.forEach((color: boolean) => { + if (color) + black++; + }); + }); + let total: int = this.size * this.size; + // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% + for (let k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) + result += QrCode.PENALTY_N4; + return result; + } + + + /*-- Private static helper functions QrCode --*/ + + // Returns a sequence of positions of the alignment patterns in ascending order. These positions are + // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). + // This stateless pure function could be implemented as table of 40 variable-length lists of integers. + private static getAlignmentPatternPositions(ver: int): Array { + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) + throw "Version number out of range"; + else if (ver == 1) + return []; + else { + let size: int = ver * 4 + 17; + let numAlign: int = Math.floor(ver / 7) + 2; + let step: int; + if (ver != 32) + step = Math.ceil((size - 13) / (2 * numAlign - 2)) * 2; + else // C-C-C-Combo breaker! + step = 26; + + let result: Array = [6]; + for (let i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.splice(1, 0, pos); + 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. + private static getNumRawDataModules(ver: int): int { + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) + throw "Version number out of range"; + let result: int = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + let numAlign: int = Math.floor(ver / 7) + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) + result -= 18 * 2; // Subtract version information + } + return result; + } + + + // 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. + private static getNumDataCodewords(ver: int, ecl: QrCode_Ecc): int { + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) + throw "Version number out of range"; + return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - + QrCode_ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * + QrCode_NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + } + + + /*-- Constants --*/ + + public static get MIN_VERSION(): int { return 1; } + public static get MAX_VERSION(): int { return 40; } + + + // For use in getPenaltyScore(), when evaluating which mask is best. + private static get PENALTY_N1(): int { return 3; } + private static get PENALTY_N2(): int { return 3; } + private static get PENALTY_N3(): int { return 40; } + private static get PENALTY_N4(): int { return 10; } + + } + + /*-- Private tables of constants --*/ + + const QrCode_ECC_CODEWORDS_PER_BLOCK: Array> = [ + // 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 + ]; + + const QrCode_NUM_ERROR_CORRECTION_BLOCKS: Array> = [ + // 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 + ]; + + + // Returns true iff the i'th bit of x is set to 1. + function getBit(x: int, i: int): boolean { + return ((x >>> i) & 1) != 0; + } + + + + /*---- Public helper enumeration ----*/ + + /* + * Represents the error correction level used in a QR Code symbol. + */ + export class QrCode_Ecc { + + /*-- Constants --*/ + + public static get LOW () { return new QrCode_Ecc(0, 1); } + public static get MEDIUM () { return new QrCode_Ecc(1, 0); } + public static get QUARTILE() { return new QrCode_Ecc(2, 3); } + public static get HIGH () { return new QrCode_Ecc(3, 2); } + + + /*-- Fields --*/ + + // In the range 0 to 3 (unsigned 2-bit integer). + public readonly ordinal: int; + + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). + public readonly formatBits: int; + + + /*-- Constructor --*/ + + private constructor(ord: int, fb: int) { + this.ordinal = ord; + this.formatBits = fb; + } + + } + + + + /*---- Data segment class ----*/ + + /* + * A public class that represents a character string to be encoded in a QR Code symbol. + * Each segment has a mode, and a sequence of characters that is already encoded as + * a sequence of bits. Instances of this class are immutable. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ + export class QrSegment { + + /*-- Static factory functions --*/ + + // Returns a segment representing the given binary data encoded in byte mode. + public static makeBytes(data: Array): QrSegment { + let bb = new BitBuffer(); + data.forEach( + (b: byte) => bb.appendBits(b, 8)); + return new QrSegment(QrSegment_Mode.BYTE, data.length, bb); + } + + + // Returns a segment representing the given string of decimal digits encoded in numeric mode. + public static makeNumeric(digits: string): QrSegment { + if (!this.NUMERIC_REGEX.test(digits)) + throw "String contains non-numeric characters"; + let bb = new BitBuffer(); + let i: int; + for (i = 0; i + 3 <= digits.length; i += 3) // Process groups of 3 + bb.appendBits(parseInt(digits.substr(i, 3), 10), 10); + let rem: int = digits.length - i; + if (rem > 0) // 1 or 2 digits remaining + bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); + return new QrSegment(QrSegment_Mode.NUMERIC, digits.length, bb); + } + + + // Returns a segment representing the given text string encoded in alphanumeric mode. + // The characters allowed are: 0 to 9, A to Z (uppercase only), space, + // dollar, percent, asterisk, plus, hyphen, period, slash, colon. + public static makeAlphanumeric(text: string): QrSegment { + if (!this.ALPHANUMERIC_REGEX.test(text)) + throw "String contains unencodable characters in alphanumeric mode"; + let bb = new BitBuffer(); + let i: int; + for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2 + let temp: int = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; + temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); + bb.appendBits(temp, 11); + } + if (i < text.length) // 1 character remaining + bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); + return new QrSegment(QrSegment_Mode.ALPHANUMERIC, text.length, bb); + } + + + // Returns a new mutable list of zero or more segments to represent the given Unicode text string. + // The result may use various segment modes and switch modes to optimize the length of the bit stream. + public static makeSegments(text: string): Array { + // Select the most efficient segment encoding automatically + if (text == "") + return []; + else if (this.NUMERIC_REGEX.test(text)) + return [QrSegment.makeNumeric(text)]; + else if (this.ALPHANUMERIC_REGEX.test(text)) + return [QrSegment.makeAlphanumeric(text)]; + else + return [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))]; + } + + + // Returns a segment representing an Extended Channel Interpretation + // (ECI) designator with the given assignment value. + public static makeEci(assignVal: int): QrSegment { + let bb = new BitBuffer(); + if (0 <= assignVal && assignVal < (1 << 7)) + bb.appendBits(assignVal, 8); + else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(assignVal, 14); + } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + bb.appendBits(6, 3); + bb.appendBits(assignVal, 21); + } else + throw "ECI assignment value out of range"; + return new QrSegment(QrSegment_Mode.ECI, 0, bb); + } + + + /*-- Fields --*/ + + // The mode indicator for this segment. + public readonly mode: QrSegment_Mode; + + // The length of this segment's unencoded data, measured in characters. Always zero or positive. + public readonly numChars: int; + + private readonly bitData: Array; + + + /*-- Constructor --*/ + + public constructor(mode: QrSegment_Mode, numChars: int, bitData: Array) { + if (numChars < 0) + throw "Invalid argument"; + this.mode = mode; + this.numChars = numChars; + this.bitData = bitData.slice(); // Make defensive copy + } + + + /*-- Methods --*/ + + // Returns a copy of all bits, which is an array of 0s and 1s. + public getBits(): Array { + return this.bitData.slice(); // Make defensive copy + } + + + // Package-private helper function. + public static getTotalBits(segs: Array, version: int): int|null { + if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) + throw "Version number out of range"; + let result: int = 0; + for (let i = 0; i < segs.length; i++) { + let seg: QrSegment = segs[i]; + let ccbits: int = seg.mode.numCharCountBits(version); + // Fail if segment length value doesn't fit in the length field's bit-width + if (seg.numChars >= (1 << ccbits)) + return null; + result += 4 + ccbits + seg.getBits().length; + } + return result; + } + + + // Returns a new array of bytes representing the given string encoded in UTF-8. + private static toUtf8ByteArray(str: string): Array { + str = encodeURI(str); + let result: Array = []; + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) != "%") + result.push(str.charCodeAt(i)); + else { + result.push(parseInt(str.substr(i + 1, 2), 16)); + i += 2; + } + } + return result; + } + + + /*-- Constants --*/ + + // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + public static get NUMERIC_REGEX(): RegExp { + return /^[0-9]*$/; + } + + // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + public static get ALPHANUMERIC_REGEX(): RegExp { + return /^[A-Z0-9 $%*+.\/:-]*$/; + } + + // The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. + private static get ALPHANUMERIC_CHARSET(): string { + return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + } + + } + + + + /*---- Public helper enumeration ----*/ + + /* + * Represents the mode field of a segment. Immutable. + */ + export class QrSegment_Mode { + + /*-- Constants --*/ + + public static get NUMERIC () { return new QrSegment_Mode(0x1, [10, 12, 14]); } + public static get ALPHANUMERIC() { return new QrSegment_Mode(0x2, [ 9, 11, 13]); } + public static get BYTE () { return new QrSegment_Mode(0x4, [ 8, 16, 16]); } + public static get KANJI () { return new QrSegment_Mode(0x8, [ 8, 10, 12]); } + public static get ECI () { return new QrSegment_Mode(0x7, [ 0, 0, 0]); } + + + /*-- Fields --*/ + + // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + public readonly modeBits: int; + + private readonly numBitsCharCount: [int,int,int]; + + + /*-- Constructor --*/ + + private constructor(mode: int, ccbits: [int,int,int]) { + this.modeBits = mode; + this.numBitsCharCount = ccbits; + } + + + /*-- Method --*/ + + // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + public numCharCountBits(ver: int): int { + if ( 1 <= ver && ver <= 9) return this.numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return this.numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return this.numBitsCharCount[2]; + else throw "Version number out of range"; + } + + } + + + + /*---- Private helper classes ----*/ + + /* + * Computes the Reed-Solomon error correction codewords for a sequence of data codewords + * at a given degree. Objects are immutable, and the state only depends on the degree. + * This class exists because each data block in a QR Code shares the same the divisor polynomial. + */ + class ReedSolomonGenerator { + + // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which + // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + private readonly coefficients: Array = []; + + + // Creates a Reed-Solomon ECC generator for the given degree. This could be implemented + // as a lookup table over all possible parameter values, instead of as an algorithm. + public constructor(degree: int) { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + let coefs = this.coefficients; + + // Start with the monomial x^0 + for (let i = 0; i < degree - 1; i++) + coefs.push(0); + coefs.push(1); + + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // drop the highest term, and store the rest of the coefficients in order of descending powers. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + let root = 1; + for (let i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (let j = 0; j < coefs.length; j++) { + coefs[j] = ReedSolomonGenerator.multiply(coefs[j], root); + if (j + 1 < coefs.length) + coefs[j] ^= coefs[j + 1]; + } + root = ReedSolomonGenerator.multiply(root, 0x02); + } + } + + + // Computes and returns the Reed-Solomon error correction codewords for the given + // sequence of data codewords. The returned object is always a new byte array. + // This method does not alter this object's state (because it is immutable). + public getRemainder(data: Array): Array { + // Compute the remainder by performing polynomial division + let result: Array = this.coefficients.map(_ => 0); + data.forEach((b: byte) => { + let factor: byte = b ^ (result.shift() as int); + result.push(0); + for (let i = 0; i < result.length; i++) + result[i] ^= ReedSolomonGenerator.multiply(this.coefficients[i], factor); + }); + 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 multiply(x: byte, y: byte): byte { + if (x >>> 8 != 0 || y >>> 8 != 0) + throw "Byte out of range"; + // Russian peasant multiplication + let z: int = 0; + for (let i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if (z >>> 8 != 0) + throw "Assertion error"; + return z as byte; + } + + } + + + + /* + * An appendable sequence of bits. The implicit constructor creates an empty bit buffer (length 0). + */ + class BitBuffer extends Array { + + // Packs this buffer's bits into bytes in big endian, + // padding with '0' bit values, and returns the new array. + public getBytes(): Array { + let result: Array = []; + while (result.length * 8 < this.length) + result.push(0); + this.forEach((b: bit, i: int) => + result[i >>> 3] |= b << (7 - (i & 7))); + return result; + } + + + // Appends the given number of low bits of the given + // value to this sequence. Requires 0 <= val < 2^len. + public appendBits(val: int, len: int): void { + if (len < 0 || len > 31 || val >>> len != 0) + throw "Value out of range"; + for (let i = len - 1; i >= 0; i--) // Append bit by bit + this.push((val >>> i) & 1); + } + + } + +} From 1a5207ece31b906fcc86eecb880d9606d128188a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 23 Jun 2018 16:18:34 +0000 Subject: [PATCH 330/810] Deleted unused method in JavaScript code (related to commit b86466ecd5b0). --- javascript/qrcodegen.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 3bd2c77..c20308a 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -142,16 +142,6 @@ var qrcodegen = new function() { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; }; - // (Package-private) Tests whether the module at the given coordinates is a function module (true) or not (false). - // The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then false is returned. - // The JavaScript version of this library has this method because it is impossible to access private variables of another object. - this.isFunctionModule = function(x, y) { - if (0 <= x && x < size && 0 <= y && y < size) - return isFunction[y][x]; - else - return false; // Infinite border - }; - /*---- Public instance methods ----*/ From 236efbeccd29524f36063a1b6398946847b05598 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 23 Jun 2018 16:18:47 +0000 Subject: [PATCH 331/810] Simplified toSvgString() in JavaScript code. --- javascript/qrcodegen.js | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index c20308a..707d67f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -169,27 +169,20 @@ var qrcodegen = new function() { this.toSvgString = function(border) { if (border < 0) throw "Border must be non-negative"; - var result = '\n'; - result += '\n'; - result += '\n'; - result += '\t\n'; - result += '\t\n' + + '\n' + + '\n' + + '\t\n' + + '\t\n' + + '\n'; }; From ea9b207b0ba881a70f81cc5b1ab35e7fc9b5a844 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 23 Jun 2018 16:21:04 +0000 Subject: [PATCH 332/810] Fixed JavaScript UTF-16 logic to handle a rare erroneous edge case. --- javascript/qrcodegen-demo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 2712985..191cc9a 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -109,7 +109,7 @@ function redrawQrCode() { var c = str.charCodeAt(i); if (c < 0xD800 || c >= 0xE000) continue; - else if (0xD800 <= c && c < 0xDC00) { // High surrogate + else if (0xD800 <= c && c < 0xDC00 && i + 1 < str.length) { // High surrogate i++; var d = str.charCodeAt(i); if (0xDC00 <= d && d < 0xE000) // Low surrogate From 80797f449eb3b33164688fc40aef107e6d68ad9c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 23 Jun 2018 16:21:40 +0000 Subject: [PATCH 333/810] Tweaked pluralization in section comments, in multiple languages. --- cpp/QrCode.hpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 2 +- rust/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index acc4fdd..e843bdf 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -120,7 +120,7 @@ class QrCode final { - /*---- Constructors ----*/ + /*---- Constructor ----*/ /* * Creates a new QR Code symbol with the given version number, error correction level, binary data array, diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 2b2344d..d1542fe 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -194,7 +194,7 @@ public final class QrCode { - /*---- Constructors ----*/ + /*---- Constructor ----*/ /** * Creates a new QR Code symbol with the specified version number, error correction level, binary data array, and mask number. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index af90c6c..c04333b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -163,7 +163,7 @@ impl QrCode { } - /*---- Constructors ----*/ + /*---- Constructor ----*/ // Creates a new QR Code symbol with the given version number, error correction level, // binary data array, and mask number. This is a cumbersome low-level constructor that From b652d6958e3ea184e3301fa3d5817afd89757302 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 23 Jun 2018 16:21:59 +0000 Subject: [PATCH 334/810] Tweaked comments and blank line, in multiple languages. --- javascript/qrcodegen-demo.js | 1 - javascript/qrcodegen.js | 2 +- rust/src/lib.rs | 2 ++ 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 191cc9a..42366a1 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -85,7 +85,6 @@ function redrawQrCode() { svgXml.value = qr.toSvgString(border); } - // Returns a string to describe the given list of segments. function describeSegments(segs) { if (segs.length == 0) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 707d67f..958b243 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -584,7 +584,7 @@ var qrcodegen = new function() { Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); - /*---- Private static helper functions QrCode ----*/ + /*---- Private static helper functions for QrCode ----*/ var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c04333b..e377938 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -189,6 +189,8 @@ impl QrCode { } + /*---- Public methods ----*/ + // Returns this QR Code's version, in the range [1, 40]. pub fn version(&self) -> Version { self.version From f2189395d091df07f84032786623057501220e1e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 03:29:46 +0000 Subject: [PATCH 335/810] Tweaked whitespace in TypeScript code to match other language versions. --- typescript/qrcodegen.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 8702e73..d61a304 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -625,20 +625,20 @@ namespace qrcodegen { const QrCode_ECC_CODEWORDS_PER_BLOCK: Array> = [ // 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 + //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 ]; const QrCode_NUM_ERROR_CORRECTION_BLOCKS: Array> = [ // 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 + //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 ]; From c0b684bc18d073f50918e4a3078e341686134fb0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 03:33:22 +0000 Subject: [PATCH 336/810] Tweaked comment for kanji demo in multiple languages, so that it corresponds to the order in the sample text string. --- c/qrcodegen-demo.c | 2 +- cpp/QrCodeGeneratorDemo.cpp | 2 +- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 2 +- python/qrcodegen-demo.py | 2 +- rust/examples/qrcodegen-demo.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c/qrcodegen-demo.c b/c/qrcodegen-demo.c index c06e2f7..b859d4b 100644 --- a/c/qrcodegen-demo.c +++ b/c/qrcodegen-demo.c @@ -190,7 +190,7 @@ static void doSegmentDemo(void) { } } - { // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + { // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; bool ok; diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 5c78b51..f241e1c 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -128,7 +128,7 @@ static void doSegmentDemo() { QrCode::Ecc::LOW); printQr(qr3); - // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters const char *madoka = // Encoded in UTF-8 "\xE3\x80\x8C\xE9\xAD\x94\xE6\xB3\x95\xE5" "\xB0\x91\xE5\xA5\xB3\xE3\x81\xBE\xE3\x81" diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index b05c2b2..0b98db3 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -132,7 +132,7 @@ public final class QrCodeGeneratorDemo { qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); - // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index 50e975e..7a1bfaa 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -106,7 +106,7 @@ def do_segment_demo(): qr = QrCode.encode_segments(segs, QrCode.Ecc.LOW) print_qr(qr) - # Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + # Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters madoka = u"\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F" qr = QrCode.encode_text(madoka, QrCode.Ecc.LOW) print_qr(qr) diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs index 1fa2d3f..c28ad72 100644 --- a/rust/examples/qrcodegen-demo.rs +++ b/rust/examples/qrcodegen-demo.rs @@ -114,7 +114,7 @@ fn do_segment_demo() { let qr = QrCode::encode_segments(&segs, QrCodeEcc::Low).unwrap(); print_qr(&qr); - // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters let madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; let qr = QrCode::encode_text(madoka, QrCodeEcc::Low).unwrap(); print_qr(&qr); From 67ebd5215dfd13177829d1ab9752c53febb248e7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 20:51:12 +0000 Subject: [PATCH 337/810] Renamed function in QrSegmentAdvanced to makeKanji(), for consistency with other static factories. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 34847e4..a57a006 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -237,7 +237,7 @@ public final class QrSegmentAdvanced { * @throws IllegalArgumentException if the string contains non-kanji-mode characters * @see #isEncodableAsKanji(String) */ - public static QrSegment makeKanjiSegment(String text) { + public static QrSegment makeKanji(String text) { Objects.requireNonNull(text); BitBuffer bb = new BitBuffer(); for (int i = 0; i < text.length(); i++) { @@ -258,7 +258,7 @@ public final class QrSegmentAdvanced { * @param text the string to test for encodability * @return {@code true} if and only if the string can be encoded in kanji mode * @throws NullPointerException if the string is {@code null} - * @see #makeKanjiSegment(String) + * @see #makeKanji(String) */ public static boolean isEncodableAsKanji(String text) { Objects.requireNonNull(text); From 49d9301b7745ce0911a4feb28125605c57fc8e07 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 20:51:36 +0000 Subject: [PATCH 338/810] Updated Javadoc text for QrSegmentAdvanced related to kanji encoding. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index a57a006..d4b8c9c 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -228,9 +228,9 @@ public final class QrSegmentAdvanced { /** * Returns a segment representing the specified string encoded in kanji mode. - *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, - * Asian punctuation, full-width ASCII}.
- * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
+ * Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. * @param text the text to be encoded, which must fall in the kanji mode subset of characters * @return a segment containing the data * @throws NullPointerException if the string is {@code null} @@ -252,9 +252,9 @@ public final class QrSegmentAdvanced { /** * Tests whether the specified text string can be encoded as a segment in kanji mode. - *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, hiragana, katakana, - * Asian punctuation, full-width ASCII}.
- * In particular, non-encodable characters are {normal ASCII, half-width katakana, more extensive Chinese hanzi}. + *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
+ * Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. * @param text the string to test for encodability * @return {@code true} if and only if the string can be encoded in kanji mode * @throws NullPointerException if the string is {@code null} From 31ba90d0db3188895ea77c02e573da51558f794a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 20:53:28 +0000 Subject: [PATCH 339/810] Tweaked QrCodeGeneratorDemo Java code to use QrSegmentAdvanced.makeKanji() instead of hard-coding the data words, while maintaining identical output image. --- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 0b98db3..6b6e880 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -137,18 +137,7 @@ public final class QrCodeGeneratorDemo { qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); - int[] kanjiChars = { // Kanji mode encoding (13 bits per character) - 0x0035, 0x1002, 0x0FC0, 0x0AED, 0x0AD7, - 0x015C, 0x0147, 0x0129, 0x0059, 0x01BD, - 0x018D, 0x018A, 0x0036, 0x0141, 0x0144, - 0x0001, 0x0000, 0x0249, 0x0240, 0x0249, - 0x0000, 0x0104, 0x0105, 0x0113, 0x0115, - 0x0000, 0x0208, 0x01FF, 0x0008, - }; - BitBuffer bb = new BitBuffer(); - for (int c : kanjiChars) - bb.appendBits(c, 13); - segs = Arrays.asList(new QrSegment(QrSegment.Mode.KANJI, kanjiChars.length, bb)); + segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-kanji-QR.png"); } From 31b092da2dbda164a88648720d7e6770f7ad7b3b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 21:02:12 +0000 Subject: [PATCH 340/810] Tweaked readme document's Java language code example to be more correct. --- Readme.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.markdown b/Readme.markdown index a0200c9..0ee2926 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -44,6 +44,7 @@ Java language: import java.awt.image.BufferedImage; import java.io.File; + import java.util.List; import javax.imageio.ImageIO; import io.nayuki.qrcodegen.*; From 52ae1e387f9d0c016fbfc399719e056e80b1ce38 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 23:41:32 +0000 Subject: [PATCH 341/810] Added integer overflow checks to toImage() and toSvgString(). --- src/io/nayuki/fastqrcodegen/QrCode.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index ac3d381..3a6e2e0 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -182,6 +182,9 @@ public final class QrCode { public BufferedImage toImage(int scale, int border) { if (scale <= 0 || border < 0) 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++) { @@ -203,6 +206,9 @@ public final class QrCode { public String toSvgString(int border) { if (border < 0) throw new IllegalArgumentException("Border must be non-negative"); + if (size + border * 2L > Integer.MAX_VALUE) + throw new IllegalArgumentException("Border too large"); + StringBuilder sb = new StringBuilder(); sb.append("\n"); sb.append("\n"); From b204202684272d02a1fd6ad725ccbd6b0f40c9e2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 23:41:53 +0000 Subject: [PATCH 342/810] Tweaked pluralization in section comments. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 2 +- src/io/nayuki/fastqrcodegen/QrCode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index d5b046b..630ad5b 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -37,7 +37,7 @@ final class BitBuffer { - /*---- Constructors ----*/ + /*---- Constructor ----*/ public BitBuffer() { data = new int[64]; diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 3a6e2e0..8fe6a3f 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -125,7 +125,7 @@ public final class QrCode { - /*---- Constructors ----*/ + /*---- Constructor ----*/ public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments From 9071594f6c0058bb544c11ae638f155f3d25eb05 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 23:42:17 +0000 Subject: [PATCH 343/810] Tweaked comment for kanji demo so that it corresponds to the order in the sample text string. --- src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java index c3aa2dd..d9b1c21 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java @@ -132,7 +132,7 @@ public final class QrCodeGeneratorDemo { qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); - // Illustration "Madoka": kanji, kana, Greek, Cyrillic, full-width Latin characters + // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); From 032b47d4e6128b824cf474587715edd72cbb55ab Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 24 Jun 2018 23:44:16 +0000 Subject: [PATCH 344/810] Added readme document. --- Readme.markdown | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Readme.markdown diff --git a/Readme.markdown b/Readme.markdown new file mode 100644 index 0000000..9e7380f --- /dev/null +++ b/Readme.markdown @@ -0,0 +1,79 @@ +Fast QR Code generator library +============================== + + +Introduction +------------ + +This Java library generates QR Code symbols, and its design is optimized for speed. It contrasts with another QR library by the same author which is slow but which optimizes for clarity and conciseness. The functionality of this library and its API are nearly identical to the slow library, but it runs anywhere from 1.5× to 6× as fast. + +Home page for the fast library (design explanation, benchmarks): https://www.nayuki.io/page/fast-qr-code-generator-library + +Home page for the slow library (live demo, QR Code introduction, competitor comparisons): [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) + + +Features +-------- + +Core features: + +* Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +* Output formats: Raw modules/pixels of the QR symbol, SVG XML string, `BufferedImage` raster bitmap +* Encodes numeric and special-alphanumeric text in less space than general text +* Open source code under the permissive MIT License + +Manual parameters: + +* User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +* User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +* User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +* User can create a list of data segments manually and add ECI segments + + +Examples +-------- + + import java.awt.image.BufferedImage; + import java.io.File; + import java.util.List; + import javax.imageio.ImageIO; + import io.nayuki.fastqrcodegen.*; + + // Simple operation + QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); + BufferedImage img = qr0.toImage(4, 10); + ImageIO.write(img, "png", new File("qr-code.png")); + + // Manual operation + List segs = QrSegment.makeSegments("3141592653589793238462643383"); + QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); + for (int y = 0; y < qr1.size; y++) { + for (int x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) + } + } + + +License +------- + +Copyright © 2018 Project Nayuki. (MIT License) +[https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +* The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + +* The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall the + authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising from, + out of or in connection with the Software or the use or other dealings in the + Software. From 4ca232b949981702df45c4f068d304c1785b5493 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 21 Aug 2018 23:12:42 +0000 Subject: [PATCH 345/810] Updated C++ code to use standard exception classes instead of bare C strings. --- cpp/BitBuffer.cpp | 3 ++- cpp/QrCode.cpp | 45 +++++++++++++++++++++++---------------------- cpp/QrSegment.cpp | 17 +++++++++-------- 3 files changed, 34 insertions(+), 31 deletions(-) diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp index d6e8cb2..ee2d277 100644 --- a/cpp/BitBuffer.cpp +++ b/cpp/BitBuffer.cpp @@ -21,6 +21,7 @@ * Software. */ +#include #include "BitBuffer.hpp" @@ -40,7 +41,7 @@ std::vector BitBuffer::getBytes() const { void BitBuffer::appendBits(std::uint32_t val, int len) { if (len < 0 || len > 31 || val >> len != 0) - throw "Value out of range"; + throw std::domain_error("Value out of range"); for (int i = len - 1; i >= 0; i--) // Append bit by bit this->push_back(((val >> i) & 1) != 0); } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index f17ab62..37f4c5f 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "BitBuffer.hpp" #include "QrCode.hpp" @@ -44,7 +45,7 @@ int QrCode::getFormatBits(Ecc ecl) { case Ecc::MEDIUM : return 0; case Ecc::QUARTILE: return 3; case Ecc::HIGH : return 2; - default: throw "Assertion error"; + default: throw std::logic_error("Assertion error"); } } @@ -64,7 +65,7 @@ QrCode QrCode::encodeBinary(const vector &data, Ecc ecl) { QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, int minVersion, int maxVersion, int mask, bool boostEcl) { if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) - throw "Invalid value"; + throw std::invalid_argument("Invalid value"); // Find the minimal version number to use int version, dataUsedBits; @@ -74,10 +75,10 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, 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 - throw "Data too long"; + throw std::length_error("Data too long"); } if (dataUsedBits == -1) - throw "Assertion error"; + throw std::logic_error("Assertion error"); // Increase the error correction level while the data still fits in the current version number for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { @@ -102,7 +103,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); if (bb.size() % 8 != 0) - throw "Assertion error"; + throw std::logic_error("Assertion error"); // Create the QR Code symbol return QrCode(version, ecl, bb.getBytes(), mask); @@ -119,7 +120,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) // Check arguments if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) - throw "Value out of range"; + throw std::domain_error("Value out of range"); // Draw function patterns, draw all codewords, do masking drawFunctionPatterns(); @@ -156,9 +157,9 @@ bool QrCode::getModule(int x, int y) const { std::string QrCode::toSvgString(int border) const { if (border < 0) - throw "Border must be non-negative"; + throw std::domain_error("Border must be non-negative"); if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) - throw "Border too large"; + throw std::overflow_error("Border too large"); std::ostringstream sb; sb << "\n"; @@ -224,7 +225,7 @@ void QrCode::drawFormatBits(int mask) { data = data << 10 | rem; data ^= 0x5412; // uint15 if (data >> 15 != 0) - throw "Assertion error"; + throw std::logic_error("Assertion error"); // Draw first copy for (int i = 0; i <= 5; i++) @@ -254,7 +255,7 @@ void QrCode::drawVersion() { rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); long data = (long)version << 12 | rem; // uint18 if (data >> 18 != 0) - throw "Assertion error"; + throw std::logic_error("Assertion error"); // Draw two copies for (int i = 0; i < 18; i++) { @@ -299,7 +300,7 @@ bool QrCode::module(int x, int y) const { vector QrCode::appendErrorCorrection(const vector &data) const { if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) - throw "Invalid argument"; + throw std::invalid_argument("Invalid argument"); // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; @@ -331,14 +332,14 @@ vector QrCode::appendErrorCorrection(const vector &data) const } } if (result.size() != static_cast(rawCodewords)) - throw "Assertion error"; + throw std::logic_error("Assertion error"); return result; } void QrCode::drawCodewords(const vector &data) { if (data.size() != static_cast(getNumRawDataModules(version) / 8)) - throw "Invalid argument"; + throw std::invalid_argument("Invalid argument"); size_t i = 0; // Bit index into the data // Do the funny zigzag scan @@ -360,13 +361,13 @@ void QrCode::drawCodewords(const vector &data) { } } if (static_cast(i) != data.size() * 8) - throw "Assertion error"; + throw std::logic_error("Assertion error"); } void QrCode::applyMask(int mask) { if (mask < 0 || mask > 7) - throw "Mask value out of range"; + throw std::domain_error("Mask value out of range"); for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { bool invert; @@ -379,7 +380,7 @@ void QrCode::applyMask(int mask) { 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 "Assertion error"; + default: throw std::logic_error("Assertion error"); } modules.at(y).at(x) = modules.at(y).at(x) ^ (invert & !isFunction.at(y).at(x)); } @@ -402,7 +403,7 @@ int QrCode::handleConstructorMasking(int mask) { } } if (mask < 0 || mask > 7) - throw "Assertion error"; + throw std::logic_error("Assertion error"); drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask return mask; // The caller shall assign this value to the final-declared field @@ -491,7 +492,7 @@ long QrCode::getPenaltyScore() const { vector QrCode::getAlignmentPatternPositions(int ver) { if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; + throw std::domain_error("Version number out of range"); else if (ver == 1) return vector(); else { @@ -514,7 +515,7 @@ vector QrCode::getAlignmentPatternPositions(int ver) { int QrCode::getNumRawDataModules(int ver) { if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; + throw std::domain_error("Version number out of range"); int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { int numAlign = ver / 7 + 2; @@ -528,7 +529,7 @@ int QrCode::getNumRawDataModules(int ver) { int QrCode::getNumDataCodewords(int ver, Ecc ecl) { if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; + throw std::domain_error("Version number out of range"); return getNumRawDataModules(ver) / 8 - ECC_CODEWORDS_PER_BLOCK[static_cast(ecl)][ver] * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; @@ -570,7 +571,7 @@ const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : coefficients() { if (degree < 1 || degree > 255) - throw "Degree out of range"; + throw std::domain_error("Degree out of range"); // Start with the monomial x^0 coefficients.resize(degree); @@ -614,7 +615,7 @@ uint8_t QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { z ^= ((y >> i) & 1) * x; } if (z >> 8 != 0) - throw "Assertion error"; + throw std::logic_error("Assertion error"); return static_cast(z); } diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index f711461..3b5d509 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include "QrSegment.hpp" @@ -49,7 +50,7 @@ int QrSegment::Mode::numCharCountBits(int ver) const { if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; - else throw "Version number out of range"; + else throw std::domain_error("Version number out of range"); } @@ -63,7 +64,7 @@ const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); QrSegment QrSegment::makeBytes(const vector &data) { if (data.size() > INT_MAX) - throw "Data too long"; + throw std::length_error("Data too long"); BitBuffer bb; for (uint8_t b : data) bb.appendBits(b, 8); @@ -79,7 +80,7 @@ QrSegment QrSegment::makeNumeric(const char *digits) { for (; *digits != '\0'; digits++, charCount++) { char c = *digits; if (c < '0' || c > '9') - throw "String contains non-numeric characters"; + throw std::domain_error("String contains non-numeric characters"); accumData = accumData * 10 + (c - '0'); accumCount++; if (accumCount == 3) { @@ -102,7 +103,7 @@ QrSegment QrSegment::makeAlphanumeric(const char *text) { for (; *text != '\0'; text++, charCount++) { const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); if (temp == nullptr) - throw "String contains unencodable characters in alphanumeric mode"; + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); accumCount++; if (accumCount == 2) { @@ -146,7 +147,7 @@ QrSegment QrSegment::makeEci(long assignVal) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else - throw "ECI assignment value out of range"; + throw std::domain_error("ECI assignment value out of range"); return QrSegment(Mode::ECI, 0, std::move(bb)); } @@ -156,7 +157,7 @@ QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : numChars(numCh), data(dt) { if (numCh < 0) - throw "Invalid value"; + throw std::domain_error("Invalid value"); } @@ -165,13 +166,13 @@ QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : numChars(numCh), data(std::move(dt)) { if (numCh < 0) - throw "Invalid value"; + throw std::domain_error("Invalid value"); } int QrSegment::getTotalBits(const vector &segs, int version) { if (version < 1 || version > 40) - throw "Version number out of range"; + throw std::domain_error("Version number out of range"); int result = 0; for (const QrSegment &seg : segs) { int ccbits = seg.mode.numCharCountBits(version); From cc939b2ebe04a5552d9a3ac43e250a7db8d49436 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 17:57:40 +0000 Subject: [PATCH 346/810] Tweaked Java code to prevent arithmetic overflow for huge input data. --- java/io/nayuki/qrcodegen/BitBuffer.java | 2 +- java/io/nayuki/qrcodegen/QrSegment.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index d1ab920..8da04ee 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -82,7 +82,7 @@ public final class BitBuffer implements Cloneable { * @return this sequence as a new array of bytes (not {@code null}) */ public byte[] getBytes() { - byte[] result = new byte[(bitLength + 7) / 8]; + byte[] result = new byte[(bitLength + 7) >>> 3]; // Round up to whole byte, won't overflow for (int i = 0; i < bitLength; i++) result[i >>> 3] |= data.get(i) ? 1 << (7 - (i & 7)) : 0; return result; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index a4e8a2a..91098a2 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -71,7 +71,7 @@ public final class QrSegment { BitBuffer bb = new BitBuffer(); int i; - for (i = 0; i + 3 <= digits.length(); i += 3) // Process groups of 3 + for (i = 0; i <= digits.length() - 3; i += 3) // Process groups of 3 bb.appendBits(Integer.parseInt(digits.substring(i, i + 3)), 10); int rem = digits.length() - i; if (rem > 0) // 1 or 2 digits remaining @@ -96,7 +96,7 @@ public final class QrSegment { BitBuffer bb = new BitBuffer(); int i; - for (i = 0; i + 2 <= text.length(); i += 2) { // Process groups of 2 + for (i = 0; i <= text.length() - 2; i += 2) { // Process groups of 2 int temp = ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; temp += ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); bb.appendBits(temp, 11); From b756fcd8b17de72a2a2060ae5a4490818cac3730 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 18:03:17 +0000 Subject: [PATCH 347/810] Tweaked Java BitBuffer code to add size overflow checks. --- java/io/nayuki/qrcodegen/BitBuffer.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 8da04ee..b2b62b8 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -94,10 +94,14 @@ public final class BitBuffer implements Cloneable { * to this sequence. Requires 0 ≤ val < 2len. * @param val the value to append * @param len the number of low bits in the value to take + * @throws IllegalStateException if appending the data + * would make bitLength exceed Integer.MAX_VALUE */ public void appendBits(int val, int len) { if (len < 0 || len > 31 || val >>> len != 0) throw new IllegalArgumentException("Value out of range"); + if (Integer.MAX_VALUE - bitLength < len) + throw new IllegalStateException("Maximum length reached"); for (int i = len - 1; i >= 0; i--, bitLength++) // Append bit by bit data.set(bitLength, QrCode.getBit(val, i)); } @@ -107,10 +111,14 @@ public final class BitBuffer implements Cloneable { * Appends the bit data of the specified segment to this bit buffer. * @param seg the segment whose data to append (not {@code null}) * @throws NullPointerException if the segment is {@code null} + * @throws IllegalStateException if appending the data + * would make bitLength exceed Integer.MAX_VALUE */ public void appendData(QrSegment seg) { Objects.requireNonNull(seg); BitBuffer bb = seg.data; + if (Integer.MAX_VALUE - bitLength < bb.bitLength) + throw new IllegalStateException("Maximum length reached"); for (int i = 0; i < bb.bitLength; i++, bitLength++) // Append bit by bit data.set(bitLength, bb.data.get(i)); } From d8d2da49e499e22cfcc8e567298f376ca6ff03a7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 18:05:43 +0000 Subject: [PATCH 348/810] Tweaked Javadoc to use the word "constructs" instead of "creates" for constructors. --- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d1542fe..f390eeb 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -197,7 +197,7 @@ public final class QrCode { /*---- Constructor ----*/ /** - * Creates a new QR Code symbol with the specified version number, error correction level, binary data array, and mask number. + * Constructs a QR Code symbol with the specified version number, error correction level, binary data array, and mask number. *

This is a cumbersome low-level constructor that should not be invoked directly by the user. * To go one level up, see the {@link #encodeSegments(List,Ecc)} function.

* @param ver the version number to use, which must be in the range 1 to 40, inclusive @@ -777,7 +777,7 @@ public final class QrCode { /*-- Constructor --*/ /** - * Creates a Reed-Solomon ECC generator for the specified degree. This could be implemented + * Constructs a Reed-Solomon ECC generator for the specified degree. This could be implemented * as a lookup table over all possible parameter values, instead of as an algorithm. * @param degree the divisor polynomial degree, which must be between 1 and 255 * @throws IllegalArgumentException if degree < 1 or degree > 255 diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 91098a2..4dbb6a6 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -169,7 +169,7 @@ public final class QrSegment { /*---- Constructor ----*/ /** - * Creates a new QR Code data segment with the specified parameters and data. + * Constructs a QR Code data segment with the specified parameters and data. * @param md the mode, which is not {@code null} * @param numCh the data length in characters, which is non-negative * @param data the data bits of this segment, which is not {@code null} From 0e2ecff58eab31cb133d11011003979fd6708645 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 18:15:15 +0000 Subject: [PATCH 349/810] Tweaked Java code to convert most explicit assertion checks to native assert statements, for compactness and because the code quality ensures that the checks shouldn't fail in normal usage. --- java/io/nayuki/qrcodegen/QrCode.java | 24 +++++++------------ .../nayuki/qrcodegen/QrSegmentAdvanced.java | 3 +-- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index f390eeb..5c8a619 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -128,8 +128,7 @@ public final class QrCode { if (version >= maxVersion) // All versions in the range could not fit the given data throw new IllegalArgumentException("Data too long"); } - if (dataUsedBits == -1) - throw new AssertionError(); + assert dataUsedBits != -1; // Increase the error correction level while the data still fits in the current version number for (Ecc newEcl : Ecc.values()) { @@ -153,8 +152,7 @@ public final class QrCode { // Pad with alternate bytes until data capacity is reached for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - if (bb.bitLength() % 8 != 0) - throw new AssertionError(); + assert bb.bitLength() % 8 == 0; // Create the QR Code symbol return new QrCode(version, ecl, bb.getBytes(), mask); @@ -354,8 +352,7 @@ public final class QrCode { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); data = data << 10 | rem; data ^= 0x5412; // uint15 - if (data >>> 15 != 0) - throw new AssertionError(); + assert data >>> 15 == 0; // Draw first copy for (int i = 0; i <= 5; i++) @@ -386,8 +383,7 @@ public final class QrCode { for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); int data = version << 12 | rem; // uint18 - if (data >>> 18 != 0) - throw new AssertionError(); + assert data >>> 18 == 0; // Draw two copies for (int i = 0; i < 18; i++) { @@ -497,8 +493,7 @@ public final class QrCode { } } } - if (i != data.length * 8) - throw new AssertionError(); + assert i == data.length * 8; } @@ -546,8 +541,7 @@ public final class QrCode { applyMask(i); // Undoes the mask due to XOR } } - if (mask < 0 || mask > 7) - throw new AssertionError(); + assert 0 <= mask && mask <= 7; drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask return mask; // The caller shall assign this value to the final-declared field @@ -837,16 +831,14 @@ public final class QrCode { // 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 multiply(int x, int y) { - if (x >>> 8 != 0 || y >>> 8 != 0) - throw new IllegalArgumentException("Byte out of range"); + 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; } - if (z >>> 8 != 0) - throw new AssertionError(); + assert z >>> 8 == 0; return z; } diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index d4b8c9c..a485c8a 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -393,8 +393,7 @@ public final class QrSegmentAdvanced { int j = ((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF); if (j == 0xFFFF) continue; - if (UNICODE_TO_QR_KANJI[j] != -1) - throw new AssertionError(); + assert UNICODE_TO_QR_KANJI[j] == -1; UNICODE_TO_QR_KANJI[j] = (short)(i / 2); } } From bf6206570029b41c52c104892f0b9209dbe8f71d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 18:33:01 +0000 Subject: [PATCH 350/810] Simplified and clarified small pieces of Java code, without changing behavior. --- java/io/nayuki/qrcodegen/BitBuffer.java | 6 ++++-- java/io/nayuki/qrcodegen/QrCode.java | 8 ++++---- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 11 ++++++----- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index b2b62b8..861ffcc 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -83,8 +83,10 @@ public final class BitBuffer implements Cloneable { */ public byte[] getBytes() { byte[] result = new byte[(bitLength + 7) >>> 3]; // Round up to whole byte, won't overflow - for (int i = 0; i < bitLength; i++) - result[i >>> 3] |= data.get(i) ? 1 << (7 - (i & 7)) : 0; + for (int i = 0; i < bitLength; i++) { + if (data.get(i)) + result[i >>> 3] |= 1 << (7 - (i & 7)); + } return result; } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 5c8a619..b56c55d 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -262,8 +262,8 @@ public final class QrCode { 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++) { - boolean val = getModule(x / scale - border, y / scale - border); - result.setRGB(x, y, val ? 0x000000 : 0xFFFFFF); + boolean color = getModule(x / scale - border, y / scale - border); + result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); } } return result; @@ -445,8 +445,8 @@ public final class QrCode { ReedSolomonGenerator rs = new ReedSolomonGenerator(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); - byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); k += dat.length; + byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); byte[] ecc = rs.getRemainder(dat); System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); blocks[i] = block; @@ -668,7 +668,7 @@ public final class QrCode { int size = ver * 4 + 17; int result = size * size; // Number of modules in the whole QR symbol square - result -= 64 * 3; // Subtract the three finders with separators + result -= 8 * 8 * 3; // Subtract the three finders with separators result -= 15 * 2 + 1; // Subtract the format information and black module result -= (size - 16) * 2; // Subtract the timing patterns // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 4dbb6a6..a9a464f 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -259,7 +259,7 @@ public final class QrSegment { /*-- Constructor --*/ private Mode(int mode, int... ccbits) { - this.modeBits = mode; + modeBits = mode; numBitsCharCount = ccbits; } diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index a485c8a..541d230 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -224,6 +224,7 @@ public final class QrSegmentAdvanced { } + /*---- Kanji mode segment encoder ----*/ /** @@ -384,17 +385,17 @@ public final class QrSegmentAdvanced { "/////////////////////////////////////////////w=="; - private static short[] UNICODE_TO_QR_KANJI = new short[65536]; + private static short[] UNICODE_TO_QR_KANJI = new short[1 << 16]; static { // Unpack the Shift JIS table into a more computation-friendly form 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) { - int j = ((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF); - if (j == 0xFFFF) + char c = (char)(((bytes[i] & 0xFF) << 8) | (bytes[i + 1] & 0xFF)); + if (c == 0xFFFF) continue; - assert UNICODE_TO_QR_KANJI[j] == -1; - UNICODE_TO_QR_KANJI[j] = (short)(i / 2); + assert UNICODE_TO_QR_KANJI[c] == -1; + UNICODE_TO_QR_KANJI[c] = (short)(i / 2); } } From a2977e635165b56f41566bd30e71f6fe0d928357 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 18:33:28 +0000 Subject: [PATCH 351/810] Refactored a Java method to reduce indirection. --- java/io/nayuki/qrcodegen/BitBuffer.java | 11 +++++------ java/io/nayuki/qrcodegen/QrCode.java | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 861ffcc..b596566 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -110,15 +110,14 @@ public final class BitBuffer implements Cloneable { /** - * Appends the bit data of the specified segment to this bit buffer. - * @param seg the segment whose data to append (not {@code null}) - * @throws NullPointerException if the segment is {@code null} + * Appends the specified bit buffer to this bit buffer. + * @param bb the bit buffer whose data to append (not {@code null}) + * @throws NullPointerException if the bit buffer is {@code null} * @throws IllegalStateException if appending the data * would make bitLength exceed Integer.MAX_VALUE */ - public void appendData(QrSegment seg) { - Objects.requireNonNull(seg); - BitBuffer bb = seg.data; + public void appendData(BitBuffer bb) { + Objects.requireNonNull(bb); if (Integer.MAX_VALUE - bitLength < bb.bitLength) throw new IllegalStateException("Maximum length reached"); for (int i = 0; i < bb.bitLength; i++, bitLength++) // Append bit by bit diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index b56c55d..ed83219 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -142,7 +142,7 @@ public final class QrCode { for (QrSegment seg : segs) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - bb.appendData(seg); + bb.appendData(seg.data); } // Add terminator and pad up to a byte if applicable From d1f53e6e7d86ba4d61c6f9dc2f489a4ebfd39c8d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:22:00 +0000 Subject: [PATCH 352/810] Added and updated 4 comments in all language versions. --- c/qrcodegen.c | 15 ++++++++------- cpp/QrCode.cpp | 6 +++--- cpp/QrCode.hpp | 9 +++++---- java/io/nayuki/qrcodegen/QrCode.java | 15 ++++++++------- javascript/qrcodegen.js | 15 ++++++++------- python/qrcodegen.py | 15 ++++++++------- rust/src/lib.rs | 15 ++++++++------- typescript/qrcodegen.ts | 15 ++++++++------- 8 files changed, 56 insertions(+), 49 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index fa02521..fceae70 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -456,7 +456,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin setModule(qrcode, qrsize - 1 - i, 8, getBit(data, i)); for (int i = 8; i < 15; i++) setModule(qrcode, 8, qrsize - 15 + i, getBit(data, i)); - setModule(qrcode, 8, qrsize - 8, true); + setModule(qrcode, 8, qrsize - 8, true); // Always black } @@ -519,10 +519,11 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { } -// XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical -// properties, calling applyMask(..., m) twice with the same value is equivalent to no change at all. -// This means it is possible to apply a mask, undo it, and try another mask. Note that a final -// well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). +// 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 symbol needs exactly one (not zero, two, etc.) mask applied. static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO int qrsize = qrcodegen_getSize(qrcode); @@ -913,12 +914,12 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz assert(dataUsedBits != -1); // Increase the error correction level while the data still fits in the current version number - for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) ecl = (enum qrcodegen_Ecc)i; } - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 37f4c5f..f137d7d 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -81,12 +81,12 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, throw std::logic_error("Assertion error"); // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { + for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb; for (const QrSegment &seg : segs) { @@ -241,7 +241,7 @@ void QrCode::drawFormatBits(int mask) { setFunctionModule(size - 1 - i, 8, getBit(data, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(data, i)); - setFunctionModule(8, size - 8, true); + setFunctionModule(8, size - 8, true); // Always black } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index e843bdf..f5c5120 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -206,10 +206,11 @@ class QrCode final { private: void drawCodewords(const std::vector &data); - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. private: void applyMask(int mask); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index ed83219..16b28ac 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -131,12 +131,12 @@ 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()) { + for (Ecc newEcl : Ecc.values()) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { @@ -368,7 +368,7 @@ public final class QrCode { setFunctionModule(size - 1 - i, 8, getBit(data, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(data, i)); - setFunctionModule(8, size - 8, true); + setFunctionModule(8, size - 8, true); // Always black } @@ -497,10 +497,11 @@ public final class QrCode { } - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. private void applyMask(int mask) { if (mask < 0 || mask > 7) throw new IllegalArgumentException("Mask value out of range"); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 958b243..08f801f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -245,7 +245,7 @@ var qrcodegen = new function() { setFunctionModule(size - 1 - i, 8, getBit(data, i)); for (var i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(data, i)); - setFunctionModule(8, size - 8, true); + setFunctionModule(8, size - 8, true); // Always black } @@ -377,10 +377,11 @@ var qrcodegen = new function() { } - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. function applyMask(mask) { if (mask < 0 || mask > 7) throw "Mask value out of range"; @@ -545,12 +546,12 @@ var qrcodegen = new function() { } // Increase the error correction level while the data still fits in the current version number - [this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { + [this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { // From low to high if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; }); - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; var bb = new BitBuffer(); segs.forEach(function(seg) { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index fbc9c54..0e0c8b0 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -111,11 +111,11 @@ class QrCode(object): raise AssertionError() # Increase the error correction level while the data still fits in the current version number - for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): + for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): # From low to high if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: ecl = newecl - # Create the data bit string by concatenating all segments + # Concatenate all segments to create the data bit string datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 bb = _BitBuffer() for seg in segs: @@ -291,7 +291,7 @@ class QrCode(object): self._set_function_module(self._size - 1 - i, 8, _get_bit(data, i)) for i in range(8, 15): self._set_function_module(8, self._size - 15 + i, _get_bit(data, i)) - self._set_function_module(8, self._size - 8, True) + self._set_function_module(8, self._size - 8, True) # Always black def _draw_version(self): @@ -404,10 +404,11 @@ class QrCode(object): def _apply_mask(self, mask): - """XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - This means it is possible to apply a mask, undo it, and try another mask. Note that a final - well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.).""" + """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 symbol needs exactly one (not zero, two, etc.) mask applied.""" if not (0 <= mask <= 7): raise ValueError("Mask value out of range") masker = QrCode._MASK_PATTERNS[mask] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e377938..b6d3d87 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -123,13 +123,13 @@ impl QrCode { } // Increase the error correction level while the data still fits in the current version number - for newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { + for newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { // From low to high if boostecl && datausedbits <= QrCode::get_num_data_codewords(version, *newecl) * 8 { ecl = *newecl; } } - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let mut bb = BitBuffer(Vec::new()); for seg in segs { @@ -334,7 +334,7 @@ impl QrCode { for i in 8 .. 15 { self.set_function_module(8, size - 15 + i, get_bit(data, i)); } - self.set_function_module(8, size - 8, true); + self.set_function_module(8, size - 8, true); // Always black } @@ -474,10 +474,11 @@ impl QrCode { } - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. fn apply_mask(&mut self, mask: Mask) { let mask: u8 = mask.value(); for y in 0 .. self.size { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d61a304..ccc2478 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -95,12 +95,12 @@ namespace qrcodegen { } // Increase the error correction level while the data still fits in the current version number - [QrCode_Ecc.MEDIUM, QrCode_Ecc.QUARTILE, QrCode_Ecc.HIGH].forEach((newEcl: QrCode_Ecc) => { + [QrCode_Ecc.MEDIUM, QrCode_Ecc.QUARTILE, QrCode_Ecc.HIGH].forEach((newEcl: QrCode_Ecc) => { // From low to high if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; }); - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; let bb = new BitBuffer(); segs.forEach((seg: QrSegment) => { @@ -308,7 +308,7 @@ namespace qrcodegen { this.setFunctionModule(this.size - 1 - i, 8, getBit(data, i)); for (let i = 8; i < 15; i++) this.setFunctionModule(8, this.size - 15 + i, getBit(data, i)); - this.setFunctionModule(8, this.size - 8, true); + this.setFunctionModule(8, this.size - 8, true); // Always black } @@ -443,10 +443,11 @@ namespace qrcodegen { } - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. private applyMask(mask: int): void { if (mask < 0 || mask > 7) throw "Mask value out of range"; From 62930ef455808081ab157d675686392bc4924a38 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:23:40 +0000 Subject: [PATCH 353/810] Updated and added 5 comments in all language versions except C. --- cpp/QrCode.hpp | 11 +++++++---- java/io/nayuki/qrcodegen/QrCode.java | 11 +++++++---- javascript/qrcodegen.js | 11 +++++++---- python/qrcodegen.py | 11 +++++++---- rust/src/lib.rs | 11 +++++++---- typescript/qrcodegen.ts | 11 +++++++---- 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index f5c5120..33edc93 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -164,6 +164,7 @@ class QrCode final { /*---- Private helper methods for constructor: Drawing function modules ----*/ + // Reads this object's version field, and draws and marks all function modules. private: void drawFunctionPatterns(); @@ -173,20 +174,22 @@ class QrCode final { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. private: void drawVersion(); - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // 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); - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // 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); // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. + // Only used by the constructor. Coordinates must be in bounds. private: void setFunctionModule(int x, int y, bool isBlack); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 16b28ac..76b0e09 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -312,6 +312,7 @@ public final class QrCode { /*---- 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++) { @@ -373,7 +374,7 @@ public final class QrCode { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. private void drawVersion() { if (version < 7) return; @@ -395,7 +396,8 @@ public final class QrCode { } - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // 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) { for (int i = -4; i <= 4; i++) { for (int j = -4; j <= 4; j++) { @@ -408,7 +410,8 @@ public final class QrCode { } - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // 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) { for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) @@ -418,7 +421,7 @@ public final class QrCode { // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. + // 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; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 08f801f..56e0317 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -188,6 +188,7 @@ var qrcodegen = new function() { /*---- Private helper methods for constructor: Drawing function modules ----*/ + // Reads this object's version field, and draws and marks all function modules. function drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (var i = 0; i < size; i++) { @@ -250,7 +251,7 @@ var qrcodegen = new function() { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. function drawVersion() { if (version < 7) return; @@ -273,7 +274,8 @@ var qrcodegen = new function() { } - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. function drawFinderPattern(x, y) { for (var i = -4; i <= 4; i++) { for (var j = -4; j <= 4; j++) { @@ -286,7 +288,8 @@ var qrcodegen = new function() { } - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. function drawAlignmentPattern(x, y) { for (var i = -2; i <= 2; i++) { for (var j = -2; j <= 2; j++) @@ -296,7 +299,7 @@ var qrcodegen = new function() { // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. + // Only used by the constructor. Coordinates must be in bounds. function setFunctionModule(x, y, isBlack) { modules[y][x] = isBlack; isFunction[y][x] = true; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 0e0c8b0..4ca2dc8 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -241,6 +241,7 @@ class QrCode(object): # ---- Private helper methods for constructor: Drawing function modules ---- def _draw_function_patterns(self): + """Reads this object's version field, and draws and marks all function modules.""" # Draw horizontal and vertical timing patterns for i in range(self._size): self._set_function_module(6, i, i % 2 == 0) @@ -296,7 +297,7 @@ class QrCode(object): def _draw_version(self): """Draws two copies of the version bits (with its own error correction code), - based on this object's version field (which only has an effect for 7 <= version <= 40).""" + based on this object's version field, iff 7 <= version <= 40.""" if self._version < 7: return @@ -316,7 +317,8 @@ class QrCode(object): def _draw_finder_pattern(self, x, y): - """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y).""" + """Draws a 9*9 finder pattern including the border separator, + with the center module at (x, y). Modules can be out of bounds.""" for i in range(-4, 5): for j in range(-4, 5): xx, yy = x + j, y + i @@ -326,7 +328,8 @@ class QrCode(object): def _draw_alignment_pattern(self, x, y): - """Draws a 5*5 alignment pattern, with the center module at (x, y).""" + """Draws a 5*5 alignment pattern, with the center module + at (x, y). All modules must be in bounds.""" for i in range(-2, 3): for j in range(-2, 3): self._set_function_module(x + j, y + i, max(abs(i), abs(j)) != 1) @@ -334,7 +337,7 @@ class QrCode(object): def _set_function_module(self, x, y, isblack): """Sets the color of a module and marks it as a function module. - Only used by the constructor. Coordinates must be in range.""" + Only used by the constructor. Coordinates must be in bounds.""" assert type(isblack) is bool self._modules[y][x] = isblack self._isfunction[y][x] = True diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b6d3d87..3adf74f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -269,6 +269,7 @@ impl QrCode { /*---- Private helper methods for constructor: Drawing function modules ----*/ + // Reads this object's version field, and draws and marks all function modules. fn draw_function_patterns(&mut self) { // Draw horizontal and vertical timing patterns let size: i32 = self.size; @@ -339,7 +340,7 @@ impl QrCode { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. fn draw_version(&mut self) { if self.version.value() < 7 { return; @@ -364,7 +365,8 @@ impl QrCode { } - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. fn draw_finder_pattern(&mut self, x: i32, y: i32) { for i in -4 .. 5 { for j in -4 .. 5 { @@ -379,7 +381,8 @@ impl QrCode { } - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. fn draw_alignment_pattern(&mut self, x: i32, y: i32) { for i in -2 .. 3 { for j in -2 .. 3 { @@ -390,7 +393,7 @@ impl QrCode { // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. + // Only used by the constructor. Coordinates must be in bounds. fn set_function_module(&mut self, x: i32, y: i32, isblack: bool) { *self.module_mut(x, y) = isblack; self.isfunction[(y * self.size + x) as usize] = true; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index ccc2478..0c49fbd 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -251,6 +251,7 @@ namespace qrcodegen { /*-- Private helper methods for constructor: Drawing function modules --*/ + // Reads this object's version field, and draws and marks all function modules. private drawFunctionPatterns(): void { // Draw horizontal and vertical timing patterns for (let i = 0; i < this.size; i++) { @@ -313,7 +314,7 @@ namespace qrcodegen { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. private drawVersion(): void { if (this.version < 7) return; @@ -337,7 +338,8 @@ namespace qrcodegen { } - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. private drawFinderPattern(x: int, y: int): void { for (let i = -4; i <= 4; i++) { for (let j = -4; j <= 4; j++) { @@ -351,7 +353,8 @@ namespace qrcodegen { } - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. private drawAlignmentPattern(x: int, y: int): void { for (let i = -2; i <= 2; i++) { for (let j = -2; j <= 2; j++) @@ -361,7 +364,7 @@ namespace qrcodegen { // Sets the color of a module and marks it as a function module. - // Only used by the constructor. Coordinates must be in range. + // Only used by the constructor. Coordinates must be in bounds. private setFunctionModule(x: int, y: int, isBlack: boolean): void { this.modules[y][x] = isBlack; this.isFunction[y][x] = true; From f3ba9c0837c3c0385929fe85a977eb28f0d24a45 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:32:03 +0000 Subject: [PATCH 354/810] Simplified QrCode.toSvgString() loop ranges in all language versions except C. --- cpp/QrCode.cpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index f137d7d..369529c 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -169,8 +169,8 @@ std::string QrCode::toSvgString(int border) const { sb << "\t\n"; sb << "\t\n"); sb.append("\t diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3adf74f..8fe722c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -249,8 +249,8 @@ impl QrCode { result += "\t\n"; result += "\t = []; - for (let y = -border; y < this.size + border; y++) { - for (let x = -border; x < this.size + border; x++) { + for (let y = 0; y < this.size; y++) { + for (let x = 0; x < this.size; x++) { if (this.getModule(x, y)) parts.push(`M${x + border},${y + border}h1v1h-1z`); } From ea29e58e9c088401dcd3e6ee98cbc3f94e25e16f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:47:27 +0000 Subject: [PATCH 355/810] Simplified Java code to use StringBuilder method chaining. --- java/io/nayuki/qrcodegen/QrCode.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 2096137..7d62e0a 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -283,14 +283,13 @@ public final class QrCode { if (size + border * 2L > Integer.MAX_VALUE) throw new IllegalArgumentException("Border too large"); - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - sb.append("\n"); - sb.append(String.format( - "\n", - size + border * 2)); - sb.append("\t\n"); - sb.append("\t\n") + .append("\n") + .append(String.format("\n", + size + border * 2)) + .append("\t\n") + .append("\t\n"); - sb.append("\n"); - return sb.toString(); + return sb + .append("\" fill=\"#000000\"/>\n") + .append("\n") + .toString(); } From 23ae555dde8fd58c742127ad08176e697cb2ab59 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:48:29 +0000 Subject: [PATCH 356/810] Added variable for clarity to QrCode.drawFormatBits() in all language versions. --- c/qrcodegen.c | 19 +++++++++---------- cpp/QrCode.cpp | 19 +++++++++---------- java/io/nayuki/qrcodegen/QrCode.java | 19 +++++++++---------- javascript/qrcodegen.js | 19 +++++++++---------- python/qrcodegen.py | 19 +++++++++---------- rust/src/lib.rs | 19 +++++++++---------- typescript/qrcodegen.ts | 19 +++++++++---------- 7 files changed, 63 insertions(+), 70 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index fceae70..112e974 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -437,25 +437,24 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - assert(data >> 15 == 0); + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert(bits >> 15 == 0); // Draw first copy for (int i = 0; i <= 5; i++) - setModule(qrcode, 8, i, getBit(data, i)); - setModule(qrcode, 8, 7, getBit(data, 6)); - setModule(qrcode, 8, 8, getBit(data, 7)); - setModule(qrcode, 7, 8, getBit(data, 8)); + setModule(qrcode, 8, i, getBit(bits, i)); + setModule(qrcode, 8, 7, getBit(bits, 6)); + setModule(qrcode, 8, 8, getBit(bits, 7)); + setModule(qrcode, 7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) - setModule(qrcode, 14 - i, 8, getBit(data, i)); + setModule(qrcode, 14 - i, 8, getBit(bits, i)); // Draw second copy int qrsize = qrcodegen_getSize(qrcode); for (int i = 0; i <= 7; i++) - setModule(qrcode, qrsize - 1 - i, 8, getBit(data, i)); + setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) - setModule(qrcode, 8, qrsize - 15 + i, getBit(data, i)); + setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); setModule(qrcode, 8, qrsize - 8, true); // Always black } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 369529c..668bdb9 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -222,25 +222,24 @@ void QrCode::drawFormatBits(int mask) { int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - if (data >> 15 != 0) + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >> 15 != 0) throw std::logic_error("Assertion error"); // Draw first copy for (int i = 0; i <= 5; i++) - setFunctionModule(8, i, getBit(data, i)); - setFunctionModule(8, 7, getBit(data, 6)); - setFunctionModule(8, 8, getBit(data, 7)); - setFunctionModule(7, 8, getBit(data, 8)); + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(data, i)); + setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy for (int i = 0; i <= 7; i++) - setFunctionModule(size - 1 - i, 8, getBit(data, i)); + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, getBit(data, i)); + setFunctionModule(8, size - 15 + i, getBit(bits, i)); setFunctionModule(8, size - 8, true); // Always black } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 7d62e0a..8a551b1 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -351,24 +351,23 @@ public final class QrCode { int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - assert data >>> 15 == 0; + 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(data, i)); - setFunctionModule(8, 7, getBit(data, 6)); - setFunctionModule(8, 8, getBit(data, 7)); - setFunctionModule(7, 8, getBit(data, 8)); + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(data, i)); + setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy for (int i = 0; i <= 7; i++) - setFunctionModule(size - 1 - i, 8, getBit(data, i)); + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, getBit(data, i)); + setFunctionModule(8, size - 15 + i, getBit(bits, i)); setFunctionModule(8, size - 8, true); // Always black } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 603a41c..36a0384 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -227,25 +227,24 @@ var qrcodegen = new function() { var rem = data; for (var i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - if (data >>> 15 != 0) + var bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >>> 15 != 0) throw "Assertion error"; // Draw first copy for (var i = 0; i <= 5; i++) - setFunctionModule(8, i, getBit(data, i)); - setFunctionModule(8, 7, getBit(data, 6)); - setFunctionModule(8, 8, getBit(data, 7)); - setFunctionModule(7, 8, getBit(data, 8)); + setFunctionModule(8, i, getBit(bits, i)); + setFunctionModule(8, 7, getBit(bits, 6)); + setFunctionModule(8, 8, getBit(bits, 7)); + setFunctionModule(7, 8, getBit(bits, 8)); for (var i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(data, i)); + setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy for (var i = 0; i <= 7; i++) - setFunctionModule(size - 1 - i, 8, getBit(data, i)); + setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (var i = 8; i < 15; i++) - setFunctionModule(8, size - 15 + i, getBit(data, i)); + setFunctionModule(8, size - 15 + i, getBit(bits, i)); setFunctionModule(8, size - 8, true); // Always black } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 2e19aed..8f0fbdb 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -274,24 +274,23 @@ class QrCode(object): rem = data for _ in range(10): rem = (rem << 1) ^ ((rem >> 9) * 0x537) - data = data << 10 | rem - data ^= 0x5412 # uint15 - assert data >> 15 == 0 + bits = (data << 10 | rem) ^ 0x5412 # uint15 + assert bits >> 15 == 0 # Draw first copy for i in range(0, 6): - self._set_function_module(8, i, _get_bit(data, i)) - self._set_function_module(8, 7, _get_bit(data, 6)) - self._set_function_module(8, 8, _get_bit(data, 7)) - self._set_function_module(7, 8, _get_bit(data, 8)) + self._set_function_module(8, i, _get_bit(bits, i)) + self._set_function_module(8, 7, _get_bit(bits, 6)) + self._set_function_module(8, 8, _get_bit(bits, 7)) + self._set_function_module(7, 8, _get_bit(bits, 8)) for i in range(9, 15): - self._set_function_module(14 - i, 8, _get_bit(data, i)) + self._set_function_module(14 - i, 8, _get_bit(bits, i)) # Draw second copy for i in range(0, 8): - self._set_function_module(self._size - 1 - i, 8, _get_bit(data, i)) + self._set_function_module(self._size - 1 - i, 8, _get_bit(bits, i)) for i in range(8, 15): - self._set_function_module(8, self._size - 15 + i, _get_bit(data, i)) + self._set_function_module(8, self._size - 15 + i, _get_bit(bits, i)) self._set_function_module(8, self._size - 8, True) # Always black diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8fe722c..dbeec47 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -313,27 +313,26 @@ impl QrCode { for _ in 0 .. 10 { rem = (rem << 1) ^ ((rem >> 9) * 0x537); } - data = data << 10 | rem; - data ^= 0x5412; // uint15 - assert_eq!(data >> 15, 0, "Assertion error"); + let bits: u32 = (data << 10 | rem) ^ 0x5412; // uint15 + assert_eq!(bits >> 15, 0, "Assertion error"); // Draw first copy for i in 0 .. 6 { - self.set_function_module(8, i, get_bit(data, i)); + self.set_function_module(8, i, get_bit(bits, i)); } - self.set_function_module(8, 7, get_bit(data, 6)); - self.set_function_module(8, 8, get_bit(data, 7)); - self.set_function_module(7, 8, get_bit(data, 8)); + self.set_function_module(8, 7, get_bit(bits, 6)); + self.set_function_module(8, 8, get_bit(bits, 7)); + self.set_function_module(7, 8, get_bit(bits, 8)); for i in 9 .. 15 { - self.set_function_module(14 - i, 8, get_bit(data, i)); + self.set_function_module(14 - i, 8, get_bit(bits, i)); } // Draw second copy for i in 0 .. 8 { - self.set_function_module(size - 1 - i, 8, get_bit(data, i)); + self.set_function_module(size - 1 - i, 8, get_bit(bits, i)); } for i in 8 .. 15 { - self.set_function_module(8, size - 15 + i, get_bit(data, i)); + self.set_function_module(8, size - 15 + i, get_bit(bits, i)); } self.set_function_module(8, size - 8, true); // Always black } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index bd0fd10..0101a46 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -290,25 +290,24 @@ namespace qrcodegen { let rem: int = data; for (let i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - if (data >>> 15 != 0) + let bits = (data << 10 | rem) ^ 0x5412; // uint15 + if (bits >>> 15 != 0) throw "Assertion error"; // Draw first copy for (let i = 0; i <= 5; i++) - this.setFunctionModule(8, i, getBit(data, i)); - this.setFunctionModule(8, 7, getBit(data, 6)); - this.setFunctionModule(8, 8, getBit(data, 7)); - this.setFunctionModule(7, 8, getBit(data, 8)); + this.setFunctionModule(8, i, getBit(bits, i)); + this.setFunctionModule(8, 7, getBit(bits, 6)); + this.setFunctionModule(8, 8, getBit(bits, 7)); + this.setFunctionModule(7, 8, getBit(bits, 8)); for (let i = 9; i < 15; i++) - this.setFunctionModule(14 - i, 8, getBit(data, i)); + this.setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy for (let i = 0; i <= 7; i++) - this.setFunctionModule(this.size - 1 - i, 8, getBit(data, i)); + this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i)); for (let i = 8; i < 15; i++) - this.setFunctionModule(8, this.size - 15 + i, getBit(data, i)); + this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i)); this.setFunctionModule(8, this.size - 8, true); // Always black } From 62e73f787ac8fe7cadb1a62987ce5a4270ee8313 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:52:50 +0000 Subject: [PATCH 357/810] Renamed variable in QrCode.drawVersion() in all language versions. --- c/qrcodegen.c | 10 +++++----- cpp/QrCode.cpp | 6 +++--- java/io/nayuki/qrcodegen/QrCode.java | 6 +++--- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 6 +++--- rust/src/lib.rs | 6 +++--- typescript/qrcodegen.ts | 6 +++--- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 112e974..e662341 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -403,16 +403,16 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { int rem = version; // version is uint6, in the range [7, 40] for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long data = (long)version << 12 | rem; // uint18 - assert(data >> 18 == 0); + long bits = (long)version << 12 | rem; // uint18 + assert(bits >> 18 == 0); // Draw two copies for (int i = 0; i < 6; i++) { for (int j = 0; j < 3; j++) { int k = qrsize - 11 + j; - setModule(qrcode, k, i, (data & 1) != 0); - setModule(qrcode, i, k, (data & 1) != 0); - data >>= 1; + setModule(qrcode, k, i, (bits & 1) != 0); + setModule(qrcode, i, k, (bits & 1) != 0); + bits >>= 1; } } } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 668bdb9..3357cd6 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -252,13 +252,13 @@ void QrCode::drawVersion() { int rem = version; // version is uint6, in the range [7, 40] for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long data = (long)version << 12 | rem; // uint18 - if (data >> 18 != 0) + long bits = (long)version << 12 | rem; // uint18 + if (bits >> 18 != 0) throw std::logic_error("Assertion error"); // Draw two copies for (int i = 0; i < 18; i++) { - bool bit = getBit(data, i); + bool bit = getBit(bits, i); int a = size - 11 + i % 3, b = i / 3; setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 8a551b1..6582fd4 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -382,12 +382,12 @@ public final class QrCode { 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 data = version << 12 | rem; // uint18 - assert data >>> 18 == 0; + int bits = version << 12 | rem; // uint18 + assert bits >>> 18 == 0; // Draw two copies for (int i = 0; i < 18; i++) { - boolean bit = getBit(data, i); + boolean bit = getBit(bits, i); int a = size - 11 + i % 3, b = i / 3; setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 36a0384..b95f77f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -259,13 +259,13 @@ var qrcodegen = new function() { var rem = version; // version is uint6, in the range [7, 40] for (var i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); - var data = version << 12 | rem; // uint18 - if (data >>> 18 != 0) + var bits = version << 12 | rem; // uint18 + if (bits >>> 18 != 0) throw "Assertion error"; // Draw two copies for (var i = 0; i < 18; i++) { - var bit = getBit(data, i); + var bit = getBit(bits, i); var a = size - 11 + i % 3, b = Math.floor(i / 3); setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8f0fbdb..db449d9 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -304,12 +304,12 @@ class QrCode(object): rem = self._version # version is uint6, in the range [7, 40] for _ in range(12): rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) - data = self._version << 12 | rem # uint18 - assert data >> 18 == 0 + bits = self._version << 12 | rem # uint18 + assert bits >> 18 == 0 # Draw two copies for i in range(18): - bit = _get_bit(data, i) + bit = _get_bit(bits, i) a, b = self._size - 11 + i % 3, i // 3 self._set_function_module(a, b, bit) self._set_function_module(b, a, bit) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index dbeec47..c4f5f59 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -350,12 +350,12 @@ impl QrCode { for _ in 0 .. 12 { rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); } - let data: u32 = (self.version.value() as u32) << 12 | rem; // uint18 - assert!(data >> 18 == 0, "Assertion error"); + let bits: u32 = (self.version.value() as u32) << 12 | rem; // uint18 + assert!(bits >> 18 == 0, "Assertion error"); // Draw two copies for i in 0 .. 18 { - let bit: bool = get_bit(data, i); + let bit: bool = get_bit(bits, i); let a: i32 = self.size - 11 + i % 3; let b: i32 = i / 3; self.set_function_module(a, b, bit); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 0101a46..081cea1 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -322,13 +322,13 @@ namespace qrcodegen { let rem: int = this.version; // version is uint6, in the range [7, 40] for (let i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); - let data: int = this.version << 12 | rem; // uint18 - if (data >>> 18 != 0) + let bits: int = this.version << 12 | rem; // uint18 + if (bits >>> 18 != 0) throw "Assertion error"; // Draw two copies for (let i = 0; i < 18; i++) { - let bt: boolean = getBit(data, i); + let bt: boolean = getBit(bits, i); let a: int = this.size - 11 + i % 3; let b: int = Math.floor(i / 3); this.setFunctionModule(a, b, bt); From 6f45d3b573fa32102c720a655fa650665d7cdbfe Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 19:56:24 +0000 Subject: [PATCH 358/810] Clarified code in QrCode.drawVersion() in 4 language versions. --- cpp/QrCode.cpp | 3 ++- java/io/nayuki/qrcodegen/QrCode.java | 3 ++- javascript/qrcodegen.js | 3 ++- python/qrcodegen.py | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 3357cd6..5c3bbc2 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -259,7 +259,8 @@ void QrCode::drawVersion() { // Draw two copies for (int i = 0; i < 18; i++) { bool bit = getBit(bits, i); - int a = size - 11 + i % 3, b = i / 3; + int a = size - 11 + i % 3; + int b = i / 3; setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 6582fd4..e596001 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -388,7 +388,8 @@ public final class QrCode { // Draw two copies for (int i = 0; i < 18; i++) { boolean bit = getBit(bits, i); - int a = size - 11 + i % 3, b = i / 3; + int a = size - 11 + i % 3; + int b = i / 3; setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index b95f77f..e2d9bdc 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -266,7 +266,8 @@ var qrcodegen = new function() { // Draw two copies for (var i = 0; i < 18; i++) { var bit = getBit(bits, i); - var a = size - 11 + i % 3, b = Math.floor(i / 3); + var a = size - 11 + i % 3; + var b = Math.floor(i / 3); setFunctionModule(a, b, bit); setFunctionModule(b, a, bit); } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index db449d9..7b1a537 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -310,7 +310,8 @@ class QrCode(object): # Draw two copies for i in range(18): bit = _get_bit(bits, i) - a, b = self._size - 11 + i % 3, i // 3 + a = self._size - 11 + i % 3 + b = i // 3 self._set_function_module(a, b, bit) self._set_function_module(b, a, bit) From 6841e3716caa604d7e395f8441c29fb1b4482c68 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 20:19:04 +0000 Subject: [PATCH 359/810] Simplified and clarified small different pieces of code in a few languages. --- python/qrcodegen.py | 3 +-- rust/src/lib.rs | 7 +++---- typescript/qrcodegen.ts | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 7b1a537..3c5bc29 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -368,8 +368,7 @@ class QrCode(object): ecc = rs.get_remainder(dat) if i < numshortblocks: dat.append(0) - dat.extend(ecc) - blocks.append(dat) + blocks.append(dat + ecc) assert k == len(data) # Interleave (not concatenate) the bytes from every block into a single sequence diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c4f5f59..ce68e6f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -240,7 +240,7 @@ impl QrCode { // Note that Unix newlines (\n) are always used, regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); - let mut result: String = String::new(); + let mut result = String::new(); result += "\n"; result += "\n"; let dimension = self.size.checked_add(border.checked_mul(2).unwrap()).unwrap(); @@ -308,7 +308,7 @@ impl QrCode { // Calculate error correction code and pack bits let size: i32 = self.size; // errcorrlvl is uint2, mask is uint3 - let mut data: u32 = self.errorcorrectionlevel.format_bits() << 3 | (mask.value() as u32); + let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | (mask.value() as u32); let mut rem: u32 = data; for _ in 0 .. 10 { rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -420,8 +420,7 @@ impl QrCode { let rs = ReedSolomonGenerator::new(blockecclen); let mut k: usize = 0; for i in 0 .. numblocks { - let mut dat = Vec::::with_capacity(shortblocklen + 1); - dat.extend_from_slice(&data[k .. k + shortblocklen - blockecclen + ((i >= numshortblocks) as usize)]); + let mut dat = data[k .. k + shortblocklen - blockecclen + ((i >= numshortblocks) as usize)].to_vec(); k += dat.len(); let ecc: Vec = rs.get_remainder(&dat); if i < numshortblocks { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 081cea1..a31605a 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -286,11 +286,11 @@ namespace qrcodegen { // based on the given mask and this object's error correction level field. private drawFormatBits(mask: int): void { // Calculate error correction code and pack bits - let data: int = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + const data: int = this.errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 let rem: int = data; for (let i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - let bits = (data << 10 | rem) ^ 0x5412; // uint15 + const bits = (data << 10 | rem) ^ 0x5412; // uint15 if (bits >>> 15 != 0) throw "Assertion error"; @@ -322,7 +322,7 @@ namespace qrcodegen { let rem: int = this.version; // version is uint6, in the range [7, 40] for (let i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); - let bits: int = this.version << 12 | rem; // uint18 + const bits: int = this.version << 12 | rem; // uint18 if (bits >>> 18 != 0) throw "Assertion error"; From bc372054e29ff41d697a822b9b9388daf90208da Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 20:34:38 +0000 Subject: [PATCH 360/810] Tweaked Java QrCode.toSvgString() to avoid overflow entirely. --- java/io/nayuki/qrcodegen/QrCode.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index e596001..06f6929 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -280,14 +280,12 @@ public final class QrCode { public String toSvgString(int border) { if (border < 0) throw new IllegalArgumentException("Border must be non-negative"); - if (size + border * 2L > Integer.MAX_VALUE) - throw new IllegalArgumentException("Border too large"); - + long brd = border; StringBuilder sb = new StringBuilder() .append("\n") .append("\n") .append(String.format("\n", - size + border * 2)) + size + brd * 2)) .append("\t\n") .append("\t Date: Wed, 22 Aug 2018 20:39:45 +0000 Subject: [PATCH 361/810] Updated Javadoc and informal comments in Java code. --- java/io/nayuki/qrcodegen/BitBuffer.java | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 37 +++++++++++++++---------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index b596566..53a0102 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -36,7 +36,7 @@ public final class BitBuffer implements Cloneable { private BitSet data; - private int bitLength; + private int bitLength; // Non-negative diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 06f6929..6ca2167 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -49,7 +49,8 @@ public final class QrCode { * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the text * @throws NullPointerException if the text or error correction level is {@code null} - * @throws IllegalArgumentException if the text fails to fit in the largest version QR Code, which means it is too long + * @throws IllegalArgumentException if the text fails to fit in the + * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeText(String text, Ecc ecl) { Objects.requireNonNull(text); @@ -68,7 +69,8 @@ public final class QrCode { * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the binary data * @throws NullPointerException if the data or error correction level is {@code null} - * @throws IllegalArgumentException if the data fails to fit in the largest version QR Code, which means it is too long + * @throws IllegalArgumentException if the data fails to fit in the + * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeBinary(byte[] data, Ecc ecl) { Objects.requireNonNull(data); @@ -87,8 +89,9 @@ public final class QrCode { * @param segs the segments to encode * @param ecl the error correction level to use (will be boosted) * @return a QR Code representing the segments - * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if the data is too long to fit in the largest version QR Code at the ECL + * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if the segments fail to fit in the + * largest version QR Code at the ECL, which means they are too long */ public static QrCode encodeSegments(List segs, Ecc ecl) { return encodeSegments(segs, ecl, MIN_VERSION, MAX_VERSION, -1, true); @@ -108,9 +111,10 @@ public final class QrCode { * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice * @param boostEcl increases the error correction level if it can be done without increasing the version number * @return a QR Code representing the segments - * @throws NullPointerException if the list of segments, a segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, or if mask - * < −1 or mask > 7, or if the data is too long to fit in a QR Code at maxVersion at the ECL + * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 + * is violated, or if mask < −1 or mask > 7, or 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); @@ -197,13 +201,14 @@ public final class QrCode { /** * Constructs a QR Code symbol with the specified version number, error correction level, binary data array, and mask number. *

This is a cumbersome low-level constructor that should not be invoked directly by the user. - * To go one level up, see the {@link #encodeSegments(List,Ecc)} function.

+ * To go one level up, see 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 dataCodewords the raw binary user data to encode - * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice + * @param dataCodewords the bytes representing segments to encode (without ECC) + * @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 + * @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 mask) { // Check arguments @@ -251,7 +256,8 @@ public final class QrCode { * @param scale the module scale factor, which must be positive * @param border the number of border modules to add, which must be non-negative * @return an image representing this QR Code, with padding and scaling - * @throws IllegalArgumentException if the scale or border is out of range + * @throws IllegalArgumentException if the scale or border is out of range, or if + * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE */ public BufferedImage toImage(int scale, int border) { if (scale <= 0 || border < 0) @@ -276,6 +282,7 @@ public final class QrCode { * Note that Unix newlines (\n) are always used, regardless of the platform. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document + * @throws IllegalArgumentException if the border is negative */ public String toSvgString(int border) { if (border < 0) @@ -526,7 +533,7 @@ public final class QrCode { } - // A messy helper function for the constructors. This QR Code must be in an unmasked state when this + // 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 mask) { @@ -672,7 +679,7 @@ public final class QrCode { int result = size * size; // Number of modules in the whole QR symbol square result -= 8 * 8 * 3; // Subtract the three finders with separators result -= 15 * 2 + 1; // Subtract the format information and black module - result -= (size - 16) * 2; // Subtract the timing patterns + result -= (size - 16) * 2; // Subtract the timing patterns (excluding finders) // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { int numAlign = ver / 7 + 2; @@ -763,7 +770,7 @@ public final class QrCode { */ private static final class ReedSolomonGenerator { - /*-- Immutable field --*/ + /*-- Field --*/ // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. From 8d6dcf2631a58c9096dd8cfdb29ff08f7e3a97c4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 20:58:16 +0000 Subject: [PATCH 362/810] Simplified and reformatted QrCode.getAlignmentPatternPositions() in different ways in all language versions. --- c/qrcodegen.c | 8 ++------ cpp/QrCode.cpp | 8 ++------ java/io/nayuki/qrcodegen/QrCode.java | 7 +++---- javascript/qrcodegen.js | 7 ++----- python/qrcodegen.py | 7 ++----- rust/src/lib.rs | 8 ++------ typescript/qrcodegen.ts | 7 ++----- 7 files changed, 15 insertions(+), 37 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index e662341..30f0bfc 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -465,12 +465,8 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { if (version == 1) return 0; int numAlign = version / 7 + 2; - int step; - if (version != 32) { - // ceil((size - 13) / (2*numAlign - 2)) * 2 - step = (version * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; - } else // C-C-C-Combo breaker! - step = 26; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) result[i] = pos; result[0] = 6; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 5c3bbc2..09f93c0 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -497,12 +497,8 @@ vector QrCode::getAlignmentPatternPositions(int ver) { return vector(); else { int numAlign = ver / 7 + 2; - int step; - if (ver != 32) { - // ceil((size - 13) / (2*numAlign - 2)) * 2 - step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; - } else // C-C-C-Combo breaker! - step = 26; + int step = (ver == 32) ? 26 : + (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; vector result; for (int i = 0, pos = ver * 4 + 10; i < numAlign - 1; i++, pos -= step) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 6ca2167..985c930 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -653,11 +653,10 @@ public final class QrCode { else { int numAlign = ver / 7 + 2; int step; - if (ver != 32) { - // ceil((size - 13) / (2*numAlign - 2)) * 2 - step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; - } else // C-C-C-Combo breaker! + if (ver == 32) // Special snowflake step = 26; + else // step = ceil[(size - 13) / (numAlign*2 - 2)] * 2 + step = (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index e2d9bdc..f0fa2b5 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -604,11 +604,8 @@ var qrcodegen = new function() { else { var size = ver * 4 + 17; var numAlign = Math.floor(ver / 7) + 2; - var step; - if (ver != 32) - step = Math.ceil((size - 13) / (2 * numAlign - 2)) * 2; - else // C-C-C-Combo breaker! - step = 26; + var step = (ver == 32) ? 26 : + Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; var result = [6]; for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 3c5bc29..833807e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -496,11 +496,8 @@ class QrCode(object): return [] else: numalign = ver // 7 + 2 - if ver != 32: - # ceil((size - 13) / (2*numalign - 2)) * 2 - step = (ver * 4 + numalign * 2 + 1) // (2 * numalign - 2) * 2 - else: # C-C-C-Combo breaker! - step = 26 + step = 26 if (ver == 32) else \ + (ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2 start = ver * 4 + 10 result = [(start - i * step) for i in range(numalign - 1)] + [6] return list(reversed(result)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ce68e6f..7d9cdd8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -629,12 +629,8 @@ impl QrCode { vec![] } else { let numalign: i32 = (ver as i32) / 7 + 2; - let step: i32 = if ver != 32 { - // ceil((size - 13) / (2*numAlign - 2)) * 2 - ((ver as i32) * 4 + numalign * 2 + 1) / (2 * numalign - 2) * 2 - } else { // C-C-C-Combo breaker! - 26 - }; + let step: i32 = if ver == 32 { 26 } else + {((ver as i32)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; let start = (ver as i32) * 4 + 10; let mut result: Vec = (0 .. numalign - 1).map(|i| start - i * step).collect(); result.push(6); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index a31605a..1e140bb 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -567,11 +567,8 @@ namespace qrcodegen { else { let size: int = ver * 4 + 17; let numAlign: int = Math.floor(ver / 7) + 2; - let step: int; - if (ver != 32) - step = Math.ceil((size - 13) / (2 * numAlign - 2)) * 2; - else // C-C-C-Combo breaker! - step = 26; + let step: int = (ver == 32) ? 26 : + Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; let result: Array = [6]; for (let i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) From ecfa0a83c1b567bc3b890eefadf1be589f72bccf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 22 Aug 2018 21:25:46 +0000 Subject: [PATCH 363/810] Simplified miscellaneous Java and Python application code. --- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 11 +++-------- python/qrcodegen-batch-test.py | 1 - 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 6b6e880..dbcf233 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -28,11 +28,9 @@ package io.nayuki.qrcodegen; import java.awt.image.BufferedImage; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; @@ -64,11 +62,8 @@ public final class QrCodeGeneratorDemo { ImageIO.write(img, "png", imgFile); // Write image to file String svg = qr.toSvgString(4); // Convert to SVG XML code - try (Writer out = new OutputStreamWriter( - new FileOutputStream("hello-world-QR.svg"), - StandardCharsets.UTF_8)) { - out.write(svg); // Create/overwrite file and write SVG data - } + Files.write(new File("hello-world-QR.svg").toPath(), + svg.getBytes(StandardCharsets.UTF_8)); } diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index fa92e52..5e6d729 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -24,7 +24,6 @@ # Software. # -from __future__ import print_function import itertools, random, subprocess, sys, time if sys.version_info.major < 3: raise RuntimeError("Requires Python 3+") From 432f3e0fede43c4f2bde42ba721ba49e0c7c7bbe Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:05:31 +0000 Subject: [PATCH 364/810] Added helper function to Java QrSegmentAdvanced to clarify code. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 541d230..e7f421d 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -113,9 +113,9 @@ public final class QrSegmentAdvanced { result[2][j] = result[2][i] + 20; // 3.33 bits per digit // Switch modes, rounding up fractional bits - result[0][j] = Math.min((Math.min(result[1][j], result[2][j]) + 5) / 6 * 6 + bytesCost , result[0][j]); - result[1][j] = Math.min((Math.min(result[2][j], result[0][j]) + 5) / 6 * 6 + alphnumCost, result[1][j]); - result[2][j] = Math.min((Math.min(result[0][j], result[1][j]) + 5) / 6 * 6 + numberCost , result[2][j]); + result[0][j] = Math.min(roundUp6(Math.min(result[1][j], result[2][j])) + bytesCost , result[0][j]); + result[1][j] = Math.min(roundUp6(Math.min(result[2][j], result[0][j])) + alphnumCost, result[1][j]); + result[2][j] = Math.min(roundUp6(Math.min(result[0][j], result[1][j])) + numberCost , result[2][j]); } return result; } @@ -147,21 +147,21 @@ public final class QrSegmentAdvanced { if (curMode == NUMERIC) { if (isNumeric(c)) curMode = NUMERIC; - else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + numberCost == bitCosts[2][i + 1]) + else if (isAlphanumeric(c) && roundUp6(bitCosts[1][i] + 33) + numberCost == bitCosts[2][i + 1]) curMode = ALPHANUMERIC; else curMode = BYTE; } else if (curMode == ALPHANUMERIC) { - if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + alphnumCost == bitCosts[1][i + 1]) + if (isNumeric(c) && roundUp6(bitCosts[2][i] + 20) + alphnumCost == bitCosts[1][i + 1]) curMode = NUMERIC; else if (isAlphanumeric(c)) curMode = ALPHANUMERIC; else curMode = BYTE; } else if (curMode == BYTE) { - if (isNumeric(c) && (bitCosts[2][i] + 20 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + if (isNumeric(c) && roundUp6(bitCosts[2][i] + 20) + bytesCost == bitCosts[0][i + 1]) curMode = NUMERIC; - else if (isAlphanumeric(c) && (bitCosts[1][i] + 33 + 5) / 6 * 6 + bytesCost == bitCosts[0][i + 1]) + else if (isAlphanumeric(c) && roundUp6(bitCosts[1][i] + 33) + bytesCost == bitCosts[0][i + 1]) curMode = ALPHANUMERIC; else curMode = BYTE; @@ -224,6 +224,11 @@ public final class QrSegmentAdvanced { } + private static int roundUp6(int x) { + return (x + 5) / 6 * 6; + } + + /*---- Kanji mode segment encoder ----*/ From 78e234fb0dbb055f90b5831b6e1d7b86d7d7a27d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:15:27 +0000 Subject: [PATCH 365/810] Simplified Java QrSegmentAdvanced.splitIntoSegments() to reduce redundancy. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index e7f421d..526d1bb 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -181,8 +181,8 @@ public final class QrSegmentAdvanced { // Accumulate run of modes QrSegment.Mode curMode = charModes[0]; int start = 0; - for (int i = 1; i < data.length; i++) { - if (charModes[i] != curMode) { + for (int i = 1; ; i++) { + if (i >= data.length || charModes[i] != curMode) { if (curMode == BYTE) result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); else { @@ -194,24 +194,12 @@ public final class QrSegmentAdvanced { else throw new AssertionError(); } + if (i >= data.length) + return result; curMode = charModes[i]; start = i; } } - - // Final segment - if (curMode == BYTE) - result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, data.length))); - else { - String temp = new String(data, start, data.length - start, StandardCharsets.US_ASCII); - if (curMode == NUMERIC) - result.add(QrSegment.makeNumeric(temp)); - else if (curMode == ALPHANUMERIC) - result.add(QrSegment.makeAlphanumeric(temp)); - else - throw new AssertionError(); - } - return result; } From cf0dbd4d0fc7729f2e37b47a8ebc2a4636852926 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:16:49 +0000 Subject: [PATCH 366/810] Refactored control flow to reduce nesting. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 526d1bb..5b2c74a 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -182,23 +182,23 @@ public final class QrSegmentAdvanced { QrSegment.Mode curMode = charModes[0]; int start = 0; for (int i = 1; ; i++) { - if (i >= data.length || charModes[i] != curMode) { - if (curMode == BYTE) - result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); - else { - String temp = new String(data, start, i - start, StandardCharsets.US_ASCII); - if (curMode == NUMERIC) - result.add(QrSegment.makeNumeric(temp)); - else if (curMode == ALPHANUMERIC) - result.add(QrSegment.makeAlphanumeric(temp)); - else - throw new AssertionError(); - } - if (i >= data.length) - return result; - curMode = charModes[i]; - start = i; + if (i < data.length && charModes[i] == curMode) + continue; + if (curMode == BYTE) + result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); + else { + String temp = new String(data, start, i - start, StandardCharsets.US_ASCII); + if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(temp)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(temp)); + else + throw new AssertionError(); } + if (i >= data.length) + return result; + curMode = charModes[i]; + start = i; } } From b3fc497299f6e67141776a6c0aa3474e7b4d2b50 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:20:58 +0000 Subject: [PATCH 367/810] Simplified loop ranges without changing visible behavior. --- src/io/nayuki/fastqrcodegen/QrCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 8fe6a3f..f611a13 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -218,8 +218,8 @@ public final class QrCode { sb.append("\t\n"); sb.append("\t Date: Sat, 25 Aug 2018 23:23:26 +0000 Subject: [PATCH 368/810] Simplified code to use StringBuilder method chaining. --- src/io/nayuki/fastqrcodegen/QrCode.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index f611a13..7124f6a 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -209,14 +209,13 @@ public final class QrCode { if (size + border * 2L > Integer.MAX_VALUE) throw new IllegalArgumentException("Border too large"); - StringBuilder sb = new StringBuilder(); - sb.append("\n"); - sb.append("\n"); - sb.append(String.format( - "\n", - size + border * 2)); - sb.append("\t\n"); - sb.append("\t\n") + .append("\n") + .append(String.format("\n", + size + border * 2)) + .append("\t\n") + .append("\t\n"); - sb.append("\n"); - return sb.toString(); + return sb + .append("\" fill=\"#000000\"/>\n") + .append("\n") + .toString(); } From 7fa8becaf72df247d0f2b2e849141ddae0f5dfd6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:24:16 +0000 Subject: [PATCH 369/810] Tweaked code to avoid overflow entirely. --- src/io/nayuki/fastqrcodegen/QrCode.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 7124f6a..dfe4448 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -206,14 +206,12 @@ public final class QrCode { public String toSvgString(int border) { if (border < 0) throw new IllegalArgumentException("Border must be non-negative"); - if (size + border * 2L > Integer.MAX_VALUE) - throw new IllegalArgumentException("Border too large"); - + long brd = border; StringBuilder sb = new StringBuilder() .append("\n") .append("\n") .append(String.format("\n", - size + border * 2)) + size + brd * 2)) .append("\t\n") .append("\t Date: Sat, 25 Aug 2018 23:46:06 +0000 Subject: [PATCH 370/810] Simplified some code, without changing behavior. --- src/io/nayuki/fastqrcodegen/QrCode.java | 3 +-- src/io/nayuki/fastqrcodegen/QrSegment.java | 6 ++---- src/io/nayuki/fastqrcodegen/QrTemplate.java | 9 ++------- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index dfe4448..95242d5 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -129,7 +129,7 @@ public final class QrCode { public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments - Objects.requireNonNull(ecl); + errorCorrectionLevel = Objects.requireNonNull(ecl); if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw new IllegalArgumentException("Value out of range"); Objects.requireNonNull(dataCodewords); @@ -137,7 +137,6 @@ public final class QrCode { // Initialize fields version = ver; size = ver * 4 + 17; - errorCorrectionLevel = ecl; QrTemplate tpl = QrTemplate.getInstance(ver); modules = tpl.template.clone(); diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index c0cc5b3..ea48ce1 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -156,13 +156,11 @@ public final class QrSegment { /*---- Constructor ----*/ public QrSegment(Mode md, int numCh, int[] data, int bitLen) { - Objects.requireNonNull(md); - Objects.requireNonNull(data); + mode = Objects.requireNonNull(md); + this.data = Objects.requireNonNull(data); if (numCh < 0 || bitLen < 0 || bitLen > data.length * 32) throw new IllegalArgumentException("Invalid value"); - mode = md; numChars = numCh; - this.data = data; bitLength = bitLen; } diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 37d292b..61acfa6 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -285,13 +285,8 @@ final class QrTemplate { return new int[]{}; else { int numAlign = ver / 7 + 2; - int step; - if (ver != 32) { - // ceil((size - 13) / (2*numAlign - 2)) * 2 - step = (ver * 4 + numAlign * 2 + 1) / (2 * numAlign - 2) * 2; - } else // C-C-C-Combo breaker! - step = 26; - + int step = (ver == 32) ? 26 : + (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; for (int i = result.length - 1, pos = ver * 4 + 10; i >= 1; i--, pos -= step) From 887b6255ed374a57f26be650515577a2906b96e2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:47:47 +0000 Subject: [PATCH 371/810] Updated and simplified Reed-Solomon ECC computation to reduce temporary buffers and copying. --- src/io/nayuki/fastqrcodegen/QrCode.java | 13 +++++------- .../fastqrcodegen/ReedSolomonGenerator.java | 20 +++++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 95242d5..4ceb507 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -295,16 +295,13 @@ public final class QrCode { int shortBlockLen = rawCodewords / numBlocks; // Split data into blocks and append ECC to each block - byte[][] blocks = new byte[numBlocks][]; + byte[][] blocks = new byte[numBlocks][shortBlockLen + 1]; ReedSolomonGenerator rs = ReedSolomonGenerator.getInstance(blockEccLen); - byte[] ecc = new byte[blockEccLen]; for (int i = 0, k = 0; i < numBlocks; i++) { - byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); - byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); - k += dat.length; - rs.getRemainder(dat, ecc); - System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); - blocks[i] = block; + int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); + System.arraycopy(data, k, blocks[i], 0, datLen); + rs.getRemainder(data, k, datLen, blocks[i], shortBlockLen + 1 - blockEccLen); + k += datLen; } // Interleave (not concatenate) the bytes from every block into a single sequence diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index ff76842..7662b65 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -116,20 +116,20 @@ final class ReedSolomonGenerator { } - public void getRemainder(byte[] data, byte[] result) { + public void getRemainder(byte[] data, int dataOff, int dataLen, byte[] result, int resultOff) { Objects.requireNonNull(data); Objects.requireNonNull(result); - if (result.length != multiplies.length) - throw new IllegalArgumentException("Array length mismatch"); // Compute the remainder by performing polynomial division - Arrays.fill(result, (byte)0); - for (byte b : data) { - int factor = (b ^ result[0]) & 0xFF; - System.arraycopy(result, 1, result, 0, result.length - 1); - result[result.length - 1] = 0; - for (int i = 0; i < result.length; i++) - result[i] ^= multiplies[i][factor]; + int resultEnd = resultOff + multiplies.length; + Arrays.fill(result, resultOff, resultEnd, (byte)0); + for (int i = dataOff, dataEnd = dataOff + dataLen; i < dataEnd; i++) { + byte b = data[i]; + int factor = (b ^ result[resultOff]) & 0xFF; + System.arraycopy(result, resultOff + 1, result, resultOff, multiplies.length - 1); + result[resultEnd - 1] = 0; + for (int j = 0; j < multiplies.length; j++) + result[resultOff + j] ^= multiplies[j][factor]; } } From 0e5e3c1b6125ffadfa3e05f2070773ce00ae7683 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 25 Aug 2018 23:52:36 +0000 Subject: [PATCH 372/810] Tweaked code to convert most explicit assertion checks to native assert statements. --- src/io/nayuki/fastqrcodegen/QrCode.java | 12 ++++-------- src/io/nayuki/fastqrcodegen/QrTemplate.java | 6 ++---- .../nayuki/fastqrcodegen/ReedSolomonGenerator.java | 6 ++---- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 4ceb507..cdb6bab 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -70,8 +70,7 @@ public final class QrCode { if (version >= maxVersion) // All versions in the range could not fit the given data throw new IllegalArgumentException("Data too long"); } - if (dataUsedBits == -1) - throw new AssertionError(); + assert dataUsedBits != -1; // Increase the error correction level while the data still fits in the current version number for (Ecc newEcl : Ecc.values()) { @@ -95,8 +94,7 @@ public final class QrCode { // Pad with alternate bytes until data capacity is reached for (int padByte = 0xEC; bb.bitLength < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - if (bb.bitLength % 8 != 0) - throw new AssertionError(); + assert bb.bitLength % 8 == 0; // Create the QR Code symbol return new QrCode(version, ecl, bb.getBytes(), mask); @@ -245,8 +243,7 @@ public final class QrCode { rem = (rem << 1) ^ ((rem >>> 9) * 0x537); data = data << 10 | rem; data ^= 0x5412; // uint15 - if (data >>> 15 != 0) - throw new AssertionError(); + assert data >>> 15 == 0; // Draw first copy for (int i = 0; i <= 5; i++) @@ -363,8 +360,7 @@ public final class QrCode { applyMask(masks[i]); // Undoes the mask due to XOR } } - if (mask < 0 || mask > 7) - throw new AssertionError(); + assert 0 <= mask && mask <= 7; drawFormatBits(mask); // Overwrite old format bits applyMask(masks[mask]); // Apply the final choice of mask return mask; // The caller shall assign this value to the final-declared field diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 61acfa6..4353e40 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -169,8 +169,7 @@ final class QrTemplate { for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); int data = version << 12 | rem; // uint18 - if (data >>> 18 != 0) - throw new AssertionError(); + assert data >>> 18 == 0; // Draw two copies for (int i = 0; i < 18; i++) { @@ -249,8 +248,7 @@ final class QrTemplate { } } } - if (i != result.length) - throw new AssertionError(); + assert i == result.length; return result; } diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index 7662b65..ae17275 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -140,16 +140,14 @@ final class ReedSolomonGenerator { // 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 multiply(int x, int y) { - if (x >>> 8 != 0 || y >>> 8 != 0) - throw new IllegalArgumentException("Byte out of range"); + 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; } - if (z >>> 8 != 0) - throw new AssertionError(); + assert z >>> 8 == 0; return z; } From b7f8d3a239663233b154ea690545bb23c71570ad Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 00:00:32 +0000 Subject: [PATCH 373/810] Tweaked some bit-drawing code for clarity. --- src/io/nayuki/fastqrcodegen/QrCode.java | 19 +++++++++---------- src/io/nayuki/fastqrcodegen/QrTemplate.java | 6 +++--- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index cdb6bab..6fed91e 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -241,24 +241,23 @@ public final class QrCode { int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - data = data << 10 | rem; - data ^= 0x5412; // uint15 - assert data >>> 15 == 0; + int bits = (data << 10 | rem) ^ 0x5412; // uint15 + assert bits >>> 15 == 0; // Draw first copy for (int i = 0; i <= 5; i++) - setModule(8, i, (data >>> i) & 1); - setModule(8, 7, (data >>> 6) & 1); - setModule(8, 8, (data >>> 7) & 1); - setModule(7, 8, (data >>> 8) & 1); + setModule(8, i, (bits >>> i) & 1); + setModule(8, 7, (bits >>> 6) & 1); + setModule(8, 8, (bits >>> 7) & 1); + setModule(7, 8, (bits >>> 8) & 1); for (int i = 9; i < 15; i++) - setModule(14 - i, 8, (data >>> i) & 1); + setModule(14 - i, 8, (bits >>> i) & 1); // Draw second copy for (int i = 0; i <= 7; i++) - setModule(size - 1 - i, 8, (data >>> i) & 1); + setModule(size - 1 - i, 8, (bits >>> i) & 1); for (int i = 8; i < 15; i++) - setModule(8, size - 15 + i, (data >>> i) & 1); + setModule(8, size - 15 + i, (bits >>> i) & 1); setModule(8, size - 8, 1); } diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 4353e40..cbc50ff 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -168,12 +168,12 @@ final class QrTemplate { 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 data = version << 12 | rem; // uint18 - assert data >>> 18 == 0; + int bits = version << 12 | rem; // uint18 + assert bits >>> 18 == 0; // Draw two copies for (int i = 0; i < 18; i++) { - int bit = (data >>> i) & 1; + int bit = (bits >>> i) & 1; int a = size - 11 + i % 3, b = i / 3; darkenFunctionModule(a, b, bit); darkenFunctionModule(b, a, bit); From 593ff051bf6606d5075ec61ba92db4737287575c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 00:01:00 +0000 Subject: [PATCH 374/810] Clarified and simplified some code. --- src/io/nayuki/fastqrcodegen/QrCode.java | 4 ++-- src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java | 11 +++-------- src/io/nayuki/fastqrcodegen/QrTemplate.java | 3 ++- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 6fed91e..a4d623f 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -185,8 +185,8 @@ public final class QrCode { 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++) { - boolean val = getModule(x / scale - border, y / scale - border); - result.setRGB(x, y, val ? 0x000000 : 0xFFFFFF); + boolean color = getModule(x / scale - border, y / scale - border); + result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); } } return result; diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java index d9b1c21..03920e2 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java @@ -28,11 +28,9 @@ package io.nayuki.fastqrcodegen; import java.awt.image.BufferedImage; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.Writer; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Arrays; import java.util.List; import javax.imageio.ImageIO; @@ -64,11 +62,8 @@ public final class QrCodeGeneratorDemo { ImageIO.write(img, "png", imgFile); // Write image to file String svg = qr.toSvgString(4); // Convert to SVG XML code - try (Writer out = new OutputStreamWriter( - new FileOutputStream("hello-world-QR.svg"), - StandardCharsets.UTF_8)) { - out.write(svg); // Create/overwrite file and write SVG data - } + Files.write(new File("hello-world-QR.svg").toPath(), + svg.getBytes(StandardCharsets.UTF_8)); } diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index cbc50ff..97a4fbd 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -174,7 +174,8 @@ final class QrTemplate { // Draw two copies for (int i = 0; i < 18; i++) { int bit = (bits >>> i) & 1; - int a = size - 11 + i % 3, b = i / 3; + int a = size - 11 + i % 3; + int b = i / 3; darkenFunctionModule(a, b, bit); darkenFunctionModule(b, a, bit); } From 206618d71c724dd936753dd58cf9ecc40b746a2e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 00:09:48 +0000 Subject: [PATCH 375/810] Updated Python test script to enable assertions for Java worker program. --- python/qrcodegen-batch-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index 5e6d729..3acf236 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -32,7 +32,7 @@ if sys.version_info.major < 3: CHILD_PROGRAMS = [ ["python2", "../python/qrcodegen-worker.py"], # Python 2 program ["python3", "../python/qrcodegen-worker.py"], # Python 3 program - ["java", "-cp", "../java", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program + ["java", "-cp", "../java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["../c/qrcodegen-worker"], # C program ["../cpp/QrCodeGeneratorWorker"], # C++ program ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program From 0ee6c41c9c1a0d0a1d11b1af1f906ae9d769f7be Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 01:33:06 +0000 Subject: [PATCH 376/810] Renamed QrCode.appendErrorCorrection() to addEccAndInterleave() in all language versions. --- c/qrcodegen-test.c | 12 ++++++------ c/qrcodegen.c | 6 +++--- cpp/QrCode.cpp | 4 ++-- cpp/QrCode.hpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 4f9c5c0..0674587 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -52,7 +52,7 @@ static int numTestCases = 0; extern const int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; extern const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); -void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); int getNumRawDataModules(int version); void calcReedSolomonGenerator(int degree, uint8_t result[]); @@ -111,7 +111,7 @@ static void testAppendBitsToBuffer(void) { // Ported from the Java version of the code. -static uint8_t *appendErrorCorrectionReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) { +static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) { // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; @@ -151,20 +151,20 @@ static uint8_t *appendErrorCorrectionReference(const uint8_t *data, int version, } -static void testAppendErrorCorrection(void) { +static void testAddEccAndInterleave(void) { for (int version = 1; version <= 40; version++) { for (int ecl = 0; ecl < 4; ecl++) { int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); uint8_t *pureData = MALLOC(dataLen, uint8_t); for (int i = 0; i < dataLen; i++) pureData[i] = rand() % 256; - uint8_t *expectOutput = appendErrorCorrectionReference(pureData, version, (enum qrcodegen_Ecc)ecl); + uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl); int dataAndEccLen = getNumRawDataModules(version) / 8; uint8_t *paddedData = MALLOC(dataAndEccLen, uint8_t); memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); uint8_t *actualOutput = MALLOC(dataAndEccLen, uint8_t); - appendErrorCorrection(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); + addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); free(pureData); @@ -1057,7 +1057,7 @@ static void testGetTotalBits(void) { int main(void) { srand(time(NULL)); testAppendBitsToBuffer(); - testAppendErrorCorrection(); + testAddEccAndInterleave(); testGetNumDataCodewords(); testGetNumRawDataModules(); testCalcReedSolomonGenerator(); diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 30f0bfc..b670ea1 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -60,7 +60,7 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen); -testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); testable int getNumRawDataModules(int version); @@ -195,7 +195,7 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] // from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains // the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area // and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. -testable void appendErrorCorrection(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { +testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { // Calculate parameter numbers assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; @@ -948,7 +948,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz assert(bitLen % 8 == 0); // Draw function and data codeword modules - appendErrorCorrection(qrcode, version, ecl, tempBuffer); + addEccAndInterleave(qrcode, version, ecl, tempBuffer); initializeFunctionModules(version, qrcode); drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); drawWhiteFunctionModules(qrcode, version); diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 09f93c0..58c234a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -124,7 +124,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) // Draw function patterns, draw all codewords, do masking drawFunctionPatterns(); - const vector allCodewords = appendErrorCorrection(dataCodewords); + const vector allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); this->mask = handleConstructorMasking(mask); } @@ -298,7 +298,7 @@ bool QrCode::module(int x, int y) const { } -vector QrCode::appendErrorCorrection(const vector &data) const { +vector QrCode::addEccAndInterleave(const vector &data) const { if (data.size() != static_cast(getNumDataCodewords(version, errorCorrectionLevel))) throw std::invalid_argument("Invalid argument"); diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 33edc93..f637513 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -201,7 +201,7 @@ class QrCode final { // 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: std::vector appendErrorCorrection(const std::vector &data) const; + private: std::vector addEccAndInterleave(const std::vector &data) const; // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 985c930..fbe16b7 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -226,7 +226,7 @@ public final class QrCode { // Draw function patterns, draw all codewords, do masking drawFunctionPatterns(); - byte[] allCodewords = appendErrorCorrection(dataCodewords); + byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); this.mask = handleConstructorMasking(mask); } @@ -437,7 +437,7 @@ public final class QrCode { // 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[] appendErrorCorrection(byte[] data) { + private byte[] addEccAndInterleave(byte[] data) { if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) throw new IllegalArgumentException(); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index f0fa2b5..3bf9f40 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -92,7 +92,7 @@ var qrcodegen = new function() { // Handle grid fields, draw function patterns, draw all codewords drawFunctionPatterns(); - var allCodewords = appendErrorCorrection(datacodewords); + var allCodewords = addEccAndInterleave(datacodewords); drawCodewords(allCodewords); // Handle masking @@ -310,7 +310,7 @@ var qrcodegen = new function() { // 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. - function appendErrorCorrection(data) { + function addEccAndInterleave(data) { if (data.length != QrCode.getNumDataCodewords(version, errCorLvl)) throw "Invalid argument"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 833807e..58b8ab3 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -169,7 +169,7 @@ class QrCode(object): self._isfunction = [[False] * self._size for _ in range(self._size)] # Indicates function modules that are not subjected to masking # Draw function patterns, draw all codewords self._draw_function_patterns() - allcodewords = self._append_error_correction(datacodewords) + allcodewords = self._add_ecc_and_interleave(datacodewords) self._draw_codewords(allcodewords) # Handle masking @@ -345,7 +345,7 @@ class QrCode(object): # ---- Private helper methods for constructor: Codewords and masking ---- - def _append_error_correction(self, data): + def _add_ecc_and_interleave(self, data): """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.""" version = self._version diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7d9cdd8..149fbb8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -182,7 +182,7 @@ impl QrCode { // Draw function patterns, draw all codewords, do masking result.draw_function_patterns(); - let allcodewords: Vec = result.append_error_correction(datacodewords); + let allcodewords: Vec = result.add_ecc_and_interleave(datacodewords); result.draw_codewords(&allcodewords); result.handle_constructor_masking(mask); result @@ -403,7 +403,7 @@ impl QrCode { // 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. - fn append_error_correction(&self, data: &[u8]) -> Vec { + fn add_ecc_and_interleave(&self, data: &[u8]) -> Vec { let ver = self.version; let ecl = self.errorcorrectionlevel; assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl), "Illegal argument"); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 1e140bb..f82adc6 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -170,7 +170,7 @@ namespace qrcodegen { // Handle grid fields, draw function patterns, draw all codewords this.drawFunctionPatterns(); - let allCodewords: Array = this.appendErrorCorrection(datacodewords); + let allCodewords: Array = this.addEccAndInterleave(datacodewords); this.drawCodewords(allCodewords); // Handle masking @@ -374,7 +374,7 @@ namespace qrcodegen { // 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 appendErrorCorrection(data: Array): Array { + private addEccAndInterleave(data: Array): Array { const ver: int = this.version; const ecl: QrCode_Ecc = this.errorCorrectionLevel; if (data.length != QrCode.getNumDataCodewords(ver, ecl)) From c9553ead71009361d191e97fc9d1d8ca2d6a9dc9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 01:49:27 +0000 Subject: [PATCH 377/810] Simplified code in QrCode.drawFunctionPatterns() in all language versions, but differently in Python. --- c/qrcodegen.c | 5 ++--- cpp/QrCode.cpp | 5 ++--- java/io/nayuki/qrcodegen/QrCode.java | 5 ++--- javascript/qrcodegen.js | 5 ++--- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 5 ++--- typescript/qrcodegen.ts | 5 ++--- 7 files changed, 14 insertions(+), 20 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index b670ea1..386fd1e 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -341,9 +341,8 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) { int numAlign = getAlignmentPatternPositions(version, alignPatPos); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) - continue; // Skip the three finder corners - else + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) fillRectangle(alignPatPos[i] - 2, alignPatPos[j] - 2, 5, 5, qrcode); } } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 58c234a..6ddb0c7 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -203,9 +203,8 @@ void QrCode::drawFunctionPatterns() { int numAlign = alignPatPos.size(); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) - continue; // Skip the three finder corners - else + // Don't draw on the three finder corners + if (!((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0))) drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index fbe16b7..d46bdd3 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -335,9 +335,8 @@ public final class QrCode { int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) - continue; // Skip the three finder corners - else + // Don't draw on the three finder corners + if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 3bf9f40..8da8e89 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -206,9 +206,8 @@ var qrcodegen = new function() { var numAlign = alignPatPos.length; for (var i = 0; i < numAlign; i++) { for (var j = 0; j < numAlign; j++) { - if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) - continue; // Skip the three finder corners - else + // Don't draw on the three finder corners + if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 58b8ab3..3399821 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -255,10 +255,10 @@ class QrCode(object): # Draw numerous alignment patterns alignpatpos = QrCode._get_alignment_pattern_positions(self._version) numalign = len(alignpatpos) - skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) # Skip the three finder corners + skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) for i in range(numalign): for j in range(numalign): - if (i, j) not in skips: + if (i, j) not in skips: # Don't draw on the three finder corners self._draw_alignment_pattern(alignpatpos[i], alignpatpos[j]) # Draw configuration data diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 149fbb8..589c01b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -288,9 +288,8 @@ impl QrCode { let numalign: usize = alignpatpos.len(); for i in 0 .. numalign { for j in 0 .. numalign { - if i == 0 && j == 0 || i == 0 && j == numalign - 1 || i == numalign - 1 && j == 0 { - continue; // Skip the three finder corners - } else { + // Don't draw on the three finder corners + if !(i == 0 && j == 0 || i == 0 && j == numalign - 1 || i == numalign - 1 && j == 0) { self.draw_alignment_pattern(alignpatpos[i], alignpatpos[j]); } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index f82adc6..0c19436 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -269,9 +269,8 @@ namespace qrcodegen { let numAlign: int = alignPatPos.length; for (let i = 0; i < numAlign; i++) { for (let j = 0; j < numAlign; j++) { - if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) - continue; // Skip the three finder corners - else + // Don't draw on the three finder corners + if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } From c86dacacb75b84ae32436e23d3c7a81abdc16856 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 02:03:27 +0000 Subject: [PATCH 378/810] Tweaked code and comment in QrCode.encodeSegments() in all language versions. --- c/qrcodegen.c | 7 ++++--- cpp/QrCode.cpp | 12 +++++++----- java/io/nayuki/qrcodegen/QrCode.java | 7 ++++--- javascript/qrcodegen.js | 12 +++++++----- python/qrcodegen.py | 7 ++++--- rust/src/lib.rs | 7 ++++--- typescript/qrcodegen.ts | 12 +++++++----- 7 files changed, 37 insertions(+), 27 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 386fd1e..eac0a21 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -914,7 +914,6 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz } // Concatenate all segments to create the data bit string - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; for (size_t i = 0; i < len; i++) { @@ -935,16 +934,18 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz } // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); int terminatorBits = dataCapacityBits - bitLen; if (terminatorBits > 4) terminatorBits = 4; appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); - // Pad with alternate bytes until data capacity is reached + // Pad with alternating bytes until data capacity is reached for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) appendBitsToBuffer(padByte, 8, qrcode, &bitLen); - assert(bitLen % 8 == 0); // Draw function and data codeword modules addEccAndInterleave(qrcode, version, ecl, tempBuffer); diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 6ddb0c7..99bdd11 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -87,7 +87,6 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, } // Concatenate all segments to create the data bit string - size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb; for (const QrSegment &seg : segs) { bb.appendBits(seg.getMode().getModeBits(), 4); @@ -96,15 +95,18 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, } // Add terminator and pad up to a byte if applicable + size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + if (bb.size() > dataCapacityBits) + throw std::logic_error("Assertion error"); bb.appendBits(0, std::min(4, dataCapacityBits - bb.size())); bb.appendBits(0, (8 - bb.size() % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); if (bb.size() % 8 != 0) throw std::logic_error("Assertion error"); + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + // Create the QR Code symbol return QrCode(version, ecl, bb.getBytes(), mask); } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d46bdd3..225f2e7 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -141,7 +141,6 @@ public final class QrCode { } // Concatenate all segments to create the data bit string - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { bb.appendBits(seg.mode.modeBits, 4); @@ -150,13 +149,15 @@ public final class QrCode { } // 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 alternate bytes until data capacity is reached + // Pad with alternating bytes until data capacity is reached for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - assert bb.bitLength() % 8 == 0; // Create the QR Code symbol return new QrCode(version, ecl, bb.getBytes(), mask); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 8da8e89..2f9579f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -554,7 +554,6 @@ var qrcodegen = new function() { }); // Concatenate all segments to create the data bit string - var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; var bb = new BitBuffer(); segs.forEach(function(seg) { bb.appendBits(seg.mode.modeBits, 4); @@ -565,15 +564,18 @@ var qrcodegen = new function() { }); // Add terminator and pad up to a byte if applicable + var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + if (bb.length > dataCapacityBits) + throw "Assertion error"; bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); bb.appendBits(0, (8 - bb.length % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); if (bb.length % 8 != 0) throw "Assertion error"; + // Pad with alternating bytes until data capacity is reached + for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + // Create the QR Code symbol return new this(bb.getBytes(), mask, version, ecl); }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 3399821..8e297af 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -116,7 +116,6 @@ class QrCode(object): ecl = newecl # Concatenate all segments to create the data bit string - datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 bb = _BitBuffer() for seg in segs: bb.append_bits(seg.get_mode().get_mode_bits(), 4) @@ -124,15 +123,17 @@ class QrCode(object): bb.extend(seg._bitdata) # Add terminator and pad up to a byte if applicable + datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 + assert len(bb) <= datacapacitybits bb.append_bits(0, min(4, datacapacitybits - len(bb))) bb.append_bits(0, -len(bb) % 8) # Note: Python's modulo on negative numbers behaves better than C family languages + assert len(bb) % 8 == 0 - # Pad with alternate bytes until data capacity is reached + # Pad with alternating bytes until data capacity is reached for padbyte in itertools.cycle((0xEC, 0x11)): if len(bb) >= datacapacitybits: break bb.append_bits(padbyte, 8) - assert len(bb) % 8 == 0 # Create the QR Code symbol return QrCode(bb.get_bytes(), mask, version, ecl) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 589c01b..82abe1d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -130,7 +130,6 @@ impl QrCode { } // Concatenate all segments to create the data bit string - let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let mut bb = BitBuffer(Vec::new()); for seg in segs { bb.append_bits(seg.mode.mode_bits(), 4); @@ -139,19 +138,21 @@ impl QrCode { } // Add terminator and pad up to a byte if applicable + let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; + assert!(bb.0.len() <= datacapacitybits); let numzerobits = std::cmp::min(4, datacapacitybits - bb.0.len()); bb.append_bits(0, numzerobits as u8); let numzerobits = bb.0.len().wrapping_neg() & 7; bb.append_bits(0, numzerobits as u8); + assert_eq!(bb.0.len() % 8, 0, "Assertion error"); - // Pad with alternate bytes until data capacity is reached + // Pad with alternating bytes until data capacity is reached for padbyte in [0xEC, 0x11].iter().cycle() { if bb.0.len() >= datacapacitybits { break; } bb.append_bits(*padbyte, 8); } - assert_eq!(bb.0.len() % 8, 0, "Assertion error"); let mut bytes = vec![0u8; bb.0.len() / 8]; for (i, bit) in bb.0.iter().enumerate() { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 0c19436..c1e4862 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -101,7 +101,6 @@ namespace qrcodegen { }); // Concatenate all segments to create the data bit string - let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; let bb = new BitBuffer(); segs.forEach((seg: QrSegment) => { bb.appendBits(seg.mode.modeBits, 4); @@ -111,15 +110,18 @@ namespace qrcodegen { }); // Add terminator and pad up to a byte if applicable + let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; + if (bb.length > dataCapacityBits) + throw "Assertion error"; bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); bb.appendBits(0, (8 - bb.length % 8) % 8); - - // Pad with alternate bytes until data capacity is reached - for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); if (bb.length % 8 != 0) throw "Assertion error"; + // Pad with alternating bytes until data capacity is reached + for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + bb.appendBits(padByte, 8); + // Create the QR Code symbol return new QrCode(bb.getBytes(), mask, version, ecl); } From ccd571624e2e380171e4d7d1fc8d27ed72863f22 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 02:17:47 +0000 Subject: [PATCH 379/810] Tweaked QrCode constructor to dispose of the isFunction array in all languages except C, but somewhat differently in each language. --- cpp/QrCode.cpp | 2 ++ java/io/nayuki/qrcodegen/QrCode.java | 1 + javascript/qrcodegen.js | 1 + python/qrcodegen.py | 1 + rust/src/lib.rs | 2 ++ typescript/qrcodegen.ts | 1 + 6 files changed, 8 insertions(+) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 99bdd11..3c6046a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -129,6 +129,8 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) const vector allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); this->mask = handleConstructorMasking(mask); + isFunction.clear(); + isFunction.shrink_to_fit(); } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 225f2e7..7cf0900 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -230,6 +230,7 @@ public final class QrCode { byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); this.mask = handleConstructorMasking(mask); + isFunction = null; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 2f9579f..dfb768a 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -113,6 +113,7 @@ var qrcodegen = new function() { throw "Assertion error"; drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask + isFunction = null; /*---- Read-only instance properties ----*/ diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8e297af..1c0cda5 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -188,6 +188,7 @@ class QrCode(object): self._draw_format_bits(mask) # Overwrite old format bits self._apply_mask(mask) # Apply the final choice of mask self._mask = mask + del self._isfunction # ---- Accessor methods ---- diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 82abe1d..5654b0a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -186,6 +186,8 @@ impl QrCode { let allcodewords: Vec = result.add_ecc_and_interleave(datacodewords); result.draw_codewords(&allcodewords); result.handle_constructor_masking(mask); + result.isfunction.clear(); + result.isfunction.shrink_to_fit(); result } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index c1e4862..3ae9bc7 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -194,6 +194,7 @@ namespace qrcodegen { this.mask = mask; this.drawFormatBits(mask); // Overwrite old format bits this.applyMask(mask); // Apply the final choice of mask + this.isFunction = []; } From c8f05d24d1b23ca2a421d46e64cf32bfd29c0d4c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 02:33:20 +0000 Subject: [PATCH 380/810] Tweaked comments about inclusive ranges so that the word is consistently parenthesized. --- c/qrcodegen.h | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 6 +++--- java/io/nayuki/qrcodegen/QrSegment.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 899feae..a08fe0f 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -107,7 +107,7 @@ struct qrcodegen_Segment { // Calculates the number of bytes needed to store any QR Code up to and including the given version number, // as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' -// can store any single QR Code from version 1 to 25, inclusive. +// can store any single QR Code from version 1 to 25 (inclusive). // Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. #define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 7cf0900..d463f32 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -180,7 +180,7 @@ public final class QrCode { public final int version; /** The width and height of this QR Code symbol, measured in modules. - * Always equal to version × 4 + 17, in the range 21 to 177. */ + * Always equal to version × 4 + 17, in the range 21 to 177 (inclusive). */ public final int size; /** The error correction level used in this QR Code symbol. Never {@code null}. */ @@ -203,7 +203,7 @@ public final class QrCode { * Constructs a QR Code symbol with the specified version number, error correction level, binary data array, and mask number. *

This is a cumbersome low-level constructor that should not be invoked directly by the user. * To go one level up, see 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 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 dataCodewords the bytes representing segments to encode (without ECC) * @param mask the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice @@ -782,7 +782,7 @@ public final class QrCode { /** * Constructs a Reed-Solomon ECC generator for the specified degree. This could be implemented * as a lookup table over all possible parameter values, instead of as an algorithm. - * @param degree the divisor polynomial degree, which must be between 1 and 255 + * @param degree the divisor polynomial degree, which must be between 1 and 255 (inclusive) * @throws IllegalArgumentException if degree < 1 or degree > 255 */ public ReedSolomonGenerator(int degree) { diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index a9a464f..1d29474 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -268,8 +268,8 @@ public final class QrSegment { /** * Returns the bit width of the segment character count field for this mode object at the specified version number. - * @param ver the version number, which is between 1 to 40, inclusive - * @return the number of bits for the character count, which is between 8 to 16, inclusive + * @param ver the version number, which is between 1 to 40 (inclusive) + * @return the number of bits for the character count, which is between 8 to 16 (inclusive) * @throws IllegalArgumentException if the version number is out of range */ int numCharCountBits(int ver) { From 1edde0571a884593e4e54b14ce7194c69a1aa815 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 02:33:36 +0000 Subject: [PATCH 381/810] Simplified some Java code regarding Objects.requireNonNull(). --- java/io/nayuki/qrcodegen/QrCode.java | 3 +-- java/io/nayuki/qrcodegen/QrSegment.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d463f32..a433f70 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -213,7 +213,7 @@ public final class QrCode { */ public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments - Objects.requireNonNull(ecl); + errorCorrectionLevel = Objects.requireNonNull(ecl); if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw new IllegalArgumentException("Value out of range"); Objects.requireNonNull(dataCodewords); @@ -221,7 +221,6 @@ public final class QrCode { // Initialize fields version = ver; size = ver * 4 + 17; - errorCorrectionLevel = ecl; modules = new boolean[size][size]; // Entirely white grid isFunction = new boolean[size][size]; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 1d29474..8705797 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -177,11 +177,10 @@ public final class QrSegment { * @throws IllegalArgumentException if the character count is negative */ public QrSegment(Mode md, int numCh, BitBuffer data) { - Objects.requireNonNull(md); + mode = Objects.requireNonNull(md); Objects.requireNonNull(data); if (numCh < 0) throw new IllegalArgumentException("Invalid value"); - mode = md; numChars = numCh; this.data = data.clone(); // Make defensive copy } From 7d7a9b4820c9d2467dfcec98a77b747b5b00edbd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 02:33:49 +0000 Subject: [PATCH 382/810] Tweaked some Java code. --- java/io/nayuki/qrcodegen/QrCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index a433f70..6b20b7f 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -214,9 +214,9 @@ public final class QrCode { public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments errorCorrectionLevel = Objects.requireNonNull(ecl); + Objects.requireNonNull(dataCodewords); if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw new IllegalArgumentException("Value out of range"); - Objects.requireNonNull(dataCodewords); // Initialize fields version = ver; @@ -438,6 +438,7 @@ public final class QrCode { // 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(); From 5073db448757c760b3944ab846cd527f373bcf9f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:20:12 +0000 Subject: [PATCH 383/810] Somewhat simplified black/white balance penalty calculation in all language versions. --- c/qrcodegen.c | 7 ++++--- cpp/QrCode.cpp | 7 ++++--- java/io/nayuki/qrcodegen/QrCode.java | 7 ++++--- javascript/qrcodegen.js | 7 ++++--- python/qrcodegen.py | 9 ++++----- rust/src/lib.rs | 10 ++++------ typescript/qrcodegen.ts | 7 ++++--- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index eac0a21..44c041a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -619,10 +619,11 @@ static long getPenaltyScore(const uint8_t qrcode[]) { black++; } } + // Note that size is odd, so black/total != 1/2 int total = qrsize * qrsize; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (int k = 0; black*20L < (9L-k)*total || black*20L > (11L+k)*total; k++) - result += PENALTY_N4; + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; return result; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 3c6046a..61d583b 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -485,10 +485,11 @@ long QrCode::getPenaltyScore() const { black++; } } + // Note that size is odd, so black/total != 1/2 int total = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (int k = 0; black*20L < (9L-k)*total || black*20L > (11L+k)*total; k++) - result += PENALTY_N4; + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + result += k * PENALTY_N4; return result; } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 6b20b7f..80a4ee9 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -632,10 +632,11 @@ public final class QrCode { black++; } } + // Note that size is odd, so black/total != 1/2 int total = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) - result += PENALTY_N4; + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = (Math.abs(black * 20 - total * 10) + total - 1) / total - 1; + result += k * PENALTY_N4; return result; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index dfb768a..0288aaa 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -480,10 +480,11 @@ var qrcodegen = new function() { black++; }); }); + // Note that size is odd, so black/total != 1/2 var total = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (var k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) - result += QrCode.PENALTY_N4; + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + var k = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; + result += k * QrCode.PENALTY_N4; return result; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 1c0cda5..22c2252 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -476,12 +476,11 @@ class QrCode(object): # Balance of black and white modules black = sum((1 if cell else 0) for row in modules for cell in row) + # Note that size is odd, so black/total != 1/2 total = size**2 - # Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for k in itertools.count(): - if (9-k)*total <= black*20 <= (11+k)*total: - break - result += QrCode._PENALTY_N4 + # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + k = (abs(black * 20 - total * 10) + total - 1) // total - 1 + result += k * QrCode._PENALTY_N4 return result diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 5654b0a..ee24836 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -609,13 +609,11 @@ impl QrCode { for color in &self.modules { black += *color as i32; } + // Note that size is odd, so black/total != 1/2 let total: i32 = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - let mut k: i32 = 0; - while black*20 < (9-k)*total || black*20 > (11+k)*total { - result += PENALTY_N4; - k += 1; - } + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + let k: i32 = ((black * 20 - total * 10).abs() + total - 1) / total - 1; + result += k * PENALTY_N4; result } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 3ae9bc7..52b2a4f 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -548,10 +548,11 @@ namespace qrcodegen { black++; }); }); + // Note that size is odd, so black/total != 1/2 let total: int = this.size * this.size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (let k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) - result += QrCode.PENALTY_N4; + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + let k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; + result += k * QrCode.PENALTY_N4; return result; } From 868b387d9a60efa7a5f175b22467c5ec20060305 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:31:09 +0000 Subject: [PATCH 384/810] Fixed C++ worker program due to changing exceptions from string to object (related to commit 4ca232b94998). --- cpp/QrCodeGeneratorWorker.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp index 2e14607..b4669ba 100644 --- a/cpp/QrCodeGeneratorWorker.cpp +++ b/cpp/QrCodeGeneratorWorker.cpp @@ -91,9 +91,9 @@ int main() { std::cout << (qr.getModule(x, y) ? 1 : 0) << std::endl; } - } catch (const char *msg) { - if (strcmp(msg, "Data too long") != 0) { - std::cerr << msg << std::endl; + } catch (const std::length_error &ex) { + if (strcmp(ex.what(), "Data too long") != 0) { + std::cerr << ex.what() << std::endl; return EXIT_FAILURE; } std::cout << -1 << std::endl; From 85072b3126c9f96d8c3222dbabf147df91f5ec0f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:41:25 +0000 Subject: [PATCH 385/810] Fixed TypeScript code's QrSegment_Mode constants to make the demo work correctly. --- typescript/qrcodegen.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 52b2a4f..03b9943 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -870,11 +870,11 @@ namespace qrcodegen { /*-- Constants --*/ - public static get NUMERIC () { return new QrSegment_Mode(0x1, [10, 12, 14]); } - public static get ALPHANUMERIC() { return new QrSegment_Mode(0x2, [ 9, 11, 13]); } - public static get BYTE () { return new QrSegment_Mode(0x4, [ 8, 16, 16]); } - public static get KANJI () { return new QrSegment_Mode(0x8, [ 8, 10, 12]); } - public static get ECI () { return new QrSegment_Mode(0x7, [ 0, 0, 0]); } + public static readonly NUMERIC = new QrSegment_Mode(0x1, [10, 12, 14]); + public static readonly ALPHANUMERIC = new QrSegment_Mode(0x2, [ 9, 11, 13]); + public static readonly BYTE = new QrSegment_Mode(0x4, [ 8, 16, 16]); + public static readonly KANJI = new QrSegment_Mode(0x8, [ 8, 10, 12]); + public static readonly ECI = new QrSegment_Mode(0x7, [ 0, 0, 0]); /*-- Fields --*/ From c3aeb24757f008ba07e32350930e01e7106f6dbc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:45:39 +0000 Subject: [PATCH 386/810] Tweaked TypeScript code to use static read-only fields instead of property getters. --- typescript/qrcodegen.ts | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 03b9943..d67ccd5 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -612,15 +612,15 @@ namespace qrcodegen { /*-- Constants --*/ - public static get MIN_VERSION(): int { return 1; } - public static get MAX_VERSION(): int { return 40; } + public static readonly MIN_VERSION: int = 1; + public static readonly MAX_VERSION: int = 40; // For use in getPenaltyScore(), when evaluating which mask is best. - private static get PENALTY_N1(): int { return 3; } - private static get PENALTY_N2(): int { return 3; } - private static get PENALTY_N3(): int { return 40; } - private static get PENALTY_N4(): int { return 10; } + private static readonly PENALTY_N1: int = 3; + private static readonly PENALTY_N2: int = 3; + private static readonly PENALTY_N3: int = 40; + private static readonly PENALTY_N4: int = 10; } @@ -661,10 +661,10 @@ namespace qrcodegen { /*-- Constants --*/ - public static get LOW () { return new QrCode_Ecc(0, 1); } - public static get MEDIUM () { return new QrCode_Ecc(1, 0); } - public static get QUARTILE() { return new QrCode_Ecc(2, 3); } - public static get HIGH () { return new QrCode_Ecc(3, 2); } + public static readonly LOW = new QrCode_Ecc(0, 1); + public static readonly MEDIUM = new QrCode_Ecc(1, 0); + public static readonly QUARTILE = new QrCode_Ecc(2, 3); + public static readonly HIGH = new QrCode_Ecc(3, 2); /*-- Fields --*/ @@ -843,19 +843,13 @@ namespace qrcodegen { /*-- Constants --*/ // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). - public static get NUMERIC_REGEX(): RegExp { - return /^[0-9]*$/; - } + public static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/; // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). - public static get ALPHANUMERIC_REGEX(): RegExp { - return /^[A-Z0-9 $%*+.\/:-]*$/; - } + public static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\/:-]*$/; // The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. - private static get ALPHANUMERIC_CHARSET(): string { - return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - } + private static readonly ALPHANUMERIC_CHARSET: string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; } From 3e2770d6c01b40d5c72e5308779df39e2476514c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:53:36 +0000 Subject: [PATCH 387/810] Somewhat simplified black/white balance penalty calculation. --- src/io/nayuki/fastqrcodegen/QrCode.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index a4d623f..797d59e 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -436,10 +436,10 @@ public final class QrCode { } // Balance of black and white modules - int total = size * size; - // Find smallest k such that (45-5k)% <= dark/total <= (55+5k)% - for (int k = 0; black*20 < (9-k)*total || black*20 > (11+k)*total; k++) - result += PENALTY_N4; + int total = size * size; // Note that size is odd, so black/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% + int k = (Math.abs(black * 20 - total * 10) + total - 1) / total - 1; + result += k * PENALTY_N4; return result; } From 98d1f0cc91d060cbb2cd19a4663e4cfac237cd51 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 26 Aug 2018 03:55:03 +0000 Subject: [PATCH 388/810] Reformatted a comment in all language versions (related to commit 5073db448757). --- c/qrcodegen.c | 3 +-- cpp/QrCode.cpp | 3 +-- java/io/nayuki/qrcodegen/QrCode.java | 3 +-- javascript/qrcodegen.js | 3 +-- python/qrcodegen.py | 3 +-- rust/src/lib.rs | 3 +-- typescript/qrcodegen.ts | 3 +-- 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 44c041a..76876cf 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -619,8 +619,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) { black++; } } - // Note that size is odd, so black/total != 1/2 - int total = qrsize * qrsize; + int total = qrsize * qrsize; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 61d583b..bb8e979 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -485,8 +485,7 @@ long QrCode::getPenaltyScore() const { black++; } } - // Note that size is odd, so black/total != 1/2 - int total = size * size; + int total = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 80a4ee9..f558770 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -632,8 +632,7 @@ public final class QrCode { black++; } } - // Note that size is odd, so black/total != 1/2 - int total = size * size; + int total = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% int k = (Math.abs(black * 20 - total * 10) + total - 1) / total - 1; result += k * PENALTY_N4; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0288aaa..27cbc0c 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -480,8 +480,7 @@ var qrcodegen = new function() { black++; }); }); - // Note that size is odd, so black/total != 1/2 - var total = size * size; + var total = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% var k = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; result += k * QrCode.PENALTY_N4; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 22c2252..130f832 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -476,8 +476,7 @@ class QrCode(object): # Balance of black and white modules black = sum((1 if cell else 0) for row in modules for cell in row) - # Note that size is odd, so black/total != 1/2 - total = size**2 + total = size**2 # Note that size is odd, so black/total != 1/2 # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% k = (abs(black * 20 - total * 10) + total - 1) // total - 1 result += k * QrCode._PENALTY_N4 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ee24836..486896f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -609,8 +609,7 @@ impl QrCode { for color in &self.modules { black += *color as i32; } - // Note that size is odd, so black/total != 1/2 - let total: i32 = size * size; + let total: i32 = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% let k: i32 = ((black * 20 - total * 10).abs() + total - 1) / total - 1; result += k * PENALTY_N4; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d67ccd5..2d59569 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -548,8 +548,7 @@ namespace qrcodegen { black++; }); }); - // Note that size is odd, so black/total != 1/2 - let total: int = this.size * this.size; + let total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% let k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; result += k * QrCode.PENALTY_N4; From 0860fee6bee6b763d342fb0259fa837622af319b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Aug 2018 02:47:33 +0000 Subject: [PATCH 389/810] Simplified example Rust code in readme. --- Readme.markdown | 12 ++++++------ rust/Readme.markdown | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 0ee2926..a35dd85 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -173,18 +173,18 @@ Rust language: use qrcodegen::QrSegment; // Simple operation - let qr0 = QrCode::encode_text("Hello, world!", + let qr = QrCode::encode_text("Hello, world!", QrCodeEcc::Medium).unwrap(); - let svg = qr0.to_svg_string(4); + let svg = qr.to_svg_string(4); // Manual operation let chrs: Vec = "3141592653589793238462643383".chars().collect(); let segs = QrSegment::make_segments(&chrs); - let qr1 = QrCode::encode_segments_advanced( + let qr = QrCode::encode_segments_advanced( &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); - for y in 0 .. qr1.size() { - for x in 0 .. qr1.size() { - (... paint qr1.get_module(x, y) ...) + for y in 0 .. qr.size() { + for x in 0 .. qr.size() { + (... paint qr.get_module(x, y) ...) } } diff --git a/rust/Readme.markdown b/rust/Readme.markdown index fd8cb50..55ae530 100644 --- a/rust/Readme.markdown +++ b/rust/Readme.markdown @@ -39,18 +39,18 @@ Examples use qrcodegen::QrSegment; // Simple operation - let qr0 = QrCode::encode_text("Hello, world!", + let qr = QrCode::encode_text("Hello, world!", QrCodeEcc::Medium).unwrap(); - let svg = qr0.to_svg_string(4); + let svg = qr.to_svg_string(4); // Manual operation let chrs: Vec = "3141592653589793238462643383".chars().collect(); let segs = QrSegment::make_segments(&chrs); - let qr1 = QrCode::encode_segments_advanced( + let qr = QrCode::encode_segments_advanced( &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); - for y in 0 .. qr1.size() { - for x in 0 .. qr1.size() { - (... paint qr1.get_module(x, y) ...) + for y in 0 .. qr.size() { + for x in 0 .. qr.size() { + (... paint qr.get_module(x, y) ...) } } From 391c5fcc20a58a5e0c1449913120cbd4aa7eca95 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Aug 2018 02:51:37 +0000 Subject: [PATCH 390/810] Synchronized readme text to add TypeScript to the list of languages (related to commit 88ff414a2498). --- python/setup.py | 2 +- rust/Readme.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/setup.py b/python/setup.py index 6026451..4d6ad77 100644 --- a/python/setup.py +++ b/python/setup.py @@ -66,7 +66,7 @@ Features Core features: -* Available in 6 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++, C, Rust +* Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol, SVG XML string diff --git a/rust/Readme.markdown b/rust/Readme.markdown index 55ae530..48b579f 100644 --- a/rust/Readme.markdown +++ b/rust/Readme.markdown @@ -15,7 +15,7 @@ Features Core features: -* Available in 6 programming languages, all with nearly equal functionality: Java, JavaScript, Python, C++, C, Rust +* Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol, SVG XML string From dae06a30aace0723d9a72cbc2e2570e23513f9ac Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Aug 2018 02:56:17 +0000 Subject: [PATCH 391/810] Synchronized example Python code in setup.py with main readme (related to commit 7e72bd8dfbb6). --- python/setup.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/python/setup.py b/python/setup.py index 4d6ad77..232fd00 100644 --- a/python/setup.py +++ b/python/setup.py @@ -97,11 +97,9 @@ Examples: # Manual operation segs = QrSegment.make_segments("3141592653589793238462643383") qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) - border = 4 - for y in range(-border, qr1.get_size() + border): - for x in range(-border, qr1.get_size() + border): - color = qr1.get_module(x, y) # False for white, True for black - # (... paint the module onto pixels ...) + for y in range(qr1.get_size()): + for x in range(qr1.get_size()): + (... paint qr1.get_module(x, y) ...) More complete set of examples: https://github.com/nayuki/QR-Code-generator/blob/master/python/qrcodegen-demo.py . From bed21e3c64244095130ce50c09d2478f03e997d6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Aug 2018 03:10:17 +0000 Subject: [PATCH 392/810] Updated TypeScript code so that QrCode_Ecc and QrSegment_Mode are changed to nested classes, thus making the API fully compatible with the JavaScript version. --- typescript/qrcodegen-demo.ts | 16 +-- typescript/qrcodegen.ts | 202 +++++++++++++++++++---------------- 2 files changed, 115 insertions(+), 103 deletions(-) diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index 35cdf87..7a376d2 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -52,19 +52,19 @@ svg.style.display = "none"; // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. - function getInputErrorCorrectionLevel(): qrcodegen.QrCode_Ecc { + function getInputErrorCorrectionLevel(): qrcodegen.QrCode.Ecc { if (getInput("errcorlvl-medium").checked) - return qrcodegen.QrCode_Ecc.MEDIUM; + return qrcodegen.QrCode.Ecc.MEDIUM; else if (getInput("errcorlvl-quartile").checked) - return qrcodegen.QrCode_Ecc.QUARTILE; + return qrcodegen.QrCode.Ecc.QUARTILE; else if (getInput("errcorlvl-high").checked) - return qrcodegen.QrCode_Ecc.HIGH; + return qrcodegen.QrCode.Ecc.HIGH; else // In case no radio button is depressed - return qrcodegen.QrCode_Ecc.LOW; + return qrcodegen.QrCode.Ecc.LOW; } // Get form inputs and compute QR Code - const ecl: qrcodegen.QrCode_Ecc = getInputErrorCorrectionLevel(); + const ecl: qrcodegen.QrCode.Ecc = getInputErrorCorrectionLevel(); const text: string = (getElem("text-input") as HTMLTextAreaElement).value; const segs: Array = qrcodegen.QrSegment.makeSegments(text); const minVer: number = parseInt(getInput("version-min-input").value, 10); @@ -98,8 +98,8 @@ if (segs.length == 0) return "none"; else if (segs.length == 1) { - const mode: qrcodegen.QrSegment_Mode = segs[0].mode; - const Mode = qrcodegen.QrSegment_Mode; + const mode: qrcodegen.QrSegment.Mode = segs[0].mode; + const Mode = qrcodegen.QrSegment.Mode; if (mode == Mode.NUMERIC ) return "numeric"; if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; if (mode == Mode.BYTE ) return "byte"; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 2d59569..1bf7d63 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -51,7 +51,7 @@ namespace qrcodegen { // Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the // ecl argument if it can be done without increasing the version. - public static encodeText(text: string, ecl: QrCode_Ecc): QrCode { + public static encodeText(text: string, ecl: QrCode.Ecc): QrCode { let segs: Array = qrcodegen.QrSegment.makeSegments(text); return QrCode.encodeSegments(segs, ecl); } @@ -61,7 +61,7 @@ namespace qrcodegen { // This function always encodes using the binary segment mode, not any text mode. The maximum number of // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - public static encodeBinary(data: Array, ecl: QrCode_Ecc): QrCode { + public static encodeBinary(data: Array, ecl: QrCode.Ecc): QrCode { let seg: QrSegment = qrcodegen.QrSegment.makeBytes(data); return QrCode.encodeSegments([seg], ecl); } @@ -72,7 +72,7 @@ namespace qrcodegen { // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. // This function is considered to be lower level than simply encoding text or binary data. - public static encodeSegments(segs: Array, ecl: QrCode_Ecc, + public static encodeSegments(segs: Array, ecl: QrCode.Ecc, minVersion: int = 1, maxVersion: int = 1, mask: int = -1, boostEcl: boolean = true): QrCode { @@ -95,7 +95,7 @@ namespace qrcodegen { } // Increase the error correction level while the data still fits in the current version number - [QrCode_Ecc.MEDIUM, QrCode_Ecc.QUARTILE, QrCode_Ecc.HIGH].forEach((newEcl: QrCode_Ecc) => { // From low to high + [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH].forEach((newEcl: QrCode.Ecc) => { // From low to high if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; }); @@ -137,7 +137,7 @@ namespace qrcodegen { public readonly size: int; // The error correction level used in this QR Code symbol. - public readonly errorCorrectionLevel: QrCode_Ecc; + public readonly errorCorrectionLevel: QrCode.Ecc; // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if the constructor was called with automatic masking requested @@ -151,7 +151,7 @@ namespace qrcodegen { private readonly isFunction: Array> = []; - public constructor(datacodewords: Array, mask: int, version: int, errCorLvl: QrCode_Ecc) { + public constructor(datacodewords: Array, mask: int, version: int, errCorLvl: QrCode.Ecc) { // Check arguments and handle simple scalar fields if (mask < -1 || mask > 7) throw "Mask value out of range"; @@ -378,7 +378,7 @@ namespace qrcodegen { // codewords appended to it, based on this object's version and error correction level. private addEccAndInterleave(data: Array): Array { const ver: int = this.version; - const ecl: QrCode_Ecc = this.errorCorrectionLevel; + const ecl: QrCode.Ecc = this.errorCorrectionLevel; if (data.length != QrCode.getNumDataCodewords(ver, ecl)) throw "Invalid argument"; @@ -600,7 +600,7 @@ namespace qrcodegen { // 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. - private static getNumDataCodewords(ver: int, ecl: QrCode_Ecc): int { + private static getNumDataCodewords(ver: int, ecl: QrCode.Ecc): int { if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw "Version number out of range"; return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - @@ -651,41 +651,6 @@ namespace qrcodegen { - /*---- Public helper enumeration ----*/ - - /* - * Represents the error correction level used in a QR Code symbol. - */ - export class QrCode_Ecc { - - /*-- Constants --*/ - - public static readonly LOW = new QrCode_Ecc(0, 1); - public static readonly MEDIUM = new QrCode_Ecc(1, 0); - public static readonly QUARTILE = new QrCode_Ecc(2, 3); - public static readonly HIGH = new QrCode_Ecc(3, 2); - - - /*-- Fields --*/ - - // In the range 0 to 3 (unsigned 2-bit integer). - public readonly ordinal: int; - - // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). - public readonly formatBits: int; - - - /*-- Constructor --*/ - - private constructor(ord: int, fb: int) { - this.ordinal = ord; - this.formatBits = fb; - } - - } - - - /*---- Data segment class ----*/ /* @@ -705,7 +670,7 @@ namespace qrcodegen { let bb = new BitBuffer(); data.forEach( (b: byte) => bb.appendBits(b, 8)); - return new QrSegment(QrSegment_Mode.BYTE, data.length, bb); + return new QrSegment(QrSegment.Mode.BYTE, data.length, bb); } @@ -720,7 +685,7 @@ namespace qrcodegen { let rem: int = digits.length - i; if (rem > 0) // 1 or 2 digits remaining bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); - return new QrSegment(QrSegment_Mode.NUMERIC, digits.length, bb); + return new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb); } @@ -739,7 +704,7 @@ namespace qrcodegen { } if (i < text.length) // 1 character remaining bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); - return new QrSegment(QrSegment_Mode.ALPHANUMERIC, text.length, bb); + return new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length, bb); } @@ -772,14 +737,14 @@ namespace qrcodegen { bb.appendBits(assignVal, 21); } else throw "ECI assignment value out of range"; - return new QrSegment(QrSegment_Mode.ECI, 0, bb); + return new QrSegment(QrSegment.Mode.ECI, 0, bb); } /*-- Fields --*/ // The mode indicator for this segment. - public readonly mode: QrSegment_Mode; + public readonly mode: QrSegment.Mode; // The length of this segment's unencoded data, measured in characters. Always zero or positive. public readonly numChars: int; @@ -789,7 +754,7 @@ namespace qrcodegen { /*-- Constructor --*/ - public constructor(mode: QrSegment_Mode, numChars: int, bitData: Array) { + public constructor(mode: QrSegment.Mode, numChars: int, bitData: Array) { if (numChars < 0) throw "Invalid argument"; this.mode = mode; @@ -854,52 +819,6 @@ namespace qrcodegen { - /*---- Public helper enumeration ----*/ - - /* - * Represents the mode field of a segment. Immutable. - */ - export class QrSegment_Mode { - - /*-- Constants --*/ - - public static readonly NUMERIC = new QrSegment_Mode(0x1, [10, 12, 14]); - public static readonly ALPHANUMERIC = new QrSegment_Mode(0x2, [ 9, 11, 13]); - public static readonly BYTE = new QrSegment_Mode(0x4, [ 8, 16, 16]); - public static readonly KANJI = new QrSegment_Mode(0x8, [ 8, 10, 12]); - public static readonly ECI = new QrSegment_Mode(0x7, [ 0, 0, 0]); - - - /*-- Fields --*/ - - // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. - public readonly modeBits: int; - - private readonly numBitsCharCount: [int,int,int]; - - - /*-- Constructor --*/ - - private constructor(mode: int, ccbits: [int,int,int]) { - this.modeBits = mode; - this.numBitsCharCount = ccbits; - } - - - /*-- Method --*/ - - // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. - public numCharCountBits(ver: int): int { - if ( 1 <= ver && ver <= 9) return this.numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return this.numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return this.numBitsCharCount[2]; - else throw "Version number out of range"; - } - - } - - - /*---- Private helper classes ----*/ /* @@ -1007,3 +926,96 @@ namespace qrcodegen { } } + + + +/*---- Public helper enumeration ----*/ + +namespace qrcodegen.QrCode { + + type int = number; + + + /* + * Represents the error correction level used in a QR Code symbol. + */ + export class Ecc { + + /*-- Constants --*/ + + public static readonly LOW = new Ecc(0, 1); + public static readonly MEDIUM = new Ecc(1, 0); + public static readonly QUARTILE = new Ecc(2, 3); + public static readonly HIGH = new Ecc(3, 2); + + + /*-- Fields --*/ + + // In the range 0 to 3 (unsigned 2-bit integer). + public readonly ordinal: int; + + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). + public readonly formatBits: int; + + + /*-- Constructor --*/ + + private constructor(ord: int, fb: int) { + this.ordinal = ord; + this.formatBits = fb; + } + + } +} + + + +/*---- Public helper enumeration ----*/ + +namespace qrcodegen.QrSegment { + + type int = number; + + + /* + * Represents the mode field of a segment. Immutable. + */ + export class Mode { + + /*-- Constants --*/ + + public static readonly NUMERIC = new Mode(0x1, [10, 12, 14]); + public static readonly ALPHANUMERIC = new Mode(0x2, [ 9, 11, 13]); + public static readonly BYTE = new Mode(0x4, [ 8, 16, 16]); + public static readonly KANJI = new Mode(0x8, [ 8, 10, 12]); + public static readonly ECI = new Mode(0x7, [ 0, 0, 0]); + + + /*-- Fields --*/ + + // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + public readonly modeBits: int; + + private readonly numBitsCharCount: [int,int,int]; + + + /*-- Constructor --*/ + + private constructor(mode: int, ccbits: [int,int,int]) { + this.modeBits = mode; + this.numBitsCharCount = ccbits; + } + + + /*-- Method --*/ + + // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + public numCharCountBits(ver: int): int { + if ( 1 <= ver && ver <= 9) return this.numBitsCharCount[0]; + else if (10 <= ver && ver <= 26) return this.numBitsCharCount[1]; + else if (27 <= ver && ver <= 40) return this.numBitsCharCount[2]; + else throw "Version number out of range"; + } + + } +} From f83f201b8dc281410b3e35a95f1a1935aecc19c1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Aug 2018 03:13:48 +0000 Subject: [PATCH 393/810] Tweaked TypeScript code to properly move array constants into QrCode class. --- typescript/qrcodegen.ts | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 1bf7d63..73f03a3 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -383,8 +383,8 @@ namespace qrcodegen { throw "Invalid argument"; // Calculate parameter numbers - let numBlocks: int = QrCode_NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; - let blockEccLen: int = QrCode_ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver]; + let numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + let blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver]; let rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8); let numShortBlocks: int = numBlocks - rawCodewords % numBlocks; let shortBlockLen: int = Math.floor(rawCodewords / numBlocks); @@ -604,8 +604,8 @@ namespace qrcodegen { if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw "Version number out of range"; return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - - QrCode_ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * - QrCode_NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * + QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; } @@ -621,28 +621,28 @@ namespace qrcodegen { private static readonly PENALTY_N3: int = 40; private static readonly PENALTY_N4: int = 10; + /*-- Private tables of constants --*/ + + private static readonly ECC_CODEWORDS_PER_BLOCK: Array> = [ + // 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 readonly NUM_ERROR_CORRECTION_BLOCKS: Array> = [ + // 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 + ]; + } - /*-- Private tables of constants --*/ - - const QrCode_ECC_CODEWORDS_PER_BLOCK: Array> = [ - // 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 - ]; - - const QrCode_NUM_ERROR_CORRECTION_BLOCKS: Array> = [ - // 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 - ]; - // Returns true iff the i'th bit of x is set to 1. function getBit(x: int, i: int): boolean { From e7d2449957fadc5074c5cf1708d49fcaa662b238 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 03:53:17 +0000 Subject: [PATCH 394/810] Deleted comment and calculated the constant in QrCode.getNumRawDataModules() in all language versions except Java, because only the Java code explains the calculation in detail. --- c/qrcodegen.c | 2 +- cpp/QrCode.cpp | 2 +- javascript/qrcodegen.js | 2 +- python/qrcodegen.py | 2 +- rust/src/lib.rs | 2 +- typescript/qrcodegen.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 76876cf..31ea560 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -253,7 +253,7 @@ testable int getNumRawDataModules(int version) { int numAlign = version / 7 + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (version >= 7) - result -= 18 * 2; // Subtract version information + result -= 36; } return result; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index bb8e979..273056a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -520,7 +520,7 @@ int QrCode::getNumRawDataModules(int ver) { int numAlign = ver / 7 + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; // Subtract version information + result -= 36; } return result; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 27cbc0c..fc3d5bd 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -628,7 +628,7 @@ var qrcodegen = new function() { var numAlign = Math.floor(ver / 7) + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; // Subtract version information + result -= 36; } return result; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 130f832..d2f07c1 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -515,7 +515,7 @@ class QrCode(object): numalign = ver // 7 + 2 result -= (25 * numalign - 10) * numalign - 55 if ver >= 7: - result -= 18 * 2 # Subtract version information + result -= 36 return result diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 486896f..78b2d2a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -649,7 +649,7 @@ impl QrCode { let numalign: usize = (ver as usize) / 7 + 2; result -= (25 * numalign - 10) * numalign - 55; if ver >= 7 { - result -= 18 * 2; // Subtract version information + result -= 36; } } result diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 73f03a3..de554a3 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -591,7 +591,7 @@ namespace qrcodegen { let numAlign: int = Math.floor(ver / 7) + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; // Subtract version information + result -= 36; } return result; } From 567dbbb0670ee5f924329d5e162056300e9893a4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:17:28 +0000 Subject: [PATCH 395/810] De-optimized Reed-Solomon generator to not store one step of precomputing multiplication tables, in preparation for next change. --- .../fastqrcodegen/ReedSolomonGenerator.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index ae17275..979faec 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -110,9 +110,11 @@ final class ReedSolomonGenerator { root = multiply(root, 0x02); } - multiplies = new byte[degree][]; - for (int i = 0; i < multiplies.length; i++) - multiplies[i] = MULTIPLICATION_TABLE[coefficients[i] & 0xFF]; + multiplies = new byte[degree][256]; + for (int i = 0; i < multiplies.length; i++) { + for (int j = 0; j < 256; j++) + multiplies[i][j] = (byte)multiply(coefficients[i] & 0xFF, j); + } } @@ -151,17 +153,4 @@ final class ReedSolomonGenerator { return z; } - - private static final byte[][] MULTIPLICATION_TABLE = new byte[256][256]; - - static { - for (int i = 0; i < MULTIPLICATION_TABLE.length; i++) { - for (int j = 0; j <= i; j++) { - byte k = (byte)multiply(i, j); - MULTIPLICATION_TABLE[i][j] = k; - MULTIPLICATION_TABLE[j][i] = k; - } - } - } - } From ccd7f3e9e89a83b6c4ad5c3067f5f50fb6a91f4f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:25:48 +0000 Subject: [PATCH 396/810] Simplified Reed-Solomon generator algorithms, without changing behavior. --- .../fastqrcodegen/ReedSolomonGenerator.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index 979faec..99ba41b 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -85,7 +85,11 @@ final class ReedSolomonGenerator { /*---- Instance members ----*/ - private byte[][] multiplies; + // A table of size 256 * degree, where polynomialMultiply[i][j] = multiply(i, coefficients[j]). + // 'coefficients' is the temporary array representing the coefficients of the divisor polynomial, + // stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + private byte[][] polynomialMultiply; private ReedSolomonGenerator(int degree) { @@ -110,10 +114,10 @@ final class ReedSolomonGenerator { root = multiply(root, 0x02); } - multiplies = new byte[degree][256]; - for (int i = 0; i < multiplies.length; i++) { - for (int j = 0; j < 256; j++) - multiplies[i][j] = (byte)multiply(coefficients[i] & 0xFF, j); + polynomialMultiply = new byte[256][degree]; + for (int i = 0; i < polynomialMultiply.length; i++) { + for (int j = 0; j < degree; j++) + polynomialMultiply[i][j] = (byte)multiply(i, coefficients[j] & 0xFF); } } @@ -123,15 +127,14 @@ final class ReedSolomonGenerator { Objects.requireNonNull(result); // Compute the remainder by performing polynomial division - int resultEnd = resultOff + multiplies.length; + int degree = polynomialMultiply[0].length; + int resultEnd = resultOff + degree; Arrays.fill(result, resultOff, resultEnd, (byte)0); for (int i = dataOff, dataEnd = dataOff + dataLen; i < dataEnd; i++) { - byte b = data[i]; - int factor = (b ^ result[resultOff]) & 0xFF; - System.arraycopy(result, resultOff + 1, result, resultOff, multiplies.length - 1); - result[resultEnd - 1] = 0; - for (int j = 0; j < multiplies.length; j++) - result[resultOff + j] ^= multiplies[j][factor]; + byte[] table = polynomialMultiply[(data[i] ^ result[resultOff]) & 0xFF]; + for (int j = 0; j < degree - 1; j++) + result[resultOff + j] = (byte)(result[resultOff + j + 1] ^ table[j]); + result[resultOff + degree - 1] = table[degree - 1]; } } From 45c4ac7f2e67308b8acbecc860fac60e4ca7d4d8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:41:21 +0000 Subject: [PATCH 397/810] Added new private constant to C code. --- c/qrcodegen.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 31ea560..49b3073 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -105,6 +105,8 @@ testable const int8_t ECC_CODEWORDS_PER_BLOCK[4][41] = { {-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 }; +#define qrcodegen_REED_SOLOMON_DEGREE_MAX 30 // Based on the table above + // For generating error correction codes. testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { // Version: (note that index 0 is for padding, and is set to an illegal value) @@ -206,7 +208,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; // Split data into blocks and append ECC after all data - uint8_t generator[30]; + uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); for (int i = 0, j = dataLen, k = 0; i < numBlocks; i++) { int blockLen = shortBlockDataLen; @@ -265,7 +267,7 @@ testable int getNumRawDataModules(int version) { // Calculates the Reed-Solomon generator polynomial of the given degree, storing in result[0 : degree]. testable void calcReedSolomonGenerator(int degree, uint8_t result[]) { // Start with the monomial x^0 - assert(1 <= degree && degree <= 30); + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); memset(result, 0, degree * sizeof(result[0])); result[degree - 1] = 1; @@ -291,7 +293,7 @@ testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]) { // Perform polynomial division - assert(1 <= degree && degree <= 30); + assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); memset(result, 0, degree * sizeof(result[0])); for (int i = 0; i < dataLen; i++) { uint8_t factor = data[i] ^ result[0]; @@ -302,6 +304,8 @@ testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, } } +#undef qrcodegen_REED_SOLOMON_DEGREE_MAX + // Returns the product of the two given field elements modulo GF(2^8/0x11D). // All inputs are valid. This could be implemented as a 256*256 lookup table. From d3fbf3173bbfaef9eed81d345106cb6c426d9c2d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:42:24 +0000 Subject: [PATCH 398/810] Tweaked C code to change dummy value initializations to early exits (return statements). --- c/qrcodegen.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 49b3073..23cde45 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -428,13 +428,13 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { // Calculate error correction code and pack bits assert(0 <= (int)mask && (int)mask <= 7); - int data = -1; // Dummy value + int data; switch (ecl) { case qrcodegen_Ecc_LOW : data = 1; break; case qrcodegen_Ecc_MEDIUM : data = 0; break; case qrcodegen_Ecc_QUARTILE: data = 3; break; case qrcodegen_Ecc_HIGH : data = 2; break; - default: assert(false); + default: assert(false); return; } data = data << 3 | (int)mask; // ecl-derived value is uint2, mask is uint3 int rem = data; @@ -529,7 +529,7 @@ static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qr for (int x = 0; x < qrsize; x++) { if (getModule(functionModules, x, y)) continue; - bool invert = false; // Dummy value + bool invert; switch ((int)mask) { case 0: invert = (x + y) % 2 == 0; break; case 1: invert = y % 2 == 0; break; @@ -539,7 +539,7 @@ static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qr 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: assert(false); + default: assert(false); return; } bool val = getModule(qrcode, x, y); setModule(qrcode, x, y, val ^ invert); @@ -922,14 +922,14 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz int bitLen = 0; for (size_t i = 0; i < len; i++) { const struct qrcodegen_Segment *seg = &segs[i]; - unsigned int modeBits = 0; // Dummy value + unsigned int modeBits; switch (seg->mode) { case qrcodegen_Mode_NUMERIC : modeBits = 0x1; break; case qrcodegen_Mode_ALPHANUMERIC: modeBits = 0x2; break; case qrcodegen_Mode_BYTE : modeBits = 0x4; break; case qrcodegen_Mode_KANJI : modeBits = 0x8; break; case qrcodegen_Mode_ECI : modeBits = 0x7; break; - default: assert(false); + default: assert(false); return false; } appendBitsToBuffer(modeBits, 4, qrcode, &bitLen); appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); @@ -1011,11 +1011,11 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int // given mode at the given version number. The result is in the range [0, 16]. static int numCharCountBits(enum qrcodegen_Mode mode, int version) { assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int i = -1; // Dummy value + int i; if ( 1 <= version && version <= 9) i = 0; else if (10 <= version && version <= 26) i = 1; else if (27 <= version && version <= 40) i = 2; - else assert(false); + else { assert(false); return -1; } // Dummy value switch (mode) { case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } @@ -1023,7 +1023,6 @@ static int numCharCountBits(enum qrcodegen_Mode mode, int version) { case qrcodegen_Mode_BYTE : { static const int temp[] = { 8, 16, 16}; return temp[i]; } case qrcodegen_Mode_KANJI : { static const int temp[] = { 8, 10, 12}; return temp[i]; } case qrcodegen_Mode_ECI : return 0; - default: assert(false); + default: assert(false); return -1; // Dummy value } - return -1; // Dummy value } From e2789cd5c4a4c413b24f6a156820cea7c7a47bda Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:55:34 +0000 Subject: [PATCH 399/810] Updated and added comments in C code. --- c/qrcodegen.c | 21 ++++++++++----------- c/qrcodegen.h | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 23cde45..7f84a8f 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -33,8 +33,7 @@ // Expose private functions #ifndef __cplusplus #define testable - #else - // Needed for const variables because they are treated as implicitly 'static' in C++ + #else // Because in C++, const variables are treated as implicitly 'static' #define testable extern #endif #endif @@ -43,7 +42,7 @@ /*---- Forward declarations for private functions ----*/ // Regarding all public and private functions defined in this source file: -// - They require all pointer/array arguments to be not null. +// - They require all pointer/array arguments to be not null unless the array length is zero. // - They only read input scalar/array arguments, write to output pointer/array // arguments, and return scalar values; they are "pure" functions. // - They don't read mutable global variables or write to any global variables. @@ -193,10 +192,10 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] /*---- Error correction code generation functions ----*/ -// Appends error correction bytes to each block of the given data array, then interleaves bytes -// from the blocks and stores them in the result array. data[0 : rawCodewords - totalEcc] contains -// the input data. data[rawCodewords - totalEcc : rawCodewords] is used as a temporary work area -// and will be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. +// Appends error correction bytes to each block of the given data array, then interleaves +// bytes from the blocks and stores them in the result array. data[0 : dataLen] contains +// the input data. data[dataLen : rawCodewords] is used as a temporary work area and will +// be clobbered by this function. The final answer is stored in result[0 : rawCodewords]. testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]) { // Calculate parameter numbers assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); @@ -220,7 +219,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec } // Interleave (not concatenate) the bytes from every block into a single sequence - for (int i = 0, k = 0; i < numBlocks; i++) { + for (int i = 0, k = 0; i < numBlocks; i++) { // Copy data for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks) result[l] = data[k]; if (i >= numShortBlocks) @@ -229,7 +228,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec for (int i = numShortBlocks, k = (numShortBlocks + 1) * shortBlockDataLen, l = numBlocks * shortBlockDataLen; i < numBlocks; i++, k += shortBlockDataLen + 1, l++) result[l] = data[k]; - for (int i = 0, k = dataLen; i < numBlocks; i++) { + for (int i = 0, k = dataLen; i < numBlocks; i++) { // Copy ECC for (int j = 0, l = dataLen + i; j < blockEccLen; j++, k++, l += numBlocks) result[l] = data[k]; } @@ -436,7 +435,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin case qrcodegen_Ecc_HIGH : data = 2; break; default: assert(false); return; } - data = data << 3 | (int)mask; // ecl-derived value is uint2, mask is uint3 + data = data << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -683,7 +682,7 @@ testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { } -// Returns true iff the i'th bit of x is set to 1. +// Returns true iff the i'th bit of x is set to 1. Requires x >= 0 and 0 <= i <= 14. static bool getBit(int x, int i) { return ((x >> i) & 1) != 0; } diff --git a/c/qrcodegen.h b/c/qrcodegen.h index a08fe0f..c216619 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -107,7 +107,7 @@ struct qrcodegen_Segment { // Calculates the number of bytes needed to store any QR Code up to and including the given version number, // as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' -// can store any single QR Code from version 1 to 25 (inclusive). +// can store any single QR Code from version 1 to 25 (inclusive). The result fits in an int (or int16). // Requires qrcodegen_VERSION_MIN <= n <= qrcodegen_VERSION_MAX. #define qrcodegen_BUFFER_LEN_FOR_VERSION(n) ((((n) * 4 + 17) * ((n) * 4 + 17) + 7) / 8 + 1) From 536a8814167bc50f9e61033a926931ce260134be Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:57:03 +0000 Subject: [PATCH 400/810] Reformatted small pieces of C code. --- c/qrcodegen.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 7f84a8f..9aaeb44 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -239,8 +239,11 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // for the given version number and error correction level. The result is in the range [9, 2956]. testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { int v = version, e = (int)ecl; - assert(0 <= e && e < 4 && qrcodegen_VERSION_MIN <= v && v <= qrcodegen_VERSION_MAX); - return getNumRawDataModules(v) / 8 - ECC_CODEWORDS_PER_BLOCK[e][v] * NUM_ERROR_CORRECTION_BLOCKS[e][v]; + assert(qrcodegen_VERSION_MIN <= v && v <= qrcodegen_VERSION_MAX + && 0 <= e && e < 4); + return getNumRawDataModules(v) / 8 + - ECC_CODEWORDS_PER_BLOCK[e][v] + * NUM_ERROR_CORRECTION_BLOCKS[e][v]; } @@ -987,9 +990,9 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); int result = 0; for (size_t i = 0; i < len; i++) { - int numChars = segs[i].numChars; + int numChars = segs[i].numChars; int bitLength = segs[i].bitLength; - assert(0 <= numChars && numChars <= INT16_MAX); + assert(0 <= numChars && numChars <= INT16_MAX); assert(0 <= bitLength && bitLength <= INT16_MAX); int ccbits = numCharCountBits(segs[i].mode, version); assert(0 <= ccbits && ccbits <= 16); From f1a1ab189ba042a771bec3517c39396cc3f6dc69 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 04:58:20 +0000 Subject: [PATCH 401/810] Clarified and simplified some pieces of C code. --- c/qrcodegen.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 9aaeb44..cd759c7 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -210,9 +210,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); for (int i = 0, j = dataLen, k = 0; i < numBlocks; i++) { - int blockLen = shortBlockDataLen; - if (i >= numShortBlocks) - blockLen++; + int blockLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[j]); j += blockEccLen; k += blockLen; @@ -430,15 +428,8 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { // Calculate error correction code and pack bits assert(0 <= (int)mask && (int)mask <= 7); - int data; - switch (ecl) { - case qrcodegen_Ecc_LOW : data = 1; break; - case qrcodegen_Ecc_MEDIUM : data = 0; break; - case qrcodegen_Ecc_QUARTILE: data = 3; break; - case qrcodegen_Ecc_HIGH : data = 2; break; - default: assert(false); return; - } - data = data << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 + static const int table[] = {1, 0, 3, 2}; + int data = table[(int)ecl] << 3 | (int)mask; // errCorrLvl is uint2, mask is uint3 int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -740,13 +731,13 @@ testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { return -1; int n = (int)numChars; - int result = -2; + int result; if (mode == qrcodegen_Mode_NUMERIC) { // n * 3 + ceil(n / 3) if (n > LIMIT / 3) goto overflow; result = n * 3; - int temp = n / 3 + (n % 3 == 0 ? 0 : 1); + int temp = n / 3 + (n % 3 > 0 ? 1 : 0); if (temp > LIMIT - result) goto overflow; result += temp; @@ -769,6 +760,10 @@ testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { result = n * 13; } else if (mode == qrcodegen_Mode_ECI && numChars == 0) result = 3 * 8; + else { + assert(false); + return -1; + } assert(0 <= result && result <= LIMIT); return result; overflow: @@ -809,7 +804,7 @@ struct qrcodegen_Segment qrcodegen_makeNumeric(const char *digits, uint8_t buf[] for (; *digits != '\0'; digits++) { char c = *digits; assert('0' <= c && c <= '9'); - accumData = accumData * 10 + (c - '0'); + accumData = accumData * 10 + (unsigned int)(c - '0'); accumCount++; if (accumCount == 3) { appendBitsToBuffer(accumData, 10, buf, &result.bitLength); @@ -843,7 +838,7 @@ struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t bu for (; *text != '\0'; text++) { const char *temp = strchr(ALPHANUMERIC_CHARSET, *text); assert(temp != NULL); - accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); + accumData = accumData * 45 + (unsigned int)(temp - ALPHANUMERIC_CHARSET); accumCount++; if (accumCount == 2) { appendBitsToBuffer(accumData, 11, buf, &result.bitLength); @@ -964,14 +959,15 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask long minPenalty = LONG_MAX; for (int i = 0; i < 8; i++) { - drawFormatBits(ecl, (enum qrcodegen_Mask)i, qrcode); - applyMask(tempBuffer, qrcode, (enum qrcodegen_Mask)i); + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + drawFormatBits(ecl, msk, qrcode); + applyMask(tempBuffer, qrcode, msk); long penalty = getPenaltyScore(qrcode); if (penalty < minPenalty) { - mask = (enum qrcodegen_Mask)i; + mask = msk; minPenalty = penalty; } - applyMask(tempBuffer, qrcode, (enum qrcodegen_Mask)i); // Undoes the mask due to XOR + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR } } assert(0 <= (int)mask && (int)mask <= 7); From de2603ab5ff7d049194fc02fc03992f975441f3f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 05:08:03 +0000 Subject: [PATCH 402/810] Simplified some C code for overflow-safe calculation. --- c/qrcodegen.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index cd759c7..715cb78 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -984,7 +984,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { assert(segs != NULL || len == 0); assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int result = 0; + long result = 0; for (size_t i = 0; i < len; i++) { int numChars = segs[i].numChars; int bitLength = segs[i].bitLength; @@ -995,13 +995,12 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int // Fail if segment length value doesn't fit in the length field's bit-width if (numChars >= (1L << ccbits)) return -1; - long temp = 4L + ccbits + bitLength; - if (temp > INT16_MAX - result) + result += 4L + ccbits + bitLength; + if (result > INT16_MAX) return -1; - result += temp; } assert(0 <= result && result <= INT16_MAX); - return result; + return (int)result; } From 08f7bba7967d119ee957e3596071245290cb198e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 05:08:27 +0000 Subject: [PATCH 403/810] Simplified C code to eliminate a loop variable. --- c/qrcodegen.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 715cb78..45e4f9a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -209,10 +209,9 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // Split data into blocks and append ECC after all data uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); - for (int i = 0, j = dataLen, k = 0; i < numBlocks; i++) { + for (int i = 0, k = 0; i < numBlocks; i++) { int blockLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[j]); - j += blockEccLen; + calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[dataLen + i * blockEccLen]); k += blockLen; } From 2c055c2e677c01cd17d307a76290b3804c6aa44c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 05:30:18 +0000 Subject: [PATCH 404/810] Renamed some related variables in C code to match other language versions. --- c/qrcodegen-test.c | 8 ++++---- c/qrcodegen.c | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 0674587..192f7dd 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -125,10 +125,10 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e calcReedSolomonGenerator(blockEccLen, generator); for (int i = 0, k = 0; i < numBlocks; i++) { uint8_t *block = MALLOC(shortBlockLen + 1, uint8_t); - int blockDataLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); - memcpy(block, &data[k], blockDataLen * sizeof(uint8_t)); - calcReedSolomonRemainder(&data[k], blockDataLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); - k += blockDataLen; + int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); + memcpy(block, &data[k], datLen * sizeof(uint8_t)); + calcReedSolomonRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); + k += datLen; blocks[i] = block; } free(generator); diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 45e4f9a..d346fe8 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -210,9 +210,9 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); for (int i = 0, k = 0; i < numBlocks; i++) { - int blockLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - calcReedSolomonRemainder(&data[k], blockLen, generator, blockEccLen, &data[dataLen + i * blockEccLen]); - k += blockLen; + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + calcReedSolomonRemainder(&data[k], datLen, generator, blockEccLen, &data[dataLen + i * blockEccLen]); + k += datLen; } // Interleave (not concatenate) the bytes from every block into a single sequence From 87cab22ffc7c8e1fcc083f698ad73bd3963be9a1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 05:31:35 +0000 Subject: [PATCH 405/810] Revamped addEccAndInterleave() in C code for simplicity and robustness. --- c/qrcodegen.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index d346fe8..cab112f 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -206,28 +206,23 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; - // Split data into blocks and append ECC after all data + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; calcReedSolomonGenerator(blockEccLen, generator); - for (int i = 0, k = 0; i < numBlocks; i++) { + const uint8_t *dat = data; + for (int i = 0; i < numBlocks; i++) { int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - calcReedSolomonRemainder(&data[k], datLen, generator, blockEccLen, &data[dataLen + i * blockEccLen]); - k += datLen; - } - - // Interleave (not concatenate) the bytes from every block into a single sequence - for (int i = 0, k = 0; i < numBlocks; i++) { // Copy data - for (int j = 0, l = i; j < shortBlockDataLen; j++, k++, l += numBlocks) - result[l] = data[k]; - if (i >= numShortBlocks) - k++; - } - for (int i = numShortBlocks, k = (numShortBlocks + 1) * shortBlockDataLen, l = numBlocks * shortBlockDataLen; - i < numBlocks; i++, k += shortBlockDataLen + 1, l++) - result[l] = data[k]; - for (int i = 0, k = dataLen; i < numBlocks; i++) { // Copy ECC - for (int j = 0, l = dataLen + i; j < blockEccLen; j++, k++, l += numBlocks) - result[l] = data[k]; + uint8_t *ecc = &data[dataLen]; // Temporary storage + calcReedSolomonRemainder(dat, datLen, generator, blockEccLen, ecc); + for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data + if (j == shortBlockDataLen) + k -= numShortBlocks; + result[k] = dat[j]; + } + for (int j = 0, k = dataLen + i; j < blockEccLen; j++, k += numBlocks) // Copy ECC + result[k] = ecc[j]; + dat += datLen; } } From 669585590be62b98f6612f8d8211d9ade46a8445 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 06:22:22 +0000 Subject: [PATCH 406/810] Added an assertion to QrCode.encodeSegmentsAdvanced() in all language versions. --- c/qrcodegen.c | 1 + cpp/QrCode.cpp | 2 ++ java/io/nayuki/qrcodegen/QrCode.java | 1 + javascript/qrcodegen.js | 2 ++ python/qrcodegen.py | 1 + rust/src/lib.rs | 1 + typescript/qrcodegen.ts | 2 ++ 7 files changed, 10 insertions(+) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index cab112f..16bb0ff 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -927,6 +927,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz for (int j = 0; j < seg->bitLength; j++) appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); } + assert(bitLen == dataUsedBits); // Add terminator and pad up to a byte if applicable int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 273056a..2d4fd3f 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -93,6 +93,8 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); } + if (bb.size() != static_cast(dataUsedBits)) + throw std::logic_error("Assertion error"); // Add terminator and pad up to a byte if applicable size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index f558770..6da7a16 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -147,6 +147,7 @@ public final class QrCode { 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; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index fc3d5bd..e2a9645 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -563,6 +563,8 @@ var qrcodegen = new function() { bb.push(bit); }); }); + if (bb.length != dataUsedBits) + throw "Assertion error"; // Add terminator and pad up to a byte if applicable var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d2f07c1..bd2119d 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -121,6 +121,7 @@ class QrCode(object): bb.append_bits(seg.get_mode().get_mode_bits(), 4) bb.append_bits(seg.get_num_chars(), seg.get_mode().num_char_count_bits(version)) bb.extend(seg._bitdata) + assert len(bb) == datausedbits # Add terminator and pad up to a byte if applicable datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 78b2d2a..dc7500d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -136,6 +136,7 @@ impl QrCode { bb.append_bits(seg.numchars as u32, seg.mode.num_char_count_bits(version)); bb.0.extend_from_slice(&seg.data); } + assert_eq!(bb.0.len(), datausedbits); // Add terminator and pad up to a byte if applicable let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index de554a3..958746f 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -108,6 +108,8 @@ namespace qrcodegen { seg.getBits().forEach( (b: bit) => bb.push(b)); }); + if (bb.length != dataUsedBits) + throw "Assertion error"; // Add terminator and pad up to a byte if applicable let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; From f8e59274f6b91e97f8320aa0827d57d4ebf9b592 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 06:54:54 +0000 Subject: [PATCH 407/810] Simplified and renamed addEccAndInterleave(), based on the slow QR library C version algorithm. --- src/io/nayuki/fastqrcodegen/QrCode.java | 37 +++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 797d59e..3390eb2 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -140,7 +140,7 @@ public final class QrCode { modules = tpl.template.clone(); // Draw function patterns, draw all codewords, do masking - byte[] allCodewords = appendErrorCorrection(dataCodewords); + byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(tpl.dataOutputBitIndexes, allCodewords); this.mask = handleConstructorMasking(tpl.masks, mask); } @@ -279,7 +279,7 @@ public final class QrCode { // 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[] appendErrorCorrection(byte[] data) { + private byte[] addEccAndInterleave(byte[] data) { if (data.length != getNumDataCodewords(version, errorCorrectionLevel)) throw new IllegalArgumentException(); @@ -288,28 +288,23 @@ public final class QrCode { int blockEccLen = ECC_CODEWORDS_PER_BLOCK[errorCorrectionLevel.ordinal()][version]; int rawCodewords = QrTemplate.getNumRawDataModules(version) / 8; int numShortBlocks = numBlocks - rawCodewords % numBlocks; - int shortBlockLen = rawCodewords / numBlocks; + int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; - // Split data into blocks and append ECC to each block - byte[][] blocks = new byte[numBlocks][shortBlockLen + 1]; - ReedSolomonGenerator rs = ReedSolomonGenerator.getInstance(blockEccLen); - for (int i = 0, k = 0; i < numBlocks; i++) { - int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); - System.arraycopy(data, k, blocks[i], 0, datLen); - rs.getRemainder(data, k, datLen, blocks[i], shortBlockLen + 1 - blockEccLen); - k += datLen; - } - - // Interleave (not concatenate) the bytes from every block into a single sequence + // Split data into blocks, calculate ECC, and interleave + // (not concatenate) the bytes into a single sequence byte[] result = new byte[rawCodewords]; - for (int i = 0, k = 0; i < blocks[0].length; i++) { - for (int j = 0; j < blocks.length; j++) { - // Skip the padding byte in short blocks - if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { - result[k] = blocks[j][i]; - k++; - } + ReedSolomonGenerator rs = ReedSolomonGenerator.getInstance(blockEccLen); + byte[] ecc = new byte[blockEccLen]; // Temporary storage per iteration + for (int i = 0, k = 0; i < numBlocks; i++) { + int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); + rs.getRemainder(data, k, datLen, ecc, 0); + for (int j = 0, l = i; j < datLen; j++, k++, l += numBlocks) { // Copy data + if (j == shortBlockDataLen) + l -= numShortBlocks; + result[l] = data[k]; } + for (int j = 0, l = data.length + i; j < blockEccLen; j++, l += numBlocks) // Copy ECC + result[l] = ecc[j]; } return result; } From 943b8815ee8ffb03dc03e034da40e709e9b4a756 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 06:59:00 +0000 Subject: [PATCH 408/810] Deleted unused parameter and simplified code in ReedSolomonGenerator.getRemainder(). --- src/io/nayuki/fastqrcodegen/QrCode.java | 2 +- .../nayuki/fastqrcodegen/ReedSolomonGenerator.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 3390eb2..61f37c8 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -297,7 +297,7 @@ public final class QrCode { byte[] ecc = new byte[blockEccLen]; // Temporary storage per iteration for (int i = 0, k = 0; i < numBlocks; i++) { int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); - rs.getRemainder(data, k, datLen, ecc, 0); + rs.getRemainder(data, k, datLen, ecc); for (int j = 0, l = i; j < datLen; j++, k++, l += numBlocks) { // Copy data if (j == shortBlockDataLen) l -= numShortBlocks; diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index 99ba41b..aef45c0 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -122,19 +122,19 @@ final class ReedSolomonGenerator { } - public void getRemainder(byte[] data, int dataOff, int dataLen, byte[] result, int resultOff) { + public void getRemainder(byte[] data, int dataOff, int dataLen, byte[] result) { Objects.requireNonNull(data); Objects.requireNonNull(result); + int degree = polynomialMultiply[0].length; + assert result.length == degree; // Compute the remainder by performing polynomial division - int degree = polynomialMultiply[0].length; - int resultEnd = resultOff + degree; - Arrays.fill(result, resultOff, resultEnd, (byte)0); + Arrays.fill(result, (byte)0); for (int i = dataOff, dataEnd = dataOff + dataLen; i < dataEnd; i++) { - byte[] table = polynomialMultiply[(data[i] ^ result[resultOff]) & 0xFF]; + byte[] table = polynomialMultiply[(data[i] ^ result[0]) & 0xFF]; for (int j = 0; j < degree - 1; j++) - result[resultOff + j] = (byte)(result[resultOff + j + 1] ^ table[j]); - result[resultOff + degree - 1] = table[degree - 1]; + result[j] = (byte)(result[j + 1] ^ table[j]); + result[degree - 1] = table[degree - 1]; } } From 518850d81a749a68927951cc4be57b4b9dfb3eba Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 07:17:45 +0000 Subject: [PATCH 409/810] Updated various comments - Javadoc, method-level, intra-method. --- src/io/nayuki/fastqrcodegen/QrCode.java | 19 +++++++++++-------- src/io/nayuki/fastqrcodegen/QrTemplate.java | 10 ++++++---- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 61f37c8..7c00e75 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -73,12 +73,12 @@ 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()) { + for (Ecc newEcl : Ecc.values()) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } - // Create the data bit string by concatenating all segments + // Concatenate all segments to create the data bit string int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { @@ -91,7 +91,7 @@ public final class QrCode { bb.appendBits(0, Math.min(4, dataCapacityBits - bb.bitLength)); bb.appendBits(0, (8 - bb.bitLength % 8) % 8); - // Pad with alternate bytes until data capacity is reached + // Pad with alternating bytes until data capacity is reached for (int padByte = 0xEC; bb.bitLength < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); assert bb.bitLength % 8 == 0; @@ -174,7 +174,8 @@ public final class QrCode { * @param scale the module scale factor, which must be positive * @param border the number of border modules to add, which must be non-negative * @return an image representing this QR Code, with padding and scaling - * @throws IllegalArgumentException if the scale or border is out of range + * @throws IllegalArgumentException if the scale or border is out of range, or if + * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE */ public BufferedImage toImage(int scale, int border) { if (scale <= 0 || border < 0) @@ -199,6 +200,7 @@ public final class QrCode { * Note that Unix newlines (\n) are always used, regardless of the platform. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document + * @throws IllegalArgumentException if the border is negative */ public String toSvgString(int border) { if (border < 0) @@ -325,10 +327,11 @@ public final class QrCode { } - // XORs the data modules in this QR Code with the given mask pattern. Due to XOR's mathematical - // properties, calling applyMask(m) twice with the same value is equivalent to no change at all. - // This means it is possible to apply a mask, undo it, and try another mask. Note that a final - // well-formed QR Code symbol needs exactly one mask applied (not zero, not two, etc.). + // 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 symbol needs exactly one (not zero, two, etc.) mask applied. private void applyMask(int[] mask) { if (mask.length != modules.length) throw new IllegalArgumentException(); diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 97a4fbd..6d4024e 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -159,7 +159,7 @@ final class QrTemplate { // Draws two copies of the version bits (with its own error correction code), - // based on this object's version field (which only has an effect for 7 <= version <= 40). + // based on this object's version field, iff 7 <= version <= 40. private void drawVersion() { if (version < 7) return; @@ -182,7 +182,8 @@ final class QrTemplate { } - // Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). + // 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) { for (int i = -4; i <= 4; i++) { for (int j = -4; j <= 4; j++) { @@ -195,7 +196,8 @@ final class QrTemplate { } - // Draws a 5*5 alignment pattern, with the center module at (x, y). + // 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) { for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) @@ -306,7 +308,7 @@ final class QrTemplate { int numAlign = ver / 7 + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; // Subtract version information + result -= 18 * 2; } return result; } From a268c93ec5a722ada9d92ec847e8d727f9fcf762 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 07:18:27 +0000 Subject: [PATCH 410/810] Clarified some assertions and code. --- src/io/nayuki/fastqrcodegen/QrCode.java | 6 ++++-- src/io/nayuki/fastqrcodegen/QrTemplate.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 7c00e75..c54d718 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -79,22 +79,24 @@ public final class QrCode { } // Concatenate all segments to create the data bit string - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; BitBuffer bb = new BitBuffer(); for (QrSegment seg : segs) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); bb.appendBits(seg.data, seg.bitLength); } + 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); - assert bb.bitLength % 8 == 0; // Create the QR Code symbol return new QrCode(version, ecl, bb.getBytes(), mask); diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 6d4024e..4087df4 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -308,7 +308,7 @@ final class QrTemplate { int numAlign = ver / 7 + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; + result -= 36; } return result; } From 0bf2d3306bbb0a72776c53b1d3219920e01cfc1d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 07:18:56 +0000 Subject: [PATCH 411/810] Simplified some code. --- c/qrcodegen.c | 12 +++++------- java/io/nayuki/qrcodegen/QrCode.java | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 16bb0ff..2c5389a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -202,7 +202,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; int rawCodewords = getNumRawDataModules(version) / 8; - int dataLen = rawCodewords - blockEccLen * numBlocks; + int dataLen = getNumDataCodewords(version, ecl); int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; @@ -384,12 +384,10 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) - continue; // Skip the three finder corners - else { - for (int k = -1; k <= 1; k++) { - for (int l = -1; l <= 1; l++) - setModule(qrcode, alignPatPos[i] + l, alignPatPos[j] + k, k == 0 && l == 0); - } + continue; // Don't draw on the three finder corners + for (int k = -1; k <= 1; k++) { + for (int l = -1; l <= 1; l++) + setModule(qrcode, alignPatPos[i] + l, alignPatPos[j] + k, k == 0 && l == 0); } } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 6da7a16..4890fed 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -688,7 +688,7 @@ public final class QrCode { result -= (numAlign - 2) * 2 * 20; // Subtract alignment patterns that overlap with timing patterns // The two lines above are equivalent to: result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) - result -= 18 * 2; // Subtract version information + result -= 6 * 3 * 2; // Subtract version information } return result; } From 719caf4d99ee326c360e151ffb1f96f270276dbb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 07:52:42 +0000 Subject: [PATCH 412/810] Added private helper method to Java QrSegmentAdvanced. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 5b2c74a..993ca2c 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -257,13 +257,18 @@ public final class QrSegmentAdvanced { public static boolean isEncodableAsKanji(String text) { Objects.requireNonNull(text); for (int i = 0; i < text.length(); i++) { - if (UNICODE_TO_QR_KANJI[text.charAt(i)] == -1) + if (!isKanji(text.charAt(i))) return false; } return true; } + private static boolean isKanji(char c) { + return 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" + From b095504d5c8365096972ba03798eaf011a8b0262 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 16:20:01 +0000 Subject: [PATCH 413/810] Tweaked and simplified QrSegment.getTotalBits() to use infinity value, in JavaScript and TypeScript code. --- javascript/qrcodegen.js | 4 ++-- typescript/qrcodegen.ts | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index e2a9645..bf3a494 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -542,7 +542,7 @@ var qrcodegen = new function() { for (version = minVersion; ; version++) { var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version); - if (dataUsedBits != null && dataUsedBits <= dataCapacityBits) + if (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 throw "Data too long"; @@ -827,7 +827,7 @@ var qrcodegen = new function() { var ccbits = seg.mode.numCharCountBits(version); // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return null; + return Infinity; result += 4 + ccbits + seg.getBits().length; } return result; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 958746f..596df59 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -85,8 +85,8 @@ namespace qrcodegen { let dataUsedBits: int; for (version = minVersion; ; version++) { let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available - let usedBits: number|null = QrSegment.getTotalBits(segs, version); - if (usedBits != null && usedBits <= dataCapacityBits) { + let usedBits: number = QrSegment.getTotalBits(segs, version); + if (usedBits <= dataCapacityBits) { dataUsedBits = usedBits; break; // This version number is found to be suitable } @@ -774,16 +774,16 @@ namespace qrcodegen { // Package-private helper function. - public static getTotalBits(segs: Array, version: int): int|null { + public static getTotalBits(segs: Array, version: int): number { if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw "Version number out of range"; - let result: int = 0; + let result: number = 0; for (let i = 0; i < segs.length; i++) { let seg: QrSegment = segs[i]; let ccbits: int = seg.mode.numCharCountBits(version); // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return null; + return Infinity; result += 4 + ccbits + seg.getBits().length; } return result; From 4c882636bb836e2074f6990ad11be325765a80eb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 16:36:07 +0000 Subject: [PATCH 414/810] Updated comments for QrSegment.getTotalBits() in all language versions, but somewhat differently per language. --- c/qrcodegen.c | 12 +++++------- cpp/QrSegment.cpp | 7 +++---- cpp/QrSegment.hpp | 4 +++- java/io/nayuki/qrcodegen/QrSegment.java | 9 +++++---- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 7 ++++--- rust/src/lib.rs | 8 +++++--- typescript/qrcodegen.ts | 6 +++--- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 2c5389a..2c041b7 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -970,10 +970,9 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz } -// Returns the number of bits needed to encode the given list of segments at the given version. -// The result is in the range [0, 32767] if successful. Otherwise, -1 is returned if any segment -// has more characters than allowed by that segment's mode's character count field at the version, -// or if the actual answer exceeds INT16_MAX. +// Calculates the number of bits needed to encode the given segments at the given version. +// Returns a non-negative number if successful. Otherwise returns -1 if a segment has too +// many characters to fit its length field, or the total bits exceeds INT16_MAX. testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { assert(segs != NULL || len == 0); assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); @@ -985,12 +984,11 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int assert(0 <= bitLength && bitLength <= INT16_MAX); int ccbits = numCharCountBits(segs[i].mode, version); assert(0 <= ccbits && ccbits <= 16); - // Fail if segment length value doesn't fit in the length field's bit-width if (numChars >= (1L << ccbits)) - return -1; + return -1; // The segment's length doesn't fit the field's bit width result += 4L + ccbits + bitLength; if (result > INT16_MAX) - return -1; + return -1; // The sum might overflow an int type } assert(0 <= result && result <= INT16_MAX); return (int)result; diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 3b5d509..70c0445 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -176,14 +176,13 @@ int QrSegment::getTotalBits(const vector &segs, int version) { int result = 0; for (const QrSegment &seg : segs) { int ccbits = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1L << ccbits)) - return -1; + return -1; // The segment's length doesn't fit the field's bit width if (4 + ccbits > INT_MAX - result) - return -1; + return -1; // The sum will overflow an int type result += 4 + ccbits; if (seg.data.size() > static_cast(INT_MAX - result)) - return -1; + return -1; // The sum will overflow an int type result += static_cast(seg.data.size()); } return result; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 2b9eb66..44c74ab 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -172,7 +172,9 @@ class QrSegment final { public: const std::vector &getData() const; - // Package-private helper function. + // Calculates the number of bits needed to encode the given segments at the given version. + // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too + // many characters to fit its length field, or the total bits exceeds INT_MAX. public: static int getTotalBits(const std::vector &segs, int version); diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 8705797..281e63a 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -197,7 +197,9 @@ public final class QrSegment { } - // Package-private helper function. + // Calculates the number of bits needed to encode the given segments at the given version. + // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too + // many characters to fit its length field, or the total bits exceeds Integer.MAX_VALUE. static int getTotalBits(List segs, int version) { Objects.requireNonNull(segs); if (version < 1 || version > 40) @@ -207,12 +209,11 @@ public final class QrSegment { for (QrSegment seg : segs) { Objects.requireNonNull(seg); int ccbits = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return -1; + return -1; // The segment's length doesn't fit the field's bit width result += 4L + ccbits + seg.data.bitLength(); if (result > Integer.MAX_VALUE) - return -1; + return -1; // The sum will overflow an int type } return (int)result; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index bf3a494..b2f6e34 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -817,7 +817,8 @@ var qrcodegen = new function() { }; - // Package-private helper function. + // Calculates and returns the number of bits needed to encode the given segments at the given version. + // The result is infinity if a segment has too many characters to fit its length field. this.QrSegment.getTotalBits = function(segs, version) { if (version < MIN_VERSION || version > MAX_VERSION) throw "Version number out of range"; @@ -825,9 +826,8 @@ var qrcodegen = new function() { for (var i = 0; i < segs.length; i++) { var seg = segs[i]; var ccbits = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return Infinity; + return Infinity; // The segment's length doesn't fit the field's bit width result += 4 + ccbits + seg.getBits().length; } return result; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index bd2119d..49cffd4 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -700,7 +700,9 @@ class QrSegment(object): return list(self._bitdata) # Make defensive copy - # Package-private helper function. + # Calculates the number of bits needed to encode the given segments at the given version. + # Returns a non-negative number if successful. Otherwise returns None if a segment has + # too many characters to fit its length field. @staticmethod def get_total_bits(segs, version): if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): @@ -708,9 +710,8 @@ class QrSegment(object): result = 0 for seg in segs: ccbits = seg.get_mode().num_char_count_bits(version) - # Fail if segment length value doesn't fit in the length field's bit-width if seg.get_num_chars() >= (1 << ccbits): - return None + return None # The segment's length doesn't fit the field's bit width result += 4 + ccbits + len(seg._bitdata) return result diff --git a/rust/src/lib.rs b/rust/src/lib.rs index dc7500d..a3f651b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -974,16 +974,18 @@ impl QrSegment { /*---- Other static functions ----*/ - // Package-private helper function. + // Calculates and returns the number of bits needed to encode the given + // segments at the given version. The result is None if a segment has too many + // characters to fit its length field, or the total bits exceeds usize::MAX. fn get_total_bits(segs: &[Self], version: Version) -> Option { let mut result: usize = 0; for seg in segs { let ccbits = seg.mode.num_char_count_bits(version); if seg.numchars >= 1 << ccbits { - return None; + return None; // The segment's length doesn't fit the field's bit width } match result.checked_add(4 + (ccbits as usize) + seg.data.len()) { - None => return None, + None => return None, // The sum will overflow a usize type Some(val) => result = val, } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 596df59..08205b4 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -773,7 +773,8 @@ namespace qrcodegen { } - // Package-private helper function. + // Calculates and returns the number of bits needed to encode the given segments at the given version. + // The result is infinity if a segment has too many characters to fit its length field. public static getTotalBits(segs: Array, version: int): number { if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw "Version number out of range"; @@ -781,9 +782,8 @@ namespace qrcodegen { for (let i = 0; i < segs.length; i++) { let seg: QrSegment = segs[i]; let ccbits: int = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return Infinity; + return Infinity; // The segment's length doesn't fit the field's bit width result += 4 + ccbits + seg.getBits().length; } return result; From a2fe36e1c0581263037ded626dab89e4f6a74192 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 16:51:20 +0000 Subject: [PATCH 415/810] Added an inner class import to Java QrSegmentAdvanced to shorten names. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 993ca2c..79c2a8d 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.List; import java.util.Objects; +import io.nayuki.qrcodegen.QrSegment.Mode; public final class QrSegmentAdvanced { @@ -82,7 +83,7 @@ public final class QrSegmentAdvanced { private static List makeSegmentsOptimally(String text, int version) { byte[] data = text.getBytes(StandardCharsets.UTF_8); int[][] bitCosts = computeBitCosts(data, version); - QrSegment.Mode[] charModes = computeCharacterModes(data, version, bitCosts); + Mode[] charModes = computeCharacterModes(data, version, bitCosts); return splitIntoSegments(data, charModes); } @@ -121,14 +122,14 @@ public final class QrSegmentAdvanced { } - private static QrSegment.Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { + private static Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { // Segment header sizes, measured in 1/6 bits int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; // Infer the mode used for last character by taking the minimum - QrSegment.Mode curMode; + Mode curMode; int end = bitCosts[0].length - 1; if (bitCosts[0][end] <= Math.min(bitCosts[1][end], bitCosts[2][end])) curMode = BYTE; @@ -138,7 +139,7 @@ public final class QrSegmentAdvanced { curMode = NUMERIC; // Work backwards to calculate optimal encoding mode for each character - QrSegment.Mode[] result = new QrSegment.Mode[data.length]; + Mode[] result = new Mode[data.length]; if (data.length == 0) return result; result[data.length - 1] = curMode; @@ -173,13 +174,13 @@ public final class QrSegmentAdvanced { } - private static List splitIntoSegments(byte[] data, QrSegment.Mode[] charModes) { + private static List splitIntoSegments(byte[] data, Mode[] charModes) { List result = new ArrayList<>(); if (data.length == 0) return result; // Accumulate run of modes - QrSegment.Mode curMode = charModes[0]; + Mode curMode = charModes[0]; int start = 0; for (int i = 1; ; i++) { if (i < data.length && charModes[i] == curMode) From b3a34bdd3df3849788a713414ba6855619f2782f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 16:54:58 +0000 Subject: [PATCH 416/810] Upgraded some of Java QrSegmentAdvanced's private methods to handle full Unicode code points instead of UTF-16 code units. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 79c2a8d..5e47b7e 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -204,11 +204,11 @@ public final class QrSegmentAdvanced { } - private static boolean isAlphanumeric(char c) { + private static boolean isAlphanumeric(int c) { return isNumeric(c) || 'A' <= c && c <= 'Z' || " $%*+./:-".indexOf(c) != -1; } - private static boolean isNumeric(char c) { + private static boolean isNumeric(int c) { return '0' <= c && c <= '9'; } @@ -265,8 +265,8 @@ public final class QrSegmentAdvanced { } - private static boolean isKanji(char c) { - return UNICODE_TO_QR_KANJI[c] != -1; + private static boolean isKanji(int c) { + return c < UNICODE_TO_QR_KANJI.length && UNICODE_TO_QR_KANJI[c] != -1; } From ce1f7d4c4dc8792fe884f6647d38adf1222f1e15 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 17:49:24 +0000 Subject: [PATCH 417/810] Refactored handling of zero-length text in Java QrSegmentAdvanced. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 5e47b7e..56b3967 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -81,6 +81,8 @@ public final class QrSegmentAdvanced { // Returns a list of segments that is optimal for the given text at the given version number. private static List makeSegmentsOptimally(String text, int version) { + if (text.length() == 0) + return new ArrayList<>(); byte[] data = text.getBytes(StandardCharsets.UTF_8); int[][] bitCosts = computeBitCosts(data, version); Mode[] charModes = computeCharacterModes(data, version, bitCosts); @@ -123,6 +125,9 @@ public final class QrSegmentAdvanced { private static Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { + if (data.length == 0) + throw new IllegalArgumentException(); + // Segment header sizes, measured in 1/6 bits int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; @@ -140,8 +145,6 @@ public final class QrSegmentAdvanced { // Work backwards to calculate optimal encoding mode for each character Mode[] result = new Mode[data.length]; - if (data.length == 0) - return result; result[data.length - 1] = curMode; for (int i = data.length - 2; i >= 0; i--) { char c = (char)data[i]; @@ -175,9 +178,9 @@ public final class QrSegmentAdvanced { private static List splitIntoSegments(byte[] data, Mode[] charModes) { - List result = new ArrayList<>(); if (data.length == 0) - return result; + throw new IllegalArgumentException(); + List result = new ArrayList<>(); // Accumulate run of modes Mode curMode = charModes[0]; From 8800acf003cd4584e8aad88096f57986b74d0377 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 18:31:44 +0000 Subject: [PATCH 418/810] Revamped Java QrSegmentAdvanced code to implement optimization of kanji text, and simplify existing algorithms. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 209 ++++++++++-------- 1 file changed, 111 insertions(+), 98 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 56b3967..b53dae4 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -25,6 +25,7 @@ package io.nayuki.qrcodegen; import static io.nayuki.qrcodegen.QrSegment.Mode.ALPHANUMERIC; import static io.nayuki.qrcodegen.QrSegment.Mode.BYTE; +import static io.nayuki.qrcodegen.QrSegment.Mode.KANJI; import static io.nayuki.qrcodegen.QrSegment.Mode.NUMERIC; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -42,10 +43,10 @@ public final class QrSegmentAdvanced { /** * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints given - * by the specified {error correction level, minimum version number, maximum version number}, plus the additional - * constraint that the segment modes {NUMERIC, ALPHANUMERIC, BYTE} can be used but KANJI cannot be used. - *

This function can be viewed as a significantly more sophisticated and slower replacement - * for {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way + * by the specified {error correction level, minimum version number, maximum version number}. + *

This function can utilize all four text encoding modes: numeric, alphanumeric, byte, and kanji. + * This can be viewed as a significantly more sophisticated and slower replacement for + * {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way * that overlaps with {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

* @param text the text to be encoded, which can be any Unicode string * @param ecl the error correction level to use @@ -83,102 +84,104 @@ public final class QrSegmentAdvanced { private static List makeSegmentsOptimally(String text, int version) { if (text.length() == 0) return new ArrayList<>(); - byte[] data = text.getBytes(StandardCharsets.UTF_8); - int[][] bitCosts = computeBitCosts(data, version); - Mode[] charModes = computeCharacterModes(data, version, bitCosts); - return splitIntoSegments(data, charModes); + int[] codePoints = toCodePoints(text); + Mode[] charModes = computeCharacterModes(codePoints, version); + return splitIntoSegments(codePoints, charModes); } - private static int[][] computeBitCosts(byte[] data, int version) { - // Segment header sizes, measured in 1/6 bits - int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; - int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; - int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; - - // result[mode][len] is the number of 1/6 bits to encode the first len characters of the text, ending in the mode - int[][] result = new int[3][data.length + 1]; - Arrays.fill(result[1], Integer.MAX_VALUE / 2); - Arrays.fill(result[2], Integer.MAX_VALUE / 2); - result[0][0] = bytesCost; - result[1][0] = alphnumCost; - result[2][0] = numberCost; - - // Calculate the cost table using dynamic programming - for (int i = 0; i < data.length; i++) { - // Encode a character - int j = i + 1; - char c = (char)data[i]; - result[0][j] = result[0][i] + 48; // 8 bits per byte - if (isAlphanumeric(c)) - result[1][j] = result[1][i] + 33; // 5.5 bits per alphanumeric char - if (isNumeric(c)) - result[2][j] = result[2][i] + 20; // 3.33 bits per digit - - // Switch modes, rounding up fractional bits - result[0][j] = Math.min(roundUp6(Math.min(result[1][j], result[2][j])) + bytesCost , result[0][j]); - result[1][j] = Math.min(roundUp6(Math.min(result[2][j], result[0][j])) + alphnumCost, result[1][j]); - result[2][j] = Math.min(roundUp6(Math.min(result[0][j], result[1][j])) + numberCost , result[2][j]); - } - return result; - } - - - private static Mode[] computeCharacterModes(byte[] data, int version, int[][] bitCosts) { - if (data.length == 0) + private static Mode[] computeCharacterModes(int[] codePoints, int version) { + if (codePoints.length == 0) throw new IllegalArgumentException(); + final Mode[] modeTypes = {BYTE, ALPHANUMERIC, NUMERIC, KANJI}; // Do not modify + final int numModes = modeTypes.length; // Segment header sizes, measured in 1/6 bits - int bytesCost = (4 + BYTE .numCharCountBits(version)) * 6; - int alphnumCost = (4 + ALPHANUMERIC.numCharCountBits(version)) * 6; - int numberCost = (4 + NUMERIC .numCharCountBits(version)) * 6; + final int[] headCosts = new int[numModes]; + for (int i = 0; i < numModes; i++) + headCosts[i] = (4 + modeTypes[i].numCharCountBits(version)) * 6; - // Infer the mode used for last character by taking the minimum - Mode curMode; - int end = bitCosts[0].length - 1; - if (bitCosts[0][end] <= Math.min(bitCosts[1][end], bitCosts[2][end])) - curMode = BYTE; - else if (bitCosts[1][end] <= bitCosts[2][end]) - curMode = ALPHANUMERIC; - else - curMode = NUMERIC; + // 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]; - // Work backwards to calculate optimal encoding mode for each character - Mode[] result = new Mode[data.length]; - result[data.length - 1] = curMode; - for (int i = data.length - 2; i >= 0; i--) { - char c = (char)data[i]; - if (curMode == NUMERIC) { - if (isNumeric(c)) - curMode = NUMERIC; - else if (isAlphanumeric(c) && roundUp6(bitCosts[1][i] + 33) + numberCost == bitCosts[2][i + 1]) - curMode = ALPHANUMERIC; + // At the beginning of each iteration of the loop below, + // prevCosts[j] is the exact minimum number of 1/6 bits needed to + // encode the entire string prefix of length i, and end in modeTypes[j] + int[] prevCosts = headCosts.clone(); + + // Calculate costs using dynamic programming + for (int i = 0; i < codePoints.length; i++) { + int c = codePoints[i]; + int[] curCosts = new int[numModes]; + { // Always extend a bytes segment + int b; + if (c < 0x80) + b = 1; + else if (c < 0x800) + b = 2; + else if (c < 0x10000) + b = 3; else - curMode = BYTE; - } else if (curMode == ALPHANUMERIC) { - if (isNumeric(c) && roundUp6(bitCosts[2][i] + 20) + alphnumCost == bitCosts[1][i + 1]) - curMode = NUMERIC; - else if (isAlphanumeric(c)) - curMode = ALPHANUMERIC; - else - curMode = BYTE; - } else if (curMode == BYTE) { - if (isNumeric(c) && roundUp6(bitCosts[2][i] + 20) + bytesCost == bitCosts[0][i + 1]) - curMode = NUMERIC; - else if (isAlphanumeric(c) && roundUp6(bitCosts[1][i] + 33) + bytesCost == bitCosts[0][i + 1]) - curMode = ALPHANUMERIC; - else - curMode = BYTE; - } else - throw new AssertionError(); - result[i] = curMode; + b = 4; + curCosts[0] = prevCosts[0] + b * 8 * 6; + charModes[i][0] = modeTypes[0]; + } + // Extend a segment if possible + if (isAlphanumeric(c)) { + curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char + charModes[i][1] = modeTypes[1]; + } + if (isNumeric(c)) { + curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit + charModes[i][2] = modeTypes[2]; + } + if (isKanji(c)) { + curCosts[3] = prevCosts[3] + 104; // 13 bits per Shift JIS char + charModes[i][3] = modeTypes[3]; + } + + // 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 = roundUp6(curCosts[k]) + headCosts[j]; + if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) { + curCosts[j] = newCost; + charModes[i][j] = modeTypes[k]; + } + } + } + + 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]; + for (int i = result.length - 1; i >= 0; i--) { + for (int j = 0; j < numModes; j++) { + if (modeTypes[j] == curMode) { + curMode = charModes[i][j]; + result[i] = curMode; + break; + } + } } return result; } - private static List splitIntoSegments(byte[] data, Mode[] charModes) { - if (data.length == 0) + private static List splitIntoSegments(int[] codePoints, Mode[] charModes) { + if (codePoints.length == 0) throw new IllegalArgumentException(); List result = new ArrayList<>(); @@ -186,20 +189,20 @@ public final class QrSegmentAdvanced { Mode curMode = charModes[0]; int start = 0; for (int i = 1; ; i++) { - if (i < data.length && charModes[i] == curMode) + if (i < codePoints.length && charModes[i] == curMode) continue; + String s = new String(codePoints, start, i - start); if (curMode == BYTE) - result.add(QrSegment.makeBytes(Arrays.copyOfRange(data, start, i))); - else { - String temp = new String(data, start, i - start, StandardCharsets.US_ASCII); - if (curMode == NUMERIC) - result.add(QrSegment.makeNumeric(temp)); - else if (curMode == ALPHANUMERIC) - result.add(QrSegment.makeAlphanumeric(temp)); - else - throw new AssertionError(); - } - if (i >= data.length) + result.add(QrSegment.makeBytes(s.getBytes(StandardCharsets.UTF_8))); + else if (curMode == NUMERIC) + result.add(QrSegment.makeNumeric(s)); + else if (curMode == ALPHANUMERIC) + result.add(QrSegment.makeAlphanumeric(s)); + else if (curMode == KANJI) + result.add(makeKanji(s)); + else + throw new AssertionError(); + if (i >= codePoints.length) return result; curMode = charModes[i]; start = i; @@ -207,6 +210,16 @@ public final class QrSegmentAdvanced { } + private static int[] toCodePoints(String s) { + int[] result = s.codePoints().toArray(); + for (int c : result) { + if (Character.isSurrogate((char)c)) + throw new IllegalArgumentException("Invalid UTF-16 string"); + } + return result; + } + + private static boolean isAlphanumeric(int c) { return isNumeric(c) || 'A' <= c && c <= 'Z' || " $%*+./:-".indexOf(c) != -1; } From 565f1d7481942a5ea7714ea8d478de17b5d9f6f7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 18:34:06 +0000 Subject: [PATCH 419/810] Removed static imports from Java QrSegmentAdvanced for clarity. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index b53dae4..d8c8421 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -23,10 +23,6 @@ package io.nayuki.qrcodegen; -import static io.nayuki.qrcodegen.QrSegment.Mode.ALPHANUMERIC; -import static io.nayuki.qrcodegen.QrSegment.Mode.BYTE; -import static io.nayuki.qrcodegen.QrSegment.Mode.KANJI; -import static io.nayuki.qrcodegen.QrSegment.Mode.NUMERIC; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; @@ -93,7 +89,7 @@ public final class QrSegmentAdvanced { private static Mode[] computeCharacterModes(int[] codePoints, int version) { if (codePoints.length == 0) throw new IllegalArgumentException(); - final Mode[] modeTypes = {BYTE, ALPHANUMERIC, NUMERIC, KANJI}; // Do not modify + final Mode[] modeTypes = {Mode.BYTE, Mode.ALPHANUMERIC, Mode.NUMERIC, Mode.KANJI}; // Do not modify final int numModes = modeTypes.length; // Segment header sizes, measured in 1/6 bits @@ -192,13 +188,13 @@ public final class QrSegmentAdvanced { if (i < codePoints.length && charModes[i] == curMode) continue; String s = new String(codePoints, start, i - start); - if (curMode == BYTE) + if (curMode == Mode.BYTE) result.add(QrSegment.makeBytes(s.getBytes(StandardCharsets.UTF_8))); - else if (curMode == NUMERIC) + else if (curMode == Mode.NUMERIC) result.add(QrSegment.makeNumeric(s)); - else if (curMode == ALPHANUMERIC) + else if (curMode == Mode.ALPHANUMERIC) result.add(QrSegment.makeAlphanumeric(s)); - else if (curMode == KANJI) + else if (curMode == Mode.KANJI) result.add(makeKanji(s)); else throw new AssertionError(); From 4ede209d9a1052a22707cd348826336d6b52191d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 19:10:47 +0000 Subject: [PATCH 420/810] Changed QrCode.getAlignmentPatternPositions() from static function to instance method, tweaked its logic, and updated comment - in all languages but differently in each. --- c/qrcodegen.c | 6 ++-- cpp/QrCode.cpp | 17 +++++------ cpp/QrCode.hpp | 8 +++--- java/io/nayuki/qrcodegen/QrCode.java | 23 +++++++-------- javascript/qrcodegen.js | 42 +++++++++++++--------------- python/qrcodegen.py | 19 ++++++------- rust/src/lib.rs | 16 +++++------ typescript/qrcodegen.ts | 24 +++++++--------- 8 files changed, 70 insertions(+), 85 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 2c041b7..8518c5f 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -447,8 +447,10 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin } -// Calculates the positions of alignment patterns in ascending order for the given version number, -// storing them to the given array and returning an array length in the range [0, 7]. +// Calculates and stores an ascending list of positions of alignment patterns +// for this version number, returning the length of the list (in the range [0,7)). +// 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. testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { if (version == 1) return 0; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 2d4fd3f..86b78f9 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -205,7 +205,7 @@ void QrCode::drawFunctionPatterns() { drawFinderPattern(3, size - 4); // Draw numerous alignment patterns - const vector alignPatPos = getAlignmentPatternPositions(version); + const vector alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.size(); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { @@ -495,18 +495,15 @@ long QrCode::getPenaltyScore() const { } -vector QrCode::getAlignmentPatternPositions(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version number out of range"); - else if (ver == 1) +vector QrCode::getAlignmentPatternPositions() const { + if (version == 1) return vector(); else { - int numAlign = ver / 7 + 2; - int step = (ver == 32) ? 26 : - (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; - + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; vector result; - for (int i = 0, pos = ver * 4 + 10; i < numAlign - 1; i++, pos -= step) + for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) result.insert(result.begin(), pos); result.insert(result.begin(), 6); return result; diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index f637513..733df6c 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -231,10 +231,10 @@ class QrCode final { /*---- Private static helper functions ----*/ - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. - private: static std::vector getAlignmentPatternPositions(int ver); + // 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. + private: std::vector getAlignmentPatternPositions() const; // Returns the number of data bits that can be stored in a QR Code of the given version number, after diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 4890fed..cf3ec07 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -333,7 +333,7 @@ public final class QrCode { drawFinderPattern(3, size - 4); // Draw numerous alignment patterns - int[] alignPatPos = getAlignmentPatternPositions(version); + int[] alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { @@ -644,25 +644,22 @@ public final class QrCode { /*---- Private static helper functions ----*/ - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. - private static int[] getAlignmentPatternPositions(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw new IllegalArgumentException("Version number out of range"); - else if (ver == 1) + // 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. + private int[] getAlignmentPatternPositions() { + if (version == 1) return new int[]{}; else { - int numAlign = ver / 7 + 2; + int numAlign = version / 7 + 2; int step; - if (ver == 32) // Special snowflake + if (version == 32) // Special snowflake step = 26; else // step = ceil[(size - 13) / (numAlign*2 - 2)] * 2 - step = (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; - + step = (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; - for (int i = result.length - 1, pos = ver * 4 + 10; i >= 1; i--, pos -= step) + for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) result[i] = pos; return result; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index b2f6e34..f328298 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -203,7 +203,7 @@ var qrcodegen = new function() { drawFinderPattern(3, size - 4); // Draw numerous alignment patterns - var alignPatPos = QrCode.getAlignmentPatternPositions(version); + var alignPatPos = getAlignmentPatternPositions(); var numAlign = alignPatPos.length; for (var i = 0; i < numAlign; i++) { for (var j = 0; j < numAlign; j++) { @@ -488,6 +488,24 @@ var qrcodegen = new function() { } + // 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 integers. + function getAlignmentPatternPositions() { + if (version == 1) + return []; + else { + var numAlign = Math.floor(version / 7) + 2; + var step = (version == 32) ? 26 : + Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; + var result = [6]; + for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + result.splice(1, 0, pos); + return result; + } + } + + // Returns true iff the i'th bit of x is set to 1. function getBit(x, i) { return ((x >>> i) & 1) != 0; @@ -597,28 +615,6 @@ var qrcodegen = new function() { var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'. - // Returns a sequence of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of integers. - QrCode.getAlignmentPatternPositions = function(ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; - else if (ver == 1) - return []; - else { - var size = ver * 4 + 17; - var numAlign = Math.floor(ver / 7) + 2; - var step = (ver == 32) ? 26 : - Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; - - var result = [6]; - for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) - result.splice(1, 0, pos); - 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. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 49cffd4..8865923 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -256,7 +256,7 @@ class QrCode(object): self._draw_finder_pattern(3, self._size - 4) # Draw numerous alignment patterns - alignpatpos = QrCode._get_alignment_pattern_positions(self._version) + alignpatpos = self._get_alignment_pattern_positions() numalign = len(alignpatpos) skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) for i in range(numalign): @@ -486,21 +486,18 @@ class QrCode(object): # ---- Private static helper functions ---- - @staticmethod - def _get_alignment_pattern_positions(ver): - """Returns a sequence of positions of the alignment patterns in ascending order. These positions are - used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). - This stateless pure function could be implemented as table of 40 variable-length lists of integers.""" - if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): - raise ValueError("Version number out of range") - elif ver == 1: + def _get_alignment_pattern_positions(self): + """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 integers.""" + ver = self._version + if ver == 1: return [] else: numalign = ver // 7 + 2 step = 26 if (ver == 32) else \ (ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2 - start = ver * 4 + 10 - result = [(start - i * step) for i in range(numalign - 1)] + [6] + result = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6] return list(reversed(result)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a3f651b..eec747b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -288,7 +288,7 @@ impl QrCode { self.draw_finder_pattern(3, size - 4); // Draw numerous alignment patterns - let alignpatpos: Vec = QrCode::get_alignment_pattern_positions(self.version); + let alignpatpos: Vec = self.get_alignment_pattern_positions(); let numalign: usize = alignpatpos.len(); for i in 0 .. numalign { for j in 0 .. numalign { @@ -620,19 +620,19 @@ impl QrCode { /*---- Private static helper functions ----*/ - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting list is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. - fn get_alignment_pattern_positions(ver: Version) -> Vec { - let ver = ver.value(); + // 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. + fn get_alignment_pattern_positions(&self) -> Vec { + let ver = self.version.value(); if ver == 1 { vec![] } else { let numalign: i32 = (ver as i32) / 7 + 2; let step: i32 = if ver == 32 { 26 } else {((ver as i32)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; - let start = (ver as i32) * 4 + 10; - let mut result: Vec = (0 .. numalign - 1).map(|i| start - i * step).collect(); + let mut result: Vec = (0 .. numalign - 1).map( + |i| self.size - 7 - i * step).collect(); result.push(6); result.reverse(); result diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 08205b4..108d30e 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -270,7 +270,7 @@ namespace qrcodegen { this.drawFinderPattern(3, this.size - 4); // Draw numerous alignment patterns - let alignPatPos: Array = QrCode.getAlignmentPatternPositions(this.version); + let alignPatPos: Array = this.getAlignmentPatternPositions(); let numAlign: int = alignPatPos.length; for (let i = 0; i < numAlign; i++) { for (let j = 0; j < numAlign; j++) { @@ -560,22 +560,18 @@ namespace qrcodegen { /*-- Private static helper functions QrCode --*/ - // Returns a sequence of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting sequence is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of integers. - private static getAlignmentPatternPositions(ver: int): Array { - if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) - throw "Version number out of range"; - else if (ver == 1) + // 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 integers. + private getAlignmentPatternPositions(): Array { + if (this.version == 1) return []; else { - let size: int = ver * 4 + 17; - let numAlign: int = Math.floor(ver / 7) + 2; - let step: int = (ver == 32) ? 26 : - Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; - + let numAlign: int = Math.floor(this.version / 7) + 2; + let step: int = (this.version == 32) ? 26 : + Math.ceil((this.size - 13) / (numAlign*2 - 2)) * 2; let result: Array = [6]; - for (let i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + for (let i = 0, pos = this.size - 7; i < numAlign - 1; i++, pos -= step) result.splice(1, 0, pos); return result; } From 9c670453a852e47c9b548d7045a4e36850acd819 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 19:32:44 +0000 Subject: [PATCH 421/810] Tweaked small pieces of code. --- c/qrcodegen.c | 14 +++++++------- java/io/nayuki/qrcodegen/QrCode.java | 5 +---- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 4 ++-- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 8518c5f..03cb677 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -61,7 +61,7 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); -testable int getNumRawDataModules(int version); +testable int getNumRawDataModules(int ver); testable void calcReedSolomonGenerator(int degree, uint8_t result[]); testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, @@ -242,13 +242,13 @@ testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { // 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. -testable int getNumRawDataModules(int version) { - assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int result = (16 * version + 128) * version + 64; - if (version >= 2) { - int numAlign = version / 7 + 2; +testable int getNumRawDataModules(int ver) { + assert(qrcodegen_VERSION_MIN <= ver && ver <= qrcodegen_VERSION_MAX); + int result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + int numAlign = ver / 7 + 2; result -= (25 * numAlign - 10) * numAlign - 55; - if (version >= 7) + if (ver >= 7) result -= 36; } return result; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index cf3ec07..acd7832 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -297,13 +297,10 @@ public final class QrCode { size + brd * 2)) .append("\t\n") .append("\t Date: Tue, 28 Aug 2018 19:40:35 +0000 Subject: [PATCH 422/810] Tweaked comments for QrSegment.getTotalBits() to express package-private access control in some languages. --- cpp/QrSegment.hpp | 6 +++--- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 7 ++++--- typescript/qrcodegen.ts | 4 ++-- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 44c74ab..2e83465 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -172,9 +172,9 @@ class QrSegment final { public: const std::vector &getData() const; - // Calculates the number of bits needed to encode the given segments at the given version. - // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too - // many characters to fit its length field, or the total bits exceeds INT_MAX. + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. public: static int getTotalBits(const std::vector &segs, int version); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index f328298..14505be 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -813,8 +813,8 @@ var qrcodegen = new function() { }; - // Calculates and returns the number of bits needed to encode the given segments at the given version. - // The result is infinity if a segment has too many characters to fit its length field. + // (Package-private) Calculates and returns the number of bits needed to encode the given segments at the + // given version. The result is infinity if a segment has too many characters to fit its length field. this.QrSegment.getTotalBits = function(segs, version) { if (version < MIN_VERSION || version > MAX_VERSION) throw "Version number out of range"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8865923..58a6f6e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -697,11 +697,12 @@ class QrSegment(object): return list(self._bitdata) # Make defensive copy - # Calculates the number of bits needed to encode the given segments at the given version. - # Returns a non-negative number if successful. Otherwise returns None if a segment has - # too many characters to fit its length field. + # Package-private function @staticmethod def get_total_bits(segs, version): + """Calculates the number of bits needed to encode the given segments at + the given version. Returns a non-negative number if successful. Otherwise + returns None if a segment has too many characters to fit its length field.""" if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): raise ValueError("Version number out of range") result = 0 diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 108d30e..54c3a2c 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -769,8 +769,8 @@ namespace qrcodegen { } - // Calculates and returns the number of bits needed to encode the given segments at the given version. - // The result is infinity if a segment has too many characters to fit its length field. + // (Package-private) Calculates and returns the number of bits needed to encode the given segments at + // the given version. The result is infinity if a segment has too many characters to fit its length field. public static getTotalBits(segs: Array, version: int): number { if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw "Version number out of range"; From 16b59e207dc5ebde360d751986fb5b1c15607436 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 19:45:09 +0000 Subject: [PATCH 423/810] Deleted unnecessary version range check in two private functions, in all language versions except Rust. --- c/qrcodegen.c | 4 +--- cpp/QrCode.cpp | 2 -- cpp/QrSegment.cpp | 2 -- java/io/nayuki/qrcodegen/QrCode.java | 2 -- java/io/nayuki/qrcodegen/QrSegment.java | 3 --- javascript/qrcodegen.js | 4 ---- python/qrcodegen.py | 4 ---- typescript/qrcodegen.ts | 4 ---- 8 files changed, 1 insertion(+), 24 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 03cb677..6a93925 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -231,8 +231,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // for the given version number and error correction level. The result is in the range [9, 2956]. testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { int v = version, e = (int)ecl; - assert(qrcodegen_VERSION_MIN <= v && v <= qrcodegen_VERSION_MAX - && 0 <= e && e < 4); + assert(0 <= e && e < 4); return getNumRawDataModules(v) / 8 - ECC_CODEWORDS_PER_BLOCK[e][v] * NUM_ERROR_CORRECTION_BLOCKS[e][v]; @@ -977,7 +976,6 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz // many characters to fit its length field, or the total bits exceeds INT16_MAX. testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version) { assert(segs != NULL || len == 0); - assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); long result = 0; for (size_t i = 0; i < len; i++) { int numChars = segs[i].numChars; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 86b78f9..2e0bedd 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -526,8 +526,6 @@ int QrCode::getNumRawDataModules(int ver) { int QrCode::getNumDataCodewords(int ver, Ecc ecl) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw std::domain_error("Version number out of range"); return getNumRawDataModules(ver) / 8 - ECC_CODEWORDS_PER_BLOCK[static_cast(ecl)][ver] * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 70c0445..1b674ef 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -171,8 +171,6 @@ QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : int QrSegment::getTotalBits(const vector &segs, int version) { - if (version < 1 || version > 40) - throw std::domain_error("Version number out of range"); int result = 0; for (const QrSegment &seg : segs) { int ccbits = seg.mode.numCharCountBits(version); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index acd7832..2dbe091 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -692,8 +692,6 @@ 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) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw new IllegalArgumentException("Version number out of range"); return getNumRawDataModules(ver) / 8 - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal()][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 281e63a..81e754c 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -202,9 +202,6 @@ public final class QrSegment { // many characters to fit its length field, or the total bits exceeds Integer.MAX_VALUE. static int getTotalBits(List segs, int version) { Objects.requireNonNull(segs); - if (version < 1 || version > 40) - throw new IllegalArgumentException("Version number out of range"); - long result = 0; for (QrSegment seg : segs) { Objects.requireNonNull(seg); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 14505be..331536f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -636,8 +636,6 @@ var qrcodegen = new function() { // 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. QrCode.getNumDataCodewords = function(ver, ecl) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; @@ -816,8 +814,6 @@ var qrcodegen = new function() { // (Package-private) Calculates and returns the number of bits needed to encode the given segments at the // given version. The result is infinity if a segment has too many characters to fit its length field. this.QrSegment.getTotalBits = function(segs, version) { - if (version < MIN_VERSION || version > MAX_VERSION) - throw "Version number out of range"; var result = 0; for (var i = 0; i < segs.length; i++) { var seg = segs[i]; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 58a6f6e..d37ce14 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -522,8 +522,6 @@ class QrCode(object): """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.""" - if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): - raise ValueError("Version number out of range") return QrCode._get_num_raw_data_modules(ver) // 8 \ - QrCode._ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] \ * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] @@ -703,8 +701,6 @@ class QrSegment(object): """Calculates the number of bits needed to encode the given segments at the given version. Returns a non-negative number if successful. Otherwise returns None if a segment has too many characters to fit its length field.""" - if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): - raise ValueError("Version number out of range") result = 0 for seg in segs: ccbits = seg.get_mode().num_char_count_bits(version) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 54c3a2c..f9d44a0 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -599,8 +599,6 @@ namespace qrcodegen { // 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. private static getNumDataCodewords(ver: int, ecl: QrCode.Ecc): int { - if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) - throw "Version number out of range"; return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; @@ -772,8 +770,6 @@ namespace qrcodegen { // (Package-private) Calculates and returns the number of bits needed to encode the given segments at // the given version. The result is infinity if a segment has too many characters to fit its length field. public static getTotalBits(segs: Array, version: int): number { - if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) - throw "Version number out of range"; let result: number = 0; for (let i = 0; i < segs.length; i++) { let seg: QrSegment = segs[i]; From 362d552c92e741b156a9ee6723e5172e7daca2e6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 20:11:05 +0000 Subject: [PATCH 424/810] Simplified QrSegment.Mode.numCharCountBits() in all language versions. --- c/qrcodegen.c | 7 +------ cpp/QrSegment.cpp | 5 +---- java/io/nayuki/qrcodegen/QrSegment.java | 6 ++---- javascript/qrcodegen.js | 5 +---- python/qrcodegen.py | 5 +---- rust/src/lib.rs | 15 ++------------- typescript/qrcodegen.ts | 5 +---- 7 files changed, 9 insertions(+), 39 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 6a93925..ccc690f 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -999,12 +999,7 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int // given mode at the given version number. The result is in the range [0, 16]. static int numCharCountBits(enum qrcodegen_Mode mode, int version) { assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); - int i; - if ( 1 <= version && version <= 9) i = 0; - else if (10 <= version && version <= 26) i = 1; - else if (27 <= version && version <= 40) i = 2; - else { assert(false); return -1; } // Dummy value - + int i = (version + 7) / 17; switch (mode) { case qrcodegen_Mode_NUMERIC : { static const int temp[] = {10, 12, 14}; return temp[i]; } case qrcodegen_Mode_ALPHANUMERIC: { static const int temp[] = { 9, 11, 13}; return temp[i]; } diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 1b674ef..4dbae22 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -47,10 +47,7 @@ int QrSegment::Mode::getModeBits() const { int QrSegment::Mode::numCharCountBits(int ver) const { - if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; - else throw std::domain_error("Version number out of range"); + return numBitsCharCount[(ver + 7) / 17]; } diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 81e754c..109b016 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -270,10 +270,8 @@ public final class QrSegment { * @throws IllegalArgumentException if the version number is out of range */ int numCharCountBits(int ver) { - if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; - else throw new IllegalArgumentException("Version number out of range"); + assert QrCode.MIN_VERSION <= ver && ver <= QrCode.MAX_VERSION; + return numBitsCharCount[(ver + 7) / 17]; } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 331536f..babdfeb 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -861,10 +861,7 @@ var qrcodegen = new function() { // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. this.numCharCountBits = function(ver) { - if ( 1 <= ver && ver <= 9) return ccbits[0]; - else if (10 <= ver && ver <= 26) return ccbits[1]; - else if (27 <= ver && ver <= 40) return ccbits[2]; - else throw "Version number out of range"; + return ccbits[Math.floor((ver + 7) / 17)]; }; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d37ce14..6083a69 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -740,10 +740,7 @@ class QrSegment(object): # Package-private method def num_char_count_bits(self, ver): """Returns the bit width of the segment character count field for this mode object at the given version number.""" - if 1 <= ver <= 9: return self._charcounts[0] - elif 10 <= ver <= 26: return self._charcounts[1] - elif 27 <= ver <= 40: return self._charcounts[2] - else: raise ValueError("Version number out of range") + return self._charcounts[(ver + 7) // 17] # Public constants. Create them outside the class. Mode.NUMERIC = Mode(0x1, (10, 12, 14)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index eec747b..1f6bf23 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1048,24 +1048,13 @@ impl QrSegmentMode { // for this mode object at the given version number. pub fn num_char_count_bits(&self, ver: Version) -> u8 { use QrSegmentMode::*; - let array: [u8; 3] = match *self { + (match *self { Numeric => [10, 12, 14], Alphanumeric => [ 9, 11, 13], Byte => [ 8, 16, 16], Kanji => [ 8, 10, 12], Eci => [ 0, 0, 0], - }; - - let ver = ver.value(); - if 1 <= ver && ver <= 9 { - array[0] - } else if 10 <= ver && ver <= 26 { - array[1] - } else if 27 <= ver && ver <= 40 { - array[2] - } else { - panic!("Version number out of range"); - } + })[((ver.value() + 7) / 17) as usize] } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index f9d44a0..f419145 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -1005,10 +1005,7 @@ namespace qrcodegen.QrSegment { // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. public numCharCountBits(ver: int): int { - if ( 1 <= ver && ver <= 9) return this.numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return this.numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return this.numBitsCharCount[2]; - else throw "Version number out of range"; + return this.numBitsCharCount[Math.floor((ver + 7) / 17)]; } } From e9e7363c55bfb52d39f817d2133ebd8595199760 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 20:27:51 +0000 Subject: [PATCH 425/810] Tweaked Java comments and added assert. --- java/io/nayuki/qrcodegen/BitBuffer.java | 1 + java/io/nayuki/qrcodegen/QrSegment.java | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 53a0102..265068f 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -59,6 +59,7 @@ public final class BitBuffer implements Cloneable { * @return the length of this sequence */ public int bitLength() { + assert bitLength >= 0; return bitLength; } diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 109b016..37152f3 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -173,7 +173,7 @@ public final class QrSegment { * @param md the mode, which is not {@code null} * @param numCh the data length in characters, which is non-negative * @param data the data bits of this segment, which is not {@code null} - * @throws NullPointerException if the mode or bit buffer is {@code null} + * @throws NullPointerException if the mode or data is {@code null} * @throws IllegalArgumentException if the character count is negative */ public QrSegment(Mode md, int numCh, BitBuffer data) { @@ -189,7 +189,7 @@ public final class QrSegment { /*---- Methods ----*/ /** - * Returns the data bits of this segment. + * Returns a copy of the data bits of this segment. * @return the data bits of this segment (not {@code null}) */ public BitBuffer getBits() { @@ -267,7 +267,6 @@ public final class QrSegment { * Returns the bit width of the segment character count field for this mode object at the specified version number. * @param ver the version number, which is between 1 to 40 (inclusive) * @return the number of bits for the character count, which is between 8 to 16 (inclusive) - * @throws IllegalArgumentException if the version number is out of range */ int numCharCountBits(int ver) { assert QrCode.MIN_VERSION <= ver && ver <= QrCode.MAX_VERSION; From b9f69cf7bd5f965c3a8802bc4026f060742bcb5e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 20:59:24 +0000 Subject: [PATCH 426/810] Updated documentation comment in 4 languages to change word "specified" to "given", related to commit d8b66fcbf10d. --- cpp/QrCode.hpp | 2 +- javascript/qrcodegen.js | 2 +- python/qrcodegen.py | 2 +- typescript/qrcodegen.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 733df6c..15c4a25 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -58,7 +58,7 @@ class QrCode final { /*---- Public static factory functions ----*/ /* - * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index babdfeb..6ff31e1 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -516,7 +516,7 @@ var qrcodegen = new function() { /*---- Public static factory functions for QrCode ----*/ /* - * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 6083a69..4435195 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -68,7 +68,7 @@ class QrCode(object): @staticmethod def encode_text(text, ecl): - """Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index f419145..ffa3d67 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -46,7 +46,7 @@ namespace qrcodegen { /*-- Public static factory functions --*/ - // Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. + // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer // Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the From 3ead3dbb840d9642adedd73dd0792c8e51ee4e6f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 21:08:00 +0000 Subject: [PATCH 427/810] Updated up to 8 comments in all language versions, but somewhat differently in each language. --- c/qrcodegen.c | 4 ++-- c/qrcodegen.h | 15 +++++++-------- cpp/QrCode.cpp | 4 ++-- cpp/QrCode.hpp | 7 +++---- cpp/QrSegment.hpp | 9 +++++---- java/io/nayuki/qrcodegen/QrCode.java | 16 ++++++++-------- java/io/nayuki/qrcodegen/QrSegment.java | 10 +++++----- javascript/qrcodegen.js | 18 ++++++++---------- python/qrcodegen.py | 20 ++++++++++---------- rust/src/lib.rs | 23 +++++++++++------------ typescript/qrcodegen.ts | 20 ++++++++++---------- 11 files changed, 71 insertions(+), 75 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index ccc690f..19caed5 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -494,8 +494,8 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { setModule(qrcode, x, y, black); i++; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } } diff --git a/c/qrcodegen.h b/c/qrcodegen.h index c216619..25ddb06 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -73,10 +73,8 @@ enum qrcodegen_Mode { /* - * A segment of user/application data that a QR Code symbol can convey. - * Each segment has a mode, a character count, and character/general data that is - * already encoded as a sequence of bits. The maximum allowed bit length is 32767, - * because even the largest QR Code (version 40) has only 31329 modules. + * Represents a segment of character data, binary data, or control data. The maximum allowed + * bit length is 32767, because even the largest QR Code (version 40) has only 31329 modules. */ struct qrcodegen_Segment { // The mode indicator for this segment. @@ -219,9 +217,10 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); /* - * Renders a QR Code symbol representing the given data segments at the given error correction - * level or higher. The smallest possible QR Code version is automatically chosen for the output. - * Returns true if QR Code creation succeeded, or false if the data is too long to fit in any version. + * Renders a QR Code symbol representing the given segments at the given error correction level. + * The smallest possible QR Code version is automatically chosen for the output. Returns true if + * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level + * of the result may be higher than the ecl argument if it can be done without increasing the version. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. * This function is considered to be lower level than simply encoding text or binary data. @@ -234,7 +233,7 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, /* - * Renders a QR Code symbol representing the given data segments with the given encoding parameters. + * Renders a QR Code symbol representing the given segments with the given encoding parameters. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 2e0bedd..a6388cc 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -360,8 +360,8 @@ void QrCode::drawCodewords(const vector &data) { modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); i++; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 15c4a25..0c71131 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -77,7 +77,7 @@ class QrCode final { /* - * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * Returns a QR Code symbol representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -154,9 +154,8 @@ class QrCode final { /* - * Based on the given number of border modules to add as padding, this returns a - * string whose contents represents an SVG XML file that depicts this QR Code symbol. - * Note that Unix newlines (\n) are always used, regardless of the platform. + * Returns a string of SVG XML code representing an image of this QR Code symbol with the given + * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. */ public: std::string toSvgString(int border) const; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 2e83465..3cd0ec3 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -31,9 +31,8 @@ namespace qrcodegen { /* - * Represents a character string to be encoded in a QR Code symbol. Each segment has - * a mode, and a sequence of characters that is already encoded as a sequence of bits. - * Instances of this class are immutable. + * Represents a segment of character data, binary data, or control data + * to be put into a QR Code symbol. Instances of this class are immutable. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -58,8 +57,10 @@ class QrSegment final { /*-- Fields --*/ + // The mode indicator bits, which is a uint4 value (range 0 to 15). private: int modeBits; + // Three values for different version ranges. private: int numBitsCharCount[3]; @@ -150,7 +151,7 @@ class QrSegment final { /*---- Constructors ----*/ /* - * Creates a new QR Code data segment with the given parameters and data. + * Creates a new QR Code segment with the given parameters and data. */ public: QrSegment(Mode md, int numCh, const std::vector &dt); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 2dbe091..532d163 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -81,8 +81,9 @@ public final class QrCode { /** - * Returns a QR Code symbol representing the specified data segments at the specified error correction - * level or higher. The smallest possible QR Code version is automatically chosen for the output. + * Returns a QR Code symbol 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 + * of the result may be higher than the ecl argument if it can be done without increasing the version. *

This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. * This function is considered to be lower level than simply encoding text or binary data.

@@ -99,7 +100,7 @@ public final class QrCode { /** - * Returns a QR Code symbol representing the specified data segments with the specified encoding parameters. + * Returns a QR Code symbol representing the specified segments with the specified encoding parameters. * The smallest possible QR Code version within the specified range is automatically chosen for the output. *

This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -279,9 +280,8 @@ public final class QrCode { /** - * Based on the specified number of border modules to add as padding, this returns a - * string whose contents represents an SVG XML file that depicts this QR Code symbol. - * Note that Unix newlines (\n) are always used, regardless of the platform. + * Returns a string of SVG XML code representing an image of this QR Code symbol with the specified + * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document * @throws IllegalArgumentException if the border is negative @@ -495,8 +495,8 @@ public final class QrCode { modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); i++; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } } diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 37152f3..8328570 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -31,9 +31,8 @@ import java.util.regex.Pattern; /** - * Represents a character string to be encoded in a QR Code symbol. Each segment has - * a mode, and a sequence of characters that is already encoded as a sequence of bits. - * Instances of this class are immutable. + * Represents a segment of character data, binary data, or control data + * to be put into a QR Code symbol. Instances of this class are immutable. *

This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes.

@@ -169,7 +168,7 @@ public final class QrSegment { /*---- Constructor ----*/ /** - * Constructs a QR Code data segment with the specified parameters and data. + * Constructs a QR Code segment with the specified parameters and data. * @param md the mode, which is not {@code null} * @param numCh the data length in characters, which is non-negative * @param data the data bits of this segment, which is not {@code null} @@ -247,9 +246,10 @@ public final class QrSegment { /*-- Fields --*/ - /** An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. */ + /** The mode indicator bits, which is a uint4 value (range 0 to 15). */ final int modeBits; + /** Three values for different version ranges. */ private final int[] numBitsCharCount; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 6ff31e1..fc09624 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -164,9 +164,8 @@ var qrcodegen = new function() { } }; - // Based on the given number of border modules to add as padding, this returns a - // string whose contents represents an SVG XML file that depicts this QR Code symbol. - // Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. this.toSvgString = function(border) { if (border < 0) throw "Border must be non-negative"; @@ -370,8 +369,8 @@ var qrcodegen = new function() { modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); i++; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } } @@ -541,7 +540,7 @@ var qrcodegen = new function() { /* - * Returns a QR Code symbol representing the given data segments with the given encoding parameters. + * Returns a QR Code symbol representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -697,9 +696,8 @@ var qrcodegen = new function() { /*---- Data segment class ----*/ /* - * A public class that represents a character string to be encoded in a QR Code symbol. - * Each segment has a mode, and a sequence of characters that is already encoded as - * a sequence of bits. Instances of this class are immutable. + * Represents a segment of character data, binary data, or control data + * to be put into a QR Code symbol. Instances of this class are immutable. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -856,7 +854,7 @@ var qrcodegen = new function() { // Private constructor. function Mode(mode, ccbits) { - // (Package-private) An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + // (Package-private) The mode indicator bits, which is a uint4 value (range 0 to 15). Object.defineProperty(this, "modeBits", {value:mode}); // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 4435195..fbf5a13 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -90,7 +90,7 @@ class QrCode(object): @staticmethod def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): - """Returns a QR Code symbol representing the given data segments with the given encoding parameters. + """Returns a QR Code symbol representing the given segments with the given encoding parameters. The smallest possible QR Code version within the given range is automatically chosen for the output. This function allows the user to create a custom sequence of segments that switches between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -223,8 +223,8 @@ class QrCode(object): # ---- Public instance methods ---- def to_svg_str(self, border): - """Based on the given number of border modules to add as padding, this returns a - string whose contents represents an SVG XML file that depicts this QR Code symbol.""" + """Returns a string of SVG XML code representing an image of this QR Code symbol with the given + number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform.""" if border < 0: raise ValueError("Border must be non-negative") parts = [] @@ -403,8 +403,8 @@ class QrCode(object): if not self._isfunction[y][x] and i < len(data) * 8: self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7)) i += 1 - # If there are any remainder bits (0 to 7), they are already - # set to 0/false/white when the grid of modules was initialized + # If this QR Code has any remainder bits (0 to 7), they were assigned as + # 0/false/white by the constructor and are left unchanged by this method assert i == len(data) * 8 @@ -583,9 +583,8 @@ class QrCode(object): # ---- Data segment class ---- class QrSegment(object): - """Represents a character string to be encoded in a QR Code symbol. Each segment has - a mode, and a sequence of characters that is already encoded as a sequence of bits. - Instances of this class are immutable. + """Represents a segment of character data, binary data, or control data + to be put into a QR Code symbol. Instances of this class are immutable. This segment class imposes no length restrictions, but QR Codes have restrictions. Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. Any segment longer than this is meaningless for the purpose of generating QR Codes.""" @@ -676,6 +675,7 @@ class QrSegment(object): # ---- Constructor ---- def __init__(self, mode, numch, bitdata): + """Creates a new QR Code segment with the given parameters and data.""" if numch < 0 or not isinstance(mode, QrSegment.Mode): raise ValueError() self._mode = mode @@ -729,8 +729,8 @@ class QrSegment(object): # Private constructor def __init__(self, modebits, charcounts): - self._modebits = modebits - self._charcounts = charcounts + self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) + self._charcounts = charcounts # Three values for different version ranges # Package-private method def get_mode_bits(self): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1f6bf23..523d87d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -82,8 +82,9 @@ impl QrCode { } - // Returns a QR Code symbol representing the given data segments at the given error correction - // level or higher. The smallest possible QR Code version is automatically chosen for the output. + // Returns a QR Code symbol representing the given segments at the given error correction level. + // The smallest possible QR Code version is automatically chosen for the output. The ECC level + // of the result may be higher than the ecl argument if it can be done without increasing the version. // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. // This function is considered to be lower level than simply encoding text or binary data. @@ -93,7 +94,7 @@ impl QrCode { } - // Returns a QR Code symbol representing the given data segments with the given encoding parameters. + // Returns a QR Code symbol representing the given segments with the given encoding parameters. // The smallest possible QR Code version within the given range is automatically chosen for the output. // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -239,9 +240,8 @@ impl QrCode { } - // Based on the given number of border modules to add as padding, this returns a - // string whose contents represents an SVG XML file that depicts this QR Code symbol. - // Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); let mut result = String::new(); @@ -468,8 +468,8 @@ impl QrCode { *self.module_mut(x, y) = get_bit(data[i >> 3] as u32, 7 - ((i & 7) as i32)); i += 1; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } right -= 2; @@ -826,9 +826,8 @@ impl ReedSolomonGenerator { /*---- QrSegment functionality ----*/ -// Represents a character string to be encoded in a QR Code symbol. -// Each segment has a mode, and a sequence of characters that is already -// encoded as a sequence of bits. Instances of this struct are immutable. +// Represents a segment of character data, binary data, or control data +// to be put into a QR Code symbol. Instances of this class are immutable. pub struct QrSegment { // The mode indicator for this segment. @@ -942,7 +941,7 @@ impl QrSegment { } - // Creates a new QR Code data segment with the given parameters and data. + // Creates a new QR Code segment with the given parameters and data. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { Self { mode: mode, diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index ffa3d67..1c626c7 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -67,7 +67,7 @@ namespace qrcodegen { } - // Returns a QR Code symbol representing the given data segments with the given encoding parameters. + // Returns a QR Code symbol representing the given segments with the given encoding parameters. // The smallest possible QR Code version within the given range is automatically chosen for the output. // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -231,9 +231,8 @@ namespace qrcodegen { } - // Based on the given number of border modules to add as padding, this returns a - // string whose contents represents an SVG XML file that depicts this QR Code symbol. - // Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. public toSvgString(border: int): string { if (border < 0) throw "Border must be non-negative"; @@ -439,8 +438,8 @@ namespace qrcodegen { this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); i++; } - // If there are any remainder bits (0 to 7), they are already - // set to 0/false/white when the grid of modules was initialized + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/white by the constructor and are left unchanged by this method } } } @@ -650,9 +649,8 @@ namespace qrcodegen { /*---- Data segment class ----*/ /* - * A public class that represents a character string to be encoded in a QR Code symbol. - * Each segment has a mode, and a sequence of characters that is already encoded as - * a sequence of bits. Instances of this class are immutable. + * Represents a segment of character data, binary data, or control data + * to be put into a QR Code symbol. Instances of this class are immutable. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -750,6 +748,7 @@ namespace qrcodegen { /*-- Constructor --*/ + // Creates a new QR Code segment with the given parameters and data. public constructor(mode: QrSegment.Mode, numChars: int, bitData: Array) { if (numChars < 0) throw "Invalid argument"; @@ -987,9 +986,10 @@ namespace qrcodegen.QrSegment { /*-- Fields --*/ - // An unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object. + // The mode indicator bits, which is a uint4 value (range 0 to 15). public readonly modeBits: int; + // Three values for different version ranges. private readonly numBitsCharCount: [int,int,int]; From b59c1811625bd766bb49c632bba77f9f4b25ffc3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 21:12:38 +0000 Subject: [PATCH 428/810] Simplified QrCode.toSvgString() in C++ and Rust, related to Java change in commit 9c670453a852. --- cpp/QrCode.cpp | 5 +---- rust/src/lib.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index a6388cc..cd6ff25 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -174,13 +174,10 @@ std::string QrCode::toSvgString(int border) const { sb << (size + border * 2) << " " << (size + border * 2) << "\" stroke=\"none\">\n"; sb << "\t\n"; sb << "\t\n", dimension); result += "\t\n"; result += "\t Date: Tue, 28 Aug 2018 21:35:31 +0000 Subject: [PATCH 429/810] Changed QrTemplate.getAlignmentPatternPositions() from static function to instance method, and updated comment. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 4087df4..41306d1 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -121,7 +121,7 @@ final class QrTemplate { drawFinderPattern(3, size - 4); // Draw numerous alignment patterns - int[] alignPatPos = getAlignmentPatternPositions(version); + int[] alignPatPos = getAlignmentPatternPositions(); int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { @@ -276,21 +276,19 @@ final class QrTemplate { /*---- Private static helper functions ----*/ - // Returns a set of positions of the alignment patterns in ascending order. These positions are - // used on both the x and y axes. Each value in the resulting array is in the range [0, 177). - // This stateless pure function could be implemented as table of 40 variable-length lists of unsigned bytes. - private static int[] getAlignmentPatternPositions(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw new IllegalArgumentException("Version number out of range"); - else if (ver == 1) + // 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. + private int[] getAlignmentPatternPositions() { + if (version == 1) return new int[]{}; else { - int numAlign = ver / 7 + 2; - int step = (ver == 32) ? 26 : - (ver*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + int numAlign = version / 7 + 2; + int step = (version == 32) ? 26 : + (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; - for (int i = result.length - 1, pos = ver * 4 + 10; i >= 1; i--, pos -= step) + for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) result[i] = pos; return result; } From 4cddfddb66202a9f24d6a3ba13f0307644711c60 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 21:44:28 +0000 Subject: [PATCH 430/810] Simplified small pieces of code. --- src/io/nayuki/fastqrcodegen/QrCode.java | 7 +------ src/io/nayuki/fastqrcodegen/QrSegment.java | 8 +------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index c54d718..9935c91 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -215,13 +215,10 @@ public final class QrCode { size + brd * 2)) .append("\t\n") .append("\t MAX_VERSION) - throw new IllegalArgumentException("Version number out of range"); return QrTemplate.getNumRawDataModules(ver) / 8 - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal()][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index ea48ce1..9f363f2 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -168,9 +168,6 @@ public final class QrSegment { // Package-private helper function. static int getTotalBits(List segs, int version) { Objects.requireNonNull(segs); - if (version < 1 || version > 40) - throw new IllegalArgumentException("Version number out of range"); - long result = 0; for (QrSegment seg : segs) { Objects.requireNonNull(seg); @@ -235,10 +232,7 @@ public final class QrSegment { /*-- Method --*/ int numCharCountBits(int ver) { - if ( 1 <= ver && ver <= 9) return numBitsCharCount[0]; - else if (10 <= ver && ver <= 26) return numBitsCharCount[1]; - else if (27 <= ver && ver <= 40) return numBitsCharCount[2]; - else throw new IllegalArgumentException("Version number out of range"); + return numBitsCharCount[(ver + 7) / 17]; } } From 6d5164fa0e9a474e599d0533d084318a0031ae4e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 28 Aug 2018 21:44:39 +0000 Subject: [PATCH 431/810] Added and updated comments. --- src/io/nayuki/fastqrcodegen/QrCode.java | 7 +++---- src/io/nayuki/fastqrcodegen/QrSegment.java | 9 +++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 9935c91..53550c2 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -197,9 +197,8 @@ public final class QrCode { /** - * Based on the specified number of border modules to add as padding, this returns a - * string whose contents represents an SVG XML file that depicts this QR Code symbol. - * Note that Unix newlines (\n) are always used, regardless of the platform. + * Returns a string of SVG XML code representing an image of this QR Code symbol with the specified + * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document * @throws IllegalArgumentException if the border is negative @@ -259,7 +258,7 @@ public final class QrCode { setModule(size - 1 - i, 8, (bits >>> i) & 1); for (int i = 8; i < 15; i++) setModule(8, size - 15 + i, (bits >>> i) & 1); - setModule(8, size - 8, 1); + setModule(8, size - 8, 1); // Always black } diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index 9f363f2..ff31a35 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -165,19 +165,20 @@ public final class QrSegment { } - // Package-private helper function. + // Calculates the number of bits needed to encode the given segments at the given version. + // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too + // many characters to fit its length field, or the total bits exceeds Integer.MAX_VALUE. static int getTotalBits(List segs, int version) { Objects.requireNonNull(segs); long result = 0; for (QrSegment seg : segs) { Objects.requireNonNull(seg); int ccbits = seg.mode.numCharCountBits(version); - // Fail if segment length value doesn't fit in the length field's bit-width if (seg.numChars >= (1 << ccbits)) - return -1; + return -1; // The segment's length doesn't fit the field's bit width result += 4L + ccbits + seg.bitLength; if (result > Integer.MAX_VALUE) - return -1; + return -1; // The sum will overflow an int type } return (int)result; } From 566ca1f930f81b7635956e07eae31aa681b7d8d1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 30 Aug 2018 17:03:31 +0000 Subject: [PATCH 432/810] Fixed readme example code for TypeScript, due to commit bed21e3c6424. --- Readme.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index a35dd85..795dbbe 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -84,14 +84,14 @@ TypeScript language: // Simple operation let qr0: qrcodegen.QrCode = qrcodegen.QrCode.encodeText( - "Hello, world!", qrcodegen.QrCode_Ecc.MEDIUM); + "Hello, world!", qrcodegen.QrCode.Ecc.MEDIUM); let svg: string = qr0.toSvgString(4); // Manual operation let segs: Array = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); let qr1: qrcodegen.QrCode = qrcodegen.QrCode.encodeSegments( - segs, qrcodegen.QrCode_Ecc.HIGH, 5, 5, 2, false); + segs, qrcodegen.QrCode.Ecc.HIGH, 5, 5, 2, false); for (let y = 0; y < qr1.size; y++) { for (let x = 0; x < qr1.size; x++) { (... paint qr1.getModule(x, y) ...) From 0b89eef36147a3072c0ffedfa95da3a2b7e1b609 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 16:06:08 +0000 Subject: [PATCH 433/810] Fixed incorrect default argument in TypeScript QrCode.encodeSegments(). --- typescript/qrcodegen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 1c626c7..1bedfff 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -73,7 +73,7 @@ namespace qrcodegen { // between modes (such as alphanumeric and binary) to encode text more efficiently. // This function is considered to be lower level than simply encoding text or binary data. public static encodeSegments(segs: Array, ecl: QrCode.Ecc, - minVersion: int = 1, maxVersion: int = 1, + minVersion: int = 1, maxVersion: int = 40, mask: int = -1, boostEcl: boolean = true): QrCode { if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION) From 0a96eec52e7d3b5f159e973dbc7f0d78258de491 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:00:52 +0000 Subject: [PATCH 434/810] Simplified small pieces of TypeScript and JavaScript code. --- javascript/qrcodegen.js | 5 +---- typescript/qrcodegen.ts | 7 ++----- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index fc09624..bb4096e 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -329,10 +329,7 @@ var qrcodegen = new function() { var ecc = rs.getRemainder(dat); if (i < numShortBlocks) dat.push(0); - ecc.forEach(function(b) { - dat.push(b); - }); - blocks.push(dat); + blocks.push(dat.concat(ecc)); } // Interleave (not concatenate) the bytes from every block into a single sequence diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 1bedfff..43012cb 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -399,9 +399,7 @@ namespace qrcodegen { let ecc: Array = rs.getRemainder(dat); if (i < numShortBlocks) dat.push(0); - ecc.forEach( - (b: byte) => dat.push(b)); - blocks.push(dat); + blocks.push(dat.concat(ecc)); } // Interleave (not concatenate) the bytes from every block into a single sequence @@ -770,8 +768,7 @@ namespace qrcodegen { // the given version. The result is infinity if a segment has too many characters to fit its length field. public static getTotalBits(segs: Array, version: int): number { let result: number = 0; - for (let i = 0; i < segs.length; i++) { - let seg: QrSegment = segs[i]; + for (let seg of segs) { let ccbits: int = seg.mode.numCharCountBits(version); if (seg.numChars >= (1 << ccbits)) return Infinity; // The segment's length doesn't fit the field's bit width From 7217de3eee9b29b4ce27c974548aaae4260099b5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:03:51 +0000 Subject: [PATCH 435/810] Refactored TypeScript code to use for-of loop instead of Array.forEach(), for simplicity and clarity. --- typescript/qrcodegen.ts | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 43012cb..5538270 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -95,19 +95,19 @@ namespace qrcodegen { } // Increase the error correction level while the data still fits in the current version number - [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH].forEach((newEcl: QrCode.Ecc) => { // From low to high + for (let newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; - }); + } // Concatenate all segments to create the data bit string let bb = new BitBuffer(); - segs.forEach((seg: QrSegment) => { + for (let seg of segs) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - seg.getBits().forEach( - (b: bit) => bb.push(b)); - }); + for (let b of seg.getBits()) + bb.push(b); + } if (bb.length != dataUsedBits) throw "Assertion error"; @@ -541,12 +541,12 @@ namespace qrcodegen { // Balance of black and white modules let black: int = 0; - this.modules.forEach((row: Array) => { - row.forEach((color: boolean) => { + for (let row of this.modules) { + for (let color of row) { if (color) black++; - }); - }); + } + } let total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% let k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; @@ -660,8 +660,8 @@ namespace qrcodegen { // Returns a segment representing the given binary data encoded in byte mode. public static makeBytes(data: Array): QrSegment { let bb = new BitBuffer(); - data.forEach( - (b: byte) => bb.appendBits(b, 8)); + for (let b of data) + bb.appendBits(b, 8); return new QrSegment(QrSegment.Mode.BYTE, data.length, bb); } @@ -857,12 +857,12 @@ namespace qrcodegen { public getRemainder(data: Array): Array { // Compute the remainder by performing polynomial division let result: Array = this.coefficients.map(_ => 0); - data.forEach((b: byte) => { + for (let b of data) { let factor: byte = b ^ (result.shift() as int); result.push(0); for (let i = 0; i < result.length; i++) result[i] ^= ReedSolomonGenerator.multiply(this.coefficients[i], factor); - }); + } return result; } From 46443ecf22fb0e87c2d750bbbe8876713cf2ee63 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:20:14 +0000 Subject: [PATCH 436/810] Tweaked integer casts and types in C++ code for robustness and simplicity. --- cpp/QrCode.cpp | 10 +++++----- cpp/QrSegment.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index cd6ff25..8160866 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -93,7 +93,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); } - if (bb.size() != static_cast(dataUsedBits)) + if (bb.size() != static_cast(dataUsedBits)) throw std::logic_error("Assertion error"); // Add terminator and pad up to a byte if applicable @@ -326,10 +326,10 @@ vector QrCode::addEccAndInterleave(const vector &data) const { // Interleave (not concatenate) the bytes from every block into a single sequence vector result; - for (int i = 0; static_cast(i) < blocks.at(0).size(); i++) { - for (int j = 0; static_cast(j) < blocks.size(); j++) { + for (size_t i = 0; i < blocks.at(0).size(); i++) { + for (size_t j = 0; j < blocks.size(); j++) { // Skip the padding byte in short blocks - if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) + if (i != static_cast(shortBlockLen - blockEccLen) || j >= static_cast(numShortBlocks)) result.push_back(blocks.at(j).at(i)); } } @@ -362,7 +362,7 @@ void QrCode::drawCodewords(const vector &data) { } } } - if (static_cast(i) != data.size() * 8) + if (i != data.size() * 8) throw std::logic_error("Assertion error"); } diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 4dbae22..8d1bde3 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -60,7 +60,7 @@ const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); QrSegment QrSegment::makeBytes(const vector &data) { - if (data.size() > INT_MAX) + if (data.size() > static_cast(INT_MAX)) throw std::length_error("Data too long"); BitBuffer bb; for (uint8_t b : data) From e7854eb797aea13011eb72a29781fa3a842a684e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:23:16 +0000 Subject: [PATCH 437/810] Reformatted some whitespace for alignment in all language versions. --- c/qrcodegen.c | 4 ++-- cpp/QrCode.cpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 19caed5..cfb7eb1 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -200,7 +200,7 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // Calculate parameter numbers assert(0 <= (int)ecl && (int)ecl < 4 && qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [(int)ecl][version]; int rawCodewords = getNumRawDataModules(version) / 8; int dataLen = getNumDataCodewords(version, ecl); int numShortBlocks = numBlocks - rawCodewords % numBlocks; @@ -233,7 +233,7 @@ testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl) { int v = version, e = (int)ecl; assert(0 <= e && e < 4); return getNumRawDataModules(v) / 8 - - ECC_CODEWORDS_PER_BLOCK[e][v] + - ECC_CODEWORDS_PER_BLOCK [e][v] * NUM_ERROR_CORRECTION_BLOCKS[e][v]; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 8160866..514435b 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -306,7 +306,7 @@ vector QrCode::addEccAndInterleave(const vector &data) const { // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[static_cast(errorCorrectionLevel)][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK[static_cast(errorCorrectionLevel)][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [static_cast(errorCorrectionLevel)][version]; int rawCodewords = getNumRawDataModules(version) / 8; int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockLen = rawCodewords / numBlocks; @@ -524,7 +524,7 @@ int QrCode::getNumRawDataModules(int ver) { int QrCode::getNumDataCodewords(int ver, Ecc ecl) { return getNumRawDataModules(ver) / 8 - - ECC_CODEWORDS_PER_BLOCK[static_cast(ecl)][ver] + - ECC_CODEWORDS_PER_BLOCK [static_cast(ecl)][ver] * NUM_ERROR_CORRECTION_BLOCKS[static_cast(ecl)][ver]; } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 532d163..36e7939 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -442,7 +442,7 @@ public final class QrCode { // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK[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; @@ -693,7 +693,7 @@ public final class QrCode { // This stateless pure function could be implemented as a (40*4)-cell lookup table. static int getNumDataCodewords(int ver, Ecc ecl) { return getNumRawDataModules(ver) / 8 - - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal()][ver] + - ECC_CODEWORDS_PER_BLOCK [ecl.ordinal()][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index bb4096e..ffc6fe0 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -315,7 +315,7 @@ var qrcodegen = new function() { // Calculate parameter numbers var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version]; - var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK[errCorLvl.ordinal][version]; + var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK [errCorLvl.ordinal][version]; var rawCodewords = Math.floor(QrCode.getNumRawDataModules(version) / 8); var numShortBlocks = numBlocks - rawCodewords % numBlocks; var shortBlockLen = Math.floor(rawCodewords / numBlocks); @@ -633,7 +633,7 @@ var qrcodegen = new function() { // This stateless pure function could be implemented as a (40*4)-cell lookup table. QrCode.getNumDataCodewords = function(ver, ecl) { return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - - QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * + QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index fbf5a13..67e45ae 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -356,7 +356,7 @@ class QrCode(object): # Calculate parameter numbers numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] - blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK[self._errcorlvl.ordinal][version] + blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK [self._errcorlvl.ordinal][version] rawcodewords = QrCode._get_num_raw_data_modules(version) // 8 numshortblocks = numblocks - rawcodewords % numblocks shortblocklen = rawcodewords // numblocks @@ -523,7 +523,7 @@ class QrCode(object): 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.""" return QrCode._get_num_raw_data_modules(ver) // 8 \ - - QrCode._ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] \ + - QrCode._ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] \ * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2b78197..f80c22a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -410,7 +410,7 @@ impl QrCode { // Calculate parameter numbers let numblocks: usize = QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, ver, ecl); - let blockecclen: usize = QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, ver, ecl); + let blockecclen: usize = QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK , ver, ecl); let rawcodewords: usize = QrCode::get_num_raw_data_modules(ver) / 8; let numshortblocks: usize = numblocks - rawcodewords % numblocks; let shortblocklen: usize = rawcodewords / numblocks; @@ -659,7 +659,7 @@ impl QrCode { // This stateless pure function could be implemented as a (40*4)-cell lookup table. fn get_num_data_codewords(ver: Version, ecl: QrCodeEcc) -> usize { QrCode::get_num_raw_data_modules(ver) / 8 - - QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK, ver, ecl) + - QrCode::table_get(&ECC_CODEWORDS_PER_BLOCK , ver, ecl) * QrCode::table_get(&NUM_ERROR_CORRECTION_BLOCKS, ver, ecl) } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 5538270..797b493 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -385,7 +385,7 @@ namespace qrcodegen { // Calculate parameter numbers let numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; - let blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver]; + let blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver]; let rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8); let numShortBlocks: int = numBlocks - rawCodewords % numBlocks; let shortBlockLen: int = Math.floor(rawCodewords / numBlocks); @@ -597,7 +597,7 @@ namespace qrcodegen { // This stateless pure function could be implemented as a (40*4)-cell lookup table. private static getNumDataCodewords(ver: int, ecl: QrCode.Ecc): int { return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - - QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * + QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; } From 8db3cfef81994ebd549a68e7f97712470e61fbdd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:41:20 +0000 Subject: [PATCH 438/810] Simplified TypeScript code by using constructor parameter properties to replace most fields. --- typescript/qrcodegen.ts | 95 +++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 57 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 797b493..52d2906 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -131,21 +131,10 @@ namespace qrcodegen { /*-- Fields --*/ - // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). - public readonly version: int; - // The width and height of this QR Code symbol, measured in modules. // Always equal to version * 4 + 17, in the range 21 to 177. public readonly size: int; - // The error correction level used in this QR Code symbol. - public readonly errorCorrectionLevel: QrCode.Ecc; - - // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). - // Note that even if the constructor was called with automatic masking requested - // (mask = -1), the resulting object will still have a mask value between 0 and 7. - public readonly mask: int; - // The modules of this QR Code symbol (false = white, true = black). private readonly modules: Array> = []; @@ -153,15 +142,28 @@ namespace qrcodegen { private readonly isFunction: Array> = []; - public constructor(datacodewords: Array, mask: int, version: int, errCorLvl: QrCode.Ecc) { + /*-- Constructor and fields --*/ + + public constructor( + datacodewords: Array, + + // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // Note that even if the constructor was called with automatic masking requested + // (mask = -1), the resulting object will still have a mask value between 0 and 7. + public readonly mask: int, + + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + public readonly version: int, + + // The error correction level used in this QR Code symbol. + public readonly errorCorrectionLevel: QrCode.Ecc) { + // Check arguments and handle simple scalar fields if (mask < -1 || mask > 7) throw "Mask value out of range"; if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw "Version value out of range"; - this.version = version; this.size = version * 4 + 17; - this.errorCorrectionLevel = errCorLvl; // Initialize both grids to be size*size arrays of Boolean false let row: Array = []; @@ -733,25 +735,20 @@ namespace qrcodegen { } - /*-- Fields --*/ - - // The mode indicator for this segment. - public readonly mode: QrSegment.Mode; - - // The length of this segment's unencoded data, measured in characters. Always zero or positive. - public readonly numChars: int; - - private readonly bitData: Array; - - - /*-- Constructor --*/ + /*-- Constructor and fields --*/ // Creates a new QR Code segment with the given parameters and data. - public constructor(mode: QrSegment.Mode, numChars: int, bitData: Array) { + public constructor( + // The mode indicator for this segment. + public readonly mode: QrSegment.Mode, + + // The length of this segment's unencoded data, measured in characters. Always zero or positive. + public readonly numChars: int, + + private readonly bitData: Array) { + if (numChars < 0) throw "Invalid argument"; - this.mode = mode; - this.numChars = numChars; this.bitData = bitData.slice(); // Make defensive copy } @@ -939,21 +936,13 @@ namespace qrcodegen.QrCode { public static readonly HIGH = new Ecc(3, 2); - /*-- Fields --*/ + /*-- Constructor and fields --*/ - // In the range 0 to 3 (unsigned 2-bit integer). - public readonly ordinal: int; - - // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). - public readonly formatBits: int; - - - /*-- Constructor --*/ - - private constructor(ord: int, fb: int) { - this.ordinal = ord; - this.formatBits = fb; - } + private constructor( + // In the range 0 to 3 (unsigned 2-bit integer). + public readonly ordinal: int, + // (Package-private) In the range 0 to 3 (unsigned 2-bit integer). + public readonly formatBits: int) {} } } @@ -981,21 +970,13 @@ namespace qrcodegen.QrSegment { public static readonly ECI = new Mode(0x7, [ 0, 0, 0]); - /*-- Fields --*/ + /*-- Constructor and fields --*/ - // The mode indicator bits, which is a uint4 value (range 0 to 15). - public readonly modeBits: int; - - // Three values for different version ranges. - private readonly numBitsCharCount: [int,int,int]; - - - /*-- Constructor --*/ - - private constructor(mode: int, ccbits: [int,int,int]) { - this.modeBits = mode; - this.numBitsCharCount = ccbits; - } + private constructor( + // The mode indicator bits, which is a uint4 value (range 0 to 15). + public readonly modeBits: int, + // Three values for different version ranges. + private readonly numBitsCharCount: [int,int,int]) {} /*-- Method --*/ From 3e642cf468b14319c4387c1645fb76b69dd1fe97 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:54:39 +0000 Subject: [PATCH 439/810] Simplified QrSegment.makeNumeric() logic in {Java, JavaScript, TypeScript, Python} language versions. --- java/io/nayuki/qrcodegen/QrSegment.java | 11 +++++------ javascript/qrcodegen.js | 11 +++++------ python/qrcodegen.py | 10 +++++----- typescript/qrcodegen.ts | 11 +++++------ 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 8328570..eb45d56 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -69,12 +69,11 @@ public final class QrSegment { throw new IllegalArgumentException("String contains non-numeric characters"); BitBuffer bb = new BitBuffer(); - int i; - for (i = 0; i <= digits.length() - 3; i += 3) // Process groups of 3 - bb.appendBits(Integer.parseInt(digits.substring(i, i + 3)), 10); - int rem = digits.length() - i; - if (rem > 0) // 1 or 2 digits remaining - bb.appendBits(Integer.parseInt(digits.substring(i)), rem * 3 + 1); + for (int i = 0; i < digits.length(); ) { // Consume up to 3 digits per iteration + int n = Math.min(digits.length() - i, 3); + bb.appendBits(Integer.parseInt(digits.substring(i, i + n)), n * 3 + 1); + i += n; + } return new QrSegment(Mode.NUMERIC, digits.length(), bb); } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index ffc6fe0..0af8399 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -738,12 +738,11 @@ var qrcodegen = new function() { if (!this.NUMERIC_REGEX.test(digits)) throw "String contains non-numeric characters"; var bb = new BitBuffer(); - var i; - for (i = 0; i + 3 <= digits.length; i += 3) // Process groups of 3 - bb.appendBits(parseInt(digits.substr(i, 3), 10), 10); - var rem = digits.length - i; - if (rem > 0) // 1 or 2 digits remaining - bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); + for (var i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration + var n = Math.min(digits.length - i, 3); + bb.appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1); + i += n; + } return new this(this.Mode.NUMERIC, digits.length, bb); }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 67e45ae..512be56 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -611,11 +611,11 @@ class QrSegment(object): if QrSegment.NUMERIC_REGEX.match(digits) is None: raise ValueError("String contains non-numeric characters") bb = _BitBuffer() - for i in range(0, len(digits) - 2, 3): # Process groups of 3 - bb.append_bits(int(digits[i : i + 3]), 10) - rem = len(digits) % 3 - if rem > 0: # 1 or 2 digits remaining - bb.append_bits(int(digits[-rem : ]), rem * 3 + 1) + i = 0 + while i < len(digits): # Consume up to 3 digits per iteration + n = min(len(digits) - i, 3) + bb.append_bits(int(digits[i : i + n]), n * 3 + 1) + i += n return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 52d2906..d197129 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -673,12 +673,11 @@ namespace qrcodegen { if (!this.NUMERIC_REGEX.test(digits)) throw "String contains non-numeric characters"; let bb = new BitBuffer(); - let i: int; - for (i = 0; i + 3 <= digits.length; i += 3) // Process groups of 3 - bb.appendBits(parseInt(digits.substr(i, 3), 10), 10); - let rem: int = digits.length - i; - if (rem > 0) // 1 or 2 digits remaining - bb.appendBits(parseInt(digits.substring(i), 10), rem * 3 + 1); + for (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration + let n: int = Math.min(digits.length - i, 3); + bb.appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1); + i += n; + } return new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb); } From 1027ad5b65e07ee8088427ca42c867ebc6071ef0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:56:33 +0000 Subject: [PATCH 440/810] Reformatted some whitespace for alignment. --- src/io/nayuki/fastqrcodegen/QrCode.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 53550c2..25b57a7 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -285,7 +285,7 @@ public final class QrCode { // Calculate parameter numbers int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[errorCorrectionLevel.ordinal()][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK[errorCorrectionLevel.ordinal()][version]; + int blockEccLen = ECC_CODEWORDS_PER_BLOCK [errorCorrectionLevel.ordinal()][version]; int rawCodewords = QrTemplate.getNumRawDataModules(version) / 8; int numShortBlocks = numBlocks - rawCodewords % numBlocks; int shortBlockDataLen = rawCodewords / numBlocks - blockEccLen; @@ -448,7 +448,7 @@ public final class QrCode { // This stateless pure function could be implemented as a (40*4)-cell lookup table. static int getNumDataCodewords(int ver, Ecc ecl) { return QrTemplate.getNumRawDataModules(ver) / 8 - - ECC_CODEWORDS_PER_BLOCK[ecl.ordinal()][ver] + - ECC_CODEWORDS_PER_BLOCK [ecl.ordinal()][ver] * NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal()][ver]; } @@ -456,8 +456,8 @@ public final class QrCode { /*---- Private tables of constants ----*/ // 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_N1 = 3; + private static final int PENALTY_N2 = 3; private static final int PENALTY_N3 = 40; private static final int PENALTY_N4 = 10; From 957219dc91e13c05a1913d12275f5da64ef5be5e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 17:57:57 +0000 Subject: [PATCH 441/810] Reformatted some whitespace for alignment in all language versions. --- c/qrcodegen.c | 4 ++-- cpp/QrCode.cpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index cfb7eb1..2d1240d 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -117,8 +117,8 @@ testable const int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41] = { }; // For automatic mask pattern selection. -static const int PENALTY_N1 = 3; -static const int PENALTY_N2 = 3; +static const int PENALTY_N1 = 3; +static const int PENALTY_N2 = 3; static const int PENALTY_N3 = 40; static const int PENALTY_N4 = 10; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 514435b..9c4db53 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -536,8 +536,8 @@ bool QrCode::getBit(long x, int i) { /*---- Tables of constants ----*/ -const int QrCode::PENALTY_N1 = 3; -const int QrCode::PENALTY_N2 = 3; +const int QrCode::PENALTY_N1 = 3; +const int QrCode::PENALTY_N2 = 3; const int QrCode::PENALTY_N3 = 40; const int QrCode::PENALTY_N4 = 10; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 36e7939..db4d83e 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -707,8 +707,8 @@ public final class QrCode { /*---- Private tables of constants ----*/ // 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_N1 = 3; + private static final int PENALTY_N2 = 3; private static final int PENALTY_N3 = 40; private static final int PENALTY_N4 = 10; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0af8399..1492733 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -641,8 +641,8 @@ var qrcodegen = new function() { /*---- Private tables of constants for QrCode ----*/ // For use in getPenaltyScore(), when evaluating which mask is best. - QrCode.PENALTY_N1 = 3; - QrCode.PENALTY_N2 = 3; + QrCode.PENALTY_N1 = 3; + QrCode.PENALTY_N2 = 3; QrCode.PENALTY_N3 = 40; QrCode.PENALTY_N4 = 10; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 512be56..e1326b8 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -530,8 +530,8 @@ class QrCode(object): # ---- Private tables of constants ---- # For use in getPenaltyScore(), when evaluating which mask is best. - _PENALTY_N1 = 3 - _PENALTY_N2 = 3 + _PENALTY_N1 = 3 + _PENALTY_N2 = 3 _PENALTY_N3 = 40 _PENALTY_N4 = 10 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f80c22a..a37a533 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -681,8 +681,8 @@ pub const QrCode_MAX_VERSION: Version = Version(40); /*---- Private tables of constants ----*/ // For use in get_penalty_score(), when evaluating which mask is best. -const PENALTY_N1: i32 = 3; -const PENALTY_N2: i32 = 3; +const PENALTY_N1: i32 = 3; +const PENALTY_N2: i32 = 3; const PENALTY_N3: i32 = 40; const PENALTY_N4: i32 = 10; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d197129..fa4f9eb 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -611,8 +611,8 @@ namespace qrcodegen { // For use in getPenaltyScore(), when evaluating which mask is best. - private static readonly PENALTY_N1: int = 3; - private static readonly PENALTY_N2: int = 3; + private static readonly PENALTY_N1: int = 3; + private static readonly PENALTY_N2: int = 3; private static readonly PENALTY_N3: int = 40; private static readonly PENALTY_N4: int = 10; From f1ec2a8f43978c98e15971f3f21dccafbb8e1586 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 31 Aug 2018 19:50:40 +0000 Subject: [PATCH 442/810] Tweaked QrCode.getPenaltyScore() to use binary literals for clarity, for Java (SE 7+) and TypeScript (ES6+). --- java/io/nayuki/qrcodegen/QrCode.java | 8 ++++---- typescript/qrcodegen.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index db4d83e..47b7195 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -608,16 +608,16 @@ public final class QrCode { // Finder-like pattern in rows for (int y = 0; y < size; y++) { for (int x = 0, bits = 0; x < size; x++) { - bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated result += PENALTY_N3; } } // Finder-like pattern in columns for (int x = 0; x < size; x++) { for (int y = 0, bits = 0; y < size; y++) { - bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated result += PENALTY_N3; } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index fa4f9eb..420407d 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -527,16 +527,16 @@ namespace qrcodegen { // Finder-like pattern in rows for (let y = 0; y < this.size; y++) { for (let x = 0, bits = 0; x < this.size; x++) { - bits = ((bits << 1) & 0x7FF) | (this.modules[y][x] ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0); + if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated result += QrCode.PENALTY_N3; } } // Finder-like pattern in columns for (let x = 0; x < this.size; x++) { for (let y = 0, bits = 0; y < this.size; y++) { - bits = ((bits << 1) & 0x7FF) | (this.modules[y][x] ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated + bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0); + if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated result += QrCode.PENALTY_N3; } } From e70b3b347fb242aff1124a8a0ffe189ebe4c5098 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 3 Sep 2018 04:38:36 +0000 Subject: [PATCH 443/810] Fixed incorrect constant in QrSegmentAdvanced.computeCharacterModes() (which caused kanji mode to be used less often than it should be). --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 849771c..549e446 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -134,7 +134,7 @@ public final class QrSegmentAdvanced { charModes[i][2] = modeTypes[2]; } if (isKanji(c)) { - curCosts[3] = prevCosts[3] + 104; // 13 bits per Shift JIS char + curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char charModes[i][3] = modeTypes[3]; } From f4d325725a447adaddfa2e39099e1778abf77e09 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 3 Sep 2018 05:57:10 +0000 Subject: [PATCH 444/810] Updated readme because C supports ECI segments (due to commits d126f7b1ed39 and 66f650022018), and Java supports kanji optimization (due to commit 8800acf003cd). --- Readme.markdown | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 795dbbe..c8f6ca0 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -27,12 +27,12 @@ Manual parameters: * User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data * User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one * User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number -* User can create a list of data segments manually and add ECI segments (all languages except C) +* User can create a list of data segments manually and add ECI segments Optional advanced features (Java only): * Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes -* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general parts +* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts More information about QR Code technology and this library's design can be found on the project home page. From a443f7bf6b541035b3711db6d389dac392a86eb5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 4 Sep 2018 04:45:08 +0000 Subject: [PATCH 445/810] Deleted HTML file for TypeScript demo because it has the same code as the JavaScript version. --- typescript/qrcodegen-js-demo.html | 121 ------------------------------ 1 file changed, 121 deletions(-) delete mode 100644 typescript/qrcodegen-js-demo.html diff --git a/typescript/qrcodegen-js-demo.html b/typescript/qrcodegen-js-demo.html deleted file mode 100644 index e18b094..0000000 --- a/typescript/qrcodegen-js-demo.html +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - QR Code generator library demo (TypeScript) - - - - -

QR Code generator demo library (TypeScript)

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Text string:
QR Code: - - - - - -
Error correction: - - - - -
Output format: - - -
Border: modules
Scale: pixels per module
Version range:Minimum = , maximum =
Mask pattern: (−1 for automatic, 0 to 7 for manual)
Boost ECC:
Statistics:
SVG XML code: - -
-
- - - -
-

Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

- - From 72deb2855ab5fce52f2ac49bcc00b9fee3dc6600 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 4 Sep 2018 04:48:39 +0000 Subject: [PATCH 446/810] Tweaked syntax for top-level demo functions in TypeScript code. --- typescript/qrcodegen-demo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index 7a376d2..ee8d987 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -30,7 +30,7 @@ /*---- Functions called from HTML code ----*/ - myWindow.redrawQrCode = (): void => { + myWindow.redrawQrCode = function(): void { // Show/hide rows based on bitmap/vector image output const bitmapOutput: boolean = getInput("output-format-bitmap").checked; const scaleRow : HTMLElement = getElem("scale-row"); @@ -137,7 +137,7 @@ } - myWindow.handleVersionMinMax = (which: "min"|"max"): void => { + myWindow.handleVersionMinMax = function(which: "min"|"max"): void { const minElem: HTMLInputElement = getInput("version-min-input"); const maxElem: HTMLInputElement = getInput("version-max-input"); let minVal: number = parseInt(minElem.value, 10); From 67bb2b426715c4984227e9d6e30501662a71f20b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 02:41:39 +0000 Subject: [PATCH 447/810] Removed C macros for MALLOC() to not support compiling code verbatim in C++ mode (undoing commit eb536b43c538). --- c/qrcodegen-test.c | 22 ++++++++-------------- c/qrcodegen-worker.c | 14 ++++---------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 192f7dd..fb91b75 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -37,12 +37,6 @@ #define ARRAY_LENGTH(name) (sizeof(name) / sizeof(name[0])) -#ifndef __cplusplus - #define MALLOC(num, type) malloc((num) * sizeof(type)) -#else - #define MALLOC(num, type) static_cast(malloc((num) * sizeof(type))) -#endif - // Global variables static int numTestCases = 0; @@ -120,11 +114,11 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e int shortBlockLen = rawCodewords / numBlocks; // Split data into blocks and append ECC to each block - uint8_t **blocks = MALLOC(numBlocks, uint8_t*); - uint8_t *generator = MALLOC(blockEccLen, uint8_t); + uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*)); + uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t)); calcReedSolomonGenerator(blockEccLen, generator); for (int i = 0, k = 0; i < numBlocks; i++) { - uint8_t *block = MALLOC(shortBlockLen + 1, uint8_t); + uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t)); int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); memcpy(block, &data[k], datLen * sizeof(uint8_t)); calcReedSolomonRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); @@ -134,7 +128,7 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e free(generator); // Interleave (not concatenate) the bytes from every block into a single sequence - uint8_t *result = MALLOC(rawCodewords, uint8_t); + uint8_t *result = malloc(rawCodewords * sizeof(uint8_t)); for (int i = 0, k = 0; i < shortBlockLen + 1; i++) { for (int j = 0; j < numBlocks; j++) { // Skip the padding byte in short blocks @@ -155,15 +149,15 @@ static void testAddEccAndInterleave(void) { for (int version = 1; version <= 40; version++) { for (int ecl = 0; ecl < 4; ecl++) { int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); - uint8_t *pureData = MALLOC(dataLen, uint8_t); + uint8_t *pureData = malloc(dataLen * sizeof(uint8_t)); for (int i = 0; i < dataLen; i++) pureData[i] = rand() % 256; uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl); int dataAndEccLen = getNumRawDataModules(version) / 8; - uint8_t *paddedData = MALLOC(dataAndEccLen, uint8_t); + uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t)); memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); - uint8_t *actualOutput = MALLOC(dataAndEccLen, uint8_t); + uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t)); addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); @@ -368,7 +362,7 @@ static void testFiniteFieldMultiply(void) { static void testInitializeFunctionModulesEtc(void) { for (int ver = 1; ver <= 40; ver++) { - uint8_t *qrcode = MALLOC(qrcodegen_BUFFER_LEN_FOR_VERSION(ver), uint8_t); + uint8_t *qrcode = malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(ver) * sizeof(uint8_t)); assert(qrcode != NULL); initializeFunctionModules(ver, qrcode); diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c index 9fa6433..2fb0930 100644 --- a/c/qrcodegen-worker.c +++ b/c/qrcodegen-worker.c @@ -34,12 +34,6 @@ #include #include "qrcodegen.h" -#ifndef __cplusplus - #define MALLOC(num, type) malloc((num) * sizeof(type)) -#else - #define MALLOC(num, type) static_cast(malloc((num) * sizeof(type))) -#endif - int main(void) { while (true) { @@ -53,7 +47,7 @@ int main(void) { // Read data bytes bool isAscii = true; - uint8_t *data = MALLOC(length, uint8_t); + uint8_t *data = malloc(length * sizeof(uint8_t)); if (data == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -73,8 +67,8 @@ int main(void) { // Allocate memory for QR Code int bufferLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - uint8_t *qrcode = MALLOC(bufferLen, uint8_t); - uint8_t *tempBuffer = MALLOC(bufferLen, uint8_t); + uint8_t *qrcode = malloc(bufferLen * sizeof(uint8_t)); + uint8_t *tempBuffer = malloc(bufferLen * sizeof(uint8_t)); if (qrcode == NULL || tempBuffer == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -83,7 +77,7 @@ int main(void) { // Try to make QR Code symbol bool ok; if (isAscii) { - char *text = MALLOC(length + 1, char); + char *text = malloc((length + 1) * sizeof(char)); for (int i = 0; i < length; i++) text[i] = (char)data[i]; text[length] = '\0'; From 070daf8c9ec66d3a201f50a692257a0c39fbe272 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:00:01 +0000 Subject: [PATCH 448/810] Tweaked C header file to be friendly to C++. --- c/qrcodegen.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 25ddb06..852b955 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -28,6 +28,11 @@ #include +#ifdef __cplusplus +extern "C" { +#endif + + /*---- Enum and struct types----*/ /* @@ -264,3 +269,8 @@ int qrcodegen_getSize(const uint8_t qrcode[]); * If the given coordinates are out of bounds, then false (white) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); + + +#ifdef __cplusplus +} +#endif From be143456be73140d4c9979f4f5e12f409acd71e4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:26:41 +0000 Subject: [PATCH 449/810] Revamped C and C++ makefiles by copying structure from another library of mine, removed shared library output from C library (from commit a3dcc36dd477) due to problematic linkage in some enviroments. --- c/Makefile | 36 +++++++++++++++++++++++++----------- cpp/Makefile | 29 ++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 16 deletions(-) diff --git a/c/Makefile b/c/Makefile index a33f8d2..99f926b 100644 --- a/c/Makefile +++ b/c/Makefile @@ -51,8 +51,9 @@ CFLAGS += -O1 # ---- Targets to build ---- -LIBSRC = qrcodegen -LIBFILE = libqrcodegen.so +LIB = qrcodegen +LIBFILE = lib$(LIB).a +LIBOBJ = qrcodegen.o MAINS = qrcodegen-demo qrcodegen-test qrcodegen-worker # Build all binaries @@ -60,16 +61,29 @@ all: $(LIBFILE) $(MAINS) # Delete build output clean: - rm -f -- $(LIBFILE) $(MAINS) - -# Shared library -$(LIBFILE): $(LIBSRC:=.c) $(LIBSRC:=.h) - $(CC) $(CFLAGS) -fPIC -shared -o $@ $(LIBSRC:=.c) + rm -f -- $(LIBOBJ) $(LIBFILE) $(MAINS:=.o) $(MAINS) + rm -rf .deps # Executable files -%: %.c $(LIBFILE) - $(CC) $(CFLAGS) -o $@ $^ +%: %.o $(LIBFILE) + $(CC) $(CFLAGS) -o $@ $< -L . -l $(LIB) # Special executable -qrcodegen-test: qrcodegen-test.c $(LIBSRC:=.c) $(LIBSRC:=.h) - $(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $< $(LIBSRC:=.c) +qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c) + $(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $^ + +# The library +$(LIBFILE): $(LIBOBJ) + $(AR) -crs $@ -- $^ + +# Object files +%.o: %.c .deps/timestamp + $(CC) $(CFLAGS) -c -o $@ -MMD -MF .deps/$*.d $< + +# Have a place to store header dependencies automatically generated by compiler +.deps/timestamp: + mkdir -p .deps + touch .deps/timestamp + +# Make use of said dependencies if available +-include .deps/*.d diff --git a/cpp/Makefile b/cpp/Makefile index 151a4da..858a1bf 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -51,16 +51,35 @@ CXXFLAGS += -O1 # ---- Targets to build ---- -LIBSRC = BitBuffer QrCode QrSegment +LIB = qrcodegen +LIBFILE = lib$(LIB).a +LIBOBJ = BitBuffer.o QrCode.o QrSegment.o MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker # Build all binaries -all: $(MAINS) +all: $(LIBFILE) $(MAINS) # Delete build output clean: - rm -f -- $(MAINS) + rm -f -- $(LIBOBJ) $(LIBFILE) $(MAINS:=.o) $(MAINS) + rm -rf .deps # Executable files -%: %.cpp $(LIBSRC:=.cpp) $(LIBSRC:=.hpp) - $(CXX) $(CXXFLAGS) -o $@ $< $(LIBSRC:=.cpp) +%: %.o $(LIBFILE) + $(CXX) $(CXXFLAGS) -o $@ $< -L . -l $(LIB) + +# The library +$(LIBFILE): $(LIBOBJ) + $(AR) -crs $@ -- $^ + +# Object files +%.o: %.cpp .deps/timestamp + $(CXX) $(CXXFLAGS) -c -o $@ -MMD -MF .deps/$*.d $< + +# Have a place to store header dependencies automatically generated by compiler +.deps/timestamp: + mkdir -p .deps + touch .deps/timestamp + +# Make use of said dependencies if available +-include .deps/*.d From 30aed54c47cbe9eda1105857042422128d26ddcf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:31:00 +0000 Subject: [PATCH 450/810] Simplified C code to directly assign useful values to the segment mode enum. --- c/qrcodegen.c | 11 +---------- c/qrcodegen.h | 10 +++++----- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 2d1240d..59ba12a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -912,16 +912,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz int bitLen = 0; for (size_t i = 0; i < len; i++) { const struct qrcodegen_Segment *seg = &segs[i]; - unsigned int modeBits; - switch (seg->mode) { - case qrcodegen_Mode_NUMERIC : modeBits = 0x1; break; - case qrcodegen_Mode_ALPHANUMERIC: modeBits = 0x2; break; - case qrcodegen_Mode_BYTE : modeBits = 0x4; break; - case qrcodegen_Mode_KANJI : modeBits = 0x8; break; - case qrcodegen_Mode_ECI : modeBits = 0x7; break; - default: assert(false); return false; - } - appendBitsToBuffer(modeBits, 4, qrcode, &bitLen); + appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); for (int j = 0; j < seg->bitLength; j++) appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 852b955..4479f1f 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -69,11 +69,11 @@ enum qrcodegen_Mask { * The mode field of a segment. */ enum qrcodegen_Mode { - qrcodegen_Mode_NUMERIC, - qrcodegen_Mode_ALPHANUMERIC, - qrcodegen_Mode_BYTE, - qrcodegen_Mode_KANJI, - qrcodegen_Mode_ECI, + qrcodegen_Mode_NUMERIC = 0x1, + qrcodegen_Mode_ALPHANUMERIC = 0x2, + qrcodegen_Mode_BYTE = 0x4, + qrcodegen_Mode_KANJI = 0x8, + qrcodegen_Mode_ECI = 0x7, }; From 0920922a32aa6023234da4233e684133a4fc0121 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:39:40 +0000 Subject: [PATCH 451/810] Tweaked qrcodegen.c to not be compilable in C++ mode (undoing commit 88cc1c05fd7e). --- c/qrcodegen.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 59ba12a..32c631e 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -30,12 +30,7 @@ #ifndef QRCODEGEN_TEST #define testable static // Keep functions private #else - // Expose private functions - #ifndef __cplusplus - #define testable - #else // Because in C++, const variables are treated as implicitly 'static' - #define testable extern - #endif + #define testable // Expose private functions #endif From 440efef8bf6719281ae096b190534a597221e3ea Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:40:08 +0000 Subject: [PATCH 452/810] Tweaked C code to avoid unnecessary array initialization. --- c/qrcodegen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 32c631e..289b581 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -329,7 +329,7 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) { fillRectangle(0, qrsize - 8, 9, 8, qrcode); // Fill numerous alignment patterns - uint8_t alignPatPos[7] = {0}; + uint8_t alignPatPos[7]; int numAlign = getAlignmentPatternPositions(version, alignPatPos); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { @@ -373,7 +373,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { } // Draw numerous alignment patterns - uint8_t alignPatPos[7] = {0}; + uint8_t alignPatPos[7]; int numAlign = getAlignmentPatternPositions(version, alignPatPos); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { From 87868d792053f6335c9f71fb6f6b364e80bf9e88 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:47:58 +0000 Subject: [PATCH 453/810] Renamed loop variables in QrCode.{drawFinderPattern(),drawAlignmentPattern()} for clarity, in all languages (but somewhat differently in C). --- c/qrcodegen.c | 22 +++++++++++----------- cpp/QrCode.cpp | 14 +++++++------- java/io/nayuki/qrcodegen/QrCode.java | 14 +++++++------- javascript/qrcodegen.js | 14 +++++++------- python/qrcodegen.py | 14 +++++++------- rust/src/lib.rs | 16 ++++++++-------- typescript/qrcodegen.ts | 16 ++++++++-------- 7 files changed, 55 insertions(+), 55 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 289b581..7ef34d0 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -359,15 +359,15 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { } // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - int dist = abs(i); - if (abs(j) > dist) - dist = abs(j); + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = abs(dy); + if (abs(dx) > dist) + dist = abs(dx); if (dist == 2 || dist == 4) { - setModuleBounded(qrcode, 3 + j, 3 + i, false); - setModuleBounded(qrcode, qrsize - 4 + j, 3 + i, false); - setModuleBounded(qrcode, 3 + j, qrsize - 4 + i, false); + setModuleBounded(qrcode, 3 + dx, 3 + dy, false); + setModuleBounded(qrcode, qrsize - 4 + dx, 3 + dy, false); + setModuleBounded(qrcode, 3 + dx, qrsize - 4 + dy, false); } } } @@ -379,9 +379,9 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { for (int j = 0; j < numAlign; j++) { if ((i == 0 && j == 0) || (i == 0 && j == numAlign - 1) || (i == numAlign - 1 && j == 0)) continue; // Don't draw on the three finder corners - for (int k = -1; k <= 1; k++) { - for (int l = -1; l <= 1; l++) - setModule(qrcode, alignPatPos[i] + l, alignPatPos[j] + k, k == 0 && l == 0); + for (int dy = -1; dy <= 1; dy++) { + for (int dx = -1; dx <= 1; dx++) + setModule(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dy == 0 && dx == 0); } } } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 9c4db53..77faed7 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -270,10 +270,10 @@ void QrCode::drawVersion() { void QrCode::drawFinderPattern(int x, int y) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - int dist = std::max(std::abs(i), std::abs(j)); // Chebyshev/infinity norm - int xx = x + j, yy = y + i; + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = std::max(std::abs(dy), std::abs(dx)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -282,9 +282,9 @@ void QrCode::drawFinderPattern(int x, int y) { void QrCode::drawAlignmentPattern(int x, int y) { - for (int i = -2; i <= 2; i++) { - for (int j = -2; j <= 2; j++) - setFunctionModule(x + j, y + i, std::max(std::abs(i), std::abs(j)) != 1); + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, std::max(std::abs(dy), std::abs(dx)) != 1); } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 47b7195..ffc8a79 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -402,10 +402,10 @@ public final class QrCode { // 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) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - int xx = x + j, yy = y + i; + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -416,9 +416,9 @@ 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) { - for (int i = -2; i <= 2; i++) { - for (int j = -2; j <= 2; j++) - setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 1492733..63d489d 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -276,10 +276,10 @@ var qrcodegen = new function() { // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. function drawFinderPattern(x, y) { - for (var i = -4; i <= 4; i++) { - for (var j = -4; j <= 4; j++) { - var dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - var xx = x + j, yy = y + i; + for (var dy = -4; dy <= 4; dy++) { + for (var dx = -4; dx <= 4; dx++) { + var dist = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + var xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -290,9 +290,9 @@ var qrcodegen = new function() { // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. function drawAlignmentPattern(x, y) { - for (var i = -2; i <= 2; i++) { - for (var j = -2; j <= 2; j++) - setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + for (var dy = -2; dy <= 2; dy++) { + for (var dx = -2; dx <= 2; dx++) + setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index e1326b8..15d9f7e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -322,20 +322,20 @@ class QrCode(object): def _draw_finder_pattern(self, x, y): """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). Modules can be out of bounds.""" - for i in range(-4, 5): - for j in range(-4, 5): - xx, yy = x + j, y + i + for dy in range(-4, 5): + for dx in range(-4, 5): + xx, yy = x + dx, y + dy if (0 <= xx < self._size) and (0 <= yy < self._size): # Chebyshev/infinity norm - self._set_function_module(xx, yy, max(abs(i), abs(j)) not in (2, 4)) + self._set_function_module(xx, yy, max(abs(dy), abs(dx)) not in (2, 4)) def _draw_alignment_pattern(self, x, y): """Draws a 5*5 alignment pattern, with the center module at (x, y). All modules must be in bounds.""" - for i in range(-2, 3): - for j in range(-2, 3): - self._set_function_module(x + j, y + i, max(abs(i), abs(j)) != 1) + for dy in range(-2, 3): + for dx in range(-2, 3): + self._set_function_module(x + dx, y + dy, max(abs(dy), abs(dx)) != 1) def _set_function_module(self, x, y, isblack): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a37a533..3bbf56a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -367,12 +367,12 @@ impl QrCode { // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. fn draw_finder_pattern(&mut self, x: i32, y: i32) { - for i in -4 .. 5 { - for j in -4 .. 5 { - let xx: i32 = x + j; - let yy: i32 = y + i; + for dy in -4 .. 5 { + for dx in -4 .. 5 { + let xx: i32 = x + dx; + let yy: i32 = y + dy; if 0 <= xx && xx < self.size && 0 <= yy && yy < self.size { - let dist: i32 = std::cmp::max(i.abs(), j.abs()); // Chebyshev/infinity norm + let dist: i32 = std::cmp::max(dy.abs(), dx.abs()); // Chebyshev/infinity norm self.set_function_module(xx, yy, dist != 2 && dist != 4); } } @@ -383,9 +383,9 @@ impl QrCode { // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. fn draw_alignment_pattern(&mut self, x: i32, y: i32) { - for i in -2 .. 3 { - for j in -2 .. 3 { - self.set_function_module(x + j, y + i, std::cmp::max(i.abs(), j.abs()) != 1); + for dy in -2 .. 3 { + for dx in -2 .. 3 { + self.set_function_module(x + dx, y + dy, std::cmp::max(dy.abs(), dx.abs()) != 1); } } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 420407d..7d20e7d 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -345,11 +345,11 @@ namespace qrcodegen { // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. private drawFinderPattern(x: int, y: int): void { - for (let i = -4; i <= 4; i++) { - for (let j = -4; j <= 4; j++) { - let dist: int = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - let xx: int = x + j; - let yy: int = y + i; + for (let dy = -4; dy <= 4; dy++) { + for (let dx = -4; dx <= 4; dx++) { + let dist: int = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + let xx: int = x + dx; + let yy: int = y + dy; if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size) this.setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -360,9 +360,9 @@ namespace qrcodegen { // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. private drawAlignmentPattern(x: int, y: int): void { - for (let i = -2; i <= 2; i++) { - for (let j = -2; j <= 2; j++) - this.setFunctionModule(x + j, y + i, Math.max(Math.abs(i), Math.abs(j)) != 1); + for (let dy = -2; dy <= 2; dy++) { + for (let dx = -2; dx <= 2; dx++) + this.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); } } From 6903d28c904d3b6768a91cbfbb4659ec794a99ca Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:51:57 +0000 Subject: [PATCH 454/810] Swapped {dy,dx} variables in commutative operations for clarity, in all languages. --- c/qrcodegen.c | 8 ++++---- cpp/QrCode.cpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 7ef34d0..c2823cd 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -361,9 +361,9 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) for (int dy = -4; dy <= 4; dy++) { for (int dx = -4; dx <= 4; dx++) { - int dist = abs(dy); - if (abs(dx) > dist) - dist = abs(dx); + int dist = abs(dx); + if (abs(dy) > dist) + dist = abs(dy); if (dist == 2 || dist == 4) { setModuleBounded(qrcode, 3 + dx, 3 + dy, false); setModuleBounded(qrcode, qrsize - 4 + dx, 3 + dy, false); @@ -381,7 +381,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { continue; // Don't draw on the three finder corners for (int dy = -1; dy <= 1; dy++) { for (int dx = -1; dx <= 1; dx++) - setModule(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dy == 0 && dx == 0); + setModule(qrcode, alignPatPos[i] + dx, alignPatPos[j] + dy, dx == 0 && dy == 0); } } } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 77faed7..3a9b25a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -272,7 +272,7 @@ void QrCode::drawVersion() { void QrCode::drawFinderPattern(int x, int y) { for (int dy = -4; dy <= 4; dy++) { for (int dx = -4; dx <= 4; dx++) { - int dist = std::max(std::abs(dy), std::abs(dx)); // Chebyshev/infinity norm + int dist = std::max(std::abs(dx), std::abs(dy)); // Chebyshev/infinity norm int xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); @@ -284,7 +284,7 @@ void QrCode::drawFinderPattern(int x, int y) { void QrCode::drawAlignmentPattern(int x, int y) { for (int dy = -2; dy <= 2; dy++) { for (int dx = -2; dx <= 2; dx++) - setFunctionModule(x + dx, y + dy, std::max(std::abs(dy), std::abs(dx)) != 1); + setFunctionModule(x + dx, y + dy, std::max(std::abs(dx), std::abs(dy)) != 1); } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index ffc8a79..6e0669c 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -404,7 +404,7 @@ public final class QrCode { private void drawFinderPattern(int x, int y) { for (int dy = -4; dy <= 4; dy++) { for (int dx = -4; dx <= 4; dx++) { - int dist = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + int dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm int xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); @@ -418,7 +418,7 @@ public final class QrCode { private void drawAlignmentPattern(int x, int y) { for (int dy = -2; dy <= 2; dy++) { for (int dx = -2; dx <= 2; dx++) - setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); + setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1); } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 63d489d..0fd17ad 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -278,7 +278,7 @@ var qrcodegen = new function() { function drawFinderPattern(x, y) { for (var dy = -4; dy <= 4; dy++) { for (var dx = -4; dx <= 4; dx++) { - var dist = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + var dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm var xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) setFunctionModule(xx, yy, dist != 2 && dist != 4); @@ -292,7 +292,7 @@ var qrcodegen = new function() { function drawAlignmentPattern(x, y) { for (var dy = -2; dy <= 2; dy++) { for (var dx = -2; dx <= 2; dx++) - setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); + setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1); } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 15d9f7e..4327f65 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -327,7 +327,7 @@ class QrCode(object): xx, yy = x + dx, y + dy if (0 <= xx < self._size) and (0 <= yy < self._size): # Chebyshev/infinity norm - self._set_function_module(xx, yy, max(abs(dy), abs(dx)) not in (2, 4)) + self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4)) def _draw_alignment_pattern(self, x, y): @@ -335,7 +335,7 @@ class QrCode(object): at (x, y). All modules must be in bounds.""" for dy in range(-2, 3): for dx in range(-2, 3): - self._set_function_module(x + dx, y + dy, max(abs(dy), abs(dx)) != 1) + self._set_function_module(x + dx, y + dy, max(abs(dx), abs(dy)) != 1) def _set_function_module(self, x, y, isblack): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3bbf56a..7e345e7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -372,7 +372,7 @@ impl QrCode { let xx: i32 = x + dx; let yy: i32 = y + dy; if 0 <= xx && xx < self.size && 0 <= yy && yy < self.size { - let dist: i32 = std::cmp::max(dy.abs(), dx.abs()); // Chebyshev/infinity norm + let dist: i32 = std::cmp::max(dx.abs(), dy.abs()); // Chebyshev/infinity norm self.set_function_module(xx, yy, dist != 2 && dist != 4); } } @@ -385,7 +385,7 @@ impl QrCode { fn draw_alignment_pattern(&mut self, x: i32, y: i32) { for dy in -2 .. 3 { for dx in -2 .. 3 { - self.set_function_module(x + dx, y + dy, std::cmp::max(dy.abs(), dx.abs()) != 1); + self.set_function_module(x + dx, y + dy, std::cmp::max(dx.abs(), dy.abs()) != 1); } } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 7d20e7d..b975987 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -347,7 +347,7 @@ namespace qrcodegen { private drawFinderPattern(x: int, y: int): void { for (let dy = -4; dy <= 4; dy++) { for (let dx = -4; dx <= 4; dx++) { - let dist: int = Math.max(Math.abs(dy), Math.abs(dx)); // Chebyshev/infinity norm + let dist: int = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm let xx: int = x + dx; let yy: int = y + dy; if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size) @@ -362,7 +362,7 @@ namespace qrcodegen { private drawAlignmentPattern(x: int, y: int): void { for (let dy = -2; dy <= 2; dy++) { for (let dx = -2; dx <= 2; dx++) - this.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dy), Math.abs(dx)) != 1); + this.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) != 1); } } From bc6f776429826bec200b4d7c36394107096d202e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 03:59:20 +0000 Subject: [PATCH 455/810] Updated JavaScript code for type purity to ensure the modules array is actually Boolean (instead of numeric) after masking, tweaked TypeScript code to correspond to new JS code. --- javascript/qrcodegen.js | 3 ++- typescript/qrcodegen.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 0fd17ad..13d7abf 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -398,7 +398,8 @@ var qrcodegen = new function() { case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; default: throw "Assertion error"; } - modules[y][x] ^= invert & !isFunction[y][x]; + if (!isFunction[y][x] && invert) + modules[y][x] = !modules[y][x]; } } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index b975987..3ab5c8b 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -470,7 +470,7 @@ namespace qrcodegen { case 7: invert = ((x + y) % 2 + x * y % 3) % 2 == 0; break; default: throw "Assertion error"; } - if (invert && !this.isFunction[y][x]) + if (!this.isFunction[y][x] && invert) this.modules[y][x] = !this.modules[y][x]; } } From a19c7aa21b977c2d0da8847081135e40b7106273 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 17 Sep 2018 04:15:29 +0000 Subject: [PATCH 456/810] Reformatted a bit of whitespace in C code, to match other languages (commit 5a5626edb27e). --- c/qrcodegen.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 4479f1f..05b8b8f 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -105,7 +105,7 @@ struct qrcodegen_Segment { /*---- Macro constants and functions ----*/ // The minimum and maximum defined QR Code version numbers for Model 2. -#define qrcodegen_VERSION_MIN 1 +#define qrcodegen_VERSION_MIN 1 #define qrcodegen_VERSION_MAX 40 // Calculates the number of bytes needed to store any QR Code up to and including the given version number, From aa264f5a4dcb66bf5cc458e423fa52ac504ceea7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 18 Sep 2018 08:58:30 +0000 Subject: [PATCH 457/810] Simplified the arithmetic of an internal C function. --- c/qrcodegen.c | 54 ++++++++++++++++----------------------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index c2823cd..998e13a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -714,48 +714,28 @@ size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars // - For ECI mode, numChars must be 0, and the worst-case number of bits is returned. // An actual ECI segment can have shorter data. For non-ECI modes, the result is exact. testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { - const int LIMIT = INT16_MAX; // Can be configured as high as INT_MAX - if (numChars > (unsigned int)LIMIT) + // All calculations are designed to avoid overflow on all platforms + if (numChars > (unsigned int)INT16_MAX) return -1; - int n = (int)numChars; - - int result; - if (mode == qrcodegen_Mode_NUMERIC) { - // n * 3 + ceil(n / 3) - if (n > LIMIT / 3) - goto overflow; - result = n * 3; - int temp = n / 3 + (n % 3 > 0 ? 1 : 0); - if (temp > LIMIT - result) - goto overflow; - result += temp; - } else if (mode == qrcodegen_Mode_ALPHANUMERIC) { - // n * 5 + ceil(n / 2) - if (n > LIMIT / 5) - goto overflow; - result = n * 5; - int temp = n / 2 + n % 2; - if (temp > LIMIT - result) - goto overflow; - result += temp; - } else if (mode == qrcodegen_Mode_BYTE) { - if (n > LIMIT / 8) - goto overflow; - result = n * 8; - } else if (mode == qrcodegen_Mode_KANJI) { - if (n > LIMIT / 13) - goto overflow; - result = n * 13; - } else if (mode == qrcodegen_Mode_ECI && numChars == 0) + long result = (long)numChars; + if (mode == qrcodegen_Mode_NUMERIC) + result = (result * 10 + 2) / 3; // ceil(10/3 * n) + else if (mode == qrcodegen_Mode_ALPHANUMERIC) + result = (result * 11 + 1) / 2; // ceil(11/2 * n) + else if (mode == qrcodegen_Mode_BYTE) + result *= 8; + else if (mode == qrcodegen_Mode_KANJI) + result *= 13; + else if (mode == qrcodegen_Mode_ECI && numChars == 0) result = 3 * 8; - else { + else { // Invalid argument assert(false); return -1; } - assert(0 <= result && result <= LIMIT); - return result; -overflow: - return -1; + assert(result >= 0); + if (result > (unsigned int)INT16_MAX) + return -1; + return (int)result; } From 4eee2d43e68d0a951b51ba4124653c4be9d79d0d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 1 Oct 2018 23:59:19 +0000 Subject: [PATCH 458/810] Tweaked radio/checkbox spacing in HTML page. --- javascript/qrcodegen-js-demo.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index 0b1ba15..224971e 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -36,10 +36,15 @@ } td:first-child { white-space: pre; + padding-right: 0.5em; + } + input[type=radio], input[type=checkbox] { + margin: 0em; + padding: 0em; } input[type=radio] + label, input[type=checkbox] + label { - margin-left: 0.1em; - margin-right: 0.7em; + margin-right: 0.8em; + padding-left: 0.2em; } From b32c467031b2be3b148f98d6d72f5bc8ee584cbf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 00:06:15 +0000 Subject: [PATCH 459/810] Tweaked JavaScript code to implement inheritance more correctly. --- javascript/qrcodegen.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 13d7abf..6ca8618 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -954,6 +954,7 @@ var qrcodegen = new function() { * This constructor creates an empty bit buffer (length 0). */ function BitBuffer() { + Array.call(this); // Packs this buffer's bits into bytes in big endian, // padding with '0' bit values, and returns the new array. @@ -978,5 +979,6 @@ var qrcodegen = new function() { } BitBuffer.prototype = Object.create(Array.prototype); + BitBuffer.prototype.constructor = BitBuffer; }; From 34408d66aa3401a65e42ece72edc4518e1ce763a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 00:21:19 +0000 Subject: [PATCH 460/810] Tweaked the BitBuffer class top-level comment in all languages except C. --- cpp/BitBuffer.hpp | 2 +- java/io/nayuki/qrcodegen/BitBuffer.java | 2 +- javascript/qrcodegen.js | 2 +- python/qrcodegen.py | 2 +- rust/src/lib.rs | 1 + typescript/qrcodegen.ts | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index be00ee4..2e6d734 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -30,7 +30,7 @@ namespace qrcodegen { /* - * An appendable sequence of bits (0's and 1's). + * An appendable sequence of bits (0s and 1s). */ class BitBuffer final : public std::vector { diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 265068f..4d8c5d7 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -28,7 +28,7 @@ import java.util.Objects; /** - * An appendable sequence of bits (0's and 1's). + * An appendable sequence of bits (0s and 1s). */ public final class BitBuffer implements Cloneable { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 6ca8618..8f1170c 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -950,7 +950,7 @@ var qrcodegen = new function() { /* - * A private helper class that represents an appendable sequence of bits. + * A private helper class that represents an appendable sequence of bits (0s and 1s). * This constructor creates an empty bit buffer (length 0). */ function BitBuffer() { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 4327f65..23482e2 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -811,7 +811,7 @@ class _ReedSolomonGenerator(object): class _BitBuffer(list): - """An appendable sequence of bits (0's and 1's).""" + """An appendable sequence of bits (0s and 1s).""" def get_bytes(self): """Packs this buffer's bits into bytes in big endian, diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7e345e7..7a34831 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1059,6 +1059,7 @@ impl QrSegmentMode { /*---- Bit buffer functionality ----*/ +// An appendable sequence of bits (0s and 1s). pub struct BitBuffer(pub Vec); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 3ab5c8b..c346dea 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -884,7 +884,7 @@ namespace qrcodegen { /* - * An appendable sequence of bits. The implicit constructor creates an empty bit buffer (length 0). + * An appendable sequence of bits (0s and 1s). The implicit constructor creates an empty bit buffer (length 0). */ class BitBuffer extends Array { From dce44caf8f677156e88d78d4e8b56ef19ed07f4e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 08:55:34 +0000 Subject: [PATCH 461/810] Tweaked BitBuffer.appendBits() comment in several language versions. --- c/qrcodegen.c | 1 + cpp/BitBuffer.hpp | 2 +- rust/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 998e13a..ffdd9bd 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -177,6 +177,7 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod // Appends the given sequence of bits to the given byte-based bit buffer, increasing the bit length. +// Requires 0 <= numBits <= 16 and 0 <= val < 2^numBits. testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index 2e6d734..436773b 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -49,7 +49,7 @@ class BitBuffer final : public std::vector { // Appends the given number of low bits of the given value - // to this sequence. Requires 0 <= val < 2^len. + // to this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. public: void appendBits(std::uint32_t val, int len); }; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7a34831..3f42530 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1065,7 +1065,7 @@ pub struct BitBuffer(pub Vec); impl BitBuffer { // Appends the given number of low bits of the given value - // to this sequence. Requires 0 <= val < 2^len. + // to this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len < 32 && (val >> len) == 0 || len == 32, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit From cdd1d0150ddb1889e632c546c444076f5daa9d15 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 08:56:07 +0000 Subject: [PATCH 462/810] Tweaked BitBuffer.appendBits() comment and code in a few language versions. --- java/io/nayuki/qrcodegen/BitBuffer.java | 1 + rust/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 4d8c5d7..722a07f 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -97,6 +97,7 @@ public final class BitBuffer implements Cloneable { * to this sequence. Requires 0 ≤ val < 2len. * @param val the value to append * @param len the number of low bits in the value to take + * @throws IllegalArgumentException if the value or number of bits is out of range * @throws IllegalStateException if appending the data * would make bitLength exceed Integer.MAX_VALUE */ diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3f42530..bb57352 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1067,7 +1067,7 @@ impl BitBuffer { // Appends the given number of low bits of the given value // to this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { - assert!(len < 32 && (val >> len) == 0 || len == 32, "Value out of range"); + assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit } } From ddc29b89d7db863f2e6fdfd3f6be6c8ae2733bfa Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 09:03:50 +0000 Subject: [PATCH 463/810] Simplified some local numeric variable types in Rust code. --- rust/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index bb57352..89a9a60 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -641,10 +641,10 @@ impl QrCode { // 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. fn get_num_raw_data_modules(ver: Version) -> usize { - let ver = ver.value(); - let mut result: usize = (16 * (ver as usize) + 128) * (ver as usize) + 64; + let ver = ver.value() as usize; + let mut result: usize = (16 * ver + 128) * ver + 64; if ver >= 2 { - let numalign: usize = (ver as usize) / 7 + 2; + let numalign: usize = ver / 7 + 2; result -= (25 * numalign - 10) * numalign - 55; if ver >= 7 { result -= 36; @@ -858,7 +858,7 @@ impl QrSegment { pub fn make_numeric(text: &[char]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3)); let mut accumdata: u32 = 0; - let mut accumcount: u32 = 0; + let mut accumcount: u8 = 0; for c in text { assert!('0' <= *c && *c <= '9', "String contains non-numeric characters"); accumdata = accumdata * 10 + ((*c as u32) - ('0' as u32)); @@ -870,7 +870,7 @@ impl QrSegment { } } if accumcount > 0 { // 1 or 2 digits remaining - bb.append_bits(accumdata, (accumcount as u8) * 3 + 1); + bb.append_bits(accumdata, accumcount * 3 + 1); } QrSegment::new(QrSegmentMode::Numeric, text.len(), bb.0) } From a3da832fc7f882b51609e730f61a6ef62a207e20 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 2 Oct 2018 09:10:20 +0000 Subject: [PATCH 464/810] Changed Rust QrCode and QrSegment structs to be cloneable. --- rust/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 89a9a60..8653a90 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -28,6 +28,7 @@ // provides static functions to create a QR Code from user-supplied textual or binary data. // This struct covers the QR Code model 2 specification, supporting all versions (sizes) // from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. +#[derive(Clone)] pub struct QrCode { // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). @@ -825,6 +826,7 @@ impl ReedSolomonGenerator { // Represents a segment of character data, binary data, or control data // to be put into a QR Code symbol. Instances of this class are immutable. +#[derive(Clone)] pub struct QrSegment { // The mode indicator for this segment. From ef097293311a26c4b2f5f8ca50513b584bf55136 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 02:34:51 +0000 Subject: [PATCH 465/810] Reformatted long line and whitespace in HTML code, without changing behavior. --- javascript/qrcodegen-js-demo.html | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index 224971e..29ca168 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -94,7 +94,10 @@ Version range: - Minimum = , maximum = + + Minimum = , + maximum = + Mask pattern: From 3ab5e7827c05af30a0eae34d46759b43eba8a8ef Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 02:49:37 +0000 Subject: [PATCH 466/810] Tweaked {HTML, JavaScript, TypeScript} code to move repetitive event handler registration from HTML to script. --- javascript/qrcodegen-demo.js | 15 ++++++++++++++- javascript/qrcodegen-js-demo.html | 22 +++++++++++----------- typescript/qrcodegen-demo.ts | 15 ++++++++++++++- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 42366a1..9ca9490 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -24,6 +24,19 @@ "use strict"; +function initialize() { + var elems = document.querySelectorAll("input[type=number], textarea"); + for (var i = 0; i < elems.length; i++) { + if (elems[i].id.indexOf("version-") != 0) + elems[i].oninput = redrawQrCode; + } + elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); + for (var i = 0; i < elems.length; i++) + elems[i].onchange = redrawQrCode; + redrawQrCode(); +} + + function redrawQrCode() { // Show/hide rows based on bitmap/vector image output var bitmapOutput = document.getElementById("output-format-bitmap").checked; @@ -147,4 +160,4 @@ function handleVersionMinMax(which) { } -redrawQrCode(); +initialize(); diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index 29ca168..d534d7f 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -56,7 +56,7 @@ Text string: - + QR Code: @@ -71,26 +71,26 @@ Error correction: - - - - + + + + Output format: - - + + Border: - modules + modules Scale: - pixels per module + pixels per module Version range: @@ -101,11 +101,11 @@ Mask pattern: - (−1 for automatic, 0 to 7 for manual) + (−1 for automatic, 0 to 7 for manual) Boost ECC: - + Statistics: diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index ee8d987..c1fe6bd 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -28,6 +28,19 @@ const myWindow = window as any; + function initialize(): void { + let elems = document.querySelectorAll("input[type=number], textarea"); + for (let el of elems) { + if (!el.id.startsWith("version-")) + (el as any).oninput = myWindow.redrawQrCode; + } + elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); + for (let el of elems) + (el as HTMLInputElement).onchange = myWindow.redrawQrCode; + myWindow.redrawQrCode(); + } + + /*---- Functions called from HTML code ----*/ myWindow.redrawQrCode = function(): void { @@ -176,5 +189,5 @@ /*---- Initialization ----*/ - myWindow.redrawQrCode(); + initialize(); } From 6d79c97c4280c616ffa77940661284fed3d196ef Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 02:53:48 +0000 Subject: [PATCH 467/810] Tweaked {JavaScript, TypeScript, HTML} code to encapsulate the application members. --- javascript/qrcodegen-demo.js | 267 +++++++++++++++--------------- javascript/qrcodegen-js-demo.html | 4 +- typescript/qrcodegen-demo.ts | 16 +- 3 files changed, 144 insertions(+), 143 deletions(-) diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js index 9ca9490..4e23ba0 100644 --- a/javascript/qrcodegen-demo.js +++ b/javascript/qrcodegen-demo.js @@ -24,140 +24,143 @@ "use strict"; -function initialize() { - var elems = document.querySelectorAll("input[type=number], textarea"); - for (var i = 0; i < elems.length; i++) { - if (elems[i].id.indexOf("version-") != 0) - elems[i].oninput = redrawQrCode; - } - elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); - for (var i = 0; i < elems.length; i++) - elems[i].onchange = redrawQrCode; - redrawQrCode(); -} - - -function redrawQrCode() { - // Show/hide rows based on bitmap/vector image output - var bitmapOutput = document.getElementById("output-format-bitmap").checked; - var scaleRow = document.getElementById("scale-row"); - var svgXmlRow = document.getElementById("svg-xml-row"); - if (bitmapOutput) { - scaleRow.style.removeProperty("display"); - svgXmlRow.style.display = "none"; - } else { - scaleRow.style.display = "none"; - svgXmlRow.style.removeProperty("display"); - } - var svgXml = document.getElementById("svg-xml-output"); - svgXml.value = ""; +var app = new function() { - // Reset output images in case of early termination - var canvas = document.getElementById("qrcode-canvas"); - var svg = document.getElementById("qrcode-svg"); - canvas.style.display = "none"; - svg.style.display = "none"; - - // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. - function getInputErrorCorrectionLevel() { - if (document.getElementById("errcorlvl-medium").checked) - return qrcodegen.QrCode.Ecc.MEDIUM; - else if (document.getElementById("errcorlvl-quartile").checked) - return qrcodegen.QrCode.Ecc.QUARTILE; - else if (document.getElementById("errcorlvl-high").checked) - return qrcodegen.QrCode.Ecc.HIGH; - else // In case no radio button is depressed - return qrcodegen.QrCode.Ecc.LOW; - } - - // Get form inputs and compute QR Code - var ecl = getInputErrorCorrectionLevel(); - var text = document.getElementById("text-input").value; - var segs = qrcodegen.QrSegment.makeSegments(text); - var minVer = parseInt(document.getElementById("version-min-input").value, 10); - var maxVer = parseInt(document.getElementById("version-max-input").value, 10); - var mask = parseInt(document.getElementById("mask-input").value, 10); - var boostEcc = document.getElementById("boost-ecc-input").checked; - var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); - - // Draw image output - var border = parseInt(document.getElementById("border-input").value, 10); - if (border < 0 || border > 100) - return; - if (bitmapOutput) { - var scale = parseInt(document.getElementById("scale-input").value, 10); - if (scale <= 0 || scale > 30) - return; - qr.drawCanvas(scale, border, canvas); - canvas.style.removeProperty("display"); - } else { - var code = qr.toSvgString(border); - svg.setAttribute("viewBox", / viewBox="([^"]*)"/.exec(code)[1]); - svg.querySelector("path").setAttribute("d", / d="([^"]*)"/.exec(code)[1]); - svg.style.removeProperty("display"); - svgXml.value = qr.toSvgString(border); - } - - // Returns a string to describe the given list of segments. - function describeSegments(segs) { - if (segs.length == 0) - return "none"; - else if (segs.length == 1) { - var mode = segs[0].mode; - var Mode = qrcodegen.QrSegment.Mode; - if (mode == Mode.NUMERIC ) return "numeric"; - if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; - if (mode == Mode.BYTE ) return "byte"; - if (mode == Mode.KANJI ) return "kanji"; - return "unknown"; - } else - return "multiple"; - } - - // Returns the number of Unicode code points in the given UTF-16 string. - function countUnicodeChars(str) { - var result = 0; - for (var i = 0; i < str.length; i++, result++) { - var c = str.charCodeAt(i); - if (c < 0xD800 || c >= 0xE000) - continue; - else if (0xD800 <= c && c < 0xDC00 && i + 1 < str.length) { // High surrogate - i++; - var d = str.charCodeAt(i); - if (0xDC00 <= d && d < 0xE000) // Low surrogate - continue; - } - throw "Invalid UTF-16 string"; + function initialize() { + var elems = document.querySelectorAll("input[type=number], textarea"); + for (var i = 0; i < elems.length; i++) { + if (elems[i].id.indexOf("version-") != 0) + elems[i].oninput = redrawQrCode; } - return result; + elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); + for (var i = 0; i < elems.length; i++) + elems[i].onchange = redrawQrCode; + redrawQrCode(); } - // Show the QR Code symbol's statistics as a string - var stats = "QR Code version = " + qr.version + ", "; - stats += "mask pattern = " + qr.mask + ", "; - stats += "character count = " + countUnicodeChars(text) + ",\n"; - stats += "encoding mode = " + describeSegments(segs) + ", "; - stats += "error correction = level " + "LMQH".charAt(qr.errorCorrectionLevel.ordinal) + ", "; - stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.version) + "."; - document.getElementById("statistics-output").textContent = stats; + + function redrawQrCode() { + // Show/hide rows based on bitmap/vector image output + var bitmapOutput = document.getElementById("output-format-bitmap").checked; + var scaleRow = document.getElementById("scale-row"); + var svgXmlRow = document.getElementById("svg-xml-row"); + if (bitmapOutput) { + scaleRow.style.removeProperty("display"); + svgXmlRow.style.display = "none"; + } else { + scaleRow.style.display = "none"; + svgXmlRow.style.removeProperty("display"); + } + var svgXml = document.getElementById("svg-xml-output"); + svgXml.value = ""; + + // Reset output images in case of early termination + var canvas = document.getElementById("qrcode-canvas"); + var svg = document.getElementById("qrcode-svg"); + canvas.style.display = "none"; + svg.style.display = "none"; + + // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. + function getInputErrorCorrectionLevel() { + if (document.getElementById("errcorlvl-medium").checked) + return qrcodegen.QrCode.Ecc.MEDIUM; + else if (document.getElementById("errcorlvl-quartile").checked) + return qrcodegen.QrCode.Ecc.QUARTILE; + else if (document.getElementById("errcorlvl-high").checked) + return qrcodegen.QrCode.Ecc.HIGH; + else // In case no radio button is depressed + return qrcodegen.QrCode.Ecc.LOW; + } + + // Get form inputs and compute QR Code + var ecl = getInputErrorCorrectionLevel(); + var text = document.getElementById("text-input").value; + var segs = qrcodegen.QrSegment.makeSegments(text); + var minVer = parseInt(document.getElementById("version-min-input").value, 10); + var maxVer = parseInt(document.getElementById("version-max-input").value, 10); + var mask = parseInt(document.getElementById("mask-input").value, 10); + var boostEcc = document.getElementById("boost-ecc-input").checked; + var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); + + // Draw image output + var border = parseInt(document.getElementById("border-input").value, 10); + if (border < 0 || border > 100) + return; + if (bitmapOutput) { + var scale = parseInt(document.getElementById("scale-input").value, 10); + if (scale <= 0 || scale > 30) + return; + qr.drawCanvas(scale, border, canvas); + canvas.style.removeProperty("display"); + } else { + var code = qr.toSvgString(border); + svg.setAttribute("viewBox", / viewBox="([^"]*)"/.exec(code)[1]); + svg.querySelector("path").setAttribute("d", / d="([^"]*)"/.exec(code)[1]); + svg.style.removeProperty("display"); + svgXml.value = qr.toSvgString(border); + } + + // Returns a string to describe the given list of segments. + function describeSegments(segs) { + if (segs.length == 0) + return "none"; + else if (segs.length == 1) { + var mode = segs[0].mode; + var Mode = qrcodegen.QrSegment.Mode; + if (mode == Mode.NUMERIC ) return "numeric"; + if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; + if (mode == Mode.BYTE ) return "byte"; + if (mode == Mode.KANJI ) return "kanji"; + return "unknown"; + } else + return "multiple"; + } + + // Returns the number of Unicode code points in the given UTF-16 string. + function countUnicodeChars(str) { + var result = 0; + for (var i = 0; i < str.length; i++, result++) { + var c = str.charCodeAt(i); + if (c < 0xD800 || c >= 0xE000) + continue; + else if (0xD800 <= c && c < 0xDC00 && i + 1 < str.length) { // High surrogate + i++; + var d = str.charCodeAt(i); + if (0xDC00 <= d && d < 0xE000) // Low surrogate + continue; + } + throw "Invalid UTF-16 string"; + } + return result; + } + + // Show the QR Code symbol's statistics as a string + var stats = "QR Code version = " + qr.version + ", "; + stats += "mask pattern = " + qr.mask + ", "; + stats += "character count = " + countUnicodeChars(text) + ",\n"; + stats += "encoding mode = " + describeSegments(segs) + ", "; + stats += "error correction = level " + "LMQH".charAt(qr.errorCorrectionLevel.ordinal) + ", "; + stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.version) + "."; + document.getElementById("statistics-output").textContent = stats; + } + + + this.handleVersionMinMax = function(which) { + var minElem = document.getElementById("version-min-input"); + var maxElem = document.getElementById("version-max-input"); + var minVal = parseInt(minElem.value, 10); + var maxVal = parseInt(maxElem.value, 10); + minVal = Math.max(Math.min(minVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); + maxVal = Math.max(Math.min(maxVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); + if (which == "min" && minVal > maxVal) + maxVal = minVal; + else if (which == "max" && maxVal < minVal) + minVal = maxVal; + minElem.value = minVal.toString(); + maxElem.value = maxVal.toString(); + redrawQrCode(); + } + + + initialize(); } - - -function handleVersionMinMax(which) { - var minElem = document.getElementById("version-min-input"); - var maxElem = document.getElementById("version-max-input"); - var minVal = parseInt(minElem.value, 10); - var maxVal = parseInt(maxElem.value, 10); - minVal = Math.max(Math.min(minVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); - maxVal = Math.max(Math.min(maxVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); - if (which == "min" && minVal > maxVal) - maxVal = minVal; - else if (which == "max" && maxVal < minVal) - minVal = maxVal; - minElem.value = minVal.toString(); - maxElem.value = maxVal.toString(); - redrawQrCode(); -} - - -initialize(); diff --git a/javascript/qrcodegen-js-demo.html b/javascript/qrcodegen-js-demo.html index d534d7f..afaa90f 100644 --- a/javascript/qrcodegen-js-demo.html +++ b/javascript/qrcodegen-js-demo.html @@ -95,8 +95,8 @@ Version range: - Minimum = , - maximum = + Minimum = , + maximum = diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index c1fe6bd..c486d05 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -24,26 +24,24 @@ "use strict"; -{ - const myWindow = window as any; - +namespace app { function initialize(): void { let elems = document.querySelectorAll("input[type=number], textarea"); for (let el of elems) { if (!el.id.startsWith("version-")) - (el as any).oninput = myWindow.redrawQrCode; + (el as any).oninput = redrawQrCode; } elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); for (let el of elems) - (el as HTMLInputElement).onchange = myWindow.redrawQrCode; - myWindow.redrawQrCode(); + (el as HTMLInputElement).onchange = redrawQrCode; + redrawQrCode(); } /*---- Functions called from HTML code ----*/ - myWindow.redrawQrCode = function(): void { + function redrawQrCode(): void { // Show/hide rows based on bitmap/vector image output const bitmapOutput: boolean = getInput("output-format-bitmap").checked; const scaleRow : HTMLElement = getElem("scale-row"); @@ -150,7 +148,7 @@ } - myWindow.handleVersionMinMax = function(which: "min"|"max"): void { + export function handleVersionMinMax(which: "min"|"max"): void { const minElem: HTMLInputElement = getInput("version-min-input"); const maxElem: HTMLInputElement = getInput("version-max-input"); let minVal: number = parseInt(minElem.value, 10); @@ -163,7 +161,7 @@ minVal = maxVal; minElem.value = minVal.toString(); maxElem.value = maxVal.toString(); - myWindow.redrawQrCode(); + redrawQrCode(); } From 4f61adf9ba18a42132e5a7c9ca719b73a24c8b01 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 02:54:29 +0000 Subject: [PATCH 468/810] Deleted section comments in TypeScript demo code. --- typescript/qrcodegen-demo.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index c486d05..55c54b5 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -39,8 +39,6 @@ namespace app { } - /*---- Functions called from HTML code ----*/ - function redrawQrCode(): void { // Show/hide rows based on bitmap/vector image output const bitmapOutput: boolean = getInput("output-format-bitmap").checked; @@ -165,9 +163,6 @@ namespace app { } - - /*---- Private helper functions ----*/ - function getElem(id: string): HTMLElement { const result: HTMLElement|null = document.getElementById(id); if (result instanceof HTMLElement) @@ -184,8 +179,5 @@ namespace app { } - - /*---- Initialization ----*/ - initialize(); } From 012d469ca5d0b173a1d003bae842ac691e0f952f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 20:59:02 +0000 Subject: [PATCH 469/810] Inlined some Java functions - refactored QrSegmentAdvanced, increased visibility of a constant in QrSegment. --- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 20 +++---------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index eb45d56..ceee566 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -223,7 +223,7 @@ public final class QrSegment { public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); /** The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. */ - private static final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + static final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 549e446..6b52f67 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -125,11 +125,11 @@ public final class QrSegmentAdvanced { charModes[i][0] = modeTypes[0]; } // Extend a segment if possible - if (isAlphanumeric(c)) { + if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1) { curCosts[1] = prevCosts[1] + 33; // 5.5 bits per alphanumeric char charModes[i][1] = modeTypes[1]; } - if (isNumeric(c)) { + if ('0' <= c && c <= '9') { curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit charModes[i][2] = modeTypes[2]; } @@ -141,7 +141,7 @@ public final class QrSegmentAdvanced { // 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 = roundUp6(curCosts[k]) + headCosts[j]; + int newCost = (curCosts[k] + 5) / 6 * 6 + headCosts[j]; if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) { curCosts[j] = newCost; charModes[i][j] = modeTypes[k]; @@ -216,20 +216,6 @@ public final class QrSegmentAdvanced { } - private static boolean isAlphanumeric(int c) { - return isNumeric(c) || 'A' <= c && c <= 'Z' || " $%*+./:-".indexOf(c) != -1; - } - - private static boolean isNumeric(int c) { - return '0' <= c && c <= '9'; - } - - - private static int roundUp6(int x) { - return (x + 5) / 6 * 6; - } - - /*---- Kanji mode segment encoder ----*/ From 18ff8dc0e552806c883821d76167eaf55c42ad30 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 21:01:23 +0000 Subject: [PATCH 470/810] Refactored Java QrSegmentAdvanced to move a piece of logic to a new function. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 6b52f67..e1e64bc 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -112,16 +112,7 @@ public final class QrSegmentAdvanced { int c = codePoints[i]; int[] curCosts = new int[numModes]; { // Always extend a bytes segment - int b; - if (c < 0x80) - b = 1; - else if (c < 0x800) - b = 2; - else if (c < 0x10000) - b = 3; - else - b = 4; - curCosts[0] = prevCosts[0] + b * 8 * 6; + curCosts[0] = prevCosts[0] + countUtf8Bytes(c) * 8 * 6; charModes[i][0] = modeTypes[0]; } // Extend a segment if possible @@ -216,6 +207,16 @@ public final class QrSegmentAdvanced { } + 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; + else throw new IllegalArgumentException("Invalid code point"); + } + + /*---- Kanji mode segment encoder ----*/ From 46b642583859c6e1d2a1891179e2a5ca15f7bbbb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 21:10:13 +0000 Subject: [PATCH 471/810] Simplified some logic in Java QrSegmentAdvanced using Java SE 8 streams and lambdas. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index e1e64bc..c3a9d71 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -234,12 +234,12 @@ public final class QrSegmentAdvanced { public static QrSegment makeKanji(String text) { Objects.requireNonNull(text); BitBuffer bb = new BitBuffer(); - for (int i = 0; i < text.length(); i++) { - int val = UNICODE_TO_QR_KANJI[text.charAt(i)]; + text.chars().forEachOrdered(c -> { + int val = UNICODE_TO_QR_KANJI[c]; if (val == -1) throw new IllegalArgumentException("String contains non-kanji-mode characters"); bb.appendBits(val, 13); - } + }); return new QrSegment(Mode.KANJI, text.length(), bb); } @@ -256,11 +256,8 @@ public final class QrSegmentAdvanced { */ public static boolean isEncodableAsKanji(String text) { Objects.requireNonNull(text); - for (int i = 0; i < text.length(); i++) { - if (!isKanji(text.charAt(i))) - return false; - } - return true; + return text.chars().allMatch( + c -> isKanji((char)c)); } From 98741eb09a4a260be4e97cfd6a67272f98047e8a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 21:14:02 +0000 Subject: [PATCH 472/810] Slightly tweaked logic in Java QrSegmentAdvanced for robustness and efficiency. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index c3a9d71..8369a6c 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -62,9 +62,11 @@ public final class QrSegmentAdvanced { // Iterate through version numbers, and make tentative segments List segs = null; + int[] codePoints = toCodePoints(text); for (int version = minVersion; version <= maxVersion; version++) { if (version == minVersion || version == 10 || version == 27) - segs = makeSegmentsOptimally(text, version); + segs = makeSegmentsOptimally(codePoints, version); + assert segs != null; // Check if the segments fit int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; @@ -77,10 +79,9 @@ public final class QrSegmentAdvanced { // Returns a list of segments that is optimal for the given text at the given version number. - private static List makeSegmentsOptimally(String text, int version) { - if (text.length() == 0) + private static List makeSegmentsOptimally(int[] codePoints, int version) { + if (codePoints.length == 0) return new ArrayList<>(); - int[] codePoints = toCodePoints(text); Mode[] charModes = computeCharacterModes(codePoints, version); return splitIntoSegments(codePoints, charModes); } From 59e3933e8ba7d54c96652c9517b7291d39a46afd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 3 Oct 2018 21:14:45 +0000 Subject: [PATCH 473/810] Added and updated comments in Java QrSegmentAdvanced. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 8369a6c..dfad00b 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -37,9 +37,9 @@ public final class QrSegmentAdvanced { /*---- Optimal list of segments encoder ----*/ /** - * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. - * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints given - * by the specified {error correction level, minimum version number, maximum version number}. + * Returns a list of zero or more segments to represent the specified Unicode text string. + * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints + * in the specified {error correction level, minimum version number, maximum version number}. *

This function can utilize all four text encoding modes: numeric, alphanumeric, byte, and kanji. * This can be viewed as a significantly more sophisticated and slower replacement for * {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way @@ -48,7 +48,7 @@ public final class QrSegmentAdvanced { * @param ecl the error correction level to use * @param minVersion the minimum allowed version of the QR symbol (at least 1) * @param maxVersion the maximum allowed version of the QR symbol (at most 40) - * @return a list of segments containing the text, minimizing the bit length with respect to the constraints + * @return a new mutable list of segments containing the text, minimizing the bit length with respect to the constraints * @throws NullPointerException if the data or error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, * or if the data is too long to fit in a QR Code at maxVersion at the ECL @@ -87,6 +87,7 @@ public final class QrSegmentAdvanced { } + // Returns an array representing the optimal mode per code point based on the given text and version. private static Mode[] computeCharacterModes(int[] codePoints, int version) { if (codePoints.length == 0) throw new IllegalArgumentException(); @@ -112,16 +113,16 @@ public final class QrSegmentAdvanced { for (int i = 0; i < codePoints.length; i++) { int c = codePoints[i]; int[] curCosts = new int[numModes]; - { // Always extend a bytes segment + { // Always extend a byte mode segment curCosts[0] = prevCosts[0] + countUtf8Bytes(c) * 8 * 6; charModes[i][0] = modeTypes[0]; } // Extend a segment if possible - if (QrSegment.ALPHANUMERIC_CHARSET.indexOf(c) != -1) { + 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 ('0' <= c && c <= '9') { + if ('0' <= c && c <= '9') { // Is numeric curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit charModes[i][2] = modeTypes[2]; } @@ -168,6 +169,8 @@ public final class QrSegmentAdvanced { } + // Returns a 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) { if (codePoints.length == 0) throw new IllegalArgumentException(); @@ -198,6 +201,7 @@ public final class QrSegmentAdvanced { } + // Returns an 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(); for (int c : result) { @@ -208,6 +212,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 cp) { if (cp < 0) throw new IllegalArgumentException("Invalid code point"); else if (cp < 0x80) return 1; From c8c29cc1165a5560b3f2628fe4368f16e2740e4c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 01:50:26 +0000 Subject: [PATCH 474/810] Renamed variables in C++ demo program to start from 0. --- cpp/QrCodeGeneratorDemo.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index f241e1c..42d5dd1 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -74,19 +74,19 @@ static void doBasicDemo() { // Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console. static void doVarietyDemo() { // Numeric mode encoding (3.33 bits per digit) - const QrCode qr1 = QrCode::encodeText("314159265358979323846264338327950288419716939937510", QrCode::Ecc::MEDIUM); - printQr(qr1); + const QrCode qr0 = QrCode::encodeText("314159265358979323846264338327950288419716939937510", QrCode::Ecc::MEDIUM); + printQr(qr0); // Alphanumeric mode encoding (5.5 bits per character) - const QrCode qr2 = QrCode::encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode::Ecc::HIGH); - printQr(qr2); + const QrCode qr1 = QrCode::encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode::Ecc::HIGH); + printQr(qr1); // Unicode text as UTF-8 - const QrCode qr3 = QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", QrCode::Ecc::QUARTILE); - printQr(qr3); + const QrCode qr2 = QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", QrCode::Ecc::QUARTILE); + printQr(qr2); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) - const QrCode qr4 = QrCode::encodeText( + const QrCode qr3 = QrCode::encodeText( "Alice was beginning to get very tired of sitting by her sister on the bank, " "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " @@ -94,7 +94,7 @@ static void doVarietyDemo() { "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); - printQr(qr4); + printQr(qr3); } From 667c744d8b444646a6a79eaad33d863f7f293971 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 18:48:29 +0000 Subject: [PATCH 475/810] Updated and synchronized the QrCode class top-level documentation comment, in all languages except C. --- cpp/QrCode.hpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 6 ++++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 8 ++++---- 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 0c71131..4639a22 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -34,8 +34,8 @@ namespace qrcodegen { /* * Represents an immutable square grid of black and white cells for a QR Code symbol, and * provides static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. + * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. */ class QrCode final { diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 6e0669c..7279f04 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -32,8 +32,8 @@ import java.util.Objects; /** * Represents an immutable square grid of black and white cells for a QR Code symbol, and * provides static functions to create a QR Code from user-supplied textual or binary data. - *

This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and only 3 character encoding modes.

+ *

This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

*/ public final class QrCode { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 8f1170c..9c9e507 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -61,9 +61,9 @@ var qrcodegen = new function() { /* * A class that represents an immutable square grid of black and white cells for a QR Code symbol, - * with associated static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels. + * and provides static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 23482e2..a971d7a 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -61,8 +61,10 @@ This module "qrcodegen", public members: # ---- QR Code symbol class ---- class QrCode(object): - """Represents an immutable square grid of black or white cells for a QR Code symbol. This class covers the - QR Code model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels.""" + """Represents an immutable square grid of black and white cells for a QR Code symbol, and + provides static functions to create a QR Code from user-supplied textual or binary data. + This class covers the QR Code Model 2 specification, supporting all versions (sizes) + from 1 to 40, all 4 error correction levels, and 4 character encoding modes.""" # ---- Public static factory functions ---- diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8653a90..069b50d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -26,8 +26,8 @@ // Represents an immutable square grid of black and white cells for a QR Code symbol, and // provides static functions to create a QR Code from user-supplied textual or binary data. -// This struct covers the QR Code model 2 specification, supporting all versions (sizes) -// from 1 to 40, all 4 error correction levels, and only 3 character encoding modes. +// This struct and impl cover the QR Code Model 2 specification, supporting all versions +// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. #[derive(Clone)] pub struct QrCode { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index c346dea..58bd14b 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -34,10 +34,10 @@ namespace qrcodegen { /*---- QR Code symbol class ----*/ /* - * A class that represents an immutable square grid of black and white cells for a QR Code symbol, - * with associated static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels. + * Represents an immutable square grid of black and white cells for a QR Code symbol, and + * provides static functions to create a QR Code from user-supplied textual or binary data. + * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. From dd47ef10c757590081e6f399893779d5c585b63b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:06:54 +0000 Subject: [PATCH 476/810] Split long text constants in demo programs. --- c/qrcodegen-demo.c | 3 ++- cpp/QrCodeGeneratorDemo.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/c/qrcodegen-demo.c b/c/qrcodegen-demo.c index b859d4b..ae78ea7 100644 --- a/c/qrcodegen-demo.c +++ b/c/qrcodegen-demo.c @@ -89,7 +89,8 @@ static void doVarietyDemo(void) { } { // Unicode text as UTF-8 - const char *text = "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4"; + const char *text = "\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81" + "\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4"; uint8_t qrcode[qrcodegen_BUFFER_LEN_MAX]; uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; bool ok = qrcodegen_encodeText(text, tempBuffer, qrcode, diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 42d5dd1..d199e23 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -82,7 +82,8 @@ static void doVarietyDemo() { printQr(qr1); // Unicode text as UTF-8 - const QrCode qr2 = QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", QrCode::Ecc::QUARTILE); + const QrCode qr2 = QrCode::encodeText("\xE3\x81\x93\xE3\x82\x93\xE3\x81\xAB\xE3\x81\xA1wa\xE3\x80\x81" + "\xE4\xB8\x96\xE7\x95\x8C\xEF\xBC\x81\x20\xCE\xB1\xCE\xB2\xCE\xB3\xCE\xB4", QrCode::Ecc::QUARTILE); printQr(qr2); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) From 0cded0ba36edb57efa2e233c819531af145ce2ce Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:08:23 +0000 Subject: [PATCH 477/810] Aligned main comments in demo programs. --- c/qrcodegen-demo.c | 2 +- cpp/QrCodeGeneratorDemo.cpp | 2 +- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 8 ++++---- python/qrcodegen-demo.py | 2 +- rust/examples/qrcodegen-demo.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/c/qrcodegen-demo.c b/c/qrcodegen-demo.c index ae78ea7..ef44108 100644 --- a/c/qrcodegen-demo.c +++ b/c/qrcodegen-demo.c @@ -55,7 +55,7 @@ int main(void) { // Creates a single QR Code, then prints it to the console. static void doBasicDemo(void) { - const char *text = "Hello, world!"; // User-supplied text + const char *text = "Hello, world!"; // User-supplied text enum qrcodegen_Ecc errCorLvl = qrcodegen_Ecc_LOW; // Error correction level // Make and print the QR Code symbol diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index d199e23..168e273 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -61,7 +61,7 @@ int main() { // Creates a single QR Code, then prints it to the console. static void doBasicDemo() { - const char *text = "Hello, world!"; // User-supplied text + const char *text = "Hello, world!"; // User-supplied text const QrCode::Ecc errCorLvl = QrCode::Ecc::LOW; // Error correction level // Make and print the QR Code symbol diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index dbcf233..2b4baa4 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -57,11 +57,11 @@ public final class QrCodeGeneratorDemo { QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image - File imgFile = new File("hello-world-QR.png"); // File path for output - ImageIO.write(img, "png", imgFile); // Write image to file + BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + File imgFile = new File("hello-world-QR.png"); // File path for output + ImageIO.write(img, "png", imgFile); // Write image to file - String svg = qr.toSvgString(4); // Convert to SVG XML code + String svg = qr.toSvgString(4); // Convert to SVG XML code Files.write(new File("hello-world-QR.svg").toPath(), svg.getBytes(StandardCharsets.UTF_8)); } diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index 7a1bfaa..13dd93e 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -41,7 +41,7 @@ def main(): def do_basic_demo(): """Creates a single QR Code, then prints it to the console.""" - text = u"Hello, world!" # User-supplied Unicode text + text = u"Hello, world!" # User-supplied Unicode text errcorlvl = QrCode.Ecc.LOW # Error correction level # Make and print the QR Code symbol diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs index c28ad72..80b2b7a 100644 --- a/rust/examples/qrcodegen-demo.rs +++ b/rust/examples/qrcodegen-demo.rs @@ -47,7 +47,7 @@ fn main() { // Creates a single QR Code, then prints it to the console. fn do_basic_demo() { - let text: &'static str = "Hello, world!"; // User-supplied Unicode text + let text: &'static str = "Hello, world!"; // User-supplied Unicode text let errcorlvl: QrCodeEcc = QrCodeEcc::Low; // Error correction level // Make and print the QR Code symbol From 55f410cc6669b0a436ed11a51186366c41569ab5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:09:26 +0000 Subject: [PATCH 478/810] Slightly tweaked demo programs for clarity. --- cpp/QrCodeGeneratorDemo.cpp | 2 +- java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 168e273..1286433 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -187,7 +187,7 @@ static void doMaskDemo() { /*---- Utilities ----*/ -// Prints the given QR Code to the console. +// Prints the given QrCode object to the console. static void printQr(const QrCode &qr) { int border = 4; for (int y = -border; y < qr.getSize() + border; y++) { diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 2b4baa4..3c50bda 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -62,7 +62,8 @@ public final class QrCodeGeneratorDemo { ImageIO.write(img, "png", imgFile); // Write image to file String svg = qr.toSvgString(4); // Convert to SVG XML code - Files.write(new File("hello-world-QR.svg").toPath(), + File svgFile = new File("hello-world-QR.svg"); // File path for output + Files.write(svgFile.toPath(), // Write image to file svg.getBytes(StandardCharsets.UTF_8)); } From af6a247121d3729a445f790ac2aa08ce7279f11f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:26:21 +0000 Subject: [PATCH 479/810] Tweaked logic in QrSegment.makeEci() to reduce redundancy, in all languages except Rust. --- c/qrcodegen.c | 8 +++++--- cpp/QrSegment.cpp | 8 +++++--- java/io/nayuki/qrcodegen/QrSegment.java | 8 +++++--- javascript/qrcodegen.js | 8 +++++--- python/qrcodegen.py | 8 +++++--- typescript/qrcodegen.ts | 8 +++++--- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index ffdd9bd..77a0bda 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -829,14 +829,16 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { result.mode = qrcodegen_Mode_ECI; result.numChars = 0; result.bitLength = 0; - if (0 <= assignVal && assignVal < (1 << 7)) { + if (assignVal < 0) + assert(false); + else if (assignVal < (1 << 7)) { memset(buf, 0, 1 * sizeof(buf[0])); appendBitsToBuffer(assignVal, 8, buf, &result.bitLength); - } else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + } else if (assignVal < (1 << 14)) { memset(buf, 0, 2 * sizeof(buf[0])); appendBitsToBuffer(2, 2, buf, &result.bitLength); appendBitsToBuffer(assignVal, 14, buf, &result.bitLength); - } else if ((1 << 14) <= assignVal && assignVal < 1000000L) { + } else if (assignVal < 1000000L) { memset(buf, 0, 3 * sizeof(buf[0])); appendBitsToBuffer(6, 3, buf, &result.bitLength); appendBitsToBuffer(assignVal >> 10, 11, buf, &result.bitLength); diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 8d1bde3..64d77f7 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -135,12 +135,14 @@ vector QrSegment::makeSegments(const char *text) { QrSegment QrSegment::makeEci(long assignVal) { BitBuffer bb; - if (0 <= assignVal && assignVal < (1 << 7)) + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) bb.appendBits(assignVal, 8); - else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if ((1 << 14) <= assignVal && assignVal < 1000000L) { + } else if (assignVal < 1000000L) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index ceee566..264c735 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -137,12 +137,14 @@ public final class QrSegment { */ public static QrSegment makeEci(int assignVal) { BitBuffer bb = new BitBuffer(); - if (0 <= assignVal && assignVal < (1 << 7)) + if (assignVal < 0) + throw new IllegalArgumentException("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) bb.appendBits(assignVal, 8); - else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + } else if (assignVal < 1000000) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 9c9e507..f1d2323 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -792,12 +792,14 @@ var qrcodegen = new function() { */ this.QrSegment.makeEci = function(assignVal) { var bb = new BitBuffer(); - if (0 <= assignVal && assignVal < (1 << 7)) + if (assignVal < 0) + throw "ECI assignment value out of range"; + else if (assignVal < (1 << 7)) bb.appendBits(assignVal, 8); - else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + } else if (assignVal < 1000000) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else diff --git a/python/qrcodegen.py b/python/qrcodegen.py index a971d7a..8edd562 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -661,12 +661,14 @@ class QrSegment(object): """Returns a segment representing an Extended Channel Interpretation (ECI) designator with the given assignment value.""" bb = _BitBuffer() - if 0 <= assignval < (1 << 7): + if assignval < 0: + raise ValueError("ECI assignment value out of range") + elif assignval < (1 << 7): bb.append_bits(assignval, 8) - elif (1 << 7) <= assignval < (1 << 14): + elif assignval < (1 << 14): bb.append_bits(2, 2) bb.append_bits(assignval, 14) - elif (1 << 14) <= assignval < 1000000: + elif assignval < 1000000: bb.append_bits(6, 3) bb.append_bits(assignval, 21) else: diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 58bd14b..ab6d1ef 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -720,12 +720,14 @@ namespace qrcodegen { // (ECI) designator with the given assignment value. public static makeEci(assignVal: int): QrSegment { let bb = new BitBuffer(); - if (0 <= assignVal && assignVal < (1 << 7)) + if (assignVal < 0) + throw "ECI assignment value out of range"; + else if (assignVal < (1 << 7)) bb.appendBits(assignVal, 8); - else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + } else if (assignVal < 1000000) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else From 859438d183c1519d29e46df42f37a6e643ad09c8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:40:43 +0000 Subject: [PATCH 480/810] Moved QrCode class's max/min version constants, tweaked associated comments and blank lines, in all languages except C. --- cpp/QrCode.hpp | 14 ++++++-------- java/io/nayuki/qrcodegen/QrCode.java | 13 +++++-------- javascript/qrcodegen.js | 15 ++++++--------- python/qrcodegen.py | 11 ++++------- rust/src/lib.rs | 4 +--- typescript/qrcodegen.ts | 5 +---- 6 files changed, 23 insertions(+), 39 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 4639a22..d4115cf 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -88,13 +88,6 @@ class QrCode final { - /*---- Public constants ----*/ - - public: static constexpr int MIN_VERSION = 1; - public: static constexpr int MAX_VERSION = 40; - - - /*---- Instance fields ----*/ // Immutable scalar parameters @@ -252,7 +245,11 @@ class QrCode final { private: static bool getBit(long x, int i); - /*---- Private tables of constants ----*/ + /*---- Constants and tables ----*/ + + public: static constexpr int MIN_VERSION = 1; + public: static constexpr int MAX_VERSION = 40; + // For use in getPenaltyScore(), when evaluating which mask is best. private: static const int PENALTY_N1; @@ -260,6 +257,7 @@ class QrCode final { private: static const int PENALTY_N3; private: static const int PENALTY_N4; + private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 7279f04..c694f13 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -167,13 +167,6 @@ public final class QrCode { - /*---- Public constants ----*/ - - public static final int MIN_VERSION = 1; - public static final int MAX_VERSION = 40; - - - /*---- Instance fields ----*/ // Public immutable scalar parameters @@ -704,7 +697,11 @@ public final class QrCode { } - /*---- Private tables of constants ----*/ + /*---- Constants and tables ----*/ + + public static final int MIN_VERSION = 1; + public static final int MAX_VERSION = 40; + // For use in getPenaltyScore(), when evaluating which mask is best. private static final int PENALTY_N1 = 3; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index f1d2323..a78bc1a 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -599,14 +599,6 @@ var qrcodegen = new function() { }; - /*---- Public constants for QrCode ----*/ - - var MIN_VERSION = 1; - var MAX_VERSION = 40; - Object.defineProperty(this.QrCode, "MIN_VERSION", {value:MIN_VERSION}); - Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); - - /*---- Private static helper functions for QrCode ----*/ var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.QrCode'. @@ -639,7 +631,12 @@ var qrcodegen = new function() { }; - /*---- Private tables of constants for QrCode ----*/ + /*---- Constants and tables for QrCode ----*/ + + var MIN_VERSION = 1; + var MAX_VERSION = 40; + Object.defineProperty(this.QrCode, "MIN_VERSION", {value:MIN_VERSION}); + Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); // For use in getPenaltyScore(), when evaluating which mask is best. QrCode.PENALTY_N1 = 3; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8edd562..f0462f7 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -142,12 +142,6 @@ class QrCode(object): return QrCode(bb.get_bytes(), mask, version, ecl) - # ---- Public constants ---- - - MIN_VERSION = 1 - MAX_VERSION = 40 - - # ---- Constructor ---- def __init__(self, datacodewords, mask, version, errcorlvl): @@ -529,7 +523,10 @@ class QrCode(object): * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] - # ---- Private tables of constants ---- + # ---- Constants and tables ---- + + MIN_VERSION = 1 + MAX_VERSION = 40 # For use in getPenaltyScore(), when evaluating which mask is best. _PENALTY_N1 = 3 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 069b50d..a43958a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -673,14 +673,12 @@ impl QrCode { } -/*---- Public constants ----*/ +/*---- Cconstants and tables ----*/ pub const QrCode_MIN_VERSION: Version = Version( 1); pub const QrCode_MAX_VERSION: Version = Version(40); -/*---- Private tables of constants ----*/ - // For use in get_penalty_score(), when evaluating which mask is best. const PENALTY_N1: i32 = 3; const PENALTY_N2: i32 = 3; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index ab6d1ef..3c11da2 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -604,20 +604,17 @@ namespace qrcodegen { } - /*-- Constants --*/ + /*-- Constants and tables --*/ public static readonly MIN_VERSION: int = 1; public static readonly MAX_VERSION: int = 40; - // For use in getPenaltyScore(), when evaluating which mask is best. private static readonly PENALTY_N1: int = 3; private static readonly PENALTY_N2: int = 3; private static readonly PENALTY_N3: int = 40; private static readonly PENALTY_N4: int = 10; - /*-- Private tables of constants --*/ - private static readonly ECC_CODEWORDS_PER_BLOCK: Array> = [ // 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 From 49e0902d9f460f4f08db8aae1a6984f1d3f1ed63 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 19:56:07 +0000 Subject: [PATCH 481/810] Updated, added, synchronized section comments to show abstraction level in classes QrCode and QrSegment, in all languages except C. --- cpp/QrCode.hpp | 6 ++++-- cpp/QrSegment.hpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 6 ++++-- java/io/nayuki/qrcodegen/QrSegment.java | 4 ++-- javascript/qrcodegen.js | 9 ++++++--- python/qrcodegen.py | 10 ++++++---- rust/src/lib.rs | 10 +++++++--- typescript/qrcodegen.ts | 10 ++++++---- 8 files changed, 37 insertions(+), 22 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index d4115cf..3166edd 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -55,7 +55,7 @@ class QrCode final { - /*---- Public static factory functions ----*/ + /*---- Static factory functions (high level) ----*/ /* * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. @@ -76,6 +76,8 @@ class QrCode final { public: static QrCode encodeBinary(const std::vector &data, Ecc ecl); + /*---- Static factory functions (mid level) ----*/ + /* * Returns a QR Code symbol representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. @@ -113,7 +115,7 @@ class QrCode final { - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ /* * Creates a new QR Code symbol with the given version number, error correction level, binary data array, diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 3cd0ec3..2bb0c3b 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -85,7 +85,7 @@ class QrSegment final { - /*---- Public static factory functions ----*/ + /*---- Static factory functions (mid level) ----*/ /* * Returns a segment representing the given binary data encoded in byte mode. @@ -148,7 +148,7 @@ class QrSegment final { private: std::vector data; - /*---- Constructors ----*/ + /*---- Constructors (low level) ----*/ /* * Creates a new QR Code segment with the given parameters and data. diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index c694f13..d8f601e 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -37,7 +37,7 @@ import java.util.Objects; */ public final class QrCode { - /*---- Public static factory functions ----*/ + /*---- Static factory functions (high level) ----*/ /** * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction level. @@ -80,6 +80,8 @@ public final class QrCode { } + /*---- Static factory functions (mid level) ----*/ + /** * Returns a QR Code symbol 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 @@ -192,7 +194,7 @@ public final class QrCode { - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ /** * Constructs a QR Code symbol with the specified version number, error correction level, binary data array, and mask number. diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 264c735..f997be5 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -39,7 +39,7 @@ import java.util.regex.Pattern; */ public final class QrSegment { - /*---- Static factory functions ----*/ + /*---- Static factory functions (mid level) ----*/ /** * Returns a segment representing the specified binary data encoded in byte mode. @@ -166,7 +166,7 @@ public final class QrSegment { final BitBuffer data; - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ /** * Constructs a QR Code segment with the specified parameters and data. diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index a78bc1a..bb556e3 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -70,7 +70,7 @@ var qrcodegen = new function() { */ this.QrCode = function(datacodewords, mask, version, errCorLvl) { - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ // Check arguments and handle simple scalar fields if (mask < -1 || mask > 7) @@ -510,7 +510,7 @@ var qrcodegen = new function() { }; - /*---- Public static factory functions for QrCode ----*/ + /*---- Static factory functions (high level) for QrCode ----*/ /* * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. @@ -537,6 +537,8 @@ var qrcodegen = new function() { }; + /*---- Static factory functions (mid level) for QrCode ----*/ + /* * Returns a QR Code symbol representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. @@ -698,6 +700,7 @@ var qrcodegen = new function() { * Any segment longer than this is meaningless for the purpose of generating QR Codes. */ this.QrSegment = function(mode, numChars, bitData) { + /*---- Constructor (low level) ----*/ if (numChars < 0 || !(mode instanceof Mode)) throw "Invalid argument"; bitData = bitData.slice(); // Make defensive copy @@ -715,7 +718,7 @@ var qrcodegen = new function() { }; - /*---- Public static factory functions for QrSegment ----*/ + /*---- Static factory functions (mid level) for QrSegment ----*/ /* * Returns a segment representing the given binary data encoded in byte mode. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index f0462f7..223e8d0 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -66,7 +66,7 @@ class QrCode(object): This class covers the QR Code Model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes.""" - # ---- Public static factory functions ---- + # ---- Static factory functions (high level) ---- @staticmethod def encode_text(text, ecl): @@ -90,6 +90,8 @@ class QrCode(object): return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) + # ---- Static factory functions (mid level) ---- + @staticmethod def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): """Returns a QR Code symbol representing the given segments with the given encoding parameters. @@ -142,7 +144,7 @@ class QrCode(object): return QrCode(bb.get_bytes(), mask, version, ecl) - # ---- Constructor ---- + # ---- Constructor (low level) ---- def __init__(self, datacodewords, mask, version, errcorlvl): """Creates a new QR Code symbol with the given version number, error correction level, binary data array, @@ -588,7 +590,7 @@ class QrSegment(object): Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. Any segment longer than this is meaningless for the purpose of generating QR Codes.""" - # ---- Public static factory functions ---- + # ---- Static factory functions (mid level) ---- @staticmethod def make_bytes(data): @@ -673,7 +675,7 @@ class QrSegment(object): return QrSegment(QrSegment.Mode.ECI, 0, bb) - # ---- Constructor ---- + # ---- Constructor (low level) ---- def __init__(self, mode, numch, bitdata): """Creates a new QR Code segment with the given parameters and data.""" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a43958a..93de6c5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -57,7 +57,7 @@ pub struct QrCode { impl QrCode { - /*---- Public static factory functions ----*/ + /*---- Static factory functions (high level) ----*/ // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode @@ -83,6 +83,8 @@ impl QrCode { } + /*---- Static factory functions (mid level) ----*/ + // Returns a QR Code symbol representing the given segments at the given error correction level. // The smallest possible QR Code version is automatically chosen for the output. The ECC level // of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -167,7 +169,7 @@ impl QrCode { } - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ // Creates a new QR Code symbol with the given version number, error correction level, // binary data array, and mask number. This is a cumbersome low-level constructor that @@ -841,7 +843,7 @@ pub struct QrSegment { impl QrSegment { - /*---- Static factory functions ----*/ + /*---- Static factory functions (mid level) ----*/ // Returns a segment representing the given binary data encoded in byte mode. pub fn make_bytes(data: &[u8]) -> Self { @@ -938,6 +940,8 @@ impl QrSegment { } + /*---- Constructor (low level) ----*/ + // Creates a new QR Code segment with the given parameters and data. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { Self { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 3c11da2..cb5d71d 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -44,7 +44,7 @@ namespace qrcodegen { */ export class QrCode { - /*-- Public static factory functions --*/ + /*-- Static factory functions (high level) --*/ // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer @@ -67,6 +67,8 @@ namespace qrcodegen { } + /*-- Static factory functions (mid level) --*/ + // Returns a QR Code symbol representing the given segments with the given encoding parameters. // The smallest possible QR Code version within the given range is automatically chosen for the output. // This function allows the user to create a custom sequence of segments that switches @@ -142,7 +144,7 @@ namespace qrcodegen { private readonly isFunction: Array> = []; - /*-- Constructor and fields --*/ + /*-- Constructor (low level) and fields --*/ public constructor( datacodewords: Array, @@ -654,7 +656,7 @@ namespace qrcodegen { */ export class QrSegment { - /*-- Static factory functions --*/ + /*-- Static factory functions (mid level) --*/ // Returns a segment representing the given binary data encoded in byte mode. public static makeBytes(data: Array): QrSegment { @@ -733,7 +735,7 @@ namespace qrcodegen { } - /*-- Constructor and fields --*/ + /*-- Constructor (low level) and fields --*/ // Creates a new QR Code segment with the given parameters and data. public constructor( From f9a40a31dbce76e3b9e838504063f08c23dcb4cd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 20:07:10 +0000 Subject: [PATCH 482/810] Updated and mostly synchronized comment for BitBuffer.appendBits(), in all languages except C. --- cpp/BitBuffer.hpp | 4 ++-- java/io/nayuki/qrcodegen/BitBuffer.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 2 +- rust/src/lib.rs | 2 +- typescript/qrcodegen.ts | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index 436773b..cd31bb4 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -48,8 +48,8 @@ class BitBuffer final : public std::vector { public: std::vector getBytes() const; - // Appends the given number of low bits of the given value - // to this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. + // Appends the given number of low bits of the given value to + // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. public: void appendBits(std::uint32_t val, int len); }; diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 722a07f..67c4d46 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -93,8 +93,8 @@ public final class BitBuffer implements Cloneable { /** - * Appends the specified number of low bits of the specified value - * to this sequence. Requires 0 ≤ val < 2len. + * Appends the specified number of low bits of the specified value to this + * sequence. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. * @param val the value to append * @param len the number of low bits in the value to take * @throws IllegalArgumentException if the value or number of bits is out of range diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index bb556e3..c1459c5 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -970,8 +970,8 @@ var qrcodegen = new function() { return result; }; - // Appends the given number of low bits of the given value - // to this sequence. Requires 0 <= val < 2^len. + // Appends the given number of low bits of the given value to + // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. this.appendBits = function(val, len) { if (len < 0 || len > 31 || val >>> len != 0) throw "Value out of range"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 223e8d0..d2ca629 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -826,7 +826,7 @@ class _BitBuffer(list): def append_bits(self, val, n): """Appends the given number of low bits of the given value - to this sequence. Requires 0 <= val < 2^n.""" + to this sequence. Requires n >= 0 and 0 <= val < 2^n.""" if n < 0 or val >> n != 0: raise ValueError("Value out of range") self.extend(((val >> i) & 1) for i in reversed(range(n))) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 93de6c5..90a0ecb 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1069,7 +1069,7 @@ pub struct BitBuffer(pub Vec); impl BitBuffer { // Appends the given number of low bits of the given value - // to this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. + // to this sequence. Requires len <= 31 and 0 <= val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index cb5d71d..00c6da5 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -901,8 +901,8 @@ namespace qrcodegen { } - // Appends the given number of low bits of the given - // value to this sequence. Requires 0 <= val < 2^len. + // Appends the given number of low bits of the given value to + // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. public appendBits(val: int, len: int): void { if (len < 0 || len > 31 || val >>> len != 0) throw "Value out of range"; From 4ee7f6df96cc0d93d6a28d4b2480aafb31ad0df8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 4 Oct 2018 22:16:25 +0000 Subject: [PATCH 483/810] Updated documentation comments for two QrSegment.Mode members, in all language versions. --- c/qrcodegen.c | 4 ++-- cpp/QrSegment.hpp | 5 +++-- java/io/nayuki/qrcodegen/QrSegment.java | 7 ++++--- javascript/qrcodegen.js | 3 ++- python/qrcodegen.py | 5 +++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 5 +++-- 7 files changed, 19 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 77a0bda..f197442 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -964,8 +964,8 @@ testable int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int } -// Returns the bit width of the segment character count field for the -// given mode at the given version number. The result is in the range [0, 16]. +// Returns the bit width of the character count field for a segment in the given mode +// in a QR Code at the given version number. The result is in the range [0, 16]. static int numCharCountBits(enum qrcodegen_Mode mode, int version) { assert(qrcodegen_VERSION_MIN <= version && version <= qrcodegen_VERSION_MAX); int i = (version + 7) / 17; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 2bb0c3b..45918ff 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -60,7 +60,7 @@ class QrSegment final { // The mode indicator bits, which is a uint4 value (range 0 to 15). private: int modeBits; - // Three values for different version ranges. + // Number of character count bits for three different version ranges. private: int numBitsCharCount[3]; @@ -77,7 +77,8 @@ class QrSegment final { public: int getModeBits() const; /* - * (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + * (Package-private) 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]. */ public: int numCharCountBits(int ver) const; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index f997be5..927ebaf 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -250,7 +250,7 @@ public final class QrSegment { /** The mode indicator bits, which is a uint4 value (range 0 to 15). */ final int modeBits; - /** Three values for different version ranges. */ + /** Number of character count bits for three different version ranges. */ private final int[] numBitsCharCount; @@ -265,9 +265,10 @@ public final class QrSegment { /*-- Method --*/ /** - * Returns the bit width of the segment character count field for this mode object at the specified version number. + * Returns the bit width of the character count field for a segment + * in this mode in a QR Code at the specified version number. * @param ver the version number, which is between 1 to 40 (inclusive) - * @return the number of bits for the character count, which is between 8 to 16 (inclusive) + * @return the number of bits for the character count, which is between 0 to 16 (inclusive) */ int numCharCountBits(int ver) { assert QrCode.MIN_VERSION <= ver && ver <= QrCode.MAX_VERSION; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index c1459c5..4f7a296 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -856,7 +856,8 @@ var qrcodegen = new function() { // (Package-private) The mode indicator bits, which is a uint4 value (range 0 to 15). Object.defineProperty(this, "modeBits", {value:mode}); - // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + // (Package-private) 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]. this.numCharCountBits = function(ver) { return ccbits[Math.floor((ver + 7) / 17)]; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d2ca629..c4bc172 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -733,7 +733,7 @@ class QrSegment(object): # Private constructor def __init__(self, modebits, charcounts): self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) - self._charcounts = charcounts # Three values for different version ranges + self._charcounts = charcounts # Number of character count bits for three different version ranges # Package-private method def get_mode_bits(self): @@ -742,7 +742,8 @@ class QrSegment(object): # Package-private method def num_char_count_bits(self, ver): - """Returns the bit width of the segment character count field for this mode object at the given version number.""" + """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].""" return self._charcounts[(ver + 7) // 17] # Public constants. Create them outside the class. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 90a0ecb..1db6288 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1044,8 +1044,8 @@ impl QrSegmentMode { } - // Returns the bit width of the segment character count field - // for this mode object at the given version number. + // 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]. pub fn num_char_count_bits(&self, ver: Version) -> u8 { use QrSegmentMode::*; (match *self { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 00c6da5..470324e 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -975,13 +975,14 @@ namespace qrcodegen.QrSegment { private constructor( // The mode indicator bits, which is a uint4 value (range 0 to 15). public readonly modeBits: int, - // Three values for different version ranges. + // Number of character count bits for three different version ranges. private readonly numBitsCharCount: [int,int,int]) {} /*-- Method --*/ - // (Package-private) Returns the bit width of the segment character count field for this mode object at the given version number. + // (Package-private) 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]. public numCharCountBits(ver: int): int { return this.numBitsCharCount[Math.floor((ver + 7) / 17)]; } From 7d49af97e1ada84eee033abd9b808e5d40114103 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:21:12 +0000 Subject: [PATCH 484/810] Updated documentation comments for three QrSegment members, in all language versions. --- c/qrcodegen.h | 8 ++++---- cpp/QrSegment.hpp | 12 +++++++++--- java/io/nayuki/qrcodegen/QrSegment.java | 12 ++++++++---- javascript/qrcodegen.js | 7 ++++++- python/qrcodegen.py | 9 ++++++++- rust/src/lib.rs | 9 ++++++--- typescript/qrcodegen.ts | 6 +++++- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 05b8b8f..7b93ca5 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -85,10 +85,9 @@ struct qrcodegen_Segment { // The mode indicator for this segment. enum qrcodegen_Mode mode; - // The length of this segment's unencoded data. Always in the range [0, 32767]. - // For numeric, alphanumeric, and kanji modes, this measures in Unicode code points. - // For byte mode, this measures in bytes (raw binary data, text in UTF-8, or other encodings). - // For ECI mode, this is always zero. + // The length of this segment's unencoded data, measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. int numChars; // The data bits of this segment, packed in bitwise big endian. @@ -97,6 +96,7 @@ struct qrcodegen_Segment { // The number of valid data bits used in the buffer. Requires // 0 <= bitLength <= 32767, and bitLength <= (capacity of data array) * 8. + // The character count (numChars) must agree with the mode and the bit buffer length. int bitLength; }; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 45918ff..65246bd 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -109,8 +109,8 @@ class QrSegment final { /* - * Returns a list of zero or more segments to represent the given text string. - * The result may use various segment modes and switch modes to optimize the length of the bit stream. + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. */ public: static std::vector makeSegments(const char *text); @@ -142,7 +142,9 @@ class QrSegment final { /* The mode indicator for this segment. */ private: Mode mode; - /* The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + /* The length of this segment's unencoded data, measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. */ private: int numChars; /* The data bits of this segment. */ @@ -153,12 +155,16 @@ class QrSegment final { /* * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. */ public: QrSegment(Mode md, int numCh, const std::vector &dt); /* * Creates a new QR Code data segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. */ public: QrSegment(Mode md, int numCh, std::vector &&dt); diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 927ebaf..b9409b9 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -106,10 +106,10 @@ public final class QrSegment { /** - * Returns a new mutable list of zero or more segments to represent the specified Unicode text string. + * Returns a list of zero or more segments to represent the specified Unicode text string. * The result may use various segment modes and switch modes to optimize the length of the bit stream. * @param text the text to be encoded, which can be any Unicode string - * @return a list of segments containing the text + * @return a new mutable list of segments containing the text * @throws NullPointerException if the text is {@code null} */ public static List makeSegments(String text) { @@ -159,7 +159,9 @@ public final class QrSegment { /** The mode indicator for this segment. Never {@code null}. */ public final Mode mode; - /** The length of this segment's unencoded data, measured in characters. Always zero or positive. */ + /** The length of this segment's unencoded data, measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. */ public final int numChars; /** The data bits of this segment. Accessed through {@link getBits()}. Not {@code null}. */ @@ -170,8 +172,10 @@ public final class QrSegment { /** * Constructs a QR Code segment with the specified parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The specified bit buffer is cloned and stored. * @param md the mode, which is not {@code null} - * @param numCh the data length in characters, which is non-negative + * @param numCh the data length in characters or bytes, which is non-negative * @param data the data bits of this segment, which is not {@code null} * @throws NullPointerException if the mode or data is {@code null} * @throws IllegalArgumentException if the character count is negative diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 4f7a296..97754fa 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -698,6 +698,9 @@ var qrcodegen = new function() { * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * This constructor creates a QR Code segment with the given parameters and data. + * The character count (numChars) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is cloned and stored. */ this.QrSegment = function(mode, numChars, bitData) { /*---- Constructor (low level) ----*/ @@ -708,7 +711,9 @@ var qrcodegen = new function() { // The mode indicator for this segment. Object.defineProperty(this, "mode", {value:mode}); - // The length of this segment's unencoded data, measured in characters. Always zero or positive. + // The length of this segment's unencoded data, measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. Object.defineProperty(this, "numChars", {value:numChars}); // Returns a copy of all bits, which is an array of 0s and 1s. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index c4bc172..bfa9369 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -678,11 +678,18 @@ class QrSegment(object): # ---- Constructor (low level) ---- def __init__(self, mode, numch, bitdata): - """Creates a new QR Code segment with the given parameters and data.""" + """Creates a new QR Code segment with the given parameters and data. + The character count (numch) must agree with the mode and the bit buffer length, + but the constraint isn't checked. The given bit buffer is cloned and stored.""" if numch < 0 or not isinstance(mode, QrSegment.Mode): raise ValueError() self._mode = mode + + # The length of this segment's unencoded data, measured in characters for + # numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + # Always zero or positive. self._numchars = numch + self._bitdata = list(bitdata) # Make defensive copy diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1db6288..05b96ee 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -832,7 +832,8 @@ pub struct QrSegment { // The mode indicator for this segment. mode: QrSegmentMode, - // The length of this segment's unencoded data, measured in characters. + // The length of this segment's unencoded data, measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. numchars: usize, // The bits of this segment. @@ -905,8 +906,8 @@ impl QrSegment { } - // Returns a new mutable list of zero or more segments to represent the given Unicode text string. - // The result may use various segment modes and switch modes to optimize the length of the bit stream. + // Returns a list of zero or more segments to represent the given Unicode text string. The result + // may use various segment modes and switch modes to optimize the length of the bit stream. pub fn make_segments(text: &[char]) -> Vec { if text.is_empty() { vec![] @@ -943,6 +944,8 @@ impl QrSegment { /*---- Constructor (low level) ----*/ // Creates a new QR Code segment with the given parameters and data. + // The character count (numchars) must agree with the mode and + // the bit buffer length, but the constraint isn't checked. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { Self { mode: mode, diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 470324e..6deaaea 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -738,11 +738,15 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ // Creates a new QR Code segment with the given parameters and data. + // The character count (numChars) must agree with the mode and the bit buffer length, + // but the constraint isn't checked. The given bit buffer is cloned and stored. public constructor( // The mode indicator for this segment. public readonly mode: QrSegment.Mode, - // The length of this segment's unencoded data, measured in characters. Always zero or positive. + // The length of this segment's unencoded data, measured in characters for + // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Always zero or positive. public readonly numChars: int, private readonly bitData: Array) { From ac6ef8fe318345f7904050ca56278b55ade7286c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:23:09 +0000 Subject: [PATCH 485/810] Tweaked some miscellaneous code. --- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- python/qrcodegen.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index b9409b9..b662ca7 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -144,7 +144,7 @@ public final class QrSegment { else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if (assignVal < 1000000) { + } else if (assignVal < 1_000_000) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else diff --git a/python/qrcodegen.py b/python/qrcodegen.py index bfa9369..730cd1e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -681,7 +681,9 @@ class QrSegment(object): """Creates a new QR Code segment with the given parameters and data. The character count (numch) must agree with the mode and the bit buffer length, but the constraint isn't checked. The given bit buffer is cloned and stored.""" - if numch < 0 or not isinstance(mode, QrSegment.Mode): + if not isinstance(mode, QrSegment.Mode): + raise TypeError("QrSegment.Mode expected") + if numch < 0: raise ValueError() self._mode = mode From 00fb83d4d5733fa98bfeab430458cf8f3c1041c1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:23:28 +0000 Subject: [PATCH 486/810] Added and updated miscellaneous comments. --- cpp/QrSegment.hpp | 2 +- python/qrcodegen.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 65246bd..efe74a2 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -162,7 +162,7 @@ class QrSegment final { /* - * Creates a new QR Code data segment with the given parameters and data. + * Creates a new QR Code segment with the given parameters and data. * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The given bit buffer is moved and stored. */ diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 730cd1e..f02e5af 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -685,6 +685,8 @@ class QrSegment(object): raise TypeError("QrSegment.Mode expected") if numch < 0: raise ValueError() + + # The mode indicator for this segment. self._mode = mode # The length of this segment's unencoded data, measured in characters for @@ -692,6 +694,7 @@ class QrSegment(object): # Always zero or positive. self._numchars = numch + # The data bits of this segment. Accessed through get_bits(). self._bitdata = list(bitdata) # Make defensive copy From 028b3774725e46b2b89c007baaebf3b01ae66e1d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:30:10 +0000 Subject: [PATCH 487/810] Updated a section comment in class QrCode, in most languages. --- cpp/QrCode.hpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 2 +- python/qrcodegen.py | 2 +- rust/src/lib.rs | 2 +- typescript/qrcodegen.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 3166edd..a9caf80 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -223,7 +223,7 @@ class QrCode final { - /*---- Private static helper functions ----*/ + /*---- 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. diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d8f601e..e63b6ee 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -634,7 +634,7 @@ public final class QrCode { - /*---- Private static helper functions ----*/ + /*---- 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. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index f02e5af..b4238b4 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -482,7 +482,7 @@ class QrCode(object): return result - # ---- Private static helper functions ---- + # ---- Private helper functions ---- def _get_alignment_pattern_positions(self): """Returns an ascending list of positions of alignment patterns for this version number. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 05b96ee..1821946 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -618,7 +618,7 @@ impl QrCode { } - /*---- Private static helper functions ----*/ + /*---- 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. diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 6deaaea..2353ce1 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -559,7 +559,7 @@ namespace qrcodegen { } - /*-- Private static helper functions QrCode --*/ + /*-- 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. From eab76f20d63c7b069ff096b73fa2cb46d7a6c03e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:40:49 +0000 Subject: [PATCH 488/810] Updated class QrCode's field comments, in most languages. --- cpp/QrCode.hpp | 12 ++++++++---- java/io/nayuki/qrcodegen/QrCode.java | 12 ++++++++---- python/qrcodegen.py | 8 ++++++-- rust/src/lib.rs | 8 ++++++-- typescript/qrcodegen.ts | 4 ++-- 5 files changed, 30 insertions(+), 14 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index a9caf80..3de3bc2 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -92,7 +92,7 @@ class QrCode final { /*---- Instance fields ----*/ - // Immutable scalar parameters + // Immutable scalar parameters: /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ private: int version; @@ -109,9 +109,13 @@ class QrCode final { * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ private: int mask; - // Private grids of modules/pixels (conceptually immutable) - private: std::vector > modules; // The modules of this QR Code symbol (false = white, true = black) - private: std::vector > isFunction; // Indicates function modules that are not subjected to masking + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. + private: std::vector > modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private: std::vector > isFunction; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index e63b6ee..24603bf 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -171,7 +171,7 @@ public final class QrCode { /*---- Instance fields ----*/ - // Public immutable scalar parameters + // Public immutable scalar parameters: /** This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ public final int version; @@ -188,9 +188,13 @@ public final class QrCode { * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ public final int mask; - // Private grids of modules/pixels (conceptually immutable) - private boolean[][] modules; // The modules of this QR Code symbol (false = white, true = black) - private boolean[][] isFunction; // Indicates function modules that are not subjected to masking + // Private grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. + private boolean[][] modules; + + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private boolean[][] isFunction; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index b4238b4..190dee2 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -164,9 +164,13 @@ class QrCode(object): if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): raise ValueError("Invalid array length") + # Initialize grids of modules - self._modules = [[False] * self._size for _ in range(self._size)] # The modules of the QR symbol; start with entirely white grid - self._isfunction = [[False] * self._size for _ in range(self._size)] # Indicates function modules that are not subjected to masking + # The modules of this QR Code symbol (False = white, True = black). Immutable after constructor finishes + self._modules = [[False] * self._size for _ in range(self._size)] + # Indicates function modules that are not subjected to masking. Discarded when constructor finishes + self._isfunction = [[False] * self._size for _ in range(self._size)] + # Draw function patterns, draw all codewords self._draw_function_patterns() allcodewords = self._add_ecc_and_interleave(datacodewords) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1821946..ac10d47 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -31,6 +31,8 @@ #[derive(Clone)] pub struct QrCode { + // Scalar parameters: + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). version: Version, @@ -46,10 +48,12 @@ pub struct QrCode { // (mask = -1), the resulting object will still have a mask value between 0 and 7. mask: Mask, - // The modules of this QR Code symbol (false = white, true = black) + // Grids of modules/pixels, with dimensions of size*size: + + // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. modules: Vec, - // Indicates function modules that are not subjected to masking + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. isfunction: Vec, } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 2353ce1..36015b9 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -137,10 +137,10 @@ namespace qrcodegen { // Always equal to version * 4 + 17, in the range 21 to 177. public readonly size: int; - // The modules of this QR Code symbol (false = white, true = black). + // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. private readonly modules: Array> = []; - // Indicates function modules that are not subjected to masking. + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. private readonly isFunction: Array> = []; From ae7b5296f2529322b79ff10a1697f4f524554f1e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:50:31 +0000 Subject: [PATCH 489/810] Renamed a constructor parameter in JavaScript and TypeScript code. --- javascript/qrcodegen.js | 6 +++--- typescript/qrcodegen.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 97754fa..15c312e 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -32,7 +32,7 @@ * - Function encodeSegments(list segs, QrCode.Ecc ecl, * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constants int MIN_VERSION, MAX_VERSION - * - Constructor QrCode(list datacodewords, int mask, int version, QrCode.Ecc ecl) + * - Constructor QrCode(list dataCodewords, int mask, int version, QrCode.Ecc ecl) * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> bool @@ -68,7 +68,7 @@ var qrcodegen = new function() { * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. */ - this.QrCode = function(datacodewords, mask, version, errCorLvl) { + this.QrCode = function(dataCodewords, mask, version, errCorLvl) { /*---- Constructor (low level) ----*/ @@ -92,7 +92,7 @@ var qrcodegen = new function() { // Handle grid fields, draw function patterns, draw all codewords drawFunctionPatterns(); - var allCodewords = addEccAndInterleave(datacodewords); + var allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); // Handle masking diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 36015b9..8e36f40 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -147,7 +147,7 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ public constructor( - datacodewords: Array, + dataCodewords: Array, // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if the constructor was called with automatic masking requested @@ -178,7 +178,7 @@ namespace qrcodegen { // Handle grid fields, draw function patterns, draw all codewords this.drawFunctionPatterns(); - let allCodewords: Array = this.addEccAndInterleave(datacodewords); + let allCodewords: Array = this.addEccAndInterleave(dataCodewords); this.drawCodewords(allCodewords); // Handle masking From fd64af3e506e40389d5251c681f825779b780d35 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 00:56:29 +0000 Subject: [PATCH 490/810] Rearranged QrCode constructor parameters in JavaScript/TypeScript/Python code to match order in Java/C++/Rust. --- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 6 +++--- typescript/qrcodegen.ts | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 15c312e..499c038 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -32,7 +32,7 @@ * - Function encodeSegments(list segs, QrCode.Ecc ecl, * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constants int MIN_VERSION, MAX_VERSION - * - Constructor QrCode(list dataCodewords, int mask, int version, QrCode.Ecc ecl) + * - Constructor QrCode(int version, QrCode.Ecc ecl, list dataCodewords, int mask) * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> bool @@ -68,7 +68,7 @@ var qrcodegen = new function() { * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. */ - this.QrCode = function(dataCodewords, mask, version, errCorLvl) { + this.QrCode = function(version, errCorLvl, dataCodewords, mask) { /*---- Constructor (low level) ----*/ @@ -597,7 +597,7 @@ var qrcodegen = new function() { bb.appendBits(padByte, 8); // Create the QR Code symbol - return new this(bb.getBytes(), mask, version, ecl); + return new this(version, ecl, bb.getBytes(), mask); }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 190dee2..dbbd5d3 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -32,7 +32,7 @@ This module "qrcodegen", public members: - Function encode_segments(list segs, QrCode.Ecc ecl, int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - Constants int MIN_VERSION, MAX_VERSION - - Constructor QrCode(bytes datacodewords, int mask, int version, QrCode.Ecc ecl) + - Constructor QrCode(int version, QrCode.Ecc ecl, bytes datacodewords, int mask) - Method get_version() -> int - Method get_size() -> int - Method get_error_correction_level() -> QrCode.Ecc @@ -141,12 +141,12 @@ class QrCode(object): bb.append_bits(padbyte, 8) # Create the QR Code symbol - return QrCode(bb.get_bytes(), mask, version, ecl) + return QrCode(version, ecl, bb.get_bytes(), mask) # ---- Constructor (low level) ---- - def __init__(self, datacodewords, mask, version, errcorlvl): + def __init__(self, version, errcorlvl, datacodewords, mask): """Creates a new QR Code symbol with the given version number, error correction level, binary data array, and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor that should not be invoked directly by the user. To go one level up, see the QrCode.encode_segments() function.""" diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 8e36f40..d8d12e9 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -127,7 +127,7 @@ namespace qrcodegen { bb.appendBits(padByte, 8); // Create the QR Code symbol - return new QrCode(bb.getBytes(), mask, version, ecl); + return new QrCode(version, ecl, bb.getBytes(), mask); } @@ -147,18 +147,18 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ public constructor( + // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + public readonly version: int, + + // The error correction level used in this QR Code symbol. + public readonly errorCorrectionLevel: QrCode.Ecc, + dataCodewords: Array, // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if the constructor was called with automatic masking requested // (mask = -1), the resulting object will still have a mask value between 0 and 7. - public readonly mask: int, - - // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). - public readonly version: int, - - // The error correction level used in this QR Code symbol. - public readonly errorCorrectionLevel: QrCode.Ecc) { + public readonly mask: int) { // Check arguments and handle simple scalar fields if (mask < -1 || mask > 7) From 0a0ac19f9ba0bcf729fe1bdfb6cc0c84e68fb50d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 01:42:57 +0000 Subject: [PATCH 491/810] Aligned code involving QrCode grid fields, in most languages. --- cpp/QrCode.cpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 2 +- javascript/qrcodegen.js | 4 ++-- rust/src/lib.rs | 2 +- typescript/qrcodegen.ts | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 3a9b25a..ab6039c 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -119,7 +119,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) version(ver), size(MIN_VERSION <= ver && ver <= MAX_VERSION ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior errorCorrectionLevel(ecl), - modules(size, vector(size)), // Entirely white grid + modules (size, vector(size)), // Entirely white grid isFunction(size, vector(size)) { // Check arguments diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 24603bf..83f17f7 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -222,7 +222,7 @@ public final class QrCode { // Initialize fields version = ver; size = ver * 4 + 17; - modules = new boolean[size][size]; // Entirely white grid + modules = new boolean[size][size]; // Entirely white grid isFunction = new boolean[size][size]; // Draw function patterns, draw all codewords, do masking diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 499c038..efe2900 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -83,10 +83,10 @@ var qrcodegen = new function() { var row = []; for (var i = 0; i < size; i++) row.push(false); - var modules = []; + var modules = []; var isFunction = []; for (var i = 0; i < size; i++) { - modules.push(row.slice()); + modules .push(row.slice()); isFunction.push(row.slice()); } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ac10d47..b0d75bd 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -186,7 +186,7 @@ impl QrCode { size: size as i32, mask: Mask::new(0), // Dummy value errorcorrectionlevel: ecl, - modules: vec![false; size * size], // Entirely white grid + modules : vec![false; size * size], // Entirely white grid isfunction: vec![false; size * size], }; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d8d12e9..fbe04ad 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -138,7 +138,7 @@ namespace qrcodegen { public readonly size: int; // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. - private readonly modules: Array> = []; + private readonly modules : Array> = []; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. private readonly isFunction: Array> = []; @@ -172,7 +172,7 @@ namespace qrcodegen { for (let i = 0; i < this.size; i++) row.push(false); for (let i = 0; i < this.size; i++) { - this.modules.push(row.slice()); + this.modules .push(row.slice()); this.isFunction.push(row.slice()); } From f63f7f79a651cdfb1efbf8e564bb4f787cdea4fa Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 01:44:06 +0000 Subject: [PATCH 492/810] Tweaked comment and blank line in QrCode constructor in some languages. --- javascript/qrcodegen.js | 3 ++- python/qrcodegen.py | 3 ++- typescript/qrcodegen.ts | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index efe2900..5c6835a 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -95,7 +95,7 @@ var qrcodegen = new function() { var allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - // Handle masking + // Do masking if (mask == -1) { // Automatically choose best mask var minPenalty = Infinity; for (var i = 0; i < 8; i++) { @@ -113,6 +113,7 @@ var qrcodegen = new function() { throw "Assertion error"; drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask + isFunction = null; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index dbbd5d3..0313f15 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -176,7 +176,7 @@ class QrCode(object): allcodewords = self._add_ecc_and_interleave(datacodewords) self._draw_codewords(allcodewords) - # Handle masking + # Do masking if mask == -1: # Automatically choose best mask minpenalty = 1 << 32 for i in range(8): @@ -191,6 +191,7 @@ class QrCode(object): self._draw_format_bits(mask) # Overwrite old format bits self._apply_mask(mask) # Apply the final choice of mask self._mask = mask + del self._isfunction diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index fbe04ad..52d8cec 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -181,7 +181,7 @@ namespace qrcodegen { let allCodewords: Array = this.addEccAndInterleave(dataCodewords); this.drawCodewords(allCodewords); - // Handle masking + // Do masking if (mask == -1) { // Automatically choose best mask let minPenalty: int = 1000000000; for (let i = 0; i < 8; i++) { @@ -200,6 +200,7 @@ namespace qrcodegen { this.mask = mask; this.drawFormatBits(mask); // Overwrite old format bits this.applyMask(mask); // Apply the final choice of mask + this.isFunction = []; } From d060e97e038b732efb59541debf02863dc1acd37 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 01:47:05 +0000 Subject: [PATCH 493/810] Tweaked two comments in QrCode constructor, in all languages except C. --- cpp/QrCode.cpp | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index ab6039c..52281f7 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -119,14 +119,14 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) version(ver), size(MIN_VERSION <= ver && ver <= MAX_VERSION ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior errorCorrectionLevel(ecl), - modules (size, vector(size)), // Entirely white grid + modules (size, vector(size)), // Initially all white isFunction(size, vector(size)) { // Check arguments if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw std::domain_error("Value out of range"); - // Draw function patterns, draw all codewords, do masking + // Compute ECC, draw modules, do masking drawFunctionPatterns(); const vector allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 83f17f7..d89781c 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -222,10 +222,10 @@ public final class QrCode { // Initialize fields version = ver; size = ver * 4 + 17; - modules = new boolean[size][size]; // Entirely white grid + modules = new boolean[size][size]; // Initially all white isFunction = new boolean[size][size]; - // Draw function patterns, draw all codewords, do masking + // Compute ECC, draw modules, do masking drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 5c6835a..25d2d09 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -83,14 +83,14 @@ var qrcodegen = new function() { var row = []; for (var i = 0; i < size; i++) row.push(false); - var modules = []; + var modules = []; // Initially all white var isFunction = []; for (var i = 0; i < size; i++) { modules .push(row.slice()); isFunction.push(row.slice()); } - // Handle grid fields, draw function patterns, draw all codewords + // Compute ECC, draw modules drawFunctionPatterns(); var allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 0313f15..d07a6cf 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -167,11 +167,11 @@ class QrCode(object): # Initialize grids of modules # The modules of this QR Code symbol (False = white, True = black). Immutable after constructor finishes - self._modules = [[False] * self._size for _ in range(self._size)] + self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white # Indicates function modules that are not subjected to masking. Discarded when constructor finishes self._isfunction = [[False] * self._size for _ in range(self._size)] - # Draw function patterns, draw all codewords + # Compute ECC, draw modules self._draw_function_patterns() allcodewords = self._add_ecc_and_interleave(datacodewords) self._draw_codewords(allcodewords) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b0d75bd..e8e8525 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -186,11 +186,11 @@ impl QrCode { size: size as i32, mask: Mask::new(0), // Dummy value errorcorrectionlevel: ecl, - modules : vec![false; size * size], // Entirely white grid + modules : vec![false; size * size], // Initially all white isfunction: vec![false; size * size], }; - // Draw function patterns, draw all codewords, do masking + // Compute ECC, draw modules, do masking result.draw_function_patterns(); let allcodewords: Vec = result.add_ecc_and_interleave(datacodewords); result.draw_codewords(&allcodewords); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 52d8cec..dde5e87 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -172,11 +172,11 @@ namespace qrcodegen { for (let i = 0; i < this.size; i++) row.push(false); for (let i = 0; i < this.size; i++) { - this.modules .push(row.slice()); + this.modules .push(row.slice()); // Initially all white this.isFunction.push(row.slice()); } - // Handle grid fields, draw function patterns, draw all codewords + // Compute ECC, draw modules this.drawFunctionPatterns(); let allCodewords: Array = this.addEccAndInterleave(dataCodewords); this.drawCodewords(allCodewords); From 06d80aade31dda4b9cff19e516e2208b9b07cced Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 02:14:43 +0000 Subject: [PATCH 494/810] Tweaked comments and statement order in QrCode constructor, in most languages. --- cpp/QrCode.cpp | 6 ++---- java/io/nayuki/qrcodegen/QrCode.java | 8 +++----- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 10 +++++----- typescript/qrcodegen.ts | 6 +++--- 5 files changed, 16 insertions(+), 20 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 52281f7..1a24bf4 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -115,16 +115,14 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) : - // Initialize fields + // Initialize fields and check arguments version(ver), - size(MIN_VERSION <= ver && ver <= MAX_VERSION ? ver * 4 + 17 : -1), // Avoid signed overflow undefined behavior errorCorrectionLevel(ecl), modules (size, vector(size)), // Initially all white isFunction(size, vector(size)) { - - // Check arguments if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw std::domain_error("Value out of range"); + size = ver * 4 + 17; // Compute ECC, draw modules, do masking drawFunctionPatterns(); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index d89781c..42373d6 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -213,15 +213,13 @@ public final class QrCode { * 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 mask) { - // Check arguments - errorCorrectionLevel = Objects.requireNonNull(ecl); - Objects.requireNonNull(dataCodewords); + // Check arguments and initialize fields if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) throw new IllegalArgumentException("Value out of range"); - - // Initialize fields version = ver; size = ver * 4 + 17; + errorCorrectionLevel = Objects.requireNonNull(ecl); + Objects.requireNonNull(dataCodewords); modules = new boolean[size][size]; // Initially all white isFunction = new boolean[size][size]; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 25d2d09..d9567bf 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -72,11 +72,11 @@ var qrcodegen = new function() { /*---- Constructor (low level) ----*/ - // Check arguments and handle simple scalar fields - if (mask < -1 || mask > 7) - throw "Mask value out of range"; + // Check scalar arguments if (version < MIN_VERSION || version > MAX_VERSION) throw "Version value out of range"; + if (mask < -1 || mask > 7) + throw "Mask value out of range"; var size = version * 4 + 17; // Initialize both grids to be size*size arrays of Boolean false diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d07a6cf..2159850 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -151,21 +151,21 @@ class QrCode(object): and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor that should not be invoked directly by the user. To go one level up, see the QrCode.encode_segments() function.""" - # Check arguments and handle simple scalar fields - if not (-1 <= mask <= 7): - raise ValueError("Mask value out of range") + # Check scalar arguments and set fields if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): raise ValueError("Version value out of range") + if not (-1 <= mask <= 7): + raise ValueError("Mask value out of range") if not isinstance(errcorlvl, QrCode.Ecc): raise TypeError("QrCode.Ecc expected") self._version = version - self._errcorlvl = errcorlvl self._size = version * 4 + 17 + self._errcorlvl = errcorlvl if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): raise ValueError("Invalid array length") - # Initialize grids of modules + # Initialize both grids to be size*size arrays of Boolean false # The modules of this QR Code symbol (False = white, True = black). Immutable after constructor finishes self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white # Indicates function modules that are not subjected to masking. Discarded when constructor finishes diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index dde5e87..82a42e6 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -160,11 +160,11 @@ namespace qrcodegen { // (mask = -1), the resulting object will still have a mask value between 0 and 7. public readonly mask: int) { - // Check arguments and handle simple scalar fields - if (mask < -1 || mask > 7) - throw "Mask value out of range"; + // Check scalar arguments if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) throw "Version value out of range"; + if (mask < -1 || mask > 7) + throw "Mask value out of range"; this.size = version * 4 + 17; // Initialize both grids to be size*size arrays of Boolean false From b6bbaddf4f684f4ce8a012a5612eae0e7d21f230 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 02:27:00 +0000 Subject: [PATCH 495/810] Updated QrCode constructor argument checks in most languages to synchronize logic. --- cpp/QrCode.cpp | 6 ++++-- java/io/nayuki/qrcodegen/QrCode.java | 6 ++++-- javascript/qrcodegen.js | 2 ++ python/qrcodegen.py | 3 --- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 1a24bf4..4500f76 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -120,8 +120,10 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) errorCorrectionLevel(ecl), modules (size, vector(size)), // Initially all white isFunction(size, vector(size)) { - if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) - throw std::domain_error("Value out of range"); + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw std::domain_error("Version value out of range"); + if (mask < -1 || mask > 7) + throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; // Compute ECC, draw modules, do masking diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 42373d6..f26b95c 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -214,8 +214,10 @@ public final class QrCode { */ public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { // Check arguments and initialize fields - if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) - throw new IllegalArgumentException("Value out of range"); + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw new IllegalArgumentException("Version value out of range"); + if (mask < -1 || mask > 7) + throw new IllegalArgumentException("Mask value out of range"); version = ver; size = ver * 4 + 17; errorCorrectionLevel = Objects.requireNonNull(ecl); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index d9567bf..b3010e3 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -77,6 +77,8 @@ var qrcodegen = new function() { throw "Version value out of range"; if (mask < -1 || mask > 7) throw "Mask value out of range"; + if (!(errCorLvl instanceof Ecc)) + throw "QrCode.Ecc expected"; var size = version * 4 + 17; // Initialize both grids to be size*size arrays of Boolean false diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 2159850..748072b 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -162,9 +162,6 @@ class QrCode(object): self._size = version * 4 + 17 self._errcorlvl = errcorlvl - if len(datacodewords) != QrCode._get_num_data_codewords(version, errcorlvl): - raise ValueError("Invalid array length") - # Initialize both grids to be size*size arrays of Boolean false # The modules of this QR Code symbol (False = white, True = black). Immutable after constructor finishes self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white From 492ebeb5ab7566b80caafa5804354fe8103e6ae2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 04:35:08 +0000 Subject: [PATCH 496/810] Fixed Javadoc syntax errors. --- java/io/nayuki/qrcodegen/QrCode.java | 2 +- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index f26b95c..b210698 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -116,7 +116,7 @@ public final class QrCode { * @return a QR Code representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * is violated, or if mask < −1 or mask > 7, or if the segments fail + * is violated, or if mask < −1 or mask > 7, or 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) { diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index dfad00b..a63225e 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -229,7 +229,7 @@ public final class QrSegmentAdvanced { /** * Returns a segment representing the specified string encoded in kanji mode. *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, - * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
+ * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
* Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. * @param text the text to be encoded, which must fall in the kanji mode subset of characters * @return a segment containing the data @@ -253,7 +253,7 @@ public final class QrSegmentAdvanced { /** * Tests whether the specified text string can be encoded as a segment in kanji mode. *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, - * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
+ * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
* Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. * @param text the string to test for encodability * @return {@code true} if and only if the string can be encoded in kanji mode From c3992b646f01bfa2e9759734d96837fdbe6b4816 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 07:28:19 +0000 Subject: [PATCH 497/810] Tweaked Javadoc HTML code to convert all non-XML-predefined character entities to numeric form, for better compatibility with XHTML. --- java/io/nayuki/qrcodegen/BitBuffer.java | 4 ++-- java/io/nayuki/qrcodegen/QrCode.java | 12 ++++++------ java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 67c4d46..f0f7ecb 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -68,7 +68,7 @@ public final class BitBuffer implements Cloneable { * Returns the bit at the specified index, yielding 0 or 1. * @param index the index to get the bit at * @return the bit at the specified index - * @throws IndexOutOfBoundsException if index < 0 or index ≥ bitLength + * @throws IndexOutOfBoundsException if index < 0 or index ≥ bitLength */ public int getBit(int index) { if (index < 0 || index >= bitLength) @@ -94,7 +94,7 @@ public final class BitBuffer implements Cloneable { /** * Appends the specified number of low bits of the specified value to this - * sequence. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. + * sequence. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. * @param val the value to append * @param len the number of low bits in the value to take * @throws IllegalArgumentException if the value or number of bits is out of range diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index b210698..2d3ed61 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -115,8 +115,8 @@ public final class QrCode { * @param boostEcl increases the error correction level if it can be done without increasing the version number * @return a QR Code representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * is violated, or if mask < −1 or mask > 7, or if the segments fail + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 + * is violated, or if mask < −1 or mask > 7, or 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) { @@ -177,7 +177,7 @@ public final class QrCode { public final int version; /** The width and height of this QR Code symbol, measured in modules. - * Always equal to version × 4 + 17, in the range 21 to 177 (inclusive). */ + * Always equal to version × 4 + 17, in the range 21 to 177 (inclusive). */ public final int size; /** The error correction level used in this QR Code symbol. Never {@code null}. */ @@ -207,7 +207,7 @@ public final class QrCode { * @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 dataCodewords the bytes representing segments to encode (without ECC) - * @param mask 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 @@ -241,8 +241,8 @@ public final class QrCode { * Returns the color of the module (pixel) at the specified coordinates, which is either * false for white or true for black. The top left corner has the coordinates (x=0, y=0). * If the specified coordinates are out of bounds, then false (white) is returned. - * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge - * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge + * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge + * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge * @return the module's color, which is either false (white) or true (black) */ public boolean getModule(int x, int y) { diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index a63225e..4cac7fd 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -50,7 +50,7 @@ public final class QrSegmentAdvanced { * @param maxVersion the maximum allowed version of the QR symbol (at most 40) * @return a new mutable list of segments containing the text, minimizing the bit length with respect to the constraints * @throws NullPointerException if the data or error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, * or if the data is too long to fit in a QR Code at maxVersion at the ECL */ public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { From b89bb889a54ce849b29c94ec062f7df39bfcc702 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 07:29:20 +0000 Subject: [PATCH 498/810] Tweaked C++ and Rust code to not use character entities (left over from copying Javadoc). --- cpp/QrCode.hpp | 2 +- rust/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 3de3bc2..94b72e5 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -98,7 +98,7 @@ class QrCode final { private: int version; /* The width and height of this QR Code symbol, measured in modules. - * Always equal to version × 4 + 17, in the range 21 to 177. */ + * Always equal to version * 4 + 17, in the range 21 to 177. */ private: int size; /* The error correction level used in this QR Code symbol. */ diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e8e8525..baa0ac9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -37,7 +37,7 @@ pub struct QrCode { version: Version, // The width and height of this QR Code symbol, measured in modules. - // Always equal to version × 4 + 17, in the range 21 to 177. + // Always equal to version * 4 + 17, in the range 21 to 177. size: i32, // The error correction level used in this QR Code symbol. From 1f2de4bbbaee70ccf8a2882f1eb19143e71d202e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 07:32:35 +0000 Subject: [PATCH 499/810] Tweaked Java QrSegmentAdvanced to be non-instantiable. --- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index 4cac7fd..f7e6969 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -400,4 +400,10 @@ public final class QrSegmentAdvanced { } } + + + /*---- Miscellaneous ----*/ + + private QrSegmentAdvanced() {} // Not instantiable + } From 29479efedfb93684eecc2f153563017c42e01bcf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 17:35:18 +0000 Subject: [PATCH 500/810] Updated class BitBuffer's documentation comments, in all languages. --- c/qrcodegen.c | 4 ++-- cpp/BitBuffer.hpp | 10 +++++----- java/io/nayuki/qrcodegen/BitBuffer.java | 20 ++++++++++---------- javascript/qrcodegen.js | 10 +++++----- python/qrcodegen.py | 10 +++++----- rust/src/lib.rs | 6 +++--- typescript/qrcodegen.ts | 11 ++++++----- 7 files changed, 36 insertions(+), 35 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index f197442..de13309 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -176,8 +176,8 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod } -// Appends the given sequence of bits to the given byte-based bit buffer, increasing the bit length. -// Requires 0 <= numBits <= 16 and 0 <= val < 2^numBits. +// Appends the given number of low-order bits of the given value to the given byte-based +// bit buffer, increasing the bit length. Requires 0 <= numBits <= 16 and val < 2^numBits. testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bitLen) { assert(0 <= numBits && numBits <= 16 && (unsigned long)val >> numBits == 0); for (int i = numBits - 1; i >= 0; i--, (*bitLen)++) diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index cd31bb4..bccabf1 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -30,7 +30,7 @@ namespace qrcodegen { /* - * An appendable sequence of bits (0s and 1s). + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. */ class BitBuffer final : public std::vector { @@ -43,13 +43,13 @@ class BitBuffer final : public std::vector { /*---- Methods ----*/ - // Packs this buffer's bits into bytes in big endian, - // padding with '0' bit values, and returns the new vector. + // Returns a vector representing this buffer's bits packed into bytes in big endian. If the + // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. public: std::vector getBytes() const; - // Appends the given number of low bits of the given value to - // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. public: void appendBits(std::uint32_t val, int len); }; diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index f0f7ecb..4ede5cd 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -28,7 +28,7 @@ import java.util.Objects; /** - * An appendable sequence of bits (0s and 1s). + * An appendable sequence of bits (0s and 1s). Mainly used by {@link QrSegment}. */ public final class BitBuffer implements Cloneable { @@ -78,9 +78,9 @@ public final class BitBuffer implements Cloneable { /** - * Packs this buffer's bits into bytes in big endian, - * padding with '0' bit values, and returns the new array. - * @return this sequence as a new array of bytes (not {@code null}) + * Returns an array representing this buffer's bits packed into bytes in big endian. If the + * bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. + * @return a new byte array (not {@code null}) representing this bit sequence */ public byte[] getBytes() { byte[] result = new byte[(bitLength + 7) >>> 3]; // Round up to whole byte, won't overflow @@ -93,10 +93,10 @@ public final class BitBuffer implements Cloneable { /** - * Appends the specified number of low bits of the specified value to this - * sequence. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. + * Appends the specified number of low-order bits of the specified value to this + * buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. * @param val the value to append - * @param len the number of low bits in the value to take + * @param len the number of low-order bits in the value to take * @throws IllegalArgumentException if the value or number of bits is out of range * @throws IllegalStateException if appending the data * would make bitLength exceed Integer.MAX_VALUE @@ -112,7 +112,7 @@ public final class BitBuffer implements Cloneable { /** - * Appends the specified bit buffer to this bit buffer. + * Appends the content of the specified bit buffer to this buffer. * @param bb the bit buffer whose data to append (not {@code null}) * @throws NullPointerException if the bit buffer is {@code null} * @throws IllegalStateException if appending the data @@ -128,8 +128,8 @@ public final class BitBuffer implements Cloneable { /** - * Returns a copy of this bit buffer object. - * @return a copy of this bit buffer object + * Returns a new copy of this buffer. + * @return a new copy of this buffer (not {@code null}) */ public BitBuffer clone() { try { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index b3010e3..db6a72c 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -962,13 +962,13 @@ var qrcodegen = new function() { /* * A private helper class that represents an appendable sequence of bits (0s and 1s). - * This constructor creates an empty bit buffer (length 0). + * Mainly used by QrSegment. This constructor creates an empty bit buffer (length 0). */ function BitBuffer() { Array.call(this); - // Packs this buffer's bits into bytes in big endian, - // padding with '0' bit values, and returns the new array. + // Returns a new array representing this buffer's bits packed into bytes in big endian. If the + // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. this.getBytes = function() { var result = []; while (result.length * 8 < this.length) @@ -979,8 +979,8 @@ var qrcodegen = new function() { return result; }; - // Appends the given number of low bits of the given value to - // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. this.appendBits = function(val, len) { if (len < 0 || len > 31 || val >>> len != 0) throw "Value out of range"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 748072b..3ab89db 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -829,19 +829,19 @@ class _ReedSolomonGenerator(object): class _BitBuffer(list): - """An appendable sequence of bits (0s and 1s).""" + """An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.""" def get_bytes(self): - """Packs this buffer's bits into bytes in big endian, - padding with '0' bit values, and returns the new list.""" + """Returns a new list representing this buffer's bits packed into bytes in big endian. If the + bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'.""" result = [0] * ((len(self) + 7) // 8) for (i, bit) in enumerate(self): result[i >> 3] |= bit << (7 - (i & 7)) return result def append_bits(self, val, n): - """Appends the given number of low bits of the given value - to this sequence. Requires n >= 0 and 0 <= val < 2^n.""" + """Appends the given number of low-order bits of the given + value to this buffer. Requires n >= 0 and 0 <= val < 2^n.""" if n < 0 or val >> n != 0: raise ValueError("Value out of range") self.extend(((val >> i) & 1) for i in reversed(range(n))) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index baa0ac9..3cf2bcd 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1070,13 +1070,13 @@ impl QrSegmentMode { /*---- Bit buffer functionality ----*/ -// An appendable sequence of bits (0s and 1s). +// An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. pub struct BitBuffer(pub Vec); impl BitBuffer { - // Appends the given number of low bits of the given value - // to this sequence. Requires len <= 31 and 0 <= val < 2^len. + // Appends the given number of low-order bits of the given value + // to this buffer. Requires len <= 31 and val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 82a42e6..46d0222 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -890,12 +890,13 @@ namespace qrcodegen { /* - * An appendable sequence of bits (0s and 1s). The implicit constructor creates an empty bit buffer (length 0). + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + * The implicit constructor creates an empty bit buffer (length 0). */ class BitBuffer extends Array { - // Packs this buffer's bits into bytes in big endian, - // padding with '0' bit values, and returns the new array. + // Returns a new array representing this buffer's bits packed into bytes in big endian. If the + // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. public getBytes(): Array { let result: Array = []; while (result.length * 8 < this.length) @@ -906,8 +907,8 @@ namespace qrcodegen { } - // Appends the given number of low bits of the given value to - // this sequence. Requires 0 <= len <= 31 and 0 <= val < 2^len. + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. public appendBits(val: int, len: int): void { if (len < 0 || len > 31 || val >>> len != 0) throw "Value out of range"; From 179f7be089a291b46970654423bc44b8f6d16a92 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 17:58:16 +0000 Subject: [PATCH 501/810] Updated and synchronized QrCode.Ecc and QrSegment.Mode enums' documentation comments, in all languages. --- c/qrcodegen.h | 14 ++++++++------ cpp/QrCode.hpp | 8 +++++--- cpp/QrSegment.hpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 11 +++++++---- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- javascript/qrcodegen.js | 13 ++++++------- python/qrcodegen.py | 12 ++++++------ rust/src/lib.rs | 12 ++++++------ typescript/qrcodegen.ts | 12 ++++++------ 9 files changed, 46 insertions(+), 40 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 7b93ca5..70b1ce8 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -36,13 +36,15 @@ extern "C" { /*---- Enum and struct types----*/ /* - * The error correction level used in a QR Code symbol. + * The error correction level in a QR Code symbol. */ enum qrcodegen_Ecc { - qrcodegen_Ecc_LOW = 0, - qrcodegen_Ecc_MEDIUM, - qrcodegen_Ecc_QUARTILE, - qrcodegen_Ecc_HIGH, + // Must be declared in ascending order of error protection + // so that an internal qrcodegen function works properly + qrcodegen_Ecc_LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + qrcodegen_Ecc_MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + qrcodegen_Ecc_QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + qrcodegen_Ecc_HIGH , // The QR Code can tolerate about 30% erroneous codewords }; @@ -66,7 +68,7 @@ enum qrcodegen_Mask { /* - * The mode field of a segment. + * Describes how a segment's data bits are interpreted. */ enum qrcodegen_Mode { qrcodegen_Mode_NUMERIC = 0x1, diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 94b72e5..1bc05a8 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -42,11 +42,13 @@ class QrCode final { /*---- Public helper enumeration ----*/ /* - * Represents the error correction level used in a QR Code symbol. + * The error correction level in a QR Code symbol. */ public: enum class Ecc { - // Constants declared in ascending order of error protection. - LOW = 0, MEDIUM = 1, QUARTILE = 2, HIGH = 3 + LOW = 0 , // The QR Code can tolerate about 7% erroneous codewords + MEDIUM , // The QR Code can tolerate about 15% erroneous codewords + QUARTILE, // The QR Code can tolerate about 25% erroneous codewords + HIGH , // The QR Code can tolerate about 30% erroneous codewords }; diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index efe74a2..4f56e97 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -42,7 +42,7 @@ class QrSegment final { /*---- Public helper enumeration ----*/ /* - * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + * Describes how a segment's data bits are interpreted. Immutable. */ public: class Mode final { diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 2d3ed61..f4e610f 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -739,12 +739,15 @@ public final class QrCode { /*---- Public helper enumeration ----*/ /** - * Represents the error correction level used in a QR Code symbol. + * The error correction level in a QR Code symbol. */ public enum Ecc { - // These enum constants must be declared in ascending order of error protection, - // for the sake of the implicit ordinal() method and values() function. - LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); + // 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; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index b662ca7..251d1a1 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -236,7 +236,7 @@ public final class QrSegment { /*---- Public helper enumeration ----*/ /** - * The mode field of a segment. Immutable. Provides methods to retrieve closely related values. + * Describes how a segment's data bits are interpreted. */ public enum Mode { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index db6a72c..afa2ba3 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -671,14 +671,13 @@ var qrcodegen = new function() { /*---- Public helper enumeration ----*/ /* - * Represents the error correction level used in a QR Code symbol. + * The error correction level in a QR Code symbol. Immutable. */ this.QrCode.Ecc = { - // Constants declared in ascending order of error protection - LOW : new Ecc(0, 1), - MEDIUM : new Ecc(1, 0), - QUARTILE: new Ecc(2, 3), - HIGH : new Ecc(3, 2), + LOW : new Ecc(0, 1), // The QR Code can tolerate about 7% erroneous codewords + MEDIUM : new Ecc(1, 0), // The QR Code can tolerate about 15% erroneous codewords + QUARTILE: new Ecc(2, 3), // The QR Code can tolerate about 25% erroneous codewords + HIGH : new Ecc(3, 2), // The QR Code can tolerate about 30% erroneous codewords }; @@ -848,7 +847,7 @@ var qrcodegen = new function() { /*---- Public helper enumeration ----*/ /* - * Represents the mode field of a segment. Immutable. + * Describes how a segment's data bits are interpreted. Immutable. */ this.QrSegment.Mode = { // Constants NUMERIC : new Mode(0x1, [10, 12, 14]), diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 3ab89db..debc3a4 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -569,17 +569,17 @@ class QrCode(object): # ---- Public helper enumeration ---- class Ecc(object): - """Represents the error correction level used in a QR Code symbol.""" + """The error correction level in a QR Code symbol. Immutable.""" # Private constructor def __init__(self, i, fb): self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) # Public constants. Create them outside the class. - Ecc.LOW = Ecc(0, 1) - Ecc.MEDIUM = Ecc(1, 0) - Ecc.QUARTILE = Ecc(2, 3) - Ecc.HIGH = Ecc(3, 2) + Ecc.LOW = Ecc(0, 1) # The QR Code can tolerate about 7% erroneous codewords + Ecc.MEDIUM = Ecc(1, 0) # The QR Code can tolerate about 15% erroneous codewords + Ecc.QUARTILE = Ecc(2, 3) # The QR Code can tolerate about 25% erroneous codewords + Ecc.HIGH = Ecc(3, 2) # The QR Code can tolerate about 30% erroneous codewords @@ -742,7 +742,7 @@ class QrSegment(object): # ---- Public helper enumeration ---- class Mode(object): - """The mode field of a segment. Immutable.""" + """Describes how a segment's data bits are interpreted. Immutable.""" # Private constructor def __init__(self, modebits, charcounts): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 3cf2bcd..ca74cf7 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -714,13 +714,13 @@ static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [ /*---- QrCodeEcc functionality ----*/ -// Represents the error correction level used in a QR Code symbol. Immutable. +// The error correction level in a QR Code symbol. #[derive(Clone, Copy)] pub enum QrCodeEcc { - Low, - Medium, - Quartile, - High, + Low , // The QR Code can tolerate about 7% erroneous codewords + Medium , // The QR Code can tolerate about 15% erroneous codewords + Quartile, // The QR Code can tolerate about 25% erroneous codewords + High , // The QR Code can tolerate about 30% erroneous codewords } @@ -1024,7 +1024,7 @@ static ALPHANUMERIC_CHARSET: [char; 45] = ['0','1','2','3','4','5','6','7','8',' /*---- QrSegmentMode functionality ----*/ -// The mode field of a segment. Immutable. +// Describes how a segment's data bits are interpreted. #[derive(Clone, Copy)] pub enum QrSegmentMode { Numeric, diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 46d0222..a587360 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -930,16 +930,16 @@ namespace qrcodegen.QrCode { /* - * Represents the error correction level used in a QR Code symbol. + * The error correction level in a QR Code symbol. Immutable. */ export class Ecc { /*-- Constants --*/ - public static readonly LOW = new Ecc(0, 1); - public static readonly MEDIUM = new Ecc(1, 0); - public static readonly QUARTILE = new Ecc(2, 3); - public static readonly HIGH = new Ecc(3, 2); + public static readonly LOW = new Ecc(0, 1); // The QR Code can tolerate about 7% erroneous codewords + public static readonly MEDIUM = new Ecc(1, 0); // The QR Code can tolerate about 15% erroneous codewords + public static readonly QUARTILE = new Ecc(2, 3); // The QR Code can tolerate about 25% erroneous codewords + public static readonly HIGH = new Ecc(3, 2); // The QR Code can tolerate about 30% erroneous codewords /*-- Constructor and fields --*/ @@ -963,7 +963,7 @@ namespace qrcodegen.QrSegment { /* - * Represents the mode field of a segment. Immutable. + * Describes how a segment's data bits are interpreted. Immutable. */ export class Mode { From 3e1454ab72697025a0e33074cd8a19cd85bbcf80 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 18:13:41 +0000 Subject: [PATCH 502/810] Revamped Java QrSegmentAdvanced's documentation comments. --- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index f7e6969..b68575e 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -32,6 +32,12 @@ import java.util.Objects; import io.nayuki.qrcodegen.QrSegment.Mode; +/** + * Splits text into optimal segments and encodes kanji segments. + * Provides static functions only; not instantiable. + * @see QrSegment + * @see QrCode + */ public final class QrSegmentAdvanced { /*---- Optimal list of segments encoder ----*/ @@ -40,18 +46,19 @@ public final class QrSegmentAdvanced { * Returns a list of zero or more segments to represent the specified Unicode text string. * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints * in the specified {error correction level, minimum version number, maximum version number}. - *

This function can utilize all four text encoding modes: numeric, alphanumeric, byte, and kanji. - * This can be viewed as a significantly more sophisticated and slower replacement for - * {@link QrSegment#makeSegments(String)}, but requiring more input parameters in a way - * that overlaps with {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

- * @param text the text to be encoded, which can be any Unicode string - * @param ecl the error correction level to use - * @param minVersion the minimum allowed version of the QR symbol (at least 1) - * @param maxVersion the maximum allowed version of the QR symbol (at most 40) - * @return a new mutable list of segments containing the text, minimizing the bit length with respect to the constraints - * @throws NullPointerException if the data or error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 is violated, - * or if the data is too long to fit in a QR Code at maxVersion at the ECL + *

This function can utilize all four text encoding modes: numeric, alphanumeric, byte (UTF-8), + * and kanji. This can be considered as a sophisticated but slower replacement for {@link + * QrSegment#makeSegments(String)}. This requires more input parameters because it searches a + * range of versions, like {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

+ * @param text the text to be encoded (not {@code null}), which can be any Unicode string + * @param ecl the error correction level to use (not {@code null}) + * @param minVersion the minimum allowed version of the QR Code symbol (at least 1) + * @param maxVersion the maximum allowed version of the QR Code symbol (at most 40) + * @return a new mutable list (not {@code null}) of segments (not {@code null}) + * containing the text, minimizing the bit length with respect to the constraints + * @throws NullPointerException if the text or error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 + * is violated, or if the data is too long to fit in a QR Code at maxVersion at ECL */ public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { // Check arguments @@ -78,7 +85,7 @@ public final class QrSegmentAdvanced { } - // Returns a list of segments that is optimal for the given text at the given version number. + // 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<>(); @@ -87,7 +94,7 @@ public final class QrSegmentAdvanced { } - // Returns an array representing the optimal mode per code point based on the given text and version. + // 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) { if (codePoints.length == 0) throw new IllegalArgumentException(); @@ -169,7 +176,7 @@ public final class QrSegmentAdvanced { } - // Returns a list of segments based on the given text and modes, such that + // 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) { if (codePoints.length == 0) @@ -201,7 +208,8 @@ public final class QrSegmentAdvanced { } - // Returns an array of Unicode code points (effectively UTF-32 / UCS-4) representing the given UTF-16 string. + // 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(); for (int c : result) { @@ -227,14 +235,15 @@ public final class QrSegmentAdvanced { /*---- Kanji mode segment encoder ----*/ /** - * Returns a segment representing the specified string encoded in kanji mode. - *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, - * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
- * Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. - * @param text the text to be encoded, which must fall in the kanji mode subset of characters - * @return a segment containing the data + * Returns a segment representing the specified text string encoded in kanji mode. + * Broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}. + * Examples of non-encodable characters include {ordinary ASCII, half-width katakana, + * more extensive Chinese hanzi}. + * @param text the text (not {@code null}), with only certain characters allowed + * @return a segment (not {@code null}) containing the text * @throws NullPointerException if the string is {@code null} - * @throws IllegalArgumentException if the string contains non-kanji-mode characters + * @throws IllegalArgumentException if the string contains non-encodable characters * @see #isEncodableAsKanji(String) */ public static QrSegment makeKanji(String text) { @@ -251,12 +260,13 @@ public final class QrSegmentAdvanced { /** - * Tests whether the specified text string can be encoded as a segment in kanji mode. - *

Note that broadly speaking, the set of encodable characters are {kanji used in Japan, - * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}.
- * Examples of non-encodable characters include {normal ASCII, half-width katakana, more extensive Chinese hanzi}. - * @param text the string to test for encodability - * @return {@code true} if and only if the string can be encoded in kanji mode + * Tests whether the specified string can be encoded as a segment in kanji mode. + * Broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}. + * Examples of non-encodable characters include {ordinary ASCII, half-width katakana, + * more extensive Chinese hanzi}. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the kanji mode character set * @throws NullPointerException if the string is {@code null} * @see #makeKanji(String) */ From 139e67eee285291566647ee5883785bda98e1856 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:27:11 +0000 Subject: [PATCH 503/810] Updated and synchronized documentation comments for QrSegment's fields, in all languages. --- c/qrcodegen.h | 6 +++--- cpp/QrSegment.hpp | 9 +++++---- java/io/nayuki/qrcodegen/QrSegment.java | 8 ++++---- javascript/qrcodegen.js | 6 +++--- python/qrcodegen.py | 7 ++++--- rust/src/lib.rs | 7 ++++--- typescript/qrcodegen.ts | 7 ++++--- 7 files changed, 27 insertions(+), 23 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 70b1ce8..3215a6c 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -84,12 +84,12 @@ enum qrcodegen_Mode { * bit length is 32767, because even the largest QR Code (version 40) has only 31329 modules. */ struct qrcodegen_Segment { - // The mode indicator for this segment. + // The mode indicator of this segment. enum qrcodegen_Mode mode; - // The length of this segment's unencoded data, measured in characters for + // The length of this segment's unencoded data. Measured in characters for // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - // Always zero or positive. + // Always zero or positive. Not the same as the data's bit length. int numChars; // The data bits of this segment, packed in bitwise big endian. diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 4f56e97..47c41b0 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -139,15 +139,16 @@ class QrSegment final { /*---- Instance fields ----*/ - /* The mode indicator for this segment. */ + /* The mode indicator of this segment. Accessed through getMode(). */ private: Mode mode; - /* The length of this segment's unencoded data, measured in characters for + /* The length of this segment's unencoded data. Measured in characters for * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. */ + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ private: int numChars; - /* The data bits of this segment. */ + /* The data bits of this segment. Accessed through getData(). */ private: std::vector data; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 251d1a1..605ab71 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -156,15 +156,15 @@ public final class QrSegment { /*---- Instance fields ----*/ - /** The mode indicator for this segment. Never {@code null}. */ + /** The mode indicator of this segment. Not {@code null}. */ public final Mode mode; - /** The length of this segment's unencoded data, measured in characters for + /** The length of this segment's unencoded data. Measured in characters for * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. */ + * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; - /** The data bits of this segment. Accessed through {@link getBits()}. Not {@code null}. */ + /** The data bits of this segment. Not {@code null}. Accessed through {@link getBits()}. */ final BitBuffer data; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index afa2ba3..f54d9a4 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -710,12 +710,12 @@ var qrcodegen = new function() { throw "Invalid argument"; bitData = bitData.slice(); // Make defensive copy - // The mode indicator for this segment. + // The mode indicator of this segment. Object.defineProperty(this, "mode", {value:mode}); - // The length of this segment's unencoded data, measured in characters for + // The length of this segment's unencoded data. Measured in characters for // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - // Always zero or positive. + // Always zero or positive. Not the same as the data's bit length. Object.defineProperty(this, "numChars", {value:numChars}); // Returns a copy of all bits, which is an array of 0s and 1s. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index debc3a4..8a9fc37 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -688,12 +688,13 @@ class QrSegment(object): if numch < 0: raise ValueError() - # The mode indicator for this segment. + # The mode indicator of this segment. Accessed through get_mode(). self._mode = mode - # The length of this segment's unencoded data, measured in characters for + # The length of this segment's unencoded data. Measured in characters for # numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - # Always zero or positive. + # Always zero or positive. Not the same as the data's bit length. + # Accessed through get_num_chars(). self._numchars = numch # The data bits of this segment. Accessed through get_bits(). diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ca74cf7..33c73c2 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -833,14 +833,15 @@ impl ReedSolomonGenerator { #[derive(Clone)] pub struct QrSegment { - // The mode indicator for this segment. + // The mode indicator of this segment. Accessed through mode(). mode: QrSegmentMode, - // The length of this segment's unencoded data, measured in characters for + // The length of this segment's unencoded data. Measured in characters for // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + // Not the same as the data's bit length. Accessed through num_chars(). numchars: usize, - // The bits of this segment. + // The data bits of this segment. Accessed through data(). data: Vec, } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index a587360..b831050 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -742,14 +742,15 @@ namespace qrcodegen { // The character count (numChars) must agree with the mode and the bit buffer length, // but the constraint isn't checked. The given bit buffer is cloned and stored. public constructor( - // The mode indicator for this segment. + // The mode indicator of this segment. public readonly mode: QrSegment.Mode, - // The length of this segment's unencoded data, measured in characters for + // The length of this segment's unencoded data. Measured in characters for // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - // Always zero or positive. + // Always zero or positive. Not the same as the data's bit length. public readonly numChars: int, + // The data bits of this segment. Accessed through getBits(). private readonly bitData: Array) { if (numChars < 0) From aa39108f0dfd10ad1ee9f49e94d6b38e3c8e3203 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:30:21 +0000 Subject: [PATCH 504/810] Added/updated/synchronized documentation comments for QrSegment's accessor methods, in all languages except C. --- cpp/QrSegment.hpp | 9 +++++++++ java/io/nayuki/qrcodegen/QrSegment.java | 2 +- javascript/qrcodegen.js | 2 +- python/qrcodegen.py | 3 +++ rust/src/lib.rs | 6 +++--- typescript/qrcodegen.ts | 2 +- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 47c41b0..565778f 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -172,12 +172,21 @@ class QrSegment final { /*---- Methods ----*/ + /* + * Returns the mode field of this segment. + */ public: Mode getMode() const; + /* + * Returns the character count field of this segment. + */ public: int getNumChars() const; + /* + * Returns the data bits of this segment. + */ public: const std::vector &getData() const; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 605ab71..74dbc1b 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -193,7 +193,7 @@ public final class QrSegment { /*---- Methods ----*/ /** - * Returns a copy of the data bits of this segment. + * Returns the data bits of this segment. * @return the data bits of this segment (not {@code null}) */ public BitBuffer getBits() { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index f54d9a4..7579698 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -718,7 +718,7 @@ var qrcodegen = new function() { // Always zero or positive. Not the same as the data's bit length. Object.defineProperty(this, "numChars", {value:numChars}); - // Returns a copy of all bits, which is an array of 0s and 1s. + // Returns a new copy of the data bits of this segment. this.getBits = function() { return bitData.slice(); // Make defensive copy }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8a9fc37..4a6c14c 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -704,12 +704,15 @@ class QrSegment(object): # ---- Accessor methods ---- def get_mode(self): + """Returns the mode field of this segment.""" return self._mode def get_num_chars(self): + """Returns the character count field of this segment.""" return self._numchars def get_bits(self): + """Returns a new copy of the data bits of this segment.""" return list(self._bitdata) # Make defensive copy diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 33c73c2..8e3ea1e 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -962,19 +962,19 @@ impl QrSegment { /*---- Instance field getters ----*/ - // Returns the mode indicator for this segment. + // Returns the mode indicator of this segment. pub fn mode(&self) -> QrSegmentMode { self.mode } - // Returns the length of this segment's unencoded data, measured in characters. + // Returns the character count field of this segment. pub fn num_chars(&self) -> usize { self.numchars } - // Returns a view of the bits of this segment. + // Returns the data bits of this segment. pub fn data(&self) -> &Vec { &self.data } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index b831050..aa31b85 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -761,7 +761,7 @@ namespace qrcodegen { /*-- Methods --*/ - // Returns a copy of all bits, which is an array of 0s and 1s. + // Returns a new copy of the data bits of this segment. public getBits(): Array { return this.bitData.slice(); // Make defensive copy } From 85eb6493fd09170f8d9adcedb47bb879143168b3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:34:42 +0000 Subject: [PATCH 505/810] Updated and synchronized documentation comments for QrSegment's constants/functions involving character sets, in all languages. --- c/qrcodegen.c | 3 ++- c/qrcodegen.h | 3 +++ cpp/QrSegment.hpp | 6 +++++- java/io/nayuki/qrcodegen/QrSegment.java | 12 +++++++++--- javascript/qrcodegen.js | 12 +++++++++--- python/qrcodegen.py | 10 ++++++++-- rust/src/lib.rs | 3 +++ typescript/qrcodegen.ts | 12 +++++++++--- 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index de13309..1fd70ba 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -86,7 +86,8 @@ static int numCharCountBits(enum qrcodegen_Mode mode, int version); /*---- Private tables of constants ----*/ -// For checking text and encoding segments. +// The set of all legal characters in alphanumeric mode, where each character +// value maps to the index in the string. For checking text and encoding segments. static const char *ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; // For generating error correction codes. diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 3215a6c..8ac9d3f 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -172,12 +172,15 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod /* * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. */ bool qrcodegen_isAlphanumeric(const char *text); /* * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. */ bool qrcodegen_isNumeric(const char *text); diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 565778f..8e1356f 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -126,12 +126,15 @@ class QrSegment final { /* * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. */ public: static bool isAlphanumeric(const char *text); /* * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. */ public: static bool isNumeric(const char *text); @@ -198,7 +201,8 @@ class QrSegment final { /*---- Private constant ----*/ - /* The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. */ + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ private: static const char *ALPHANUMERIC_CHARSET; }; diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 74dbc1b..da04109 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -222,13 +222,19 @@ public final class QrSegment { /*---- Constants ----*/ - /** Can test whether a string is encodable in numeric mode (such as by using {@link #makeNumeric(String)}). */ + /** Describes precisely all strings that are encodable in numeric mode. To test whether a + * string {@code s} is encodable: {@code boolean ok = NUMERIC_REGEX.matcher(s).matches();}. + * A string is encodable iff each character is in the range 0 to 9. */ public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); - /** Can test whether a string is encodable in alphanumeric mode (such as by using {@link #makeAlphanumeric(String)}). */ + /** Describes precisely all strings that are encodable in alphanumeric mode. To test whether a + * string {@code s} is encodable: {@code boolean ok = ALPHANUMERIC_REGEX.matcher(s).matches();}. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. */ public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); - /** The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. */ + /** The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ static final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 7579698..e275cbd 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -834,13 +834,19 @@ var qrcodegen = new function() { var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'. - // (Public) Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + // (Public) Describes precisely all strings that are encodable in numeric mode. + // To test whether a string s is encodable: var ok = NUMERIC_REGEX.test(s); + // A string is encodable iff each character is in the range 0 to 9. this.QrSegment.NUMERIC_REGEX = /^[0-9]*$/; - // (Public) Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + // (Public) Describes precisely all strings that are encodable in alphanumeric mode. + // To test whether a string s is encodable: var ok = ALPHANUMERIC_REGEX.test(s); + // A string is encodable iff each character is in the following set: 0 to 9, A to Z + // (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; - // (Private) The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. + // (Private) The set of all legal characters in alphanumeric mode, + // where each character value maps to the index in the string. QrSegment.ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 4a6c14c..55319cf 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -733,10 +733,16 @@ class QrSegment(object): # ---- Constants ---- - # (Public) Can test whether a string is encodable in numeric mode (such as by using make_numeric()) + # (Public) Describes precisely all strings that are encodable in numeric mode. + # To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None + # A string is encodable iff each character is in the range 0 to 9. NUMERIC_REGEX = re.compile(r"[0-9]*\Z") - # (Public) Can test whether a string is encodable in alphanumeric mode (such as by using make_alphanumeric()) + # (Public) Describes precisely all strings that are encodable in alphanumeric mode. + # To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None + # A string is encodable iff each character is in the following set: 0 to 9, A to Z + # (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*\Z") # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8e3ea1e..de2fca8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1002,12 +1002,15 @@ impl QrSegment { // Tests whether the given string can be encoded as a segment in alphanumeric mode. + // A string is encodable iff each character is in the following set: 0 to 9, A to Z + // (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. fn is_alphanumeric(text: &[char]) -> bool { text.iter().all(|c| ALPHANUMERIC_CHARSET.contains(c)) } // Tests whether the given string can be encoded as a segment in numeric mode. + // A string is encodable iff each character is in the range 0 to 9. fn is_numeric(text: &[char]) -> bool { text.iter().all(|c| '0' <= *c && *c <= '9') } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index aa31b85..49e0d7c 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -799,13 +799,19 @@ namespace qrcodegen { /*-- Constants --*/ - // Can test whether a string is encodable in numeric mode (such as by using QrSegment.makeNumeric()). + // Describes precisely all strings that are encodable in numeric mode. To test + // whether a string s is encodable: let ok: boolean = NUMERIC_REGEX.test(s); + // A string is encodable iff each character is in the range 0 to 9. public static readonly NUMERIC_REGEX: RegExp = /^[0-9]*$/; - // Can test whether a string is encodable in alphanumeric mode (such as by using QrSegment.makeAlphanumeric()). + // Describes precisely all strings that are encodable in alphanumeric mode. To test + // whether a string s is encodable: let ok: boolean = ALPHANUMERIC_REGEX.test(s); + // A string is encodable iff each character is in the following set: 0 to 9, A to Z + // (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. public static readonly ALPHANUMERIC_REGEX: RegExp = /^[A-Z0-9 $%*+.\/:-]*$/; - // The set of all legal characters in alphanumeric mode, where each character value maps to the index in the string. + // The set of all legal characters in alphanumeric mode, + // where each character value maps to the index in the string. private static readonly ALPHANUMERIC_CHARSET: string = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; } From 79796901ea8c81cc4dac9dc27f5b1efb1798f4de Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:38:36 +0000 Subject: [PATCH 506/810] Updated and synchronized documentation comments for QrSegment class's {top-level, constructor, makeBytes()}, in all languages. --- c/qrcodegen.h | 15 ++++++++++++--- cpp/QrSegment.hpp | 14 ++++++++++---- java/io/nayuki/qrcodegen/QrSegment.java | 19 ++++++++++++++----- javascript/qrcodegen.js | 14 ++++++++++---- python/qrcodegen.py | 14 ++++++++++---- rust/src/lib.rs | 19 +++++++++++++++---- typescript/qrcodegen.ts | 14 ++++++++++---- 7 files changed, 81 insertions(+), 28 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 8ac9d3f..fb26d50 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -80,8 +80,15 @@ enum qrcodegen_Mode { /* - * Represents a segment of character data, binary data, or control data. The maximum allowed - * bit length is 32767, because even the largest QR Code (version 40) has only 31329 modules. + * A segment of character/binary/control data in a QR Code symbol. + * The mid-level way to create a segment is to take the payload data + * and call a factory function such as qrcodegen_makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and initialize a qrcodegen_Segment struct with appropriate values. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * Moreover, the maximum allowed bit length is 32767 because + * the largest QR Code (version 40) has 31329 modules. */ struct qrcodegen_Segment { // The mode indicator of this segment. @@ -200,7 +207,9 @@ size_t qrcodegen_calcSegmentBufferSize(enum qrcodegen_Mode mode, size_t numChars /* - * Returns a segment representing the given binary data encoded in byte mode. + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. */ struct qrcodegen_Segment qrcodegen_makeBytes(const uint8_t data[], size_t len, uint8_t buf[]); diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp index 8e1356f..663b8bb 100644 --- a/cpp/QrSegment.hpp +++ b/cpp/QrSegment.hpp @@ -31,8 +31,12 @@ namespace qrcodegen { /* - * Represents a segment of character data, binary data, or control data - * to be put into a QR Code symbol. Instances of this class are immutable. + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -89,7 +93,9 @@ class QrSegment final { /*---- Static factory functions (mid level) ----*/ /* - * Returns a segment representing the given binary data encoded in byte mode. + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. */ public: static QrSegment makeBytes(const std::vector &data); @@ -158,7 +164,7 @@ class QrSegment final { /*---- Constructors (low level) ----*/ /* - * Creates a new QR Code segment with the given parameters and data. + * Creates a new QR Code segment with the given attributes and data. * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The given bit buffer is copied and stored. */ diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index da04109..718e777 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -31,18 +31,27 @@ import java.util.regex.Pattern; /** - * Represents a segment of character data, binary data, or control data - * to be put into a QR Code symbol. Instances of this class are immutable. + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + *

The mid-level way to create a segment is to take the payload data and call a + * static factory function such as {@link QrSegment#makeNumeric(String)}. The low-level + * way to create a segment is to custom-make the bit buffer and call the {@link + * QrSegment#QrSegment(Mode,int,BitBuffer) constructor} with appropriate values.

*

This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes.

+ * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * This class can represent kanji mode segments, but provides no help in encoding them + * - see {@link QrSegmentAdvanced} for full kanji support.

*/ public final class QrSegment { /*---- Static factory functions (mid level) ----*/ /** - * Returns a segment representing the specified binary data encoded in byte mode. + * Returns a segment representing the specified binary data + * encoded in byte mode. All input byte arrays are acceptable. + *

Any text string can be converted to UTF-8 bytes ({@code + * s.getBytes(StandardCharsets.UTF_8)}) and encoded as a byte mode segment.

* @param data the binary data * @return a segment containing the data * @throws NullPointerException if the array is {@code null} @@ -171,7 +180,7 @@ public final class QrSegment { /*---- Constructor (low level) ----*/ /** - * Constructs a QR Code segment with the specified parameters and data. + * Constructs a QR Code segment with the specified attributes and data. * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The specified bit buffer is cloned and stored. * @param md the mode, which is not {@code null} diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index e275cbd..c66dadf 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -695,12 +695,16 @@ var qrcodegen = new function() { /*---- Data segment class ----*/ /* - * Represents a segment of character data, binary data, or control data - * to be put into a QR Code symbol. Instances of this class are immutable. + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment.makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. - * This constructor creates a QR Code segment with the given parameters and data. + * This constructor creates a QR Code segment with the given attributes and data. * The character count (numChars) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The given bit buffer is cloned and stored. */ @@ -728,7 +732,9 @@ var qrcodegen = new function() { /*---- Static factory functions (mid level) for QrSegment ----*/ /* - * Returns a segment representing the given binary data encoded in byte mode. + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte arrays are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. */ this.QrSegment.makeBytes = function(data) { var bb = new BitBuffer(); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 55319cf..b1692d5 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -586,8 +586,12 @@ class QrCode(object): # ---- Data segment class ---- class QrSegment(object): - """Represents a segment of character data, binary data, or control data - to be put into a QR Code symbol. Instances of this class are immutable. + """A segment of character/binary/control data in a QR Code symbol. + Instances of this class are immutable. + The mid-level way to create a segment is to take the payload data + and call a static factory function such as QrSegment.make_numeric(). + The low-level way to create a segment is to custom-make the bit buffer + and call the QrSegment() constructor with appropriate values. This segment class imposes no length restrictions, but QR Codes have restrictions. Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. Any segment longer than this is meaningless for the purpose of generating QR Codes.""" @@ -596,7 +600,9 @@ class QrSegment(object): @staticmethod def make_bytes(data): - """Returns a segment representing the given binary data encoded in byte mode.""" + """Returns a segment representing the given binary data encoded in byte mode. + All input byte lists are acceptable. Any text string can be converted to + UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment.""" py3 = sys.version_info.major >= 3 if (py3 and isinstance(data, str)) or (not py3 and isinstance(data, unicode)): raise TypeError("Byte string/list expected") @@ -680,7 +686,7 @@ class QrSegment(object): # ---- Constructor (low level) ---- def __init__(self, mode, numch, bitdata): - """Creates a new QR Code segment with the given parameters and data. + """Creates a new QR Code segment with the given attributes and data. The character count (numch) must agree with the mode and the bit buffer length, but the constraint isn't checked. The given bit buffer is cloned and stored.""" if not isinstance(mode, QrSegment.Mode): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index de2fca8..a6f10d5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -828,8 +828,17 @@ impl ReedSolomonGenerator { /*---- QrSegment functionality ----*/ -// Represents a segment of character data, binary data, or control data -// to be put into a QR Code symbol. Instances of this class are immutable. +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this struct are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::make_numeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment::new() constructor with appropriate values. + * This segment struct imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ #[derive(Clone)] pub struct QrSegment { @@ -851,7 +860,9 @@ impl QrSegment { /*---- Static factory functions (mid level) ----*/ - // Returns a segment representing the given binary data encoded in byte mode. + // Returns a segment representing the given binary data encoded in + // byte mode. All input byte slices are acceptable. Any text string + // can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); for b in data { @@ -948,7 +959,7 @@ impl QrSegment { /*---- Constructor (low level) ----*/ - // Creates a new QR Code segment with the given parameters and data. + // Creates a new QR Code segment with the given attributes and data. // The character count (numchars) must agree with the mode and // the bit buffer length, but the constraint isn't checked. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 49e0d7c..79ca04b 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -649,8 +649,12 @@ namespace qrcodegen { /*---- Data segment class ----*/ /* - * Represents a segment of character data, binary data, or control data - * to be put into a QR Code symbol. Instances of this class are immutable. + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment.makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. * This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -659,7 +663,9 @@ namespace qrcodegen { /*-- Static factory functions (mid level) --*/ - // Returns a segment representing the given binary data encoded in byte mode. + // Returns a segment representing the given binary data encoded in + // byte mode. All input byte arrays are acceptable. Any text string + // can be converted to UTF-8 bytes and encoded as a byte mode segment. public static makeBytes(data: Array): QrSegment { let bb = new BitBuffer(); for (let b of data) @@ -738,7 +744,7 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ - // Creates a new QR Code segment with the given parameters and data. + // Creates a new QR Code segment with the given attributes and data. // The character count (numChars) must agree with the mode and the bit buffer length, // but the constraint isn't checked. The given bit buffer is cloned and stored. public constructor( From 2d12b392e896641aa881edc65e524e1b495f8892 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:45:35 +0000 Subject: [PATCH 507/810] Updated Javadoc-specific sections of the Java QrSegment class. --- java/io/nayuki/qrcodegen/QrSegment.java | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 718e777..d00bc4c 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -67,8 +67,8 @@ public final class QrSegment { /** * Returns a segment representing the specified string of decimal digits encoded in numeric mode. - * @param digits a string consisting of digits from 0 to 9 - * @return a segment containing the data + * @param digits the text, with only digits from 0 to 9 allowed + * @return a segment containing the text * @throws NullPointerException if the string is {@code null} * @throws IllegalArgumentException if the string contains non-digit characters */ @@ -91,8 +91,8 @@ public final class QrSegment { * Returns a segment representing the specified text string encoded in alphanumeric mode. * The characters allowed are: 0 to 9, A to Z (uppercase only), space, * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - * @param text a string of text, with only certain characters allowed - * @return a segment containing the data + * @param text the text, with only certain characters allowed + * @return a segment containing the text * @throws NullPointerException if the string is {@code null} * @throws IllegalArgumentException if the string contains non-encodable characters */ @@ -183,9 +183,9 @@ public final class QrSegment { * Constructs a QR Code segment with the specified attributes and data. * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The specified bit buffer is cloned and stored. - * @param md the mode, which is not {@code null} + * @param md the mode (not {@code null}) * @param numCh the data length in characters or bytes, which is non-negative - * @param data the data bits of this segment, which is not {@code null} + * @param data the data bits (not {@code null}) * @throws NullPointerException if the mode or data is {@code null} * @throws IllegalArgumentException if the character count is negative */ @@ -203,7 +203,7 @@ public final class QrSegment { /** * Returns the data bits of this segment. - * @return the data bits of this segment (not {@code null}) + * @return a new copy of the data bits (not {@code null}) */ public BitBuffer getBits() { return data.clone(); // Make defensive copy @@ -233,13 +233,15 @@ public final class QrSegment { /** Describes precisely all strings that are encodable in numeric mode. To test whether a * string {@code s} is encodable: {@code boolean ok = NUMERIC_REGEX.matcher(s).matches();}. - * A string is encodable iff each character is in the range 0 to 9. */ + * A string is encodable iff each character is in the range 0 to 9. + * @see #makeNumeric(String) */ public static final Pattern NUMERIC_REGEX = Pattern.compile("[0-9]*"); /** Describes precisely all strings that are encodable in alphanumeric mode. To test whether a * string {@code s} is encodable: {@code boolean ok = ALPHANUMERIC_REGEX.matcher(s).matches();}. * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. */ + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + * @see #makeAlphanumeric(String) */ public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); /** The set of all legal characters in alphanumeric mode, where From 7d31874345153d20a4a5118794b7ac7117a4759e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 19:45:46 +0000 Subject: [PATCH 508/810] Updated Javadoc-specific sections of the Java QrSegment class to add not-null qualifications. --- java/io/nayuki/qrcodegen/QrSegment.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index d00bc4c..dc0cd30 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -52,8 +52,8 @@ public final class QrSegment { * encoded in byte mode. All input byte arrays are acceptable. *

Any text string can be converted to UTF-8 bytes ({@code * s.getBytes(StandardCharsets.UTF_8)}) and encoded as a byte mode segment.

- * @param data the binary data - * @return a segment containing the data + * @param data the binary data (not {@code null}) + * @return a segment (not {@code null}) containing the data * @throws NullPointerException if the array is {@code null} */ public static QrSegment makeBytes(byte[] data) { @@ -67,8 +67,8 @@ public final class QrSegment { /** * Returns a segment representing the specified string of decimal digits encoded in numeric mode. - * @param digits the text, with only digits from 0 to 9 allowed - * @return a segment containing the text + * @param digits the text (not {@code null}), with only digits from 0 to 9 allowed + * @return a segment (not {@code null}) containing the text * @throws NullPointerException if the string is {@code null} * @throws IllegalArgumentException if the string contains non-digit characters */ @@ -91,8 +91,8 @@ public final class QrSegment { * Returns a segment representing the specified text string encoded in alphanumeric mode. * The characters allowed are: 0 to 9, A to Z (uppercase only), space, * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - * @param text the text, with only certain characters allowed - * @return a segment containing the text + * @param text the text (not {@code null}), with only certain characters allowed + * @return a segment (not {@code null}) containing the text * @throws NullPointerException if the string is {@code null} * @throws IllegalArgumentException if the string contains non-encodable characters */ @@ -118,7 +118,7 @@ public final class QrSegment { * Returns a list of zero or more segments to represent the specified Unicode text string. * The result may use various segment modes and switch modes to optimize the length of the bit stream. * @param text the text to be encoded, which can be any Unicode string - * @return a new mutable list of segments containing the text + * @return a new mutable list (not {@code null}) of segments (not {@code null}) containing the text * @throws NullPointerException if the text is {@code null} */ public static List makeSegments(String text) { @@ -141,7 +141,7 @@ public final class QrSegment { * Returns a segment representing an Extended Channel Interpretation * (ECI) designator with the specified assignment value. * @param assignVal the ECI assignment number (see the AIM ECI specification) - * @return a segment containing the data + * @return a segment (not {@code null}) containing the data * @throws IllegalArgumentException if the value is outside the range [0, 106) */ public static QrSegment makeEci(int assignVal) { From eebae19fb2744d3fbf25d264f3c8d291aa9ea4f5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 20:30:26 +0000 Subject: [PATCH 509/810] Reduced the use of the word "symbol" when referring to QR Codes, in all languages. --- c/qrcodegen.c | 2 +- c/qrcodegen.h | 8 +++---- cpp/QrCode.cpp | 2 +- cpp/QrCode.hpp | 24 ++++++++++----------- java/io/nayuki/qrcodegen/QrCode.java | 32 ++++++++++++++-------------- javascript/qrcodegen.js | 26 +++++++++++----------- python/qrcodegen.py | 26 +++++++++++----------- rust/src/lib.rs | 28 ++++++++++++------------ typescript/qrcodegen.ts | 28 ++++++++++++------------ 9 files changed, 88 insertions(+), 88 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 1fd70ba..3e631f9 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -504,7 +504,7 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t 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 symbol needs exactly one (not zero, two, etc.) mask applied. +// QR Code needs exactly one (not zero, two, etc.) mask applied. static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask) { assert(0 <= (int)mask && (int)mask <= 7); // Disallows qrcodegen_Mask_AUTO int qrsize = qrcodegen_getSize(qrcode); diff --git a/c/qrcodegen.h b/c/qrcodegen.h index fb26d50..cdcc879 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -133,7 +133,7 @@ struct qrcodegen_Segment { /*---- Functions to generate QR Codes ----*/ /* - * Encodes the given text string to a QR Code symbol, returning true if encoding succeeded. + * Encodes the given text string to a QR Code, returning true if encoding succeeded. * If the data is too long to fit in any version in the given range * at the given ECC level, then false is returned. * - The input text must be encoded in UTF-8 and contain no NULs. @@ -156,7 +156,7 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode /* - * Encodes the given binary data to a QR Code symbol, returning true if encoding succeeded. + * Encodes the given binary data to a QR Code, returning true if encoding succeeded. * If the data is too long to fit in any version in the given range * at the given ECC level, then false is returned. * - The input array range dataAndTemp[0 : dataLen] should normally be @@ -236,7 +236,7 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); /* - * Renders a QR Code symbol representing the given segments at the given error correction level. + * Renders a QR Code representing the given segments at the given error correction level. * The smallest possible QR Code version is automatically chosen for the output. Returns true if * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level * of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -252,7 +252,7 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, /* - * Renders a QR Code symbol representing the given segments with the given encoding parameters. + * Renders a QR Code representing the given segments with the given encoding parameters. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 4500f76..ce2de59 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -109,7 +109,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - // Create the QR Code symbol + // Create the QR Code object return QrCode(version, ecl, bb.getBytes(), mask); } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 1bc05a8..075591f 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -60,7 +60,7 @@ class QrCode final { /*---- Static factory functions (high level) ----*/ /* - * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + * Returns a QR Code representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 2953 or fewer * UTF-8 code units (not Unicode code points) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than @@ -70,7 +70,7 @@ class QrCode final { /* - * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * Returns a QR Code representing the given binary data string at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -81,7 +81,7 @@ class QrCode final { /*---- Static factory functions (mid level) ----*/ /* - * Returns a QR Code symbol representing the given segments with the given encoding parameters. + * Returns a QR Code representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -96,24 +96,24 @@ class QrCode final { // Immutable scalar parameters: - /* This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + /* This QR Code's version number, which is always between 1 and 40 (inclusive). */ private: int version; - /* The width and height of this QR Code symbol, measured in modules. + /* The width and height of this QR Code, measured in modules. * Always equal to version * 4 + 17, in the range 21 to 177. */ private: int size; - /* The error correction level used in this QR Code symbol. */ + /* The error correction level used in this QR Code. */ private: Ecc errorCorrectionLevel; - /* The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + /* The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). * Note that even if a constructor was called with automatic masking requested * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ private: int mask; // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. private: std::vector > modules; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. @@ -124,7 +124,7 @@ class QrCode final { /*---- Constructor (low level) ----*/ /* - * Creates a new QR Code symbol with the given version number, error correction level, binary data array, + * Creates a new QR Code with the given version number, error correction level, binary data array, * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. * To go one level up, see the encodeSegments() function. */ @@ -155,7 +155,7 @@ class QrCode final { /* - * Returns a string of SVG XML code representing an image of this QR Code symbol with the given + * Returns a string of SVG XML code representing an image of this QR Code with the given * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. */ public: std::string toSvgString(int border) const; @@ -205,7 +205,7 @@ class QrCode final { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // data area of this QR Code. Function modules need to be marked off before this is called. private: void drawCodewords(const std::vector &data); @@ -213,7 +213,7 @@ class QrCode final { // 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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. private: void applyMask(int mask); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index f4e610f..683eedc 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -40,7 +40,7 @@ public final class QrCode { /*---- Static factory functions (high level) ----*/ /** - * Returns a QR Code symbol representing the specified Unicode text string at the specified error correction 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 * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the @@ -61,7 +61,7 @@ public final class QrCode { /** - * Returns a QR Code symbol representing the specified binary data string at the specified error correction level. + * Returns a QR Code representing the specified binary data string at the specified error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -83,7 +83,7 @@ public final class QrCode { /*---- Static factory functions (mid level) ----*/ /** - * Returns a QR Code symbol representing the specified segments at the specified error correction + * 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 * of the result may be higher than the ecl argument if it can be done without increasing the version. *

This function allows the user to create a custom sequence of segments that switches @@ -102,7 +102,7 @@ public final class QrCode { /** - * Returns a QR Code symbol representing the specified segments with the specified encoding parameters. + * 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 chosen for the output. *

This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -163,7 +163,7 @@ public final class QrCode { for (int padByte = 0xEC; bb.bitLength() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - // Create the QR Code symbol + // Create the QR Code object return new QrCode(version, ecl, bb.getBytes(), mask); } @@ -173,24 +173,24 @@ public final class QrCode { // Public immutable scalar parameters: - /** This QR Code symbol's version number, which is always between 1 and 40 (inclusive). */ + /** This QR Code's version number, which is always between 1 and 40 (inclusive). */ public final int version; - /** The width and height of this QR Code symbol, measured in modules. + /** The width and height of this QR Code, measured in modules. * Always equal to version × 4 + 17, in the range 21 to 177 (inclusive). */ public final int size; - /** The error correction level used in this QR Code symbol. Never {@code null}. */ + /** The error correction level used in this QR Code. Never {@code null}. */ public final Ecc errorCorrectionLevel; - /** The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + /** The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). * Note that even if a constructor was called with automatic masking requested * (mask = -1), the resulting object will still have 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 symbol (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. private boolean[][] modules; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. @@ -201,7 +201,7 @@ public final class QrCode { /*---- Constructor (low level) ----*/ /** - * Constructs a QR Code symbol with the specified version number, error correction level, binary data array, and mask number. + * Constructs a QR Code with the specified version number, error correction level, binary data array, and mask number. *

This is a cumbersome low-level constructor that should not be invoked directly by the user. * To go one level up, see 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) @@ -252,7 +252,7 @@ public final class QrCode { /** * Returns a new image object representing this QR Code, with the specified module scale and number - * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code symbol + * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code * with 4 white border modules on all four edges, then use 10*10 pixels to represent each module. * The resulting image only contains the hex colors 000000 and FFFFFF. * @param scale the module scale factor, which must be positive @@ -279,7 +279,7 @@ public final class QrCode { /** - * Returns a string of SVG XML code representing an image of this QR Code symbol with the specified + * Returns a string of SVG XML code representing an image of this QR Code with the specified * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document @@ -474,7 +474,7 @@ public final class QrCode { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // 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) @@ -507,7 +507,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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. private void applyMask(int mask) { if (mask < 0 || mask > 7) throw new IllegalArgumentException("Mask value out of range"); @@ -670,7 +670,7 @@ public final class QrCode { throw new IllegalArgumentException("Version number out of range"); int size = ver * 4 + 17; - int result = size * size; // Number of modules in the whole QR symbol square + int result = size * size; // Number of modules in the whole QR Code square result -= 8 * 8 * 3; // Subtract the three finders with separators result -= 15 * 2 + 1; // Subtract the format information and black module result -= (size - 16) * 2; // Subtract the timing patterns (excluding finders) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index c66dadf..d2d3ede 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -64,7 +64,7 @@ var qrcodegen = new function() { * and provides static functions to create a QR Code from user-supplied textual or binary data. * This class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, + * This constructor creates a new QR Code with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. */ @@ -121,17 +121,17 @@ var qrcodegen = new function() { /*---- Read-only instance properties ----*/ - // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + // This QR Code's version number, which is always between 1 and 40 (inclusive). Object.defineProperty(this, "version", {value:version}); - // The width and height of this QR Code symbol, measured in modules. + // The width and height of this QR Code, measured in modules. // Always equal to version * 4 + 17, in the range 21 to 177. Object.defineProperty(this, "size", {value:size}); - // The error correction level used in this QR Code symbol. + // The error correction level used in this QR Code. Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); - // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if the constructor was called with automatic masking requested // (mask = -1), the resulting object will still have a mask value between 0 and 7. Object.defineProperty(this, "mask", {value:mask}); @@ -149,7 +149,7 @@ var qrcodegen = new function() { /*---- Public instance methods ----*/ - // Draws this QR Code symbol with the given module scale and number of modules onto the given HTML canvas element. + // Draws this QR Code with the given module scale and number of modules onto the given HTML canvas element. // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. this.drawCanvas = function(scale, border, canvas) { @@ -167,7 +167,7 @@ var qrcodegen = new function() { } }; - // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // Returns a string of SVG XML code representing an image of this QR Code with the given // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. this.toSvgString = function(border) { if (border < 0) @@ -351,7 +351,7 @@ var qrcodegen = new function() { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // data area of this QR Code. Function modules need to be marked off before this is called. function drawCodewords(data) { if (data.length != Math.floor(QrCode.getNumRawDataModules(version) / 8)) throw "Invalid argument"; @@ -383,7 +383,7 @@ var qrcodegen = new function() { // 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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. function applyMask(mask) { if (mask < 0 || mask > 7) throw "Mask value out of range"; @@ -516,7 +516,7 @@ var qrcodegen = new function() { /*---- Static factory functions (high level) for QrCode ----*/ /* - * Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + * Returns a QR Code representing the given Unicode text string at the given error correction level. * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the @@ -529,7 +529,7 @@ var qrcodegen = new function() { /* - * Returns a QR Code symbol representing the given binary data string at the given error correction level. + * Returns a QR Code representing the given binary data string at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -543,7 +543,7 @@ var qrcodegen = new function() { /*---- Static factory functions (mid level) for QrCode ----*/ /* - * Returns a QR Code symbol representing the given segments with the given encoding parameters. + * Returns a QR Code representing the given segments with the given encoding parameters. * The smallest possible QR Code version within the given range is automatically chosen for the output. * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -599,7 +599,7 @@ var qrcodegen = new function() { for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - // Create the QR Code symbol + // Create the QR Code object return new this(version, ecl, bb.getBytes(), mask); }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index b1692d5..9e49c25 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -70,7 +70,7 @@ class QrCode(object): @staticmethod def encode_text(text, ecl): - """Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + """Returns a QR Code representing the given Unicode text string at the given error correction level. As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the @@ -81,7 +81,7 @@ class QrCode(object): @staticmethod def encode_binary(data, ecl): - """Returns a QR Code symbol representing the given binary data string at the given error correction level. + """Returns a QR Code representing the given binary data string at the given error correction level. This function always encodes using the binary segment mode, not any text mode. The maximum number of bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" @@ -94,7 +94,7 @@ class QrCode(object): @staticmethod def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): - """Returns a QR Code symbol representing the given segments with the given encoding parameters. + """Returns a QR Code representing the given segments with the given encoding parameters. The smallest possible QR Code version within the given range is automatically chosen for the output. This function allows the user to create a custom sequence of segments that switches between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -140,14 +140,14 @@ class QrCode(object): break bb.append_bits(padbyte, 8) - # Create the QR Code symbol + # Create the QR Code object return QrCode(version, ecl, bb.get_bytes(), mask) # ---- Constructor (low level) ---- def __init__(self, version, errcorlvl, datacodewords, mask): - """Creates a new QR Code symbol with the given version number, error correction level, binary data array, + """Creates a new QR Code with the given version number, error correction level, binary data array, and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor that should not be invoked directly by the user. To go one level up, see the QrCode.encode_segments() function.""" @@ -163,7 +163,7 @@ class QrCode(object): self._errcorlvl = errcorlvl # Initialize both grids to be size*size arrays of Boolean false - # The modules of this QR Code symbol (False = white, True = black). Immutable after constructor finishes + # The modules of this QR Code (False = white, True = black). Immutable after constructor finishes self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white # Indicates function modules that are not subjected to masking. Discarded when constructor finishes self._isfunction = [[False] * self._size for _ in range(self._size)] @@ -195,20 +195,20 @@ class QrCode(object): # ---- Accessor methods ---- def get_version(self): - """Returns this QR Code symbol's version number, which is always between 1 and 40 (inclusive).""" + """Returns this QR Code's version number, which is always between 1 and 40 (inclusive).""" return self._version def get_size(self): - """Returns the width and height of this QR Code symbol, measured in modules. + """Returns the width and height of this QR Code, measured in modules. Always equal to version * 4 + 17, in the range 21 to 177.""" return self._size def get_error_correction_level(self): - """Returns the error correction level used in this QR Code symbol.""" + """Returns the error correction level used in this QR Code.""" return self._errcorlvl def get_mask(self): - """Returns the mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + """Returns the mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). Note that even if a constructor was called with automatic masking requested (mask = -1), the resulting object will still have a mask value between 0 and 7.""" return self._mask @@ -223,7 +223,7 @@ class QrCode(object): # ---- Public instance methods ---- def to_svg_str(self, border): - """Returns a string of SVG XML code representing an image of this QR Code symbol with the given + """Returns a string of SVG XML code representing an image of this QR Code with the given number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform.""" if border < 0: raise ValueError("Border must be non-negative") @@ -387,7 +387,7 @@ class QrCode(object): def _draw_codewords(self, data): """Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - data area of this QR Code symbol. Function modules need to be marked off before this is called.""" + data area of this QR Code. Function modules need to be marked off before this is called.""" assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 i = 0 # Bit index into the data @@ -413,7 +413,7 @@ class QrCode(object): 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 symbol needs exactly one (not zero, two, etc.) mask applied.""" + QR Code needs exactly one (not zero, two, etc.) mask applied.""" if not (0 <= mask <= 7): raise ValueError("Mask value out of range") masker = QrCode._MASK_PATTERNS[mask] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a6f10d5..66e8c1a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -33,24 +33,24 @@ pub struct QrCode { // Scalar parameters: - // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + // This QR Code's version number, which is always between 1 and 40 (inclusive). version: Version, - // The width and height of this QR Code symbol, measured in modules. + // The width and height of this QR Code, measured in modules. // Always equal to version * 4 + 17, in the range 21 to 177. size: i32, - // The error correction level used in this QR Code symbol. + // The error correction level used in this QR Code. errorcorrectionlevel: QrCodeEcc, - // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if a constructor was called with automatic masking requested // (mask = -1), the resulting object will still have a mask value between 0 and 7. mask: Mask, // Grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. modules: Vec, // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. @@ -63,7 +63,7 @@ impl QrCode { /*---- Static factory functions (high level) ----*/ - // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + // Returns a QR Code representing the given Unicode text string at the given error correction level. // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode // code points (not UTF-8 code units) if the low error correction level is used. The smallest possible // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than @@ -76,7 +76,7 @@ impl QrCode { } - // Returns a QR Code symbol representing the given binary data string at the given error correction level. + // Returns a QR Code representing the given binary data string at the given error correction level. // This function always encodes using the binary segment mode, not any text mode. The maximum number of // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -89,7 +89,7 @@ impl QrCode { /*---- Static factory functions (mid level) ----*/ - // Returns a QR Code symbol representing the given segments at the given error correction level. + // Returns a QR Code representing the given segments at the given error correction level. // The smallest possible QR Code version is automatically chosen for the output. The ECC level // of the result may be higher than the ecl argument if it can be done without increasing the version. // This function allows the user to create a custom sequence of segments that switches @@ -101,7 +101,7 @@ impl QrCode { } - // Returns a QR Code symbol representing the given segments with the given encoding parameters. + // Returns a QR Code representing the given segments with the given encoding parameters. // The smallest possible QR Code version within the given range is automatically chosen for the output. // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -168,14 +168,14 @@ impl QrCode { bytes[i >> 3] |= (*bit as u8) << (7 - (i & 7)); } - // Create the QR Code symbol + // Create the QR Code object Some(QrCode::encode_codewords(version, ecl, &bytes, mask)) } /*---- Constructor (low level) ----*/ - // Creates a new QR Code symbol with the given version number, error correction level, + // Creates a new QR Code with the given version number, error correction level, // binary data array, and mask number. This is a cumbersome low-level constructor that // should not be invoked directly by the user. To go one level up, see the encode_segments() function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { @@ -247,7 +247,7 @@ impl QrCode { } - // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // Returns a string of SVG XML code representing an image of this QR Code with the given // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); @@ -452,7 +452,7 @@ impl QrCode { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // data area of this QR Code. Function modules need to be marked off before this is called. fn draw_codewords(&mut self, data: &[u8]) { assert_eq!(data.len(), QrCode::get_num_raw_data_modules(self.version) / 8, "Illegal argument"); @@ -486,7 +486,7 @@ impl 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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. fn apply_mask(&mut self, mask: Mask) { let mask: u8 = mask.value(); for y in 0 .. self.size { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 79ca04b..8c4e165 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -38,7 +38,7 @@ namespace qrcodegen { * provides static functions to create a QR Code from user-supplied textual or binary data. * This class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * This constructor creates a new QR Code symbol with the given version number, error correction level, binary data array, + * This constructor creates a new QR Code with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. */ @@ -46,7 +46,7 @@ namespace qrcodegen { /*-- Static factory functions (high level) --*/ - // Returns a QR Code symbol representing the given Unicode text string at the given error correction level. + // Returns a QR Code representing the given Unicode text string at the given error correction level. // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer // Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the @@ -57,7 +57,7 @@ namespace qrcodegen { } - // Returns a QR Code symbol representing the given binary data string at the given error correction level. + // Returns a QR Code representing the given binary data string at the given error correction level. // This function always encodes using the binary segment mode, not any text mode. The maximum number of // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -69,7 +69,7 @@ namespace qrcodegen { /*-- Static factory functions (mid level) --*/ - // Returns a QR Code symbol representing the given segments with the given encoding parameters. + // Returns a QR Code representing the given segments with the given encoding parameters. // The smallest possible QR Code version within the given range is automatically chosen for the output. // This function allows the user to create a custom sequence of segments that switches // between modes (such as alphanumeric and binary) to encode text more efficiently. @@ -126,18 +126,18 @@ namespace qrcodegen { for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - // Create the QR Code symbol + // Create the QR Code object return new QrCode(version, ecl, bb.getBytes(), mask); } /*-- Fields --*/ - // The width and height of this QR Code symbol, measured in modules. + // The width and height of this QR Code, measured in modules. // Always equal to version * 4 + 17, in the range 21 to 177. public readonly size: int; - // The modules of this QR Code symbol (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. private readonly modules : Array> = []; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. @@ -147,15 +147,15 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ public constructor( - // This QR Code symbol's version number, which is always between 1 and 40 (inclusive). + // This QR Code's version number, which is always between 1 and 40 (inclusive). public readonly version: int, - // The error correction level used in this QR Code symbol. + // The error correction level used in this QR Code. public readonly errorCorrectionLevel: QrCode.Ecc, dataCodewords: Array, - // The mask pattern used in this QR Code symbol, in the range 0 to 7 (i.e. unsigned 3-bit integer). + // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). // Note that even if the constructor was called with automatic masking requested // (mask = -1), the resulting object will still have a mask value between 0 and 7. public readonly mask: int) { @@ -217,7 +217,7 @@ namespace qrcodegen { /*-- Public instance methods --*/ - // Draws this QR Code symbol with the given module scale and number of modules onto the given HTML canvas element. + // Draws this QR Code with the given module scale and number of modules onto the given HTML canvas element. // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { @@ -236,7 +236,7 @@ namespace qrcodegen { } - // Returns a string of SVG XML code representing an image of this QR Code symbol with the given + // Returns a string of SVG XML code representing an image of this QR Code with the given // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. public toSvgString(border: int): string { if (border < 0) @@ -423,7 +423,7 @@ namespace qrcodegen { // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // data area of this QR Code. Function modules need to be marked off before this is called. private drawCodewords(data: Array): void { if (data.length != Math.floor(QrCode.getNumRawDataModules(this.version) / 8)) throw "Invalid argument"; @@ -455,7 +455,7 @@ namespace qrcodegen { // 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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. private applyMask(mask: int): void { if (mask < 0 || mask > 7) throw "Mask value out of range"; From a5b6c28a1f3dba1bb96a10696ce98c058d3d8ceb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 20:56:50 +0000 Subject: [PATCH 510/810] Updated and synchronized documentation comments for QrCode class's fields and constants, in all languages. --- c/qrcodegen.h | 5 ++--- cpp/QrCode.hpp | 16 ++++++++++------ java/io/nayuki/qrcodegen/QrCode.java | 18 +++++++++++------- javascript/qrcodegen.js | 17 +++++++++-------- python/qrcodegen.py | 16 ++++++++++++++-- rust/src/lib.rs | 16 ++++++++++------ typescript/qrcodegen.ts | 15 +++++++++------ 7 files changed, 65 insertions(+), 38 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index cdcc879..bc410f9 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -113,9 +113,8 @@ struct qrcodegen_Segment { /*---- Macro constants and functions ----*/ -// The minimum and maximum defined QR Code version numbers for Model 2. -#define qrcodegen_VERSION_MIN 1 -#define qrcodegen_VERSION_MAX 40 +#define qrcodegen_VERSION_MIN 1 // The minimum version number supported in the QR Code Model 2 standard +#define qrcodegen_VERSION_MAX 40 // The maximum version number supported in the QR Code Model 2 standard // Calculates the number of bytes needed to store any QR Code up to and including the given version number, // as a compile-time constant. For example, 'uint8_t buffer[qrcodegen_BUFFER_LEN_FOR_VERSION(25)];' diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 075591f..3a644d5 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -96,19 +96,20 @@ class QrCode final { // Immutable scalar parameters: - /* This QR Code's version number, which is always between 1 and 40 (inclusive). */ + /* The version number of this QR Code, which is between 1 and 40 (inclusive). + * This determines the size of this barcode. */ private: int version; - /* The width and height of this QR Code, measured in modules. - * Always equal to version * 4 + 17, in the range 21 to 177. */ + /* The width and height of this QR Code, measured in modules, between + * 21 and 177 (inclusive). This is equal to version * 4 + 17. */ private: int size; /* The error correction level used in this QR Code. */ private: Ecc errorCorrectionLevel; - /* The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - * Note that even if a constructor was called with automatic masking requested - * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ + /* 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. */ private: int mask; // Private grids of modules/pixels, with dimensions of size*size: @@ -255,7 +256,10 @@ class QrCode final { /*---- Constants and tables ----*/ + // The minimum version number supported in the QR Code Model 2 standard. public: static constexpr int MIN_VERSION = 1; + + // The maximum version number supported in the QR Code Model 2 standard. public: static constexpr int MAX_VERSION = 40; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 683eedc..ca57ebe 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -173,19 +173,20 @@ public final class QrCode { // Public immutable scalar parameters: - /** This QR Code's version number, which is always between 1 and 40 (inclusive). */ + /** 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. - * Always equal to version × 4 + 17, in the range 21 to 177 (inclusive). */ + /** 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. Never {@code null}. */ + /** The error correction level used in this QR Code, which is not {@code null}. */ public final Ecc errorCorrectionLevel; - /** The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - * Note that even if a constructor was called with automatic masking requested - * (mask = -1), the resulting object will still have a mask value between 0 and 7. */ + /** 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: @@ -705,7 +706,10 @@ public final class QrCode { /*---- 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; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index d2d3ede..d395c21 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -121,19 +121,20 @@ var qrcodegen = new function() { /*---- Read-only instance properties ----*/ - // This QR Code's version number, which is always between 1 and 40 (inclusive). + // The version number of this QR Code, which is between 1 and 40 (inclusive). + // This determines the size of this barcode. Object.defineProperty(this, "version", {value:version}); - // The width and height of this QR Code, measured in modules. - // Always equal to version * 4 + 17, in the range 21 to 177. + // The width and height of this QR Code, measured in modules, between + // 21 and 177 (inclusive). This is equal to version * 4 + 17. Object.defineProperty(this, "size", {value:size}); // The error correction level used in this QR Code. Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); - // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - // Note that even if the constructor was called with automatic masking requested - // (mask = -1), the resulting object will still have a mask value between 0 and 7. + // 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. Object.defineProperty(this, "mask", {value:mask}); @@ -638,8 +639,8 @@ var qrcodegen = new function() { /*---- Constants and tables for QrCode ----*/ - var MIN_VERSION = 1; - var MAX_VERSION = 40; + var MIN_VERSION = 1; // The minimum version number supported in the QR Code Model 2 standard + var MAX_VERSION = 40; // The maximum version number supported in the QR Code Model 2 standard Object.defineProperty(this.QrCode, "MIN_VERSION", {value:MIN_VERSION}); Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 9e49c25..377c352 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -158,8 +158,16 @@ class QrCode(object): raise ValueError("Mask value out of range") if not isinstance(errcorlvl, QrCode.Ecc): raise TypeError("QrCode.Ecc expected") + + # The version number of this QR Code, which is between 1 and 40 (inclusive). + # This determines the size of this barcode. self._version = version + + # The width and height of this QR Code, measured in modules, between + # 21 and 177 (inclusive). This is equal to version * 4 + 17. self._size = version * 4 + 17 + + # The error correction level used in this QR Code. self._errcorlvl = errcorlvl # Initialize both grids to be size*size arrays of Boolean false @@ -187,6 +195,10 @@ class QrCode(object): assert 0 <= mask <= 7 self._draw_format_bits(mask) # Overwrite old format bits self._apply_mask(mask) # Apply the final choice of mask + + # 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. self._mask = mask del self._isfunction @@ -529,8 +541,8 @@ class QrCode(object): # ---- Constants and tables ---- - MIN_VERSION = 1 - MAX_VERSION = 40 + MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard + MAX_VERSION = 40 # The maximum version number supported in the QR Code Model 2 standard # For use in getPenaltyScore(), when evaluating which mask is best. _PENALTY_N1 = 3 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 66e8c1a..4100d37 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -33,19 +33,20 @@ pub struct QrCode { // Scalar parameters: - // This QR Code's version number, which is always between 1 and 40 (inclusive). + // The version number of this QR Code, which is between 1 and 40 (inclusive). + // This determines the size of this barcode. version: Version, - // The width and height of this QR Code, measured in modules. - // Always equal to version * 4 + 17, in the range 21 to 177. + // The width and height of this QR Code, measured in modules, between + // 21 and 177 (inclusive). This is equal to version * 4 + 17. size: i32, // The error correction level used in this QR Code. errorcorrectionlevel: QrCodeEcc, - // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - // Note that even if a constructor was called with automatic masking requested - // (mask = -1), the resulting object will still have a mask value between 0 and 7. + // 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 = None), + // the resulting object still has a mask value between 0 and 7. mask: Mask, // Grids of modules/pixels, with dimensions of size*size: @@ -681,7 +682,10 @@ impl QrCode { /*---- Cconstants and tables ----*/ +// The minimum version number supported in the QR Code Model 2 standard. pub const QrCode_MIN_VERSION: Version = Version( 1); + +// The maximum version number supported in the QR Code Model 2 standard. pub const QrCode_MAX_VERSION: Version = Version(40); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 8c4e165..928ca89 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -133,8 +133,8 @@ namespace qrcodegen { /*-- Fields --*/ - // The width and height of this QR Code, measured in modules. - // Always equal to version * 4 + 17, in the range 21 to 177. + // The width and height of this QR Code, measured in modules, between + // 21 and 177 (inclusive). This is equal to version * 4 + 17. public readonly size: int; // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. @@ -147,7 +147,8 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ public constructor( - // This QR Code's version number, which is always between 1 and 40 (inclusive). + // The version number of this QR Code, which is between 1 and 40 (inclusive). + // This determines the size of this barcode. public readonly version: int, // The error correction level used in this QR Code. @@ -155,9 +156,9 @@ namespace qrcodegen { dataCodewords: Array, - // The mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - // Note that even if the constructor was called with automatic masking requested - // (mask = -1), the resulting object will still have a mask value between 0 and 7. + // 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 readonly mask: int) { // Check scalar arguments @@ -609,7 +610,9 @@ namespace qrcodegen { /*-- Constants and tables --*/ + // The minimum version number supported in the QR Code Model 2 standard. public static readonly MIN_VERSION: int = 1; + // The maximum version number supported in the QR Code Model 2 standard. public static readonly MAX_VERSION: int = 40; // For use in getPenaltyScore(), when evaluating which mask is best. From 95e5f14c36c20f0a25ded479b24355cef5f1b5c7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 21:17:52 +0000 Subject: [PATCH 511/810] Updated and synchronized QrCode class's top-level documentation comment, in all languages. --- c/qrcodegen.h | 16 ++++++++++++++++ cpp/QrCode.hpp | 16 +++++++++++++--- java/io/nayuki/qrcodegen/QrCode.java | 20 +++++++++++++++++--- javascript/qrcodegen.js | 17 ++++++++++++++--- python/qrcodegen.py | 18 ++++++++++++++---- rust/src/lib.rs | 21 +++++++++++++++++---- typescript/qrcodegen.ts | 17 ++++++++++++++--- 7 files changed, 105 insertions(+), 20 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index bc410f9..f09d887 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -33,6 +33,22 @@ extern "C" { #endif +/* + * This library creates QR Code symbols, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * A QR Code structure is an immutable square grid of black and white cells. + * The library provides functions to create a QR Code from text or binary data. + * The library covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call qrcodegen_encodeText() or qrcodegen_encodeBinary(). + * - Low level: Custom-make the list of segments and call + * qrcodegen_encodeSegments() or qrcodegen_encodeSegmentsAdvanced(). + * (Note that all ways require supplying the desired error correction level and various byte buffers.) + */ + + /*---- Enum and struct types----*/ /* diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 3a644d5..ec59a14 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -32,10 +32,20 @@ namespace qrcodegen { /* - * Represents an immutable square grid of black and white cells for a QR Code symbol, and - * provides static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encodeText() or QrCode::encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode::encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) */ class QrCode final { diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index ca57ebe..3930441 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -30,10 +30,24 @@ import java.util.Objects; /** - * Represents an immutable square grid of black and white cells for a QR Code symbol, and - * provides static functions to create a QR Code from user-supplied textual or binary data. - *

This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + *

Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

+ *

Ways to create a QR Code object:

+ *
    + *
  • High level: Take the payload data and call {@link QrCode#encodeText(String,Ecc)} + * or {@link QrCode#encodeBinary(byte[],Ecc)}.

  • + *
  • Mid level: Custom-make the list of {@link QrSegment segments} + * and call {@link QrCode#encodeSegments(List,Ecc)} or + * {@link QrCode#encodeSegments(List,Ecc,int,int,int,boolean)}

  • + *
  • Low level: Custom-make the array of data codeword bytes (including segment headers and + * final padding, excluding error correction codewords), supply the appropriate version number, + * and call the {@link QrCode#QrCode(int,Ecc,byte[],int) constructor}.

  • + *
+ *

(Note that all ways require supplying the desired error correction level.)

*/ public final class QrCode { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index d395c21..2925f04 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -60,10 +60,21 @@ var qrcodegen = new function() { /*---- QR Code symbol class ----*/ /* - * A class that represents an immutable square grid of black and white cells for a QR Code symbol, - * and provides static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * A class that represents a QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + * * This constructor creates a new QR Code with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 377c352..2d08082 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -61,10 +61,20 @@ This module "qrcodegen", public members: # ---- QR Code symbol class ---- class QrCode(object): - """Represents an immutable square grid of black and white cells for a QR Code symbol, and - provides static functions to create a QR Code from user-supplied textual or binary data. - This class covers the QR Code Model 2 specification, supporting all versions (sizes) - from 1 to 40, all 4 error correction levels, and 4 character encoding modes.""" + """A QR Code symbol, which is a type of two-dimension barcode. + Invented by Denso Wave and described in the ISO/IEC 18004 standard. + Instances of this class represent an immutable square grid of black and white cells. + The class provides static factory functions to create a QR Code from text or binary data. + The class covers the QR Code Model 2 specification, supporting all versions (sizes) + from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + + Ways to create a QR Code object: + - High level: Take the payload data and call QrCode.encode_text() or QrCode.encode_binary(). + - Mid level: Custom-make the list of segments and call QrCode.encode_segments(). + - Low level: Custom-make the array of data codeword bytes (including + segment headers and final padding, excluding error correction codewords), + supply the appropriate version number, and call the QrCode() constructor. + (Note that all ways require supplying the desired error correction level.)""" # ---- Static factory functions (high level) ---- diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4100d37..31a3b24 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -24,10 +24,23 @@ /*---- QrCode functionality ----*/ -// Represents an immutable square grid of black and white cells for a QR Code symbol, and -// provides static functions to create a QR Code from user-supplied textual or binary data. -// This struct and impl cover the QR Code Model 2 specification, supporting all versions -// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this struct represent an immutable square grid of black and white cells. + * The impl provides static factory functions to create a QR Code from text or binary data. + * The struct and impl cover the QR Code Model 2 specification, supporting all versions + * (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode::encode_text() or QrCode::encode_binary(). + * - Mid level: Custom-make the list of segments and call + * QrCode.encode_segments() or QrCode.encode_segments_advanced(). + * - Low level: Custom-make the array of data codeword bytes (including segment + * headers and final padding, excluding error correction codewords), supply the + * appropriate version number, and call the QrCode::encode_codewords() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ #[derive(Clone)] pub struct QrCode { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 928ca89..f326ecb 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -34,10 +34,21 @@ namespace qrcodegen { /*---- QR Code symbol class ----*/ /* - * Represents an immutable square grid of black and white cells for a QR Code symbol, and - * provides static functions to create a QR Code from user-supplied textual or binary data. - * This class covers the QR Code Model 2 specification, supporting all versions (sizes) + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + * * This constructor creates a new QR Code with the given version number, error correction level, binary data array, * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. From fe2c384e97d27f43b186829407a082f8ee8e0b72 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 5 Oct 2018 21:20:09 +0000 Subject: [PATCH 512/810] Moved and tweaked a comment in TypeScript code. --- typescript/qrcodegen.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index f326ecb..c8148fd 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -48,10 +48,6 @@ namespace qrcodegen { * segment headers and final padding, excluding error correction codewords), * supply the appropriate version number, and call the QrCode() constructor. * (Note that all ways require supplying the desired error correction level.) - * - * This constructor creates a new QR Code with the given version number, error correction level, binary data array, - * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor - * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. */ export class QrCode { @@ -157,6 +153,9 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ + // Creates a new QR Code with the given version number, error correction level, binary data array, + // and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor + // that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. public constructor( // The version number of this QR Code, which is between 1 and 40 (inclusive). // This determines the size of this barcode. From 76f97dd0b8de835d89fe0ff83d2a569af34661f8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:11:03 +0000 Subject: [PATCH 513/810] Updated and synchronized documentation comments for QrCode class's constructor and static factory functions, in all languages. --- c/qrcodegen.h | 14 +++++++++----- cpp/QrCode.hpp | 19 ++++++++++++------- java/io/nayuki/qrcodegen/QrCode.java | 25 ++++++++++++++++--------- javascript/qrcodegen.js | 19 ++++++++++++------- python/qrcodegen.py | 19 ++++++++++++------- rust/src/lib.rs | 23 ++++++++++++++--------- typescript/qrcodegen.ts | 19 ++++++++++++------- 7 files changed, 87 insertions(+), 51 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index f09d887..4757ed1 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -256,8 +256,8 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level * of the result may be higher than the ecl argument if it can be done without increasing the version. * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will * result in them being clobbered, but the QR Code output will still be correct. * But the qrcode array must not overlap tempBuffer or any segment's data buffer. @@ -269,10 +269,14 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, /* * Renders a QR Code representing the given segments with the given encoding parameters. * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. - * The smallest possible QR Code version within the given range is automatically chosen for the output. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will * result in them being clobbered, but the QR Code output will still be correct. * But the qrcode array must not overlap tempBuffer or any segment's data buffer. diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index ec59a14..f552b8f 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -80,7 +80,7 @@ class QrCode final { /* - * Returns a QR Code representing the given binary data string at the given error correction level. + * Returns a QR Code representing the given binary data at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -92,10 +92,14 @@ class QrCode final { /* * Returns a QR Code representing the given segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically chosen for the output. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). */ public: static QrCode encodeSegments(const std::vector &segs, Ecc ecl, int minVersion=1, int maxVersion=40, int mask=-1, bool boostEcl=true); // All optional parameters @@ -135,9 +139,10 @@ class QrCode final { /*---- Constructor (low level) ----*/ /* - * Creates a new QR Code with the given version number, error correction level, binary data array, - * and mask number. This is a cumbersome low-level constructor that should not be invoked directly by the user. - * To go one level up, see the encodeSegments() function. + * Creates a new QR Code with the given 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 encodeSegments() function. */ public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int mask); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 3930441..b9bfbff 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -75,7 +75,7 @@ public final class QrCode { /** - * Returns a QR Code representing the specified binary data string at the specified error correction level. + * 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 * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -101,8 +101,9 @@ public final class QrCode { * level. The smallest possible QR Code version is automatically chosen for the output. The ECC level * of the result may be higher than the ecl argument if it can be done without increasing the version. *

This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data.

+ * 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 (will be boosted) * @return a QR Code representing the segments @@ -117,10 +118,15 @@ public final class QrCode { /** * 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 chosen for the output. + * The smallest possible QR Code version within the specified range is automatically + * chosen for the output. Iff boostEcl is {@code true}, then the ECC level of the + * result may be higher than the ecl argument if it can be done without increasing + * the version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or −1 to automatically choose an appropriate mask (which may be slow). *

This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data.

+ * 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 (may be boosted) * @param minVersion the minimum allowed version of the QR symbol (at least 1) @@ -216,9 +222,10 @@ public final class QrCode { /*---- Constructor (low level) ----*/ /** - * Constructs a QR Code with the specified version number, error correction level, binary data array, and mask number. - *

This is a cumbersome low-level constructor that should not be invoked directly by the user. - * To go one level up, see the {@link #encodeSegments(List,Ecc,int,int,int,boolean)} function.

+ * 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 dataCodewords the bytes representing segments to encode (without ECC) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 2925f04..c5c778f 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -75,9 +75,10 @@ var qrcodegen = new function() { * supply the appropriate version number, and call the QrCode() constructor. * (Note that all ways require supplying the desired error correction level.) * - * This constructor creates a new QR Code with the given version number, error correction level, binary data array, - * and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor - * that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. + * This constructor creates a new QR Code with the given 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 encodeSegments() function. */ this.QrCode = function(version, errCorLvl, dataCodewords, mask) { @@ -541,7 +542,7 @@ var qrcodegen = new function() { /* - * Returns a QR Code representing the given binary data string at the given error correction level. + * Returns a QR Code representing the given binary data at the given error correction level. * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -556,10 +557,14 @@ var qrcodegen = new function() { /* * Returns a QR Code representing the given segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically chosen for the output. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and binary) to encode text more efficiently. - * This function is considered to be lower level than simply encoding text or binary data. + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). */ this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { if (minVersion == undefined) minVersion = MIN_VERSION; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 2d08082..a71a8ba 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -91,7 +91,7 @@ class QrCode(object): @staticmethod def encode_binary(data, ecl): - """Returns a QR Code representing the given binary data string at the given error correction level. + """Returns a QR Code representing the given binary data at the given error correction level. This function always encodes using the binary segment mode, not any text mode. The maximum number of bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" @@ -105,10 +105,14 @@ class QrCode(object): @staticmethod def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): """Returns a QR Code representing the given segments with the given encoding parameters. - The smallest possible QR Code version within the given range is automatically chosen for the output. + The smallest possible QR Code version within the given range is automatically + chosen for the output. Iff boostecl is true, then the ECC level of the result + may be higher than the ecl argument if it can be done without increasing the + version. The mask number is either between 0 to 7 (inclusive) to force that + mask, or -1 to automatically choose an appropriate mask (which may be slow). This function allows the user to create a custom sequence of segments that switches - between modes (such as alphanumeric and binary) to encode text more efficiently. - This function is considered to be lower level than simply encoding text or binary data.""" + between modes (such as alphanumeric and byte) to encode text in less space. + This is a mid-level API; the high-level API is encode_text() and encode_binary().""" if not (QrCode.MIN_VERSION <= minversion <= maxversion <= QrCode.MAX_VERSION) or not (-1 <= mask <= 7): raise ValueError("Invalid value") @@ -157,9 +161,10 @@ class QrCode(object): # ---- Constructor (low level) ---- def __init__(self, version, errcorlvl, datacodewords, mask): - """Creates a new QR Code with the given version number, error correction level, binary data array, - and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor - that should not be invoked directly by the user. To go one level up, see the QrCode.encode_segments() function.""" + """Creates a new QR Code with the given 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 encode_segments() function.""" # Check scalar arguments and set fields if not (QrCode.MIN_VERSION <= version <= QrCode.MAX_VERSION): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 31a3b24..f690350 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -90,7 +90,7 @@ impl QrCode { } - // Returns a QR Code representing the given binary data string at the given error correction level. + // Returns a QR Code representing the given binary data at the given error correction level. // This function always encodes using the binary segment mode, not any text mode. The maximum number of // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -107,8 +107,8 @@ impl QrCode { // The smallest possible QR Code version is automatically chosen for the output. The ECC level // of the result may be higher than the ecl argument if it can be done without increasing the version. // This function allows the user to create a custom sequence of segments that switches - // between modes (such as alphanumeric and binary) to encode text more efficiently. - // This function is considered to be lower level than simply encoding text or binary data. + // between modes (such as alphanumeric and byte) to encode text in less space. + // This is a mid-level API; the high-level API is encode_text() and encode_binary(). // Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) @@ -116,10 +116,14 @@ impl QrCode { // Returns a QR Code representing the given segments with the given encoding parameters. - // The smallest possible QR Code version within the given range is automatically chosen for the output. + // The smallest possible QR Code version within the given range is automatically + // chosen for the output. Iff boostecl is true, then the ECC level of the result + // may be higher than the ecl argument if it can be done without increasing the + // version. The mask number is either between 0 to 7 (inclusive) to force that + // mask, or -1 to automatically choose an appropriate mask (which may be slow). // This function allows the user to create a custom sequence of segments that switches - // between modes (such as alphanumeric and binary) to encode text more efficiently. - // This function is considered to be lower level than simply encoding text or binary data. + // between modes (such as alphanumeric and byte) to encode text in less space. + // This is a mid-level API; the high-level API is encodeText() and encodeBinary(). // Returns a wrapped QrCode if successful, or None if the data is too long to fit // in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, @@ -189,9 +193,10 @@ impl QrCode { /*---- Constructor (low level) ----*/ - // Creates a new QR Code with the given version number, error correction level, - // binary data array, and mask number. This is a cumbersome low-level constructor that - // should not be invoked directly by the user. To go one level up, see the encode_segments() function. + // Creates a new QR Code with the given 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 encode_segments() function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { // Initialize fields let size: usize = (ver.value() as usize) * 4 + 17; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index c8148fd..a424b75 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -64,7 +64,7 @@ namespace qrcodegen { } - // Returns a QR Code representing the given binary data string at the given error correction level. + // Returns a QR Code representing the given binary data at the given error correction level. // This function always encodes using the binary segment mode, not any text mode. The maximum number of // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -77,10 +77,14 @@ namespace qrcodegen { /*-- Static factory functions (mid level) --*/ // Returns a QR Code representing the given segments with the given encoding parameters. - // The smallest possible QR Code version within the given range is automatically chosen for the output. + // The smallest possible QR Code version within the given range is automatically + // chosen for the output. Iff boostEcl is true, then the ECC level of the result + // may be higher than the ecl argument if it can be done without increasing the + // version. The mask number is either between 0 to 7 (inclusive) to force that + // mask, or -1 to automatically choose an appropriate mask (which may be slow). // This function allows the user to create a custom sequence of segments that switches - // between modes (such as alphanumeric and binary) to encode text more efficiently. - // This function is considered to be lower level than simply encoding text or binary data. + // between modes (such as alphanumeric and byte) to encode text in less space. + // This is a mid-level API; the high-level API is encodeText() and encodeBinary(). public static encodeSegments(segs: Array, ecl: QrCode.Ecc, minVersion: int = 1, maxVersion: int = 40, mask: int = -1, boostEcl: boolean = true): QrCode { @@ -153,9 +157,10 @@ namespace qrcodegen { /*-- Constructor (low level) and fields --*/ - // Creates a new QR Code with the given version number, error correction level, binary data array, - // and mask number. mask = -1 is for automatic choice, or 0 to 7 for fixed choice. This is a cumbersome low-level constructor - // that should not be invoked directly by the user. To go one level up, see the QrCode.encodeSegments() function. + // Creates a new QR Code with the given 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 encodeSegments() function. public constructor( // The version number of this QR Code, which is between 1 and 40 (inclusive). // This determines the size of this barcode. From f011e527709ff2ce3d4e436f5b05a53cefa46de2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:26:54 +0000 Subject: [PATCH 514/810] Updated and synchronized documentation comments for QrCode's instance methods, in all languages. --- c/qrcodegen.h | 4 ++-- cpp/QrCode.hpp | 8 ++++---- java/io/nayuki/qrcodegen/QrCode.java | 16 ++++++++-------- javascript/qrcodegen.js | 15 ++++++++------- python/qrcodegen.py | 8 ++++---- rust/src/lib.rs | 10 +++++----- typescript/qrcodegen.ts | 15 ++++++++------- 7 files changed, 39 insertions(+), 37 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 4757ed1..13e8eab 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -297,8 +297,8 @@ int qrcodegen_getSize(const uint8_t qrcode[]); /* - * Returns the color of the module (pixel) at the given coordinates, which is either - * false for white or true for black. The top left corner has the coordinates (x=0, y=0). + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). * If the given coordinates are out of bounds, then false (white) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index f552b8f..4d33300 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -163,16 +163,16 @@ class QrCode final { /* - * Returns the color of the module (pixel) at the given coordinates, which is either - * false for white or true for black. The top left corner has the coordinates (x=0, y=0). + * Returns the color of the module (pixel) at the given coordinates, which is false + * for white or true for black. The top left corner has the coordinates (x=0, y=0). * If the given coordinates are out of bounds, then false (white) is returned. */ public: bool getModule(int x, int y) const; /* - * Returns a string of SVG XML code representing an image of this QR Code with the given - * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + * Returns a string of SVG code for an image depicting this QR Code, with the given number + * of border modules. The string always uses Unix newlines (\n), regardless of the platform. */ public: std::string toSvgString(int border) const; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index b9bfbff..00364a7 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -260,9 +260,9 @@ public final class QrCode { /*---- Public instance methods ----*/ /** - * Returns the color of the module (pixel) at the specified coordinates, which is either - * false for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the specified coordinates are out of bounds, then false (white) is returned. + * 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). + * If the specified coordinates are out of bounds, then {@code false} (white) is returned. * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge * @return the module's color, which is either false (white) or true (black) @@ -273,9 +273,9 @@ public final class QrCode { /** - * Returns a new image object representing this QR Code, with the specified module scale and number - * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code - * with 4 white border modules on all four edges, then use 10*10 pixels to represent each module. + * 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 + * border modules on all four sides, and use 10×10 pixels to represent each module. * The resulting image only contains the hex colors 000000 and FFFFFF. * @param scale the module scale factor, which must be positive * @param border the number of border modules to add, which must be non-negative @@ -301,8 +301,8 @@ public final class QrCode { /** - * Returns a string of SVG XML code representing an image of this QR Code with the specified - * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + * 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. * @param border the number of border modules to add, which must be non-negative * @return a string representing this QR Code as an SVG document * @throws IllegalArgumentException if the border is negative diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index c5c778f..2324d3b 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -152,8 +152,8 @@ var qrcodegen = new function() { /*---- Accessor methods ----*/ - // (Public) Returns the color of the module (pixel) at the given coordinates, which is either - // false for white or true for black. The top left corner has the coordinates (x=0, y=0). + // Returns the color of the module (pixel) at the given coordinates, which is false + // for white or true for black. The top left corner has the coordinates (x=0, y=0). // If the given coordinates are out of bounds, then false (white) is returned. this.getModule = function(x, y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; @@ -162,9 +162,10 @@ var qrcodegen = new function() { /*---- Public instance methods ----*/ - // Draws this QR Code with the given module scale and number of modules onto the given HTML canvas element. - // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely - // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. + // Draws this QR Code, with the given module scale and border modules, onto the given HTML + // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. + // The drawn image is be purely black and white, and fully opaque. + // The scale must be a positive integer and the border must be a non-negative integer. this.drawCanvas = function(scale, border, canvas) { if (scale <= 0 || border < 0) throw "Value out of range"; @@ -180,8 +181,8 @@ var qrcodegen = new function() { } }; - // Returns a string of SVG XML code representing an image of this QR Code with the given - // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG code for an image depicting this QR Code, with the given number + // of border modules. The string always uses Unix newlines (\n), regardless of the platform. this.toSvgString = function(border) { if (border < 0) throw "Border must be non-negative"; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index a71a8ba..9c75982 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -241,8 +241,8 @@ class QrCode(object): return self._mask def get_module(self, x, y): - """Returns the color of the module (pixel) at the given coordinates, which is either - False for white or True for black. The top left corner has the coordinates (x=0, y=0). + """Returns the color of the module (pixel) at the given coordinates, which is False + for white or True for black. The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then False (white) is returned.""" return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x] @@ -250,8 +250,8 @@ class QrCode(object): # ---- Public instance methods ---- def to_svg_str(self, border): - """Returns a string of SVG XML code representing an image of this QR Code with the given - number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform.""" + """Returns a string of SVG code for an image depicting this QR Code, with the given number + of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" if border < 0: raise ValueError("Border must be non-negative") parts = [] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f690350..f68d15b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -246,9 +246,9 @@ impl QrCode { } - // Returns the color of the module (pixel) at the given coordinates, which is either - // false for white or true for black. The top left corner has the coordinates (x=0, y=0). - // If the given coordinates are out of bounds, then 0 (white) is returned. + // Returns the color of the module (pixel) at the given coordinates, which is false + // for white or true for black. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then false (white) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) } @@ -266,8 +266,8 @@ impl QrCode { } - // Returns a string of SVG XML code representing an image of this QR Code with the given - // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG code for an image depicting this QR Code, with the given number + // of border modules. The string always uses Unix newlines (\n), regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); let mut result = String::new(); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index a424b75..2aca061 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -223,8 +223,8 @@ namespace qrcodegen { /*-- Accessor methods --*/ - // Returns the color of the module (pixel) at the given coordinates, which is either - // false for white or true for black. The top left corner has the coordinates (x=0, y=0). + // Returns the color of the module (pixel) at the given coordinates, which is false + // for white or true for black. The top left corner has the coordinates (x=0, y=0). // If the given coordinates are out of bounds, then false (white) is returned. public getModule(x: int, y: int): boolean { return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x]; @@ -233,9 +233,10 @@ namespace qrcodegen { /*-- Public instance methods --*/ - // Draws this QR Code with the given module scale and number of modules onto the given HTML canvas element. - // The canvas will be resized to a width and height of (this.size + border * 2) * scale. The painted image will be purely - // black and white with no transparent regions. The scale must be a positive integer, and the border must be a non-negative integer. + // Draws this QR Code, with the given module scale and border modules, onto the given HTML + // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. + // The drawn image is be purely black and white, and fully opaque. + // The scale must be a positive integer and the border must be a non-negative integer. public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) throw "Value out of range"; @@ -252,8 +253,8 @@ namespace qrcodegen { } - // Returns a string of SVG XML code representing an image of this QR Code with the given - // number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + // Returns a string of SVG code for an image depicting this QR Code, with the given number + // of border modules. The string always uses Unix newlines (\n), regardless of the platform. public toSvgString(border: int): string { if (border < 0) throw "Border must be non-negative"; From 012d4a3e5f48bf02e5262221aa0b892d4e79a8f1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:29:38 +0000 Subject: [PATCH 515/810] Updated Javadoc-specific sections of documentation comments in Java classes. --- java/io/nayuki/qrcodegen/QrCode.java | 32 ++++++++++--------- .../nayuki/qrcodegen/QrSegmentAdvanced.java | 4 +-- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 00364a7..4df24ad 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -48,6 +48,7 @@ import java.util.Objects; * and call the {@link QrCode#QrCode(int,Ecc,byte[],int) constructor}.

* *

(Note that all ways require supplying the desired error correction level.)

+ * @see QrSegment */ public final class QrCode { @@ -60,7 +61,7 @@ public final class QrCode { * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the * ecl argument if it can be done without increasing the version. * @param text the text to be encoded, which can be any Unicode string - * @param ecl the error correction level to use (will be boosted) + * @param ecl the error correction level to use (boostable) * @return a QR Code representing the text * @throws NullPointerException if the text or error correction level is {@code null} * @throws IllegalArgumentException if the text fails to fit in the @@ -80,8 +81,8 @@ public final class QrCode { * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. * @param data the binary data to encode - * @param ecl the error correction level to use (will be boosted) - * @return a QR Code representing the binary data + * @param ecl the error correction level to use (boostable) + * @return a QR Code representing the data * @throws NullPointerException if the data or error correction level is {@code null} * @throws IllegalArgumentException if the data fails to fit in the * largest version QR Code at the ECL, which means it is too long @@ -105,7 +106,7 @@ public final class QrCode { * 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 (will be boosted) + * @param ecl the error correction level to use (boostable) * @return a QR Code representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if the segments fail to fit in the @@ -128,16 +129,16 @@ public final class QrCode { * 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 (may be boosted) - * @param minVersion the minimum allowed version of the QR symbol (at least 1) - * @param maxVersion the maximum allowed version of the QR symbol (at most 40) - * @param mask the mask pattern to use, which is either -1 for automatic choice or from 0 to 7 for fixed choice - * @param boostEcl increases the error correction level if it can be done without increasing the version number + * @param ecl the error correction level to use (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 + * @param boostEcl increases the ECC level as long as it doesn't increase the version number * @return a QR Code representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * is violated, or if mask < −1 or mask > 7, or if the segments fail - * to fit in the maxVersion QR Code at the ECL, which means they are too long + * or −1 ≤ mask ≤ 7 is violated; or 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); @@ -265,7 +266,8 @@ public final class QrCode { * If the specified coordinates are out of bounds, then {@code false} (white) is returned. * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge - * @return the module's color, which is either false (white) or true (black) + * @return {@code true} if the coordinates are in bounds and the module + * at that location is black, or {@code false} (white) otherwise */ public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; @@ -277,9 +279,9 @@ public final class QrCode { *

For example, toImage(scale=10, border=4) means to pad the QR Code with 4 white * border modules on all four sides, and use 10×10 pixels to represent each module. * The resulting image only contains the hex colors 000000 and FFFFFF. - * @param scale the module scale factor, which must be positive + * @param scale the side length (measured in pixels, must be positive) of each module * @param border the number of border modules to add, which must be non-negative - * @return an image representing this QR Code, with padding and scaling + * @return a new image representing this QR Code, with padding and scaling * @throws IllegalArgumentException if the scale or border is out of range, or if * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE */ @@ -304,7 +306,7 @@ public final class QrCode { * 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. * @param border the number of border modules to add, which must be non-negative - * @return a string representing this QR Code as an SVG document + * @return a string representing this QR Code as an SVG XML document * @throws IllegalArgumentException if the border is negative */ public String toSvgString(int border) { diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index b68575e..c435a92 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -52,8 +52,8 @@ public final class QrSegmentAdvanced { * range of versions, like {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

* @param text the text to be encoded (not {@code null}), which can be any Unicode string * @param ecl the error correction level to use (not {@code null}) - * @param minVersion the minimum allowed version of the QR Code symbol (at least 1) - * @param maxVersion the maximum allowed version of the QR Code symbol (at most 40) + * @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) * @return a new mutable list (not {@code null}) of segments (not {@code null}) * containing the text, minimizing the bit length with respect to the constraints * @throws NullPointerException if the text or error correction level is {@code null} From efed57782e85a6daeb278ac9a4c207c05b14f63a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:29:57 +0000 Subject: [PATCH 516/810] Updated Javadoc-specific sections of the Java QrSegment class to add not-null qualifications. --- java/io/nayuki/qrcodegen/QrCode.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 4df24ad..faafc4a 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -60,9 +60,9 @@ public final class QrCode { * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the * ecl argument if it can be done without increasing the version. - * @param text the text to be encoded, which can be any Unicode string - * @param ecl the error correction level to use (boostable) - * @return a QR Code representing the text + * @param text the text to be encoded (not {@code null}), which can be any Unicode string + * @param ecl the error correction level to use (not {@code null}) (boostable) + * @return a QR Code (not {@code null}) representing the text * @throws NullPointerException if the text or error correction level is {@code null} * @throws IllegalArgumentException if the text fails to fit in the * largest version QR Code at the ECL, which means it is too long @@ -80,9 +80,9 @@ public final class QrCode { * This function always encodes using the binary segment mode, not any text mode. The maximum number of * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - * @param data the binary data to encode - * @param ecl the error correction level to use (boostable) - * @return a QR Code representing the data + * @param data the binary data to encode (not {@code null}) + * @param ecl the error correction level to use (not {@code null}) (boostable) + * @return a QR Code (not {@code null}) representing the data * @throws NullPointerException if the data or error correction level is {@code null} * @throws IllegalArgumentException if the data fails to fit in the * largest version QR Code at the ECL, which means it is too long @@ -106,8 +106,8 @@ public final class QrCode { * 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 (boostable) - * @return a QR Code representing the segments + * @param ecl the error correction level to use (not {@code null}) (boostable) + * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if the segments fail to fit in the * largest version QR Code at the ECL, which means they are too long @@ -129,12 +129,12 @@ public final class QrCode { * 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 (boostable) + * @param ecl 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 * @param boostEcl increases the ECC level as long as it doesn't increase the version number - * @return a QR Code representing the segments + * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 * or −1 ≤ mask ≤ 7 is violated; or if the segments fail to From 1c9a9cf60c10ef1806f17bc59df03f7ca0e93204 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:37:21 +0000 Subject: [PATCH 517/810] Tweaked a few documentation comments. --- javascript/qrcodegen.js | 4 ++-- rust/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 2324d3b..2b4a470 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -32,7 +32,7 @@ * - Function encodeSegments(list segs, QrCode.Ecc ecl, * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode * - Constants int MIN_VERSION, MAX_VERSION - * - Constructor QrCode(int version, QrCode.Ecc ecl, list dataCodewords, int mask) + * - Constructor QrCode(int version, QrCode.Ecc ecl, list dataCodewords, int mask) * - Fields int version, size, mask * - Field QrCode.Ecc errorCorrectionLevel * - Method getModule(int x, int y) -> bool @@ -42,7 +42,7 @@ * - Constants LOW, MEDIUM, QUARTILE, HIGH * - Field int ordinal * - Class QrSegment: - * - Function makeBytes(list data) -> QrSegment + * - Function makeBytes(list data) -> QrSegment * - Function makeNumeric(str data) -> QrSegment * - Function makeAlphanumeric(str data) -> QrSegment * - Function makeSegments(str text) -> list diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f68d15b..4c08978 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -779,7 +779,7 @@ impl QrCodeEcc { // Computes the Reed-Solomon error correction codewords for a sequence of data codewords // at a given degree. Objects are immutable, and the state only depends on the degree. -// This class exists because each data block in a QR Code shares the same the divisor polynomial. +// This struct and impl exist because each data block in a QR Code shares the same the divisor polynomial. struct ReedSolomonGenerator { // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which From f4b16f25b01a1ae534ba2c9b27afc1f875ef84da Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:40:31 +0000 Subject: [PATCH 518/810] Updated documentation comment for field QrCode.modules, in most languages. --- cpp/QrCode.hpp | 3 ++- java/io/nayuki/qrcodegen/QrCode.java | 3 ++- python/qrcodegen.py | 3 ++- rust/src/lib.rs | 3 ++- typescript/qrcodegen.ts | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 4d33300..9bcb2a6 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -128,7 +128,8 @@ class QrCode final { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through getModule(). private: std::vector > modules; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index faafc4a..a63b721 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -212,7 +212,8 @@ public final class QrCode { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. + // 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. diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 9c75982..e202fcb 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -186,7 +186,8 @@ class QrCode(object): self._errcorlvl = errcorlvl # Initialize both grids to be size*size arrays of Boolean false - # The modules of this QR Code (False = white, True = black). Immutable after constructor finishes + # The modules of this QR Code (False = white, True = black). + # Immutable after constructor finishes. Accessed through get_module(). self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white # Indicates function modules that are not subjected to masking. Discarded when constructor finishes self._isfunction = [[False] * self._size for _ in range(self._size)] diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4c08978..8cd84c6 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -64,7 +64,8 @@ pub struct QrCode { // Grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through get_module(). modules: Vec, // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 2aca061..9b59dbf 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -148,7 +148,8 @@ namespace qrcodegen { // 21 and 177 (inclusive). This is equal to version * 4 + 17. public readonly size: int; - // The modules of this QR Code (false = white, true = black). Immutable after constructor finishes. + // The modules of this QR Code (false = white, true = black). + // Immutable after constructor finishes. Accessed through getModule(). private readonly modules : Array> = []; // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. From 8c262c00ddbcf7735c7b868df9a39cd0016a271f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 03:46:20 +0000 Subject: [PATCH 519/810] Added and synchronized documentation comments for QrCode's scalar field accessor methods in C++ and Python with existing comments in Rust. --- cpp/QrCode.hpp | 12 ++++++++++++ python/qrcodegen.py | 11 ++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 9bcb2a6..1904844 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -151,15 +151,27 @@ class QrCode final { /*---- Public instance methods ----*/ + /* + * Returns this QR Code's version, in the range [1, 40]. + */ public: int getVersion() const; + /* + * Returns this QR Code's size, in the range [21, 177]. + */ public: int getSize() const; + /* + * Returns this QR Code's error correction level. + */ public: Ecc getErrorCorrectionLevel() const; + /* + * Returns this QR Code's mask, in the range [0, 7]. + */ public: int getMask() const; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index e202fcb..51a93d0 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -223,22 +223,19 @@ class QrCode(object): # ---- Accessor methods ---- def get_version(self): - """Returns this QR Code's version number, which is always between 1 and 40 (inclusive).""" + """Returns this QR Code's version number, in the range [1, 40].""" return self._version def get_size(self): - """Returns the width and height of this QR Code, measured in modules. - Always equal to version * 4 + 17, in the range 21 to 177.""" + """Returns this QR Code's size, in the range [21, 177].""" return self._size def get_error_correction_level(self): - """Returns the error correction level used in this QR Code.""" + """Returns this QR Code's error correction level.""" return self._errcorlvl def get_mask(self): - """Returns the mask pattern used in this QR Code, in the range 0 to 7 (i.e. unsigned 3-bit integer). - Note that even if a constructor was called with automatic masking requested - (mask = -1), the resulting object will still have a mask value between 0 and 7.""" + """Returns this QR Code's mask, in the range [0, 7].""" return self._mask def get_module(self, x, y): From c7bc281e186e24b0feb6c9a57ea742c8cededc5f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 04:44:03 +0000 Subject: [PATCH 520/810] Inlined BitBuffer.getBytes() into its single usage (with simplification because the bit length is a multiple of 8) in most language versions, synchronized comment and variable name in Rust version. --- cpp/BitBuffer.cpp | 8 -------- cpp/BitBuffer.hpp | 7 +------ cpp/QrCode.cpp | 7 ++++++- java/io/nayuki/qrcodegen/BitBuffer.java | 15 --------------- java/io/nayuki/qrcodegen/QrCode.java | 7 ++++++- javascript/qrcodegen.js | 22 +++++++++------------- python/qrcodegen.py | 15 ++++++--------- rust/src/lib.rs | 7 ++++--- typescript/qrcodegen.ts | 21 ++++++++------------- 9 files changed, 40 insertions(+), 69 deletions(-) diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp index ee2d277..e22e9d3 100644 --- a/cpp/BitBuffer.cpp +++ b/cpp/BitBuffer.cpp @@ -31,14 +31,6 @@ BitBuffer::BitBuffer() : std::vector() {} -std::vector BitBuffer::getBytes() const { - std::vector result(size() / 8 + (size() % 8 == 0 ? 0 : 1)); - for (std::size_t i = 0; i < size(); i++) - result[i >> 3] |= (*this)[i] ? 1 << (7 - (i & 7)) : 0; - return result; -} - - void BitBuffer::appendBits(std::uint32_t val, int len) { if (len < 0 || len > 31 || val >> len != 0) throw std::domain_error("Value out of range"); diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp index bccabf1..f30913a 100644 --- a/cpp/BitBuffer.hpp +++ b/cpp/BitBuffer.hpp @@ -41,12 +41,7 @@ class BitBuffer final : public std::vector { - /*---- Methods ----*/ - - // Returns a vector representing this buffer's bits packed into bytes in big endian. If the - // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. - public: std::vector getBytes() const; - + /*---- Method ----*/ // Appends the given number of low-order bits of the given value // to this buffer. Requires 0 <= len <= 31 and val < 2^len. diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index ce2de59..f6c8da6 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -109,8 +109,13 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, for (uint8_t padByte = 0xEC; bb.size() < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); + // Pack bits into bytes in big endian + vector dataCodewords(bb.size() / 8); + for (size_t i = 0; i < bb.size(); i++) + dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + // Create the QR Code object - return QrCode(version, ecl, bb.getBytes(), mask); + return QrCode(version, ecl, dataCodewords, mask); } diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/io/nayuki/qrcodegen/BitBuffer.java index 4ede5cd..624d129 100644 --- a/java/io/nayuki/qrcodegen/BitBuffer.java +++ b/java/io/nayuki/qrcodegen/BitBuffer.java @@ -77,21 +77,6 @@ public final class BitBuffer implements Cloneable { } - /** - * Returns an array representing this buffer's bits packed into bytes in big endian. If the - * bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. - * @return a new byte array (not {@code null}) representing this bit sequence - */ - public byte[] getBytes() { - byte[] result = new byte[(bitLength + 7) >>> 3]; // Round up to whole byte, won't overflow - for (int i = 0; i < bitLength; i++) { - if (data.get(i)) - result[i >>> 3] |= 1 << (7 - (i & 7)); - } - return result; - } - - /** * Appends the specified number of low-order bits of the specified value to this * buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index a63b721..eba69ec 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -184,8 +184,13 @@ public final class QrCode { 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, bb.getBytes(), mask); + return new QrCode(version, ecl, dataCodewords, mask); } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 2b4a470..b4a6d89 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -617,8 +617,16 @@ var qrcodegen = new function() { for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); + // Pack bits into bytes in big endian + var dataCodewords = []; + while (dataCodewords.length * 8 < bb.length) + dataCodewords.push(0); + bb.forEach(function(bit, i) { + dataCodewords[i >>> 3] |= bit << (7 - (i & 7)); + }); + // Create the QR Code object - return new this(version, ecl, bb.getBytes(), mask); + return new this(version, ecl, dataCodewords, mask); }; @@ -996,18 +1004,6 @@ var qrcodegen = new function() { function BitBuffer() { Array.call(this); - // Returns a new array representing this buffer's bits packed into bytes in big endian. If the - // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. - this.getBytes = function() { - var result = []; - while (result.length * 8 < this.length) - result.push(0); - this.forEach(function(bit, i) { - result[i >>> 3] |= bit << (7 - (i & 7)); - }); - return result; - }; - // Appends the given number of low-order bits of the given value // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. this.appendBits = function(val, len) { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 51a93d0..0be9a2d 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -154,8 +154,13 @@ class QrCode(object): break bb.append_bits(padbyte, 8) + # Pack bits into bytes in big endian + datacodewords = [0] * (len(bb) // 8) + for (i, bit) in enumerate(bb): + datacodewords[i >> 3] |= bit << (7 - (i & 7)) + # Create the QR Code object - return QrCode(version, ecl, bb.get_bytes(), mask) + return QrCode(version, ecl, datacodewords, mask) # ---- Constructor (low level) ---- @@ -872,14 +877,6 @@ class _ReedSolomonGenerator(object): class _BitBuffer(list): """An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.""" - def get_bytes(self): - """Returns a new list representing this buffer's bits packed into bytes in big endian. If the - bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'.""" - result = [0] * ((len(self) + 7) // 8) - for (i, bit) in enumerate(self): - result[i >> 3] |= bit << (7 - (i & 7)) - return result - def append_bits(self, val, n): """Appends the given number of low-order bits of the given value to this buffer. Requires n >= 0 and 0 <= val < 2^n.""" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8cd84c6..e17e8b0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -182,13 +182,14 @@ impl QrCode { bb.append_bits(*padbyte, 8); } - let mut bytes = vec![0u8; bb.0.len() / 8]; + // Pack bits into bytes in big endian + let mut datacodewords = vec![0u8; bb.0.len() / 8]; for (i, bit) in bb.0.iter().enumerate() { - bytes[i >> 3] |= (*bit as u8) << (7 - (i & 7)); + datacodewords[i >> 3] |= (*bit as u8) << (7 - (i & 7)); } // Create the QR Code object - Some(QrCode::encode_codewords(version, ecl, &bytes, mask)) + Some(QrCode::encode_codewords(version, ecl, &datacodewords, mask)) } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 9b59dbf..20d9869 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -137,8 +137,15 @@ namespace qrcodegen { for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); + // Pack bits into bytes in big endian + let dataCodewords: Array = []; + while (dataCodewords.length * 8 < bb.length) + dataCodewords.push(0); + bb.forEach((b: bit, i: int) => + dataCodewords[i >>> 3] |= b << (7 - (i & 7))); + // Create the QR Code object - return new QrCode(version, ecl, bb.getBytes(), mask); + return new QrCode(version, ecl, dataCodewords, mask); } @@ -928,18 +935,6 @@ namespace qrcodegen { */ class BitBuffer extends Array { - // Returns a new array representing this buffer's bits packed into bytes in big endian. If the - // bit length isn't a multiple of 8, then the remaining bits of the final byte are all '0'. - public getBytes(): Array { - let result: Array = []; - while (result.length * 8 < this.length) - result.push(0); - this.forEach((b: bit, i: int) => - result[i >>> 3] |= b << (7 - (i & 7))); - return result; - } - - // Appends the given number of low-order bits of the given value // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. public appendBits(val: int, len: int): void { From b7922a8dce1006cb98dd00a8b13756d738e0e1c2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 04:48:47 +0000 Subject: [PATCH 521/810] Tweaked code slightly. --- java/io/nayuki/qrcodegen/QrSegment.java | 2 +- typescript/qrcodegen.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index dc0cd30..9f9c74c 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -173,7 +173,7 @@ public final class QrSegment { * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; - /** The data bits of this segment. Not {@code null}. Accessed through {@link getBits()}. */ + /** The data bits of this segment. Not {@code null}. Accessed through {@link #getBits()}. */ final BitBuffer data; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 20d9869..a1a6987 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -808,7 +808,7 @@ namespace qrcodegen { let ccbits: int = seg.mode.numCharCountBits(version); if (seg.numChars >= (1 << ccbits)) return Infinity; // The segment's length doesn't fit the field's bit width - result += 4 + ccbits + seg.getBits().length; + result += 4 + ccbits + seg.bitData.length; } return result; } From 950955a4c5d97662317f6cfe625b0c7364beafff Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 04:49:04 +0000 Subject: [PATCH 522/810] Renamed QrSegment.getBits() to getData() in {Java, JavaScript, TypeScript, Python} code, to match C++ and Rust. --- java/io/nayuki/qrcodegen/QrSegment.java | 4 ++-- javascript/qrcodegen.js | 10 ++++++---- python/qrcodegen.py | 6 +++--- typescript/qrcodegen.ts | 6 +++--- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 9f9c74c..0f13947 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -173,7 +173,7 @@ public final class QrSegment { * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; - /** The data bits of this segment. Not {@code null}. Accessed through {@link #getBits()}. */ + /** The data bits of this segment. Not {@code null}. Accessed through {@link #getData()}. */ final BitBuffer data; @@ -205,7 +205,7 @@ public final class QrSegment { * Returns the data bits of this segment. * @return a new copy of the data bits (not {@code null}) */ - public BitBuffer getBits() { + public BitBuffer getData() { return data.clone(); // Make defensive copy } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index b4a6d89..1834294 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -50,7 +50,7 @@ * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) * - Field QrSegment.Mode mode * - Field int numChars - * - Method getBits() -> list + * - Method getData() -> list * - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX * - Enum Mode: * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI @@ -597,7 +597,7 @@ var qrcodegen = new function() { segs.forEach(function(seg) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - seg.getBits().forEach(function(bit) { + seg.getData().forEach(function(bit) { bb.push(bit); }); }); @@ -738,6 +738,8 @@ var qrcodegen = new function() { /*---- Constructor (low level) ----*/ if (numChars < 0 || !(mode instanceof Mode)) throw "Invalid argument"; + + // The data bits of this segment. Accessed through getData(). bitData = bitData.slice(); // Make defensive copy // The mode indicator of this segment. @@ -749,7 +751,7 @@ var qrcodegen = new function() { Object.defineProperty(this, "numChars", {value:numChars}); // Returns a new copy of the data bits of this segment. - this.getBits = function() { + this.getData = function() { return bitData.slice(); // Make defensive copy }; }; @@ -856,7 +858,7 @@ var qrcodegen = new function() { var ccbits = seg.mode.numCharCountBits(version); if (seg.numChars >= (1 << ccbits)) return Infinity; // The segment's length doesn't fit the field's bit width - result += 4 + ccbits + seg.getBits().length; + result += 4 + ccbits + seg.getData().length; } return result; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 0be9a2d..ca9bdf4 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -51,7 +51,7 @@ This module "qrcodegen", public members: - Constructor QrSegment(QrSegment.Mode mode, int numch, list bitdata) - Method get_mode() -> QrSegment.Mode - Method get_num_chars() -> int - - Method get_bits() -> list + - Method get_data() -> list - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX - Enum Mode: - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI @@ -733,7 +733,7 @@ class QrSegment(object): # Accessed through get_num_chars(). self._numchars = numch - # The data bits of this segment. Accessed through get_bits(). + # The data bits of this segment. Accessed through get_data(). self._bitdata = list(bitdata) # Make defensive copy @@ -747,7 +747,7 @@ class QrSegment(object): """Returns the character count field of this segment.""" return self._numchars - def get_bits(self): + def get_data(self): """Returns a new copy of the data bits of this segment.""" return list(self._bitdata) # Make defensive copy diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index a1a6987..1fbfa45 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -118,7 +118,7 @@ namespace qrcodegen { for (let seg of segs) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - for (let b of seg.getBits()) + for (let b of seg.getData()) bb.push(b); } if (bb.length != dataUsedBits) @@ -783,7 +783,7 @@ namespace qrcodegen { // Always zero or positive. Not the same as the data's bit length. public readonly numChars: int, - // The data bits of this segment. Accessed through getBits(). + // The data bits of this segment. Accessed through getData(). private readonly bitData: Array) { if (numChars < 0) @@ -795,7 +795,7 @@ namespace qrcodegen { /*-- Methods --*/ // Returns a new copy of the data bits of this segment. - public getBits(): Array { + public getData(): Array { return this.bitData.slice(); // Make defensive copy } From a6ef65d237628a03dee3ae1df592df9a3359204d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Oct 2018 04:49:29 +0000 Subject: [PATCH 523/810] Fixed initialization order in C++ code (due to commit 06d80aade31d). --- cpp/QrCode.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index f6c8da6..0a8b12f 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -122,14 +122,14 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) : // Initialize fields and check arguments version(ver), - errorCorrectionLevel(ecl), - modules (size, vector(size)), // Initially all white - isFunction(size, vector(size)) { + errorCorrectionLevel(ecl) { if (ver < MIN_VERSION || ver > MAX_VERSION) throw std::domain_error("Version value out of range"); if (mask < -1 || mask > 7) throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; + modules = vector >(size, vector(size)); // Initially all white + isFunction = vector >(size, vector(size)); // Compute ECC, draw modules, do masking drawFunctionPatterns(); From b0a7a4240aa4f423b6506b58aec5c5e142a32c91 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:49:13 +0000 Subject: [PATCH 524/810] Added private helper function to reduce occurrences of low-level bit arithmetic. --- src/io/nayuki/fastqrcodegen/QrCode.java | 30 +++++++++++++++---------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 25b57a7..ff0ebaf 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -162,7 +162,7 @@ public final class QrCode { public boolean getModule(int x, int y) { if (0 <= x && x < size && 0 <= y && y < size) { int i = y * size + x; - return ((modules[i >>> 5] >>> i) & 1) != 0; + return getBit(modules[i >>> 5], i) != 0; } else return false; } @@ -246,18 +246,18 @@ public final class QrCode { // Draw first copy for (int i = 0; i <= 5; i++) - setModule(8, i, (bits >>> i) & 1); - setModule(8, 7, (bits >>> 6) & 1); - setModule(8, 8, (bits >>> 7) & 1); - setModule(7, 8, (bits >>> 8) & 1); + setModule(8, i, getBit(bits, i)); + setModule(8, 7, getBit(bits, 6)); + setModule(8, 8, getBit(bits, 7)); + setModule(7, 8, getBit(bits, 8)); for (int i = 9; i < 15; i++) - setModule(14 - i, 8, (bits >>> i) & 1); + setModule(14 - i, 8, getBit(bits, i)); // Draw second copy for (int i = 0; i <= 7; i++) - setModule(size - 1 - i, 8, (bits >>> i) & 1); + setModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) - setModule(8, size - 15 + i, (bits >>> i) & 1); + setModule(8, size - 15 + i, getBit(bits, i)); setModule(8, size - 8, 1); // Always black } @@ -319,7 +319,7 @@ public final class QrCode { throw new IllegalArgumentException(); for (int i = 0; i < dataOutputBitIndexes.length; i++) { int j = dataOutputBitIndexes[i]; - int bit = (allCodewords[i >>> 3] >>> (~i & 7)) & 1; + int bit = getBit(allCodewords[i >>> 3], ~i & 7); modules[j >>> 5] |= bit << j; } } @@ -377,7 +377,7 @@ public final class QrCode { for (int x = 0; x < size; x++, index++, downIndex++) { // Adjacent modules having same color - int bit = (modules[index >>> 5] >>> index) & 1; + int bit = getBit(modules[index >>> 5], index); if (bit != runColor) { runColor = bit; runLen = 1; @@ -392,7 +392,7 @@ public final class QrCode { black += bit; bits = ((bits & 0b1111111111) << 1) | bit; if (downIndex < end) { - downBits = ((downBits & 1) << 1) | ((modules[downIndex >>> 5] >>> downIndex) & 1); + downBits = ((downBits & 1) << 1) | getBit(modules[downIndex >>> 5], downIndex); // 2*2 blocks of modules having same color if (x >= 1 && (downBits == 0 || downBits == 3) && downBits == (bits & 3)) result += PENALTY_N2; @@ -412,7 +412,7 @@ public final class QrCode { for (int y = 0, index = x; y < size; y++, index += size) { // Adjacent modules having same color - int bit = (modules[index >>> 5] >>> index) & 1; + int bit = getBit(modules[index >>> 5], index); if (bit != runColor) { runColor = bit; runLen = 1; @@ -453,6 +453,12 @@ public final class QrCode { } + // Returns 0 or 1 based on the i'th bit of x. + private static int getBit(int x, int i) { + return (x >>> i) & 1; + } + + /*---- Private tables of constants ----*/ // For use in getPenaltyScore(), when evaluating which mask is best. From 6f79d4c68e42321b603ed9749cd61f0eaaa45a73 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:51:10 +0000 Subject: [PATCH 525/810] Tweaked comments and statement order in QrCode constructor. --- src/io/nayuki/fastqrcodegen/QrCode.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index ff0ebaf..bd6e51a 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -128,20 +128,20 @@ public final class QrCode { /*---- Constructor ----*/ public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int mask) { - // Check arguments - errorCorrectionLevel = Objects.requireNonNull(ecl); - if (ver < MIN_VERSION || ver > MAX_VERSION || mask < -1 || mask > 7) - throw new IllegalArgumentException("Value out of range"); - Objects.requireNonNull(dataCodewords); - - // Initialize fields + // Check arguments and initialize fields + if (ver < MIN_VERSION || ver > MAX_VERSION) + throw new IllegalArgumentException("Version value out of range"); + if (mask < -1 || mask > 7) + throw new IllegalArgumentException("Mask value out of range"); version = ver; size = ver * 4 + 17; + errorCorrectionLevel = Objects.requireNonNull(ecl); + Objects.requireNonNull(dataCodewords); QrTemplate tpl = QrTemplate.getInstance(ver); modules = tpl.template.clone(); - // Draw function patterns, draw all codewords, do masking + // Compute ECC, draw modules, do masking byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(tpl.dataOutputBitIndexes, allCodewords); this.mask = handleConstructorMasking(tpl.masks, mask); From 6a5fdc5687339093e5059c0d1b49e166803511d2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:51:58 +0000 Subject: [PATCH 526/810] Tweaked whitespace for consistency. --- src/io/nayuki/fastqrcodegen/QrCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index bd6e51a..b6d5f85 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -139,7 +139,7 @@ public final class QrCode { Objects.requireNonNull(dataCodewords); QrTemplate tpl = QrTemplate.getInstance(ver); - modules = tpl.template.clone(); + modules = tpl.template.clone(); // Compute ECC, draw modules, do masking byte[] allCodewords = addEccAndInterleave(dataCodewords); @@ -241,7 +241,7 @@ public final class QrCode { int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - int bits = (data << 10 | rem) ^ 0x5412; // uint15 + int bits = (data << 10 | rem) ^ 0x5412; // uint15 assert bits >>> 15 == 0; // Draw first copy From 820b2ca60a5c31de19373f5c13894d7a418b6297 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:54:24 +0000 Subject: [PATCH 527/810] Moved QrCode class's max/min version constants, tweaked associated comments and blank lines. --- src/io/nayuki/fastqrcodegen/QrCode.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index b6d5f85..ea295f4 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -104,13 +104,6 @@ public final class QrCode { - /*---- Public constants ----*/ - - public static final int MIN_VERSION = 1; - public static final int MAX_VERSION = 40; - - - /*---- Instance fields ----*/ public final int version; @@ -459,7 +452,11 @@ public final class QrCode { } - /*---- Private tables of constants ----*/ + /*---- Constants and tables ----*/ + + public static final int MIN_VERSION = 1; + public static final int MAX_VERSION = 40; + // For use in getPenaltyScore(), when evaluating which mask is best. private static final int PENALTY_N1 = 3; From 735994ed2a7dee1774cae19c268d952b3bf74caf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:56:01 +0000 Subject: [PATCH 528/810] Aligned main comments in demo program. --- src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java index 03920e2..10ed286 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java @@ -57,11 +57,11 @@ public final class QrCodeGeneratorDemo { QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image - File imgFile = new File("hello-world-QR.png"); // File path for output - ImageIO.write(img, "png", imgFile); // Write image to file + BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + File imgFile = new File("hello-world-QR.png"); // File path for output + ImageIO.write(img, "png", imgFile); // Write image to file - String svg = qr.toSvgString(4); // Convert to SVG XML code + String svg = qr.toSvgString(4); // Convert to SVG XML code Files.write(new File("hello-world-QR.svg").toPath(), svg.getBytes(StandardCharsets.UTF_8)); } From 509881ea9f7a8b73315800011a60584675f11356 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 05:56:31 +0000 Subject: [PATCH 529/810] Slightly tweaked demo program for clarity. --- src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java index 10ed286..f57836c 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorDemo.java @@ -62,7 +62,8 @@ public final class QrCodeGeneratorDemo { ImageIO.write(img, "png", imgFile); // Write image to file String svg = qr.toSvgString(4); // Convert to SVG XML code - Files.write(new File("hello-world-QR.svg").toPath(), + File svgFile = new File("hello-world-QR.svg"); // File path for output + Files.write(svgFile.toPath(), // Write image to file svg.getBytes(StandardCharsets.UTF_8)); } From 35f3b292e71d9a896991c4661c053276a3edaf77 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:12:46 +0000 Subject: [PATCH 530/810] Converted Javadoc comments of non-public members of Java QrSegment class to informal comments. --- java/io/nayuki/qrcodegen/QrSegment.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/io/nayuki/qrcodegen/QrSegment.java index 0f13947..f416200 100644 --- a/java/io/nayuki/qrcodegen/QrSegment.java +++ b/java/io/nayuki/qrcodegen/QrSegment.java @@ -173,7 +173,7 @@ public final class QrSegment { * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; - /** The data bits of this segment. Not {@code null}. Accessed through {@link #getData()}. */ + // The data bits of this segment. Not null. Accessed through getData(). final BitBuffer data; @@ -244,8 +244,8 @@ public final class QrSegment { * @see #makeAlphanumeric(String) */ public static final Pattern ALPHANUMERIC_REGEX = Pattern.compile("[A-Z0-9 $%*+./:-]*"); - /** The set of all legal characters in alphanumeric mode, where - * each character value maps to the index in the string. */ + // The set of all legal characters in alphanumeric mode, where + // each character value maps to the index in the string. static final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; @@ -268,10 +268,10 @@ public final class QrSegment { /*-- Fields --*/ - /** The mode indicator bits, which is a uint4 value (range 0 to 15). */ + // The mode indicator bits, which is a uint4 value (range 0 to 15). final int modeBits; - /** Number of character count bits for three different version ranges. */ + // Number of character count bits for three different version ranges. private final int[] numBitsCharCount; @@ -285,12 +285,8 @@ public final class QrSegment { /*-- Method --*/ - /** - * Returns the bit width of the character count field for a segment - * in this mode in a QR Code at the specified version number. - * @param ver the version number, which is between 1 to 40 (inclusive) - * @return the number of bits for the character count, which is between 0 to 16 (inclusive) - */ + // 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]; From 132c8f32e22bc6d59e0ae0a20f200cc7b050c2e8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:19:22 +0000 Subject: [PATCH 531/810] Added and updated many Javadoc and informal comments, synchronizing from the parent "QR Code generator library" project. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 19 +++ src/io/nayuki/fastqrcodegen/QrCode.java | 161 ++++++++++++++++++--- src/io/nayuki/fastqrcodegen/QrSegment.java | 65 ++++++++- 3 files changed, 221 insertions(+), 24 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index 630ad5b..a2d573e 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -27,6 +27,9 @@ import java.util.Arrays; import java.util.Objects; +/** + * An appendable sequence of bits (0s and 1s). Mainly used by {@link QrSegment}. + */ final class BitBuffer { /*---- Fields ----*/ @@ -39,6 +42,9 @@ final class BitBuffer { /*---- Constructor ----*/ + /** + * Constructs an empty bit buffer (length 0). + */ public BitBuffer() { data = new int[64]; bitLength = 0; @@ -48,6 +54,10 @@ final class BitBuffer { /*---- Methods ----*/ + /** + * Returns the length of this sequence, which is a non-negative value. + * @return the length of this sequence + */ public int getBit(int index) { if (index < 0 || index >= bitLength) throw new IndexOutOfBoundsException(); @@ -65,6 +75,15 @@ final class BitBuffer { } + /** + * Appends the specified number of low-order bits of the specified value to this + * buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. + * @param val the value to append + * @param len the number of low-order bits in the value to take + * @throws IllegalArgumentException if the value or number of bits is out of range + * @throws IllegalStateException if appending the data + * would make bitLength exceed Integer.MAX_VALUE + */ public void appendBits(int val, int len) { if (len < 0 || len > 31 || val >>> len != 0) throw new IllegalArgumentException("Value out of range"); diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index ea295f4..42fdaec 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -29,10 +29,44 @@ import java.util.List; import java.util.Objects; +/** + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + *

Instances of this class represent an immutable square grid of black and white cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

+ *

Ways to create a QR Code object:

+ *
    + *
  • High level: Take the payload data and call {@link QrCode#encodeText(String,Ecc)} + * or {@link QrCode#encodeBinary(byte[],Ecc)}.

  • + *
  • Mid level: Custom-make the list of {@link QrSegment segments} + * and call {@link QrCode#encodeSegments(List,Ecc)} or + * {@link QrCode#encodeSegments(List,Ecc,int,int,int,boolean)}

  • + *
  • Low level: Custom-make the array of data codeword bytes (including segment headers and + * final padding, excluding error correction codewords), supply the appropriate version number, + * and call the {@link QrCode#QrCode(int,Ecc,byte[],int) constructor}.

  • + *
+ *

(Note that all ways require supplying the desired error correction level.)

+ * @see QrSegment + */ public final class QrCode { - /*---- Public static factory functions ----*/ + /*---- 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 + * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible + * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + * ecl argument if it can be done without increasing the version. + * @param text the text to be encoded (not {@code null}), which can be any Unicode string + * @param ecl the error correction level to use (not {@code null}) (boostable) + * @return a QR Code (not {@code null}) representing the text + * @throws NullPointerException if the text or error correction level is {@code null} + * @throws IllegalArgumentException if the text fails to fit in the + * largest version QR Code at the ECL, which means it is too long + */ public static QrCode encodeText(String text, Ecc ecl) { Objects.requireNonNull(text); Objects.requireNonNull(ecl); @@ -41,6 +75,18 @@ public final class QrCode { } + /** + * 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 + * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + * @param data the binary data to encode (not {@code null}) + * @param ecl the error correction level to use (not {@code null}) (boostable) + * @return a QR Code (not {@code null}) representing the data + * @throws NullPointerException if the data or error correction level is {@code null} + * @throws IllegalArgumentException if the data fails to fit in the + * largest version QR Code at the ECL, which means it is too long + */ public static QrCode encodeBinary(byte[] data, Ecc ecl) { Objects.requireNonNull(data); Objects.requireNonNull(ecl); @@ -49,11 +95,51 @@ public final class QrCode { } + /*---- 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 + * of the result may be higher than the ecl argument if it can be done without increasing the version. + *

This function allows the user to create a custom sequence of segments that switches + * 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) + * @return a QR Code (not {@code null}) representing the segments + * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if the segments fail to fit in the + * largest version QR Code at the ECL, which means they are too long + */ 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 + * chosen for the output. Iff boostEcl is {@code true}, then the ECC level of the + * result may be higher than the ecl argument if it can be done without increasing + * the version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or −1 to automatically choose an appropriate mask (which may be slow). + *

This function allows the user to create a custom sequence of segments that switches + * 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 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 + * @param boostEcl increases the ECC level as long as it doesn't increase the version number + * @return a QR Code (not {@code null}) representing the segments + * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 + * or −1 ≤ mask ≤ 7 is violated; or 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); @@ -98,7 +184,7 @@ public final class QrCode { for (int padByte = 0xEC; bb.bitLength < dataCapacityBits; padByte ^= 0xEC ^ 0x11) bb.appendBits(padByte, 8); - // Create the QR Code symbol + // Create the QR Code object return new QrCode(version, ecl, bb.getBytes(), mask); } @@ -106,20 +192,41 @@ public final class QrCode { /*---- Instance fields ----*/ + /** 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 final int[] modules; - /*---- Constructor ----*/ + /*---- 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 dataCodewords the bytes representing segments to encode (without ECC) + * @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 mask) { // Check arguments and initialize fields if (ver < MIN_VERSION || ver > MAX_VERSION) @@ -145,12 +252,13 @@ public final class QrCode { /*---- Public instance methods ----*/ /** - * Returns the color of the module (pixel) at the specified coordinates, which is either - * false for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the specified coordinates are out of bounds, then false (white) is returned. - * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge - * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge - * @return the module's color, which is either false (white) or true (black) + * 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). + * If the specified coordinates are out of bounds, then {@code false} (white) is returned. + * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge + * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge + * @return {@code true} if the coordinates are in bounds and the module + * at that location is black, or {@code false} (white) otherwise */ public boolean getModule(int x, int y) { if (0 <= x && x < size && 0 <= y && y < size) { @@ -162,13 +270,13 @@ public final class QrCode { /** - * Returns a new image object representing this QR Code, with the specified module scale and number - * of border modules. For example, the arguments scale=10, border=4 means to pad the QR Code symbol - * with 4 white border modules on all four edges, then use 10*10 pixels to represent each module. + * 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 + * border modules on all four sides, and use 10×10 pixels to represent each module. * The resulting image only contains the hex colors 000000 and FFFFFF. - * @param scale the module scale factor, which must be positive + * @param scale the side length (measured in pixels, must be positive) of each module * @param border the number of border modules to add, which must be non-negative - * @return an image representing this QR Code, with padding and scaling + * @return a new image representing this QR Code, with padding and scaling * @throws IllegalArgumentException if the scale or border is out of range, or if * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE */ @@ -190,10 +298,10 @@ public final class QrCode { /** - * Returns a string of SVG XML code representing an image of this QR Code symbol with the specified - * number of border modules. Note that Unix newlines (\n) are always used, regardless of the platform. + * 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. * @param border the number of border modules to add, which must be non-negative - * @return a string representing this QR Code as an SVG document + * @return a string representing this QR Code as an SVG XML document * @throws IllegalArgumentException if the border is negative */ public String toSvgString(int border) { @@ -331,7 +439,7 @@ public final class QrCode { } - // A messy helper function for the constructors. This QR Code must be in an unmasked state when this + // 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[][] masks, int mask) { @@ -434,7 +542,7 @@ public final class QrCode { - /*---- Private static helper functions ----*/ + /*---- Private helper functions ----*/ // 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. @@ -454,7 +562,10 @@ public final class QrCode { /*---- 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; @@ -487,10 +598,16 @@ public final class QrCode { /*---- Public helper enumeration ----*/ + /** + * The error correction level in a QR Code symbol. + */ public enum Ecc { - // These enum constants must be declared in ascending order of error protection, - // for the sake of the implicit ordinal() method and values() function. - LOW(1), MEDIUM(0), QUARTILE(3), HIGH(2); + // 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; diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index ff31a35..ee6a6a2 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -32,8 +32,17 @@ import java.util.Objects; public final class QrSegment { - /*---- Static factory functions ----*/ + /*---- Static factory functions (mid level) ----*/ + /** + * Returns a segment representing the specified binary data + * encoded in byte mode. All input byte arrays are acceptable. + *

Any text string can be converted to UTF-8 bytes ({@code + * s.getBytes(StandardCharsets.UTF_8)}) and encoded as a byte mode segment.

+ * @param data the binary data (not {@code null}) + * @return a segment (not {@code null}) containing the data + * @throws NullPointerException if the array is {@code null} + */ public static QrSegment makeBytes(byte[] data) { Objects.requireNonNull(data); int[] bits = new int[(data.length + 3) / 4]; @@ -43,6 +52,13 @@ public final class QrSegment { } + /** + * Returns a segment representing the specified string of decimal digits encoded in numeric mode. + * @param digits the text (not {@code null}), with only digits from 0 to 9 allowed + * @return a segment (not {@code null}) containing the text + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-digit characters + */ public static QrSegment makeNumeric(String digits) { Objects.requireNonNull(digits); BitBuffer bb = new BitBuffer(); @@ -66,6 +82,15 @@ public final class QrSegment { } + /** + * Returns a segment representing the specified text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + * @param text the text (not {@code null}), with only certain characters allowed + * @return a segment (not {@code null}) containing the text + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-encodable characters + */ public static QrSegment makeAlphanumeric(String text) { Objects.requireNonNull(text); BitBuffer bb = new BitBuffer(); @@ -89,6 +114,13 @@ public final class QrSegment { } + /** + * Returns a list of zero or more segments to represent the specified Unicode text string. + * The result may use various segment modes and switch modes to optimize the length of the bit stream. + * @param text the text to be encoded, which can be any Unicode string + * @return a new mutable list (not {@code null}) of segments (not {@code null}) containing the text + * @throws NullPointerException if the text is {@code null} + */ public static List makeSegments(String text) { Objects.requireNonNull(text); @@ -105,6 +137,13 @@ public final class QrSegment { } + /** + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the specified assignment value. + * @param assignVal the ECI assignment number (see the AIM ECI specification) + * @return a segment (not {@code null}) containing the data + * @throws IllegalArgumentException if the value is outside the range [0, 106) + */ public static QrSegment makeEci(int assignVal) { BitBuffer bb = new BitBuffer(); if (0 <= assignVal && assignVal < (1 << 7)) @@ -144,17 +183,32 @@ public final class QrSegment { /*---- Instance fields ----*/ + /** The mode indicator of this segment. Not {@code null}. */ public final Mode mode; + /** The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; + // The data bits of this segment. Not null. Accessed through getData(). final int[] data; final int bitLength; - /*---- Constructor ----*/ + /*---- Constructor (low level) ----*/ + /** + * Constructs a QR Code segment with the specified attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The specified bit buffer is cloned and stored. + * @param md the mode (not {@code null}) + * @param numCh the data length in characters or bytes, which is non-negative + * @param data the data bits (not {@code null}) + * @throws NullPointerException if the mode or data is {@code null} + * @throws IllegalArgumentException if the character count is negative + */ public QrSegment(Mode md, int numCh, int[] data, int bitLen) { mode = Objects.requireNonNull(md); this.data = Objects.requireNonNull(data); @@ -204,6 +258,9 @@ public final class QrSegment { /*---- Public helper enumeration ----*/ + /** + * Describes how a segment's data bits are interpreted. + */ public enum Mode { /*-- Constants --*/ @@ -217,8 +274,10 @@ public final class QrSegment { /*-- Fields --*/ + // The mode indicator bits, which is a uint4 value (range 0 to 15). final int modeBits; + // Number of character count bits for three different version ranges. private final int[] numBitsCharCount; @@ -232,6 +291,8 @@ public final class QrSegment { /*-- 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) { return numBitsCharCount[(ver + 7) / 17]; } From f6e7b1760271928de92ed932592ae5d657ed2e72 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:25:12 +0000 Subject: [PATCH 532/810] Tweaked small pieces of code, synchronizing from the parent "QR Code generator library" project. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 2 ++ src/io/nayuki/fastqrcodegen/QrCode.java | 1 + src/io/nayuki/fastqrcodegen/QrSegment.java | 11 +++++++---- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index a2d573e..f187e9f 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -87,6 +87,8 @@ final class BitBuffer { public void appendBits(int val, int len) { if (len < 0 || len > 31 || val >>> len != 0) throw new IllegalArgumentException("Value out of range"); + if (Integer.MAX_VALUE - bitLength < len) + throw new IllegalStateException("Maximum length reached"); if (bitLength + len + 1 > data.length << 5) data = Arrays.copyOf(data, data.length * 2); diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 42fdaec..4366d5f 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -381,6 +381,7 @@ public final class QrCode { // 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(); diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index ee6a6a2..9f91ac4 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -146,12 +146,14 @@ public final class QrSegment { */ public static QrSegment makeEci(int assignVal) { BitBuffer bb = new BitBuffer(); - if (0 <= assignVal && assignVal < (1 << 7)) + if (assignVal < 0) + throw new IllegalArgumentException("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) bb.appendBits(assignVal, 8); - else if ((1 << 7) <= assignVal && assignVal < (1 << 14)) { + else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); bb.appendBits(assignVal, 14); - } else if ((1 << 14) <= assignVal && assignVal < 1000000) { + } else if (assignVal < 1_000_000) { bb.appendBits(6, 3); bb.appendBits(assignVal, 21); } else @@ -284,7 +286,7 @@ public final class QrSegment { /*-- Constructor --*/ private Mode(int mode, int... ccbits) { - this.modeBits = mode; + modeBits = mode; numBitsCharCount = ccbits; } @@ -294,6 +296,7 @@ public final class QrSegment { // 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]; } From b669311c2eed9c9afe5f8d6f54ca0da551cab927 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:33:33 +0000 Subject: [PATCH 533/810] Added Javadoc and informal comments to some members, which differ from the parent project. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 5 ++++ src/io/nayuki/fastqrcodegen/QrCode.java | 3 +++ src/io/nayuki/fastqrcodegen/QrSegment.java | 29 ++++++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index f187e9f..7031914 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -65,6 +65,11 @@ final class BitBuffer { } + /** + * Returns an array representing this buffer's bits packed into bytes + * in big endian. The current bit length must be a multiple of 8. + * @return a new byte array (not {@code null}) representing this bit sequence + */ public byte[] getBytes() { if (bitLength % 8 != 0) throw new IllegalStateException("Data is not a whole number of bytes"); diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 4366d5f..a1e71e0 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -208,6 +208,7 @@ public final class QrCode { * −1), the resulting object still has a mask value between 0 and 7. */ public final int mask; + // The modules of this QR Code. Immutable after constructor finishes. Accessed through getModule(). private final int[] modules; @@ -363,6 +364,8 @@ public final class QrCode { } + // Sets the module at the given coordinates to the given color. + // Only used by the constructor. Coordinates must be in bounds. private void setModule(int x, int y, int black) { assert 0 <= x && x < size; assert 0 <= y && y < size; diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index 9f91ac4..cf0d655 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -30,6 +30,18 @@ import java.util.List; import java.util.Objects; +/** + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + *

The mid-level way to create a segment is to take the payload data and call a + * static factory function such as {@link QrSegment#makeNumeric(String)}. The low-level + * way to create a segment is to custom-make the bit buffer and call the {@link + * QrSegment#QrSegment(Mode,int,int[],int) constructor} with appropriate values.

+ *

This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + * This class can represent kanji mode segments, but provides no help in encoding them.

+ */ public final class QrSegment { /*---- Static factory functions (mid level) ----*/ @@ -162,6 +174,14 @@ public final class QrSegment { } + /** + * Tests whether the specified string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the range 0 to 9. + * @throws NullPointerException if the string is {@code null} + * @see #makeNumeric(String) + */ public static boolean isNumeric(String text) { for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); @@ -172,6 +192,15 @@ public final class QrSegment { } + /** + * Tests whether the specified string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the alphanumeric mode character set + * @throws NullPointerException if the string is {@code null} + * @see #makeAlphanumeric(String) + */ public static boolean isAlphanumeric(String text) { for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); From 61241872480da6e6ab4306c8d5c16bb36031bc91 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:38:54 +0000 Subject: [PATCH 534/810] Refactored QrTemplate to use QrCode.getBit(). --- src/io/nayuki/fastqrcodegen/QrCode.java | 2 +- src/io/nayuki/fastqrcodegen/QrTemplate.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index a1e71e0..dcd958f 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -559,7 +559,7 @@ public final class QrCode { // Returns 0 or 1 based on the i'th bit of x. - private static int getBit(int x, int i) { + static int getBit(int x, int i) { return (x >>> i) & 1; } diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 41306d1..9ecbbbd 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -173,7 +173,7 @@ final class QrTemplate { // Draw two copies for (int i = 0; i < 18; i++) { - int bit = (bits >>> i) & 1; + int bit = QrCode.getBit(bits, i); int a = size - 11 + i % 3; int b = i / 3; darkenFunctionModule(a, b, bit); @@ -260,7 +260,7 @@ final class QrTemplate { assert 0 <= x && x < size; assert 0 <= y && y < size; int i = y * size + x; - return (grid[i >>> 5] >>> i) & 1; + return QrCode.getBit(grid[i >>> 5], i); } From 3e6381cea83f5c3a2c5f93beed59a3439981aba0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:50:45 +0000 Subject: [PATCH 535/810] Renamed loop variables and swapped commutative arguments for clarity. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 9ecbbbd..49aad5d 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -185,10 +185,10 @@ final class QrTemplate { // 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) { - for (int i = -4; i <= 4; i++) { - for (int j = -4; j <= 4; j++) { - int dist = Math.max(Math.abs(i), Math.abs(j)); // Chebyshev/infinity norm - int xx = x + j, yy = y + i; + for (int dy = -4; dy <= 4; dy++) { + for (int dx = -4; dx <= 4; dx++) { + int dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm + int xx = x + dx, yy = y + dy; if (0 <= xx && xx < size && 0 <= yy && yy < size) darkenFunctionModule(xx, yy, (dist != 2 && dist != 4) ? 1 : 0); } @@ -199,9 +199,9 @@ final class QrTemplate { // 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) { - for (int i = -2; i <= 2; i++) { - for (int j = -2; j <= 2; j++) - darkenFunctionModule(x + j, y + i, (Math.max(Math.abs(i), Math.abs(j)) != 1) ? 1 : 0); + for (int dy = -2; dy <= 2; dy++) { + for (int dx = -2; dx <= 2; dx++) + darkenFunctionModule(x + dx, y + dy, (Math.max(Math.abs(dx), Math.abs(dy)) != 1) ? 1 : 0); } } From eb072ba84e1bd0a29413d598bf8f40303f835746 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:52:30 +0000 Subject: [PATCH 536/810] Simplified small pieces of logic. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 49aad5d..6c81748 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -125,9 +125,7 @@ final class QrTemplate { int numAlign = alignPatPos.length; for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { - if (i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0) - continue; // Skip the three finder corners - else + if (!(i == 0 && j == 0 || i == 0 && j == numAlign - 1 || i == numAlign - 1 && j == 0)) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } } @@ -201,7 +199,7 @@ final class QrTemplate { private void drawAlignmentPattern(int x, int y) { for (int dy = -2; dy <= 2; dy++) { for (int dx = -2; dx <= 2; dx++) - darkenFunctionModule(x + dx, y + dy, (Math.max(Math.abs(dx), Math.abs(dy)) != 1) ? 1 : 0); + darkenFunctionModule(x + dx, y + dy, Math.abs(Math.max(Math.abs(dx), Math.abs(dy)) - 1)); } } From 83a3c3b957714ddd38d8e5a2e941a996a913381c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 06:52:37 +0000 Subject: [PATCH 537/810] Added and updated comments. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 6c81748..931d9b0 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -90,7 +90,7 @@ final class QrTemplate { final int[][] masks; final int[] dataOutputBitIndexes; - private int[] isFunction; // Discarded at end of constructor + private int[] isFunction; // Discarded when constructor finishes private QrTemplate(int ver) { @@ -125,6 +125,7 @@ final class QrTemplate { 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)) drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); } @@ -262,6 +263,8 @@ final class QrTemplate { } + // Marks the module at the given coordinates as a function module. + // Also either sets that module black or keeps its color unchanged. private void darkenFunctionModule(int x, int y, int enable) { assert 0 <= x && x < size; assert 0 <= y && y < size; From b967ae4aaec57079e486ec0a67b6c5ef20cb54d1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 07:00:35 +0000 Subject: [PATCH 538/810] Added and updated some strict overflow checks. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 6 ++++-- src/io/nayuki/fastqrcodegen/QrSegment.java | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index 7031914..4a2faf1 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -92,7 +92,7 @@ final class BitBuffer { public void appendBits(int val, int len) { if (len < 0 || len > 31 || val >>> len != 0) throw new IllegalArgumentException("Value out of range"); - if (Integer.MAX_VALUE - bitLength < len) + if (len > Integer.MAX_VALUE - bitLength) throw new IllegalStateException("Maximum length reached"); if (bitLength + len + 1 > data.length << 5) @@ -118,12 +118,14 @@ final class BitBuffer { Objects.requireNonNull(vals); if (len == 0) return; - if (len < 0 || len > vals.length * 32) + if (len < 0 || len > vals.length * 32L) throw new IllegalArgumentException("Value out of range"); int wholeWords = len / 32; int tailBits = len % 32; if (tailBits > 0 && vals[wholeWords] << tailBits != 0) throw new IllegalArgumentException("Last word must have low bits clear"); + if (len > Integer.MAX_VALUE - bitLength) + throw new IllegalStateException("Maximum length reached"); while (bitLength + len > data.length * 32) data = Arrays.copyOf(data, data.length * 2); diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index cf0d655..bf7d5ef 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -57,6 +57,8 @@ public final class QrSegment { */ public static QrSegment makeBytes(byte[] data) { Objects.requireNonNull(data); + if (data.length * 8L > Integer.MAX_VALUE) + throw new IllegalArgumentException("Data too long"); int[] bits = new int[(data.length + 3) / 4]; for (int i = 0; i < data.length; i++) bits[i >>> 2] |= (data[i] & 0xFF) << (~i << 3); @@ -243,7 +245,7 @@ public final class QrSegment { public QrSegment(Mode md, int numCh, int[] data, int bitLen) { mode = Objects.requireNonNull(md); this.data = Objects.requireNonNull(data); - if (numCh < 0 || bitLen < 0 || bitLen > data.length * 32) + if (numCh < 0 || bitLen < 0 || bitLen > data.length * 32L) throw new IllegalArgumentException("Invalid value"); numChars = numCh; bitLength = bitLen; From 72d350e3a0d278e256f1e1f13623c910a1f7cdc3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 07:04:53 +0000 Subject: [PATCH 539/810] Added and updated comments. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 8 ++++++++ src/io/nayuki/fastqrcodegen/QrSegment.java | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index 4a2faf1..18e1027 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -114,6 +114,14 @@ final class BitBuffer { } + /** + * Appends the specified sequence of bits to this buffer. + * Requires 0 ≤ len ≤ 32 × vals.length. + * @param vals the sequence of bits to append (not {@code null}) + * @param len the number of prefix bits to read from the array + * @throws IllegalStateException if appending the data + * would make bitLength exceed Integer.MAX_VALUE + */ public void appendBits(int[] vals, int len) { Objects.requireNonNull(vals); if (len == 0) diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index bf7d5ef..677bcda 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -227,6 +227,7 @@ public final class QrSegment { // The data bits of this segment. Not null. Accessed through getData(). final int[] data; + // Requires 0 <= bitLength <= data.length * 32. final int bitLength; @@ -239,6 +240,7 @@ public final class QrSegment { * @param md the mode (not {@code null}) * @param numCh the data length in characters or bytes, which is non-negative * @param data the data bits (not {@code null}) + * @param bitLen the number of valid prefix bits in the data array * @throws NullPointerException if the mode or data is {@code null} * @throws IllegalArgumentException if the character count is negative */ From 13e4b5b3105780a6f2d1c36d06709200801330ff Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 20:43:23 +0000 Subject: [PATCH 540/810] Added Java package summary Javadoc comment. --- java/io/nayuki/qrcodegen/package-info.java | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 java/io/nayuki/qrcodegen/package-info.java diff --git a/java/io/nayuki/qrcodegen/package-info.java b/java/io/nayuki/qrcodegen/package-info.java new file mode 100644 index 0000000..805a4d7 --- /dev/null +++ b/java/io/nayuki/qrcodegen/package-info.java @@ -0,0 +1,52 @@ +/** + * Generates QR Codes from text strings and byte arrays. + * + *

This project aims to be the best, clearest QR Code generator library. The primary goals are flexible options and absolute correctness. Secondary goals are compact implementation size and good documentation comments.

+ *

Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: https://www.nayuki.io/page/qr-code-generator-library

+ * + *

Features

+ *

Core features:

+ *
    + *
  • Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust

  • + *
  • Significantly shorter code but more documentation comments compared to competing libraries

  • + *
  • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

  • + *
  • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

  • + *
  • Encodes numeric and special-alphanumeric text in less space than general text

  • + *
  • Open source code under the permissive MIT License

  • + *
+ *

Manual parameters:

+ *
    + *
  • User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data

  • + *
  • User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one

  • + *
  • User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number

  • + *
  • User can create a list of data segments manually and add ECI segments

  • + *
+ *

Optional advanced features:

+ *
    + *
  • Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes

  • + *
  • Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts

  • + *
+ * + *

Examples

+ *

Simple operation:

+ *
import java.awt.image.BufferedImage;
+ *import java.io.File;
+ *import javax.imageio.ImageIO;
+ *import io.nayuki.qrcodegen.*;
+ *
+ *QrCode qr = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM);
+ *BufferedImage img = qr.toImage(4, 10);
+ *ImageIO.write(img, "png", new File("qr-code.png"));
+ *

Manual operation:

+ *
import java.util.List;
+ *import io.nayuki.qrcodegen.*;
+ *
+ *List<QrSegment> segs = QrSegment.makeSegments("3141592653589793238462643383");
+ *QrCode qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false);
+ *for (int y = 0; y < qr.size; y++) {
+ *    for (int x = 0; x < qr.size; x++) {
+ *        (... paint qr.getModule(x, y) ...)
+ *    }
+ *}
+ */ +package io.nayuki.qrcodegen; From 5657ba274b21da3e4bba9f3bfb26a9044b448b92 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 7 Oct 2018 20:47:04 +0000 Subject: [PATCH 541/810] Added package summary Javadoc comment. --- src/io/nayuki/fastqrcodegen/package-info.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/io/nayuki/fastqrcodegen/package-info.java diff --git a/src/io/nayuki/fastqrcodegen/package-info.java b/src/io/nayuki/fastqrcodegen/package-info.java new file mode 100644 index 0000000..e70dc6c --- /dev/null +++ b/src/io/nayuki/fastqrcodegen/package-info.java @@ -0,0 +1,48 @@ +/** + * Generates QR Codes from text strings and byte arrays. + * + *

This library generates QR Code symbols, and its design is optimized for speed. It contrasts with another QR library by the same author which is slow but which optimizes for clarity and conciseness. The functionality of this library and its API are nearly identical to the slow library, but it runs anywhere from 1.5× to 6× as fast.

+ *

Home page for the fast library (design explanation, benchmarks): https://www.nayuki.io/page/fast-qr-code-generator-library

+ *

Home page for the slow library (live demo, QR Code introduction, competitor comparisons): https://www.nayuki.io/page/qr-code-generator-library

+ * + *

Features

+ *

Core features:

+ *
    + *
  • Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust

  • + *
  • Significantly shorter code but more documentation comments compared to competing libraries

  • + *
  • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

  • + *
  • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

  • + *
  • Encodes numeric and special-alphanumeric text in less space than general text

  • + *
  • Open source code under the permissive MIT License

  • + *
+ *

Manual parameters:

+ *
    + *
  • User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data

  • + *
  • User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one

  • + *
  • User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number

  • + *
  • User can create a list of data segments manually and add ECI segments

  • + *
+ * + *

Examples

+ *

Simple operation:

+ *
import java.awt.image.BufferedImage;
+ *import java.io.File;
+ *import javax.imageio.ImageIO;
+ *import io.nayuki.fastqrcodegen.*;
+ *
+ *QrCode qr = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM);
+ *BufferedImage img = qr.toImage(4, 10);
+ *ImageIO.write(img, "png", new File("qr-code.png"));
+ *

Manual operation:

+ *
import java.util.List;
+ *import io.nayuki.fastqrcodegen.*;
+ *
+ *List<QrSegment> segs = QrSegment.makeSegments("3141592653589793238462643383");
+ *QrCode qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false);
+ *for (int y = 0; y < qr.size; y++) {
+ *    for (int x = 0; x < qr.size; x++) {
+ *        (... paint qr.getModule(x, y) ...)
+ *    }
+ *}
+ */ +package io.nayuki.fastqrcodegen; From 614169fc88150725554ea775a6c53dceeb7dfd26 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 Oct 2018 03:02:56 +0000 Subject: [PATCH 542/810] Fixed a comment in C code. --- c/qrcodegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 3e631f9..a2d0517 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -444,7 +444,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin // Calculates and stores an ascending list of positions of alignment patterns -// for this version number, returning the length of the list (in the range [0,7)). +// for this version number, returning the length of the list (in the range [0,7]). // 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. testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { From 7dbfabd3883876d30e2a2a80294b818a74b741bc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 Oct 2018 18:01:14 +0000 Subject: [PATCH 543/810] Reordered functions and sections in C code for better flow for human readers, without changing functionality. --- c/qrcodegen.c | 183 +++++++++++++++++++++++++------------------------- c/qrcodegen.h | 74 ++++++++++---------- 2 files changed, 131 insertions(+), 126 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index a2d0517..8f6de55 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -187,6 +187,99 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] +/*---- Low-level QR Code encoding functions ----*/ + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { + return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); +} + + +// Public function - see documentation comment in header file. +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + assert(segs != NULL || len == 0); + assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); + assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); + + // 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 = getTotalBits(segs, len, 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 + qrcode[0] = 0; // Set size to invalid value for safety + return false; + } + } + assert(dataUsedBits != -1); + + // Increase the error correction level while the data still fits in the current version number + for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high + if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) + ecl = (enum qrcodegen_Ecc)i; + } + + // Concatenate all segments to create the data bit string + memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + int bitLen = 0; + for (size_t i = 0; i < len; i++) { + const struct qrcodegen_Segment *seg = &segs[i]; + appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) + appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); + } + assert(bitLen == dataUsedBits); + + // Add terminator and pad up to a byte if applicable + int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + assert(bitLen <= dataCapacityBits); + int terminatorBits = dataCapacityBits - bitLen; + if (terminatorBits > 4) + terminatorBits = 4; + appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); + appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); + assert(bitLen % 8 == 0); + + // Pad with alternating bytes until data capacity is reached + for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) + appendBitsToBuffer(padByte, 8, qrcode, &bitLen); + + // Draw function and data codeword modules + addEccAndInterleave(qrcode, version, ecl, tempBuffer); + initializeFunctionModules(version, qrcode); + drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); + drawWhiteFunctionModules(qrcode, version); + initializeFunctionModules(version, tempBuffer); + + // Handle masking + if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; + drawFormatBits(ecl, msk, qrcode); + applyMask(tempBuffer, qrcode, msk); + long penalty = getPenaltyScore(qrcode); + if (penalty < minPenalty) { + mask = msk; + minPenalty = penalty; + } + applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR + } + } + assert(0 <= (int)mask && (int)mask <= 7); + drawFormatBits(ecl, mask, qrcode); + applyMask(tempBuffer, qrcode, mask); + return true; +} + + + /*---- Error correction code generation functions ----*/ // Appends error correction bytes to each block of the given data array, then interleaves @@ -851,96 +944,6 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { } -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { - return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); -} - - -// Public function - see documentation comment in header file. -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { - assert(segs != NULL || len == 0); - assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); - assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); - - // 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 = getTotalBits(segs, len, 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 - qrcode[0] = 0; // Set size to invalid value for safety - return false; - } - } - assert(dataUsedBits != -1); - - // Increase the error correction level while the data still fits in the current version number - for (int i = (int)qrcodegen_Ecc_MEDIUM; i <= (int)qrcodegen_Ecc_HIGH; i++) { // From low to high - if (boostEcl && dataUsedBits <= getNumDataCodewords(version, (enum qrcodegen_Ecc)i) * 8) - ecl = (enum qrcodegen_Ecc)i; - } - - // Concatenate all segments to create the data bit string - memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); - int bitLen = 0; - for (size_t i = 0; i < len; i++) { - const struct qrcodegen_Segment *seg = &segs[i]; - appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); - appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); - for (int j = 0; j < seg->bitLength; j++) - appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); - } - assert(bitLen == dataUsedBits); - - // Add terminator and pad up to a byte if applicable - int dataCapacityBits = getNumDataCodewords(version, ecl) * 8; - assert(bitLen <= dataCapacityBits); - int terminatorBits = dataCapacityBits - bitLen; - if (terminatorBits > 4) - terminatorBits = 4; - appendBitsToBuffer(0, terminatorBits, qrcode, &bitLen); - appendBitsToBuffer(0, (8 - bitLen % 8) % 8, qrcode, &bitLen); - assert(bitLen % 8 == 0); - - // Pad with alternating bytes until data capacity is reached - for (uint8_t padByte = 0xEC; bitLen < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - appendBitsToBuffer(padByte, 8, qrcode, &bitLen); - - // Draw function and data codeword modules - addEccAndInterleave(qrcode, version, ecl, tempBuffer); - initializeFunctionModules(version, qrcode); - drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); - drawWhiteFunctionModules(qrcode, version); - initializeFunctionModules(version, tempBuffer); - - // Handle masking - if (mask == qrcodegen_Mask_AUTO) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (int i = 0; i < 8; i++) { - enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; - drawFormatBits(ecl, msk, qrcode); - applyMask(tempBuffer, qrcode, msk); - long penalty = getPenaltyScore(qrcode); - if (penalty < minPenalty) { - mask = msk; - minPenalty = penalty; - } - applyMask(tempBuffer, qrcode, msk); // Undoes the mask due to XOR - } - } - assert(0 <= (int)mask && (int)mask <= 7); - drawFormatBits(ecl, mask, qrcode); - applyMask(tempBuffer, qrcode, mask); - return true; -} - - // Calculates the number of bits needed to encode the given segments at the given version. // Returns a non-negative number if successful. Otherwise returns -1 if a segment has too // many characters to fit its length field, or the total bits exceeds INT16_MAX. diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 13e8eab..30f9db2 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -145,7 +145,7 @@ struct qrcodegen_Segment { -/*---- Functions to generate QR Codes ----*/ +/*---- Functions (high level) to generate QR Codes ----*/ /* * Encodes the given text string to a QR Code, returning true if encoding succeeded. @@ -192,6 +192,43 @@ bool qrcodegen_encodeBinary(uint8_t dataAndTemp[], size_t dataLen, uint8_t qrcod enum qrcodegen_Ecc ecl, int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl); +/*---- Functions (low level) to generate QR Codes ----*/ + +/* + * Renders a QR Code representing the given segments at the given error correction level. + * The smallest possible QR Code version is automatically chosen for the output. Returns true if + * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level + * of the result may be higher than the ecl argument if it can be done without increasing the version. + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, + enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); + + +/* + * Renders a QR Code representing the given segments with the given encoding parameters. + * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. + * The smallest possible QR Code version within the given range is automatically + * chosen for the output. Iff boostEcl is true, then the ECC level of the result + * may be higher than the ecl argument if it can be done without increasing the + * version. The mask number is either between 0 to 7 (inclusive) to force that + * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * This function allows the user to create a custom sequence of segments that switches + * between modes (such as alphanumeric and byte) to encode text in less space. + * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). + * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will + * result in them being clobbered, but the QR Code output will still be correct. + * But the qrcode array must not overlap tempBuffer or any segment's data buffer. + */ +bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, + int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + + /* * Tests whether the given string can be encoded as a segment in alphanumeric mode. * A string is encodable iff each character is in the following set: 0 to 9, A to Z @@ -250,41 +287,6 @@ struct qrcodegen_Segment qrcodegen_makeAlphanumeric(const char *text, uint8_t bu struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]); -/* - * Renders a QR Code representing the given segments at the given error correction level. - * The smallest possible QR Code version is automatically chosen for the output. Returns true if - * QR Code creation succeeded, or false if the data is too long to fit in any version. The ECC level - * of the result may be higher than the ecl argument if it can be done without increasing the version. - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, - enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]); - - -/* - * Renders a QR Code representing the given segments with the given encoding parameters. - * Returns true if QR Code creation succeeded, or false if the data is too long to fit in the range of versions. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). - * To save memory, the segments' data buffers can alias/overlap tempBuffer, and will - * result in them being clobbered, but the QR Code output will still be correct. - * But the qrcode array must not overlap tempBuffer or any segment's data buffer. - */ -bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); - - /*---- Functions to extract raw data from QR Codes ----*/ /* From 0723ac0d17a2d4850f70ef60e66cbe02f1c9a9f2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 Oct 2018 18:38:22 +0000 Subject: [PATCH 544/810] Updated Python and Rust package version number. --- python/setup.py | 2 +- rust/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/setup.py b/python/setup.py index 232fd00..c67d31c 100644 --- a/python/setup.py +++ b/python/setup.py @@ -27,7 +27,7 @@ import setuptools setuptools.setup( name = "qrcodegen", description = "High quality QR Code generator library for Python 2 and 3", - version = "1.2.0", + version = "1.3.0", platforms = "OS Independent", license = "MIT License", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 4fe927c..2b3c542 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qrcodegen" -version = "1.2.1" +version = "1.3.0" authors = ["Project Nayuki"] description = "High-quality QR Code generator library" homepage = "https://www.nayuki.io/page/qr-code-generator-library" From 4e774437b38431d5d7c1ca15d7bee8d50eaf838a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 8 Oct 2018 22:39:18 +0000 Subject: [PATCH 545/810] Simplified some Rust code. --- rust/examples/qrcodegen-worker.rs | 5 +---- rust/src/lib.rs | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs index 5537f19..f35ec80 100644 --- a/rust/examples/qrcodegen-worker.rs +++ b/rust/examples/qrcodegen-worker.rs @@ -102,10 +102,7 @@ fn read_int() -> i16 { let mut chrs: Vec = line.chars().collect(); assert_eq!(chrs.pop().unwrap(), '\n'); let line: String = chrs.iter().cloned().collect(); - match line.parse::() { - Ok(x) => x, - Err(_) => panic!("Invalid number"), - } + line.parse::().expect("Invalid number") } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e17e8b0..00e6aaf 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -927,10 +927,8 @@ impl QrSegment { let mut accumdata: u32 = 0; let mut accumcount: u32 = 0; for c in text { - let i = match ALPHANUMERIC_CHARSET.iter().position(|x| *x == *c) { - None => panic!("String contains unencodable characters in alphanumeric mode"), - Some(j) => j, - }; + let i = ALPHANUMERIC_CHARSET.iter().position(|x| *x == *c) + .expect("String contains unencodable characters in alphanumeric mode"); accumdata = accumdata * 45 + (i as u32); accumcount += 1; if accumcount == 2 { From 2f77345d24a3b7e73591a82c1b9540022b790ca9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 9 Oct 2018 01:41:05 +0000 Subject: [PATCH 546/810] Simplified a bit of JavaScript and TypeScript code. --- javascript/qrcodegen.js | 2 +- typescript/qrcodegen.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 1834294..520daad 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -513,7 +513,7 @@ var qrcodegen = new function() { var step = (version == 32) ? 26 : Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; var result = [6]; - for (var i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) + for (var pos = size - 7; result.length < numAlign; pos -= step) result.splice(1, 0, pos); return result; } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 1fbfa45..e2c1964 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -598,7 +598,7 @@ namespace qrcodegen { let step: int = (this.version == 32) ? 26 : Math.ceil((this.size - 13) / (numAlign*2 - 2)) * 2; let result: Array = [6]; - for (let i = 0, pos = this.size - 7; i < numAlign - 1; i++, pos -= step) + for (let pos = this.size - 7; result.length < numAlign; pos -= step) result.splice(1, 0, pos); return result; } From 4b6e151955894d2344257675ab5624858d8b5d43 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 00:57:13 +0000 Subject: [PATCH 547/810] Tweaked comments in Rust code to convert all public members to real documentation comments. --- rust/src/lib.rs | 198 ++++++++++++++++++++++++------------------------ 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 00e6aaf..c6283d0 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -24,23 +24,21 @@ /*---- QrCode functionality ----*/ -/* - * A QR Code symbol, which is a type of two-dimension barcode. - * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this struct represent an immutable square grid of black and white cells. - * The impl provides static factory functions to create a QR Code from text or binary data. - * The struct and impl cover the QR Code Model 2 specification, supporting all versions - * (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * - * Ways to create a QR Code object: - * - High level: Take the payload data and call QrCode::encode_text() or QrCode::encode_binary(). - * - Mid level: Custom-make the list of segments and call - * QrCode.encode_segments() or QrCode.encode_segments_advanced(). - * - Low level: Custom-make the array of data codeword bytes (including segment - * headers and final padding, excluding error correction codewords), supply the - * appropriate version number, and call the QrCode::encode_codewords() constructor. - * (Note that all ways require supplying the desired error correction level.) - */ +/// A QR Code symbol, which is a type of two-dimension barcode. +/// Invented by Denso Wave and described in the ISO/IEC 18004 standard. +/// Instances of this struct represent an immutable square grid of black and white cells. +/// The impl provides static factory functions to create a QR Code from text or binary data. +/// The struct and impl cover the QR Code Model 2 specification, supporting all versions +/// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. +/// +/// Ways to create a QR Code object: +/// - High level: Take the payload data and call QrCode::encode_text() or QrCode::encode_binary(). +/// - Mid level: Custom-make the list of segments and call +/// QrCode.encode_segments() or QrCode.encode_segments_advanced(). +/// - Low level: Custom-make the array of data codeword bytes (including segment +/// headers and final padding, excluding error correction codewords), supply the +/// appropriate version number, and call the QrCode::encode_codewords() constructor. +/// (Note that all ways require supplying the desired error correction level.) #[derive(Clone)] pub struct QrCode { @@ -78,12 +76,12 @@ impl QrCode { /*---- Static factory functions (high level) ----*/ - // Returns a QR Code representing the given Unicode text string at the given error correction level. - // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode - // code points (not UTF-8 code units) if the low error correction level is used. The smallest possible - // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than - // the ecl argument if it can be done without increasing the version. Returns a wrapped QrCode if successful, - // or None if the data is too long to fit in any version at the given ECC level. + /// Returns a QR Code representing the given Unicode text string at the given error correction level. + /// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode + /// code points (not UTF-8 code units) if the low error correction level is used. The smallest possible + /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than + /// the ecl argument if it can be done without increasing the version. Returns a wrapped QrCode if successful, + /// or None if the data is too long to fit in any version at the given ECC level. pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option { let chrs: Vec = text.chars().collect(); let segs: Vec = QrSegment::make_segments(&chrs); @@ -91,11 +89,11 @@ impl QrCode { } - // Returns a QR Code representing the given binary data at the given error correction level. - // This function always encodes using the binary segment mode, not any text mode. The maximum number of - // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - // Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + /// Returns a QR Code representing the given binary data at the given error correction level. + /// This function always encodes using the binary segment mode, not any text mode. The maximum number of + /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option { let segs: Vec = vec![QrSegment::make_bytes(data)]; QrCode::encode_segments(&segs, ecl) @@ -104,29 +102,29 @@ impl QrCode { /*---- Static factory functions (mid level) ----*/ - // Returns a QR Code representing the given segments at the given error correction level. - // The smallest possible QR Code version is automatically chosen for the output. The ECC level - // of the result may be higher than the ecl argument if it can be done without increasing the version. - // This function allows the user to create a custom sequence of segments that switches - // between modes (such as alphanumeric and byte) to encode text in less space. - // This is a mid-level API; the high-level API is encode_text() and encode_binary(). - // Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + /// Returns a QR Code representing the given segments at the given error correction level. + /// The smallest possible QR Code version is automatically chosen for the output. The ECC level + /// of the result may be higher than the ecl argument if it can be done without increasing the version. + /// This function allows the user to create a custom sequence of segments that switches + /// between modes (such as alphanumeric and byte) to encode text in less space. + /// This is a mid-level API; the high-level API is encode_text() and encode_binary(). + /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) } - // Returns a QR Code representing the given segments with the given encoding parameters. - // The smallest possible QR Code version within the given range is automatically - // chosen for the output. Iff boostecl is true, then the ECC level of the result - // may be higher than the ecl argument if it can be done without increasing the - // version. The mask number is either between 0 to 7 (inclusive) to force that - // mask, or -1 to automatically choose an appropriate mask (which may be slow). - // This function allows the user to create a custom sequence of segments that switches - // between modes (such as alphanumeric and byte) to encode text in less space. - // This is a mid-level API; the high-level API is encodeText() and encodeBinary(). - // Returns a wrapped QrCode if successful, or None if the data is too long to fit - // in any version in the given range at the given ECC level. + /// Returns a QR Code representing the given segments with the given encoding parameters. + /// The smallest possible QR Code version within the given range is automatically + /// chosen for the output. Iff boostecl is true, then the ECC level of the result + /// may be higher than the ecl argument if it can be done without increasing the + /// version. The mask number is either between 0 to 7 (inclusive) to force that + /// mask, or -1 to automatically choose an appropriate mask (which may be slow). + /// This function allows the user to create a custom sequence of segments that switches + /// between modes (such as alphanumeric and byte) to encode text in less space. + /// This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + /// Returns a wrapped QrCode if successful, or None if the data is too long to fit + /// in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, minversion: Version, maxversion: Version, mask: Option, boostecl: bool) -> Option { assert!(minversion.value() <= maxversion.value(), "Invalid value"); @@ -195,10 +193,10 @@ impl QrCode { /*---- Constructor (low level) ----*/ - // Creates a new QR Code with the given 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 encode_segments() function. + /// Creates a new QR Code with the given 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 encode_segments() function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { // Initialize fields let size: usize = (ver.value() as usize) * 4 + 17; @@ -224,33 +222,33 @@ impl QrCode { /*---- Public methods ----*/ - // Returns this QR Code's version, in the range [1, 40]. + /// Returns this QR Code's version, in the range [1, 40]. pub fn version(&self) -> Version { self.version } - // Returns this QR Code's size, in the range [21, 177]. + /// Returns this QR Code's size, in the range [21, 177]. pub fn size(&self) -> i32 { self.size } - // Returns this QR Code's error correction level. + /// Returns this QR Code's error correction level. pub fn error_correction_level(&self) -> QrCodeEcc { self.errorcorrectionlevel } - // Returns this QR Code's mask, in the range [0, 7]. + /// Returns this QR Code's mask, in the range [0, 7]. pub fn mask(&self) -> Mask { self.mask } - // Returns the color of the module (pixel) at the given coordinates, which is false - // for white or true for black. The top left corner has the coordinates (x=0, y=0). - // If the given coordinates are out of bounds, then false (white) is returned. + /// Returns the color of the module (pixel) at the given coordinates, which is false + /// for white or true for black. The top left corner has the coordinates (x=0, y=0). + /// If the given coordinates are out of bounds, then false (white) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) } @@ -268,8 +266,8 @@ impl QrCode { } - // Returns a string of SVG code for an image depicting this QR Code, with the given number - // of border modules. The string always uses Unix newlines (\n), regardless of the platform. + /// Returns a string of SVG code for an image depicting this QR Code, with the given number + /// of border modules. The string always uses Unix newlines (\n), regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); let mut result = String::new(); @@ -702,10 +700,10 @@ impl QrCode { /*---- Cconstants and tables ----*/ -// The minimum version number supported in the QR Code Model 2 standard. +/// The minimum version number supported in the QR Code Model 2 standard. pub const QrCode_MIN_VERSION: Version = Version( 1); -// The maximum version number supported in the QR Code Model 2 standard. +/// The maximum version number supported in the QR Code Model 2 standard. pub const QrCode_MAX_VERSION: Version = Version(40); @@ -738,13 +736,17 @@ static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [ /*---- QrCodeEcc functionality ----*/ -// The error correction level in a QR Code symbol. +/// The error correction level in a QR Code symbol. #[derive(Clone, Copy)] pub enum QrCodeEcc { - Low , // The QR Code can tolerate about 7% erroneous codewords - Medium , // The QR Code can tolerate about 15% erroneous codewords - Quartile, // The QR Code can tolerate about 25% erroneous codewords - High , // The QR Code can tolerate about 30% erroneous codewords + /// The QR Code can tolerate about 7% erroneous codewords + Low , + /// The QR Code can tolerate about 15% erroneous codewords + Medium , + /// The QR Code can tolerate about 25% erroneous codewords + Quartile, + /// The QR Code can tolerate about 30% erroneous codewords + High , } @@ -852,17 +854,15 @@ impl ReedSolomonGenerator { /*---- QrSegment functionality ----*/ -/* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this struct are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment::make_numeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment::new() constructor with appropriate values. - * This segment struct imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ +/// A segment of character/binary/control data in a QR Code symbol. +/// Instances of this struct are immutable. +/// The mid-level way to create a segment is to take the payload data +/// and call a static factory function such as QrSegment::make_numeric(). +/// The low-level way to create a segment is to custom-make the bit buffer +/// and call the QrSegment::new() constructor with appropriate values. +/// This segment struct imposes no length restrictions, but QR Codes have restrictions. +/// Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. +/// Any segment longer than this is meaningless for the purpose of generating QR Codes. #[derive(Clone)] pub struct QrSegment { @@ -884,9 +884,9 @@ impl QrSegment { /*---- Static factory functions (mid level) ----*/ - // Returns a segment representing the given binary data encoded in - // byte mode. All input byte slices are acceptable. Any text string - // can be converted to UTF-8 bytes and encoded as a byte mode segment. + /// Returns a segment representing the given binary data encoded in + /// byte mode. All input byte slices are acceptable. Any text string + /// can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); for b in data { @@ -896,8 +896,8 @@ impl QrSegment { } - // Returns a segment representing the given string of decimal digits encoded in numeric mode. - // Panics if the string contains non-digit characters. + /// Returns a segment representing the given string of decimal digits encoded in numeric mode. + /// Panics if the string contains non-digit characters. pub fn make_numeric(text: &[char]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3)); let mut accumdata: u32 = 0; @@ -919,9 +919,9 @@ impl QrSegment { } - // Returns a segment representing the given text string encoded in alphanumeric mode. - // The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, - // plus, hyphen, period, slash, colon. Panics if the string contains non-encodable characters. + /// Returns a segment representing the given text string encoded in alphanumeric mode. + /// The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, + /// plus, hyphen, period, slash, colon. Panics if the string contains non-encodable characters. pub fn make_alphanumeric(text: &[char]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 5 + (text.len() + 1) / 2)); let mut accumdata: u32 = 0; @@ -944,8 +944,8 @@ impl QrSegment { } - // Returns a list of zero or more segments to represent the given Unicode text string. The result - // may use various segment modes and switch modes to optimize the length of the bit stream. + /// Returns a list of zero or more segments to represent the given Unicode text string. The result + /// may use various segment modes and switch modes to optimize the length of the bit stream. pub fn make_segments(text: &[char]) -> Vec { if text.is_empty() { vec![] @@ -960,8 +960,8 @@ impl QrSegment { } - // Returns a segment representing an Extended Channel Interpretation - // (ECI) designator with the given assignment value. + /// Returns a segment representing an Extended Channel Interpretation + /// (ECI) designator with the given assignment value. pub fn make_eci(assignval: u32) -> Self { let mut bb = BitBuffer(Vec::with_capacity(24)); if assignval < (1 << 7) { @@ -981,9 +981,9 @@ impl QrSegment { /*---- Constructor (low level) ----*/ - // Creates a new QR Code segment with the given attributes and data. - // The character count (numchars) must agree with the mode and - // the bit buffer length, but the constraint isn't checked. + /// Creates a new QR Code segment with the given attributes and data. + /// The character count (numchars) must agree with the mode and + /// the bit buffer length, but the constraint isn't checked. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { Self { mode: mode, @@ -995,19 +995,19 @@ impl QrSegment { /*---- Instance field getters ----*/ - // Returns the mode indicator of this segment. + /// Returns the mode indicator of this segment. pub fn mode(&self) -> QrSegmentMode { self.mode } - // Returns the character count field of this segment. + /// Returns the character count field of this segment. pub fn num_chars(&self) -> usize { self.numchars } - // Returns the data bits of this segment. + /// Returns the data bits of this segment. pub fn data(&self) -> &Vec { &self.data } @@ -1061,7 +1061,7 @@ static ALPHANUMERIC_CHARSET: [char; 45] = ['0','1','2','3','4','5','6','7','8',' /*---- QrSegmentMode functionality ----*/ -// Describes how a segment's data bits are interpreted. +/// Describes how a segment's data bits are interpreted. #[derive(Clone, Copy)] pub enum QrSegmentMode { Numeric, @@ -1107,13 +1107,13 @@ impl QrSegmentMode { /*---- Bit buffer functionality ----*/ -// An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. +/// An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. pub struct BitBuffer(pub Vec); impl BitBuffer { - // Appends the given number of low-order bits of the given value - // to this buffer. Requires len <= 31 and val < 2^len. + /// Appends the given number of low-order bits of the given value + /// to this buffer. Requires len <= 31 and val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit From aef9d3dc4ce5fa55e6816484a97920a82925c49d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:05:16 +0000 Subject: [PATCH 548/810] Tweaked comments in Rust code to start new paragraph after first sentence, just like Javadoc. --- rust/src/lib.rs | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index c6283d0..a4173f4 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -25,6 +25,7 @@ /*---- QrCode functionality ----*/ /// A QR Code symbol, which is a type of two-dimension barcode. +/// /// Invented by Denso Wave and described in the ISO/IEC 18004 standard. /// Instances of this struct represent an immutable square grid of black and white cells. /// The impl provides static factory functions to create a QR Code from text or binary data. @@ -77,6 +78,7 @@ impl QrCode { /*---- Static factory functions (high level) ----*/ /// Returns a QR Code representing the given Unicode text string at the given error correction level. + /// /// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode /// code points (not UTF-8 code units) if the low error correction level is used. The smallest possible /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than @@ -90,6 +92,7 @@ impl QrCode { /// Returns a QR Code representing the given binary data at the given error correction level. + /// /// This function always encodes using the binary segment mode, not any text mode. The maximum number of /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. @@ -103,6 +106,7 @@ impl QrCode { /*---- Static factory functions (mid level) ----*/ /// Returns a QR Code representing the given segments at the given error correction level. + /// /// The smallest possible QR Code version is automatically chosen for the output. The ECC level /// of the result may be higher than the ecl argument if it can be done without increasing the version. /// This function allows the user to create a custom sequence of segments that switches @@ -115,6 +119,7 @@ impl QrCode { /// Returns a QR Code representing the given segments with the given encoding parameters. + /// /// The smallest possible QR Code version within the given range is automatically /// chosen for the output. Iff boostecl is true, then the ECC level of the result /// may be higher than the ecl argument if it can be done without increasing the @@ -195,6 +200,7 @@ impl QrCode { /// Creates a new QR Code with the given 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 encode_segments() function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { @@ -247,7 +253,9 @@ impl QrCode { /// Returns the color of the module (pixel) at the given coordinates, which is false - /// for white or true for black. The top left corner has the coordinates (x=0, y=0). + /// for white or true for black. + /// + /// The top left corner has the coordinates (x=0, y=0). /// If the given coordinates are out of bounds, then false (white) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) @@ -267,7 +275,9 @@ impl QrCode { /// Returns a string of SVG code for an image depicting this QR Code, with the given number - /// of border modules. The string always uses Unix newlines (\n), regardless of the platform. + /// of border modules. + /// + /// The string always uses Unix newlines (\n), regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { assert!(border >= 0, "Border must be non-negative"); let mut result = String::new(); @@ -855,6 +865,7 @@ impl ReedSolomonGenerator { /*---- QrSegment functionality ----*/ /// A segment of character/binary/control data in a QR Code symbol. +/// /// Instances of this struct are immutable. /// The mid-level way to create a segment is to take the payload data /// and call a static factory function such as QrSegment::make_numeric(). @@ -885,7 +896,9 @@ impl QrSegment { /*---- Static factory functions (mid level) ----*/ /// Returns a segment representing the given binary data encoded in - /// byte mode. All input byte slices are acceptable. Any text string + /// byte mode. + /// + /// All input byte slices are acceptable. Any text string /// can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); @@ -897,6 +910,7 @@ impl QrSegment { /// Returns a segment representing the given string of decimal digits encoded in numeric mode. + /// /// Panics if the string contains non-digit characters. pub fn make_numeric(text: &[char]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3)); @@ -920,6 +934,7 @@ impl QrSegment { /// Returns a segment representing the given text string encoded in alphanumeric mode. + /// /// The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, /// plus, hyphen, period, slash, colon. Panics if the string contains non-encodable characters. pub fn make_alphanumeric(text: &[char]) -> Self { @@ -944,7 +959,9 @@ impl QrSegment { } - /// Returns a list of zero or more segments to represent the given Unicode text string. The result + /// Returns a list of zero or more segments to represent the given Unicode text string. + /// + /// The result /// may use various segment modes and switch modes to optimize the length of the bit stream. pub fn make_segments(text: &[char]) -> Vec { if text.is_empty() { @@ -982,6 +999,7 @@ impl QrSegment { /*---- Constructor (low level) ----*/ /// Creates a new QR Code segment with the given attributes and data. + /// /// The character count (numchars) must agree with the mode and /// the bit buffer length, but the constraint isn't checked. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { @@ -1107,13 +1125,17 @@ impl QrSegmentMode { /*---- Bit buffer functionality ----*/ -/// An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. +/// An appendable sequence of bits (0s and 1s). +/// +/// Mainly used by QrSegment. pub struct BitBuffer(pub Vec); impl BitBuffer { /// Appends the given number of low-order bits of the given value - /// to this buffer. Requires len <= 31 and val < 2^len. + /// to this buffer. + /// + /// Requires len <= 31 and val < 2^len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit From b8e8658de94364974bc1231265ea260bd650057b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:10:59 +0000 Subject: [PATCH 549/810] Tweaked comments in Rust code to add paragraph breaks to correspond to Javadoc. --- rust/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a4173f4..777d725 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -27,18 +27,21 @@ /// A QR Code symbol, which is a type of two-dimension barcode. /// /// Invented by Denso Wave and described in the ISO/IEC 18004 standard. +/// /// Instances of this struct represent an immutable square grid of black and white cells. /// The impl provides static factory functions to create a QR Code from text or binary data. /// The struct and impl cover the QR Code Model 2 specification, supporting all versions /// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. /// /// Ways to create a QR Code object: +/// /// - High level: Take the payload data and call QrCode::encode_text() or QrCode::encode_binary(). /// - Mid level: Custom-make the list of segments and call /// QrCode.encode_segments() or QrCode.encode_segments_advanced(). /// - Low level: Custom-make the array of data codeword bytes (including segment /// headers and final padding, excluding error correction codewords), supply the /// appropriate version number, and call the QrCode::encode_codewords() constructor. +/// /// (Note that all ways require supplying the desired error correction level.) #[derive(Clone)] pub struct QrCode { @@ -109,6 +112,7 @@ impl QrCode { /// /// The smallest possible QR Code version is automatically chosen for the output. The ECC level /// of the result may be higher than the ecl argument if it can be done without increasing the version. + /// /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. /// This is a mid-level API; the high-level API is encode_text() and encode_binary(). @@ -125,6 +129,7 @@ impl QrCode { /// may be higher than the ecl argument if it can be done without increasing the /// version. The mask number is either between 0 to 7 (inclusive) to force that /// mask, or -1 to automatically choose an appropriate mask (which may be slow). + /// /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. /// This is a mid-level API; the high-level API is encodeText() and encodeBinary(). @@ -867,10 +872,12 @@ impl ReedSolomonGenerator { /// A segment of character/binary/control data in a QR Code symbol. /// /// Instances of this struct are immutable. +/// /// The mid-level way to create a segment is to take the payload data /// and call a static factory function such as QrSegment::make_numeric(). /// The low-level way to create a segment is to custom-make the bit buffer /// and call the QrSegment::new() constructor with appropriate values. +/// /// This segment struct imposes no length restrictions, but QR Codes have restrictions. /// Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. /// Any segment longer than this is meaningless for the purpose of generating QR Codes. @@ -898,7 +905,9 @@ impl QrSegment { /// Returns a segment representing the given binary data encoded in /// byte mode. /// - /// All input byte slices are acceptable. Any text string + /// All input byte slices are acceptable. + /// + /// Any text string /// can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); From a8039110d72e840d48b7e47551400c89fe5c7541 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:13:10 +0000 Subject: [PATCH 550/810] Tweaked comments in Rust code to add new paragraph breaks. --- rust/src/lib.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 777d725..01ba442 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -85,7 +85,9 @@ impl QrCode { /// As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode /// code points (not UTF-8 code units) if the low error correction level is used. The smallest possible /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than - /// the ecl argument if it can be done without increasing the version. Returns a wrapped QrCode if successful, + /// the ecl argument if it can be done without increasing the version. + /// + /// Returns a wrapped QrCode if successful, /// or None if the data is too long to fit in any version at the given ECC level. pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option { let chrs: Vec = text.chars().collect(); @@ -99,6 +101,7 @@ impl QrCode { /// This function always encodes using the binary segment mode, not any text mode. The maximum number of /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + /// /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option { let segs: Vec = vec![QrSegment::make_bytes(data)]; @@ -116,6 +119,7 @@ impl QrCode { /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. /// This is a mid-level API; the high-level API is encode_text() and encode_binary(). + /// /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) @@ -133,6 +137,7 @@ impl QrCode { /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. /// This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + /// /// Returns a wrapped QrCode if successful, or None if the data is too long to fit /// in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, @@ -945,7 +950,9 @@ impl QrSegment { /// Returns a segment representing the given text string encoded in alphanumeric mode. /// /// The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, - /// plus, hyphen, period, slash, colon. Panics if the string contains non-encodable characters. + /// plus, hyphen, period, slash, colon. + /// + /// Panics if the string contains non-encodable characters. pub fn make_alphanumeric(text: &[char]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 5 + (text.len() + 1) / 2)); let mut accumdata: u32 = 0; From 0810860b57b4707f810667305f40b8738ea874b3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:27:28 +0000 Subject: [PATCH 551/810] Tweaked comments in Rust code to use Markdown formatting. --- rust/src/lib.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 01ba442..119d456 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -35,12 +35,12 @@ /// /// Ways to create a QR Code object: /// -/// - High level: Take the payload data and call QrCode::encode_text() or QrCode::encode_binary(). +/// - High level: Take the payload data and call `QrCode::encode_text()` or `QrCode::encode_binary()`. /// - Mid level: Custom-make the list of segments and call -/// QrCode.encode_segments() or QrCode.encode_segments_advanced(). +/// `QrCode.encode_segments()` or `QrCode.encode_segments_advanced()`. /// - Low level: Custom-make the array of data codeword bytes (including segment /// headers and final padding, excluding error correction codewords), supply the -/// appropriate version number, and call the QrCode::encode_codewords() constructor. +/// appropriate version number, and call the `QrCode::encode_codewords()` constructor. /// /// (Note that all ways require supplying the desired error correction level.) #[derive(Clone)] @@ -87,8 +87,8 @@ impl QrCode { /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than /// the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped QrCode if successful, - /// or None if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, + /// or `None` if the data is too long to fit in any version at the given ECC level. pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option { let chrs: Vec = text.chars().collect(); let segs: Vec = QrSegment::make_segments(&chrs); @@ -102,7 +102,7 @@ impl QrCode { /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit in any version at the given ECC level. pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option { let segs: Vec = vec![QrSegment::make_bytes(data)]; QrCode::encode_segments(&segs, ecl) @@ -118,9 +118,9 @@ impl QrCode { /// /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. - /// This is a mid-level API; the high-level API is encode_text() and encode_binary(). + /// This is a mid-level API; the high-level API is `encode_text()` and `encode_binary()`. /// - /// Returns a wrapped QrCode if successful, or None if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) } @@ -129,16 +129,16 @@ impl QrCode { /// Returns a QR Code representing the given segments with the given encoding parameters. /// /// The smallest possible QR Code version within the given range is automatically - /// chosen for the output. Iff boostecl is true, then the ECC level of the result + /// chosen for the output. Iff boostecl is `true`, then the ECC level of the result /// may be higher than the ecl argument if it can be done without increasing the /// version. The mask number is either between 0 to 7 (inclusive) to force that /// mask, or -1 to automatically choose an appropriate mask (which may be slow). /// /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. - /// This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + /// This is a mid-level API; the high-level API is `encodeText()` and `encodeBinary()`. /// - /// Returns a wrapped QrCode if successful, or None if the data is too long to fit + /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit /// in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, minversion: Version, maxversion: Version, mask: Option, boostecl: bool) -> Option { @@ -212,7 +212,7 @@ impl 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 encode_segments() function. + /// A mid-level API is the `encode_segments()` function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { // Initialize fields let size: usize = (ver.value() as usize) * 4 + 17; @@ -262,11 +262,11 @@ impl QrCode { } - /// Returns the color of the module (pixel) at the given coordinates, which is false - /// for white or true for black. + /// Returns the color of the module (pixel) at the given coordinates, which is `false` + /// for white or `true` for black. /// /// The top left corner has the coordinates (x=0, y=0). - /// If the given coordinates are out of bounds, then false (white) is returned. + /// If the given coordinates are out of bounds, then `false` (white) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) } @@ -879,9 +879,9 @@ impl ReedSolomonGenerator { /// Instances of this struct are immutable. /// /// The mid-level way to create a segment is to take the payload data -/// and call a static factory function such as QrSegment::make_numeric(). +/// and call a static factory function such as `QrSegment::make_numeric()`. /// The low-level way to create a segment is to custom-make the bit buffer -/// and call the QrSegment::new() constructor with appropriate values. +/// and call the `QrSegment::new()` constructor with appropriate values. /// /// This segment struct imposes no length restrictions, but QR Codes have restrictions. /// Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. @@ -1151,7 +1151,7 @@ impl BitBuffer { /// Appends the given number of low-order bits of the given value /// to this buffer. /// - /// Requires len <= 31 and val < 2^len. + /// Requires len ≤ 31 and val < 2len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit From 7d84b8b1281c76ff6d7d9774d936d548c37cee0a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:27:50 +0000 Subject: [PATCH 552/810] Tweaked comments in Rust code to fix Rust-specific facts. --- rust/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 119d456..96f8681 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -37,7 +37,7 @@ /// /// - High level: Take the payload data and call `QrCode::encode_text()` or `QrCode::encode_binary()`. /// - Mid level: Custom-make the list of segments and call -/// `QrCode.encode_segments()` or `QrCode.encode_segments_advanced()`. +/// `QrCode::encode_segments()` or `QrCode::encode_segments_advanced()`. /// - Low level: Custom-make the array of data codeword bytes (including segment /// headers and final padding, excluding error correction codewords), supply the /// appropriate version number, and call the `QrCode::encode_codewords()` constructor. @@ -132,11 +132,11 @@ impl QrCode { /// chosen for the output. Iff boostecl is `true`, then the ECC level of the result /// may be higher than the ecl argument if it can be done without increasing the /// version. The mask number is either between 0 to 7 (inclusive) to force that - /// mask, or -1 to automatically choose an appropriate mask (which may be slow). + /// mask, or `None` to automatically choose an appropriate mask (which may be slow). /// /// This function allows the user to create a custom sequence of segments that switches /// between modes (such as alphanumeric and byte) to encode text in less space. - /// This is a mid-level API; the high-level API is `encodeText()` and `encodeBinary()`. + /// This is a mid-level API; the high-level API is `encode_text()` and `encode_binary()`. /// /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit /// in any version in the given range at the given ECC level. From 40858c7b11f3ab40c3116b51bc57d818951b27f4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:31:28 +0000 Subject: [PATCH 553/810] Tweaked comments in Rust code to reflow the hard line wrapping, without changing HTML output. --- rust/src/lib.rs | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 96f8681..a512469 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -87,8 +87,8 @@ impl QrCode { /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than /// the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped `QrCode` if successful, - /// or `None` if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the + /// data is too long to fit in any version at the given ECC level. pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option { let chrs: Vec = text.chars().collect(); let segs: Vec = QrSegment::make_segments(&chrs); @@ -102,7 +102,8 @@ impl QrCode { /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the + /// data is too long to fit in any version at the given ECC level. pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option { let segs: Vec = vec![QrSegment::make_bytes(data)]; QrCode::encode_segments(&segs, ecl) @@ -120,7 +121,8 @@ impl 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 `encode_text()` and `encode_binary()`. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit in any version at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the + /// data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) } @@ -138,8 +140,8 @@ impl 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 `encode_text()` and `encode_binary()`. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the data is too long to fit - /// in any version in the given range at the given ECC level. + /// Returns a wrapped `QrCode` if successful, or `None` if the data is too + /// long to fit in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, minversion: Version, maxversion: Version, mask: Option, boostecl: bool) -> Option { assert!(minversion.value() <= maxversion.value(), "Invalid value"); @@ -262,11 +264,11 @@ impl QrCode { } - /// Returns the color of the module (pixel) at the given coordinates, which is `false` - /// for white or `true` for black. + /// Returns the color of the module (pixel) at the given coordinates, + /// which is `false` for white or `true` for black. /// - /// The top left corner has the coordinates (x=0, y=0). - /// If the given coordinates are out of bounds, then `false` (white) is returned. + /// The top left corner has the coordinates (x=0, y=0). If the given + /// coordinates are out of bounds, then `false` (white) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) } @@ -284,8 +286,8 @@ impl QrCode { } - /// Returns a string of SVG code for an image depicting this QR Code, with the given number - /// of border modules. + /// Returns a string of SVG code for an image depicting + /// this QR Code, with the given number of border modules. /// /// The string always uses Unix newlines (\n), regardless of the platform. pub fn to_svg_string(&self, border: i32) -> String { @@ -907,13 +909,11 @@ impl QrSegment { /*---- Static factory functions (mid level) ----*/ - /// Returns a segment representing the given binary data encoded in - /// byte mode. + /// Returns a segment representing the given binary data encoded in byte mode. /// /// All input byte slices are acceptable. /// - /// Any text string - /// can be converted to UTF-8 bytes and encoded as a byte mode segment. + /// Any text string can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); for b in data { @@ -949,8 +949,8 @@ impl QrSegment { /// Returns a segment representing the given text string encoded in alphanumeric mode. /// - /// The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, - /// plus, hyphen, period, slash, colon. + /// The characters allowed are: 0 to 9, A to Z (uppercase only), space, + /// dollar, percent, asterisk, plus, hyphen, period, slash, colon. /// /// Panics if the string contains non-encodable characters. pub fn make_alphanumeric(text: &[char]) -> Self { @@ -977,8 +977,8 @@ impl QrSegment { /// Returns a list of zero or more segments to represent the given Unicode text string. /// - /// The result - /// may use various segment modes and switch modes to optimize the length of the bit stream. + /// The result may use various segment modes and switch + /// modes to optimize the length of the bit stream. pub fn make_segments(text: &[char]) -> Vec { if text.is_empty() { vec![] @@ -1148,8 +1148,7 @@ pub struct BitBuffer(pub Vec); impl BitBuffer { - /// Appends the given number of low-order bits of the given value - /// to this buffer. + /// Appends the given number of low-order bits of the given value to this buffer. /// /// Requires len ≤ 31 and val < 2len. pub fn append_bits(&mut self, val: u32, len: u8) { From d2e2f8b596eae491153b8c7369bb5cc943005dea Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:32:29 +0000 Subject: [PATCH 554/810] Tweaked comments in Rust code to end in a period, for enum constants. --- rust/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a512469..be7a29b 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -761,13 +761,13 @@ static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [ /// The error correction level in a QR Code symbol. #[derive(Clone, Copy)] pub enum QrCodeEcc { - /// The QR Code can tolerate about 7% erroneous codewords + /// The QR Code can tolerate about 7% erroneous codewords. Low , - /// The QR Code can tolerate about 15% erroneous codewords + /// The QR Code can tolerate about 15% erroneous codewords. Medium , - /// The QR Code can tolerate about 25% erroneous codewords + /// The QR Code can tolerate about 25% erroneous codewords. Quartile, - /// The QR Code can tolerate about 30% erroneous codewords + /// The QR Code can tolerate about 30% erroneous codewords. High , } From 38d8d3377f1ba43c038547c5ec080c8f66f59002 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 01:41:45 +0000 Subject: [PATCH 555/810] Added Rust crate summary documentation comment. --- rust/src/lib.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index be7a29b..e2f1359 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -22,6 +22,65 @@ */ +//! Generates QR Codes from text strings and byte arrays. +//! +//! This project aims to be the best, clearest QR Code generator library. +//! The primary goals are flexible options and absolute correctness. +//! Secondary goals are compact implementation size and good documentation comments. +//! +//! Home page with live JavaScript demo, extensive descriptions, and competitor comparisons: +//! [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) +//! +//! # Features +//! +//! Core features: +//! +//! - Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust +//! - Significantly shorter code but more documentation comments compared to competing libraries +//! - Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard +//! - Output formats: Raw modules/pixels of the QR symbol, SVG XML string +//! - Encodes numeric and special-alphanumeric text in less space than general text +//! - Open source code under the permissive MIT License +//! +//! Manual parameters: +//! +//! - User can specify minimum and maximum version numbers allowed, then library will automatically choose smallest version in the range that fits the data +//! - User can specify mask pattern manually, otherwise library will automatically evaluate all 8 masks and select the optimal one +//! - User can specify absolute error correction level, or allow the library to boost it if it doesn't increase the version number +//! - User can create a list of data segments manually and add ECI segments +//! +//! # Examples +//! +//! ``` +//! extern crate qrcodegen; +//! use qrcodegen::QrCode; +//! use qrcodegen::QrCodeEcc; +//! use qrcodegen::QrSegment; +//! ``` +//! +//! Simple operation: +//! +//! ``` +//! let qr = QrCode::encode_text("Hello, world!", +//! QrCodeEcc::Medium).unwrap(); +//! let svg = qr.to_svg_string(4); +//! ``` +//! +//! Manual operation: +//! +//! ``` +//! let chrs: Vec = "3141592653589793238462643383".chars().collect(); +//! let segs = QrSegment::make_segments(&chrs); +//! let qr = QrCode::encode_segments_advanced( +//! &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); +//! for y in 0 .. qr.size() { +//! for x in 0 .. qr.size() { +//! (... paint qr.get_module(x, y) ...) +//! } +//! } +//! ``` + + /*---- QrCode functionality ----*/ /// A QR Code symbol, which is a type of two-dimension barcode. From d234e1c73b1f3d69382d5230eaed9c1fb025fa69 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 03:20:42 +0000 Subject: [PATCH 556/810] Added Rust documentation comments for Version and Mask struct+impl. --- rust/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e2f1359..ffaaeff 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1220,30 +1220,40 @@ impl BitBuffer { /*---- Miscellaneous values ----*/ +/// A number between 1 and 40 (inclusive). #[derive(Copy, Clone)] pub struct Version(u8); impl Version { + /// Creates a version object from the given number. + /// + /// Panics if the number is outside the range [1, 40]. pub fn new(ver: u8) -> Self { assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range"); Version(ver) } + /// Returns the value, which is in the range [1, 40]. pub fn value(&self) -> u8 { self.0 } } +/// A number between 0 and 7 (inclusive). #[derive(Copy, Clone)] pub struct Mask(u8); impl Mask { + /// Creates a mask object from the given number. + /// + /// Panics if the number is outside the range [0, 7]. pub fn new(mask: u8) -> Self { assert!(mask <= 7, "Mask value out of range"); Mask(mask) } + /// Returns the value, which is in the range [0, 7]. pub fn value(&self) -> u8 { self.0 } From e883e31f0a9515f02285a0bacb5881893d5c5312 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 03:24:57 +0000 Subject: [PATCH 557/810] Updated Rust crate version number. --- rust/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 2b3c542..c01d154 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qrcodegen" -version = "1.3.0" +version = "1.3.1" authors = ["Project Nayuki"] description = "High-quality QR Code generator library" homepage = "https://www.nayuki.io/page/qr-code-generator-library" From f2ea49b7d480e2383271288af9a1e9e4bbe210d2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 19:54:30 +0000 Subject: [PATCH 558/810] Tweaked QrCode.drawFormatBits() to use end-exclusive range in second copy for clarity, in most languages. --- c/qrcodegen.c | 2 +- cpp/QrCode.cpp | 2 +- java/io/nayuki/qrcodegen/QrCode.java | 2 +- javascript/qrcodegen.js | 2 +- typescript/qrcodegen.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 8f6de55..79bf810 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -528,7 +528,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin // Draw second copy int qrsize = qrcodegen_getSize(qrcode); - for (int i = 0; i <= 7; i++) + for (int i = 0; i < 8; i++) setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 0a8b12f..5005949 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -243,7 +243,7 @@ void QrCode::drawFormatBits(int mask) { setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy - for (int i = 0; i <= 7; i++) + for (int i = 0; i < 8; i++) setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index eba69ec..e689955 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -396,7 +396,7 @@ public final class QrCode { setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy - for (int i = 0; i <= 7; i++) + for (int i = 0; i < 8; i++) setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 520daad..16531a5 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -257,7 +257,7 @@ var qrcodegen = new function() { setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy - for (var i = 0; i <= 7; i++) + for (var i = 0; i < 8; i++) setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (var i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index e2c1964..fe17bcd 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -337,7 +337,7 @@ namespace qrcodegen { this.setFunctionModule(14 - i, 8, getBit(bits, i)); // Draw second copy - for (let i = 0; i <= 7; i++) + for (let i = 0; i < 8; i++) this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i)); for (let i = 8; i < 15; i++) this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i)); From badeca0dc9c0eb84ed72585448cb090f7510b192 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 13 Oct 2018 21:22:52 +0000 Subject: [PATCH 559/810] Imported QrSegmentAdvanced from the parent project, changed a constant to be package-private, updated readme. --- Readme.markdown | 2 + src/io/nayuki/fastqrcodegen/QrSegment.java | 2 +- .../fastqrcodegen/QrSegmentAdvanced.java | 419 ++++++++++++++++++ 3 files changed, 422 insertions(+), 1 deletion(-) create mode 100644 src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java diff --git a/Readme.markdown b/Readme.markdown index 9e7380f..bd70143 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -20,6 +20,8 @@ Core features: * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol, SVG XML string, `BufferedImage` raster bitmap * Encodes numeric and special-alphanumeric text in less space than general text +* Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes +* Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts * Open source code under the permissive MIT License Manual parameters: diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index 677bcda..86cce95 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -276,7 +276,7 @@ public final class QrSegment { /*---- Constants ----*/ - private static final int[] ALPHANUMERIC_MAP; + static final int[] ALPHANUMERIC_MAP; static { final String ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; diff --git a/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java b/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java new file mode 100644 index 0000000..6cbdb7b --- /dev/null +++ b/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java @@ -0,0 +1,419 @@ +/* + * Fast QR Code generator library + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/fast-qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.fastqrcodegen; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.List; +import java.util.Objects; +import io.nayuki.fastqrcodegen.QrSegment.Mode; + + +/** + * Splits text into optimal segments and encodes kanji segments. + * Provides static functions only; not instantiable. + * @see QrSegment + * @see QrCode + */ +public final class QrSegmentAdvanced { + + /*---- Optimal list of segments encoder ----*/ + + /** + * Returns a list of zero or more segments to represent the specified Unicode text string. + * The resulting list optimally minimizes the total encoded bit length, subjected to the constraints + * in the specified {error correction level, minimum version number, maximum version number}. + *

This function can utilize all four text encoding modes: numeric, alphanumeric, byte (UTF-8), + * and kanji. This can be considered as a sophisticated but slower replacement for {@link + * QrSegment#makeSegments(String)}. This requires more input parameters because it searches a + * range of versions, like {@link QrCode#encodeSegments(List,QrCode.Ecc,int,int,int,boolean)}.

+ * @param text the text to be encoded (not {@code null}), which can be any Unicode string + * @param ecl the error correction level to use (not {@code null}) + * @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) + * @return a new mutable list (not {@code null}) of segments (not {@code null}) + * containing the text, minimizing the bit length with respect to the constraints + * @throws NullPointerException if the text or error correction level is {@code null} + * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 + * is violated, or if the data is too long to fit in a QR Code at maxVersion at ECL + */ + public static List makeSegmentsOptimally(String text, QrCode.Ecc ecl, int minVersion, int maxVersion) { + // Check arguments + Objects.requireNonNull(text); + Objects.requireNonNull(ecl); + if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) + 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 <= maxVersion; version++) { + if (version == minVersion || version == 10 || version == 27) + segs = makeSegmentsOptimally(codePoints, version); + assert segs != null; + + // Check if the segments fit + int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + int dataUsedBits = QrSegment.getTotalBits(segs, version); + if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) + return segs; + } + throw new IllegalArgumentException("Data too long"); + } + + + // 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); + 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) { + if (codePoints.length == 0) + throw new IllegalArgumentException(); + final Mode[] modeTypes = {Mode.BYTE, Mode.ALPHANUMERIC, Mode.NUMERIC, Mode.KANJI}; // Do not modify + final int numModes = modeTypes.length; + + // Segment header sizes, measured in 1/6 bits + final int[] headCosts = new int[numModes]; + for (int i = 0; i < numModes; i++) + headCosts[i] = (4 + modeTypes[i].numCharCountBits(version)) * 6; + + // 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]; + + // At the beginning of each iteration of the loop below, + // prevCosts[j] is the exact minimum number of 1/6 bits needed to + // encode the entire string prefix of length i, and end in modeTypes[j] + int[] prevCosts = headCosts.clone(); + + // Calculate costs using dynamic programming + for (int i = 0; i < codePoints.length; i++) { + int c = 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]; + } + // Extend a segment if possible + if (QrSegment.ALPHANUMERIC_MAP[c] != -1) { // 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 + curCosts[2] = prevCosts[2] + 20; // 3.33 bits per digit + charModes[i][2] = modeTypes[2]; + } + if (isKanji(c)) { + curCosts[3] = prevCosts[3] + 78; // 13 bits per Shift JIS char + charModes[i][3] = modeTypes[3]; + } + + // 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]; + if (charModes[i][k] != null && (charModes[i][j] == null || newCost < curCosts[j])) { + curCosts[j] = newCost; + charModes[i][j] = modeTypes[k]; + } + } + } + + 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]; + for (int i = result.length - 1; i >= 0; i--) { + for (int j = 0; j < numModes; j++) { + if (modeTypes[j] == curMode) { + curMode = charModes[i][j]; + result[i] = curMode; + break; + } + } + } + return result; + } + + + // 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) { + if (codePoints.length == 0) + throw new IllegalArgumentException(); + List result = new ArrayList<>(); + + // Accumulate run of modes + Mode 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(); + if (i >= codePoints.length) + return result; + curMode = charModes[i]; + start = i; + } + } + + + // 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(); + for (int c : result) { + if (Character.isSurrogate((char)c)) + throw new IllegalArgumentException("Invalid UTF-16 string"); + } + return result; + } + + + // 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; + else throw new IllegalArgumentException("Invalid code point"); + } + + + + /*---- Kanji mode segment encoder ----*/ + + /** + * Returns a segment representing the specified text string encoded in kanji mode. + * Broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}. + * Examples of non-encodable characters include {ordinary ASCII, half-width katakana, + * more extensive Chinese hanzi}. + * @param text the text (not {@code null}), with only certain characters allowed + * @return a segment (not {@code null}) containing the text + * @throws NullPointerException if the string is {@code null} + * @throws IllegalArgumentException if the string contains non-encodable characters + * @see #isEncodableAsKanji(String) + */ + public static QrSegment makeKanji(String text) { + Objects.requireNonNull(text); + BitBuffer bb = new BitBuffer(); + text.chars().forEachOrdered(c -> { + int val = UNICODE_TO_QR_KANJI[c]; + if (val == -1) + throw new IllegalArgumentException("String contains non-kanji-mode characters"); + bb.appendBits(val, 13); + }); + return new QrSegment(Mode.KANJI, text.length(), bb.data, bb.bitLength); + } + + + /** + * Tests whether the specified string can be encoded as a segment in kanji mode. + * Broadly speaking, the set of encodable characters are {kanji used in Japan, + * hiragana, katakana, East Asian punctuation, full-width ASCII, Greek, Cyrillic}. + * Examples of non-encodable characters include {ordinary ASCII, half-width katakana, + * more extensive Chinese hanzi}. + * @param text the string to test for encodability (not {@code null}) + * @return {@code true} iff each character is in the kanji mode character set + * @throws NullPointerException if the string is {@code null} + * @see #makeKanji(String) + */ + public static boolean isEncodableAsKanji(String text) { + Objects.requireNonNull(text); + return text.chars().allMatch( + c -> isKanji((char)c)); + } + + + private static boolean isKanji(int c) { + 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=="; + + + private static short[] UNICODE_TO_QR_KANJI = new short[1 << 16]; + + static { // Unpack the Shift JIS table into a more computation-friendly form + 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) + continue; + assert UNICODE_TO_QR_KANJI[c] == -1; + UNICODE_TO_QR_KANJI[c] = (short)(i / 2); + } + } + + + + /*---- Miscellaneous ----*/ + + private QrSegmentAdvanced() {} // Not instantiable + +} From 3da57e5aa01ebc5c228fd9adfb1f5956c63d1648 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Oct 2018 05:03:33 +0000 Subject: [PATCH 560/810] Fixed Rust code examples in multiple files (due to commit 9628e259710c). --- Readme.markdown | 2 +- rust/Readme.markdown | 2 +- rust/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index c8f6ca0..e5f6507 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -181,7 +181,7 @@ Rust language: let chrs: Vec = "3141592653589793238462643383".chars().collect(); let segs = QrSegment::make_segments(&chrs); let qr = QrCode::encode_segments_advanced( - &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); + &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); for y in 0 .. qr.size() { for x in 0 .. qr.size() { (... paint qr.get_module(x, y) ...) diff --git a/rust/Readme.markdown b/rust/Readme.markdown index 48b579f..2b56474 100644 --- a/rust/Readme.markdown +++ b/rust/Readme.markdown @@ -47,7 +47,7 @@ Examples let chrs: Vec = "3141592653589793238462643383".chars().collect(); let segs = QrSegment::make_segments(&chrs); let qr = QrCode::encode_segments_advanced( - &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); + &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); for y in 0 .. qr.size() { for x in 0 .. qr.size() { (... paint qr.get_module(x, y) ...) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index ffaaeff..b2753a3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -72,7 +72,7 @@ //! let chrs: Vec = "3141592653589793238462643383".chars().collect(); //! let segs = QrSegment::make_segments(&chrs); //! let qr = QrCode::encode_segments_advanced( -//! &segs, QrCodeEcc::High, 5, 5, Some(2), false).unwrap(); +//! &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); //! for y in 0 .. qr.size() { //! for x in 0 .. qr.size() { //! (... paint qr.get_module(x, y) ...) From 4635e8a8a876bfdce0139603fa6fdc76934fd039 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 02:39:46 +0000 Subject: [PATCH 561/810] Added new unused "data too long" exception/error class to Java, C++, Python, Rust code. --- cpp/QrCode.cpp | 4 ++ cpp/QrCode.hpp | 21 +++++++ .../qrcodegen/DataTooLongException.java | 55 +++++++++++++++++++ python/qrcodegen.py | 14 +++++ rust/src/lib.rs | 28 ++++++++++ 5 files changed, 122 insertions(+) create mode 100644 java/io/nayuki/qrcodegen/DataTooLongException.java diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 5005949..842c72d 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -617,4 +617,8 @@ uint8_t QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { return static_cast(z); } + +data_too_long::data_too_long(const std::string &msg) : + std::length_error(msg) {} + } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 1904844..df2fcde 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -348,4 +348,25 @@ class QrCode final { }; + + +/*---- Public exception class ----*/ + +/* + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + * - Decrease the error correction level if it was greater than Ecc::LOW. + * - If the encodeSegments() function was called with a maxVersion argument, then increase + * it if it was less than QrCode::MAX_VERSION. (This advice does not apply to the other + * factory functions because they search all versions up to QrCode::MAX_VERSION.) + * - Split the text data into better or optimal segments in order to reduce the number of bits required. + * - Change the text or binary data to be shorter. + * - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + * - Propagate the error upward to the caller/user. + */ +class data_too_long : public std::length_error { + + public: explicit data_too_long(const std::string &msg); + +}; + } diff --git a/java/io/nayuki/qrcodegen/DataTooLongException.java b/java/io/nayuki/qrcodegen/DataTooLongException.java new file mode 100644 index 0000000..4baadfc --- /dev/null +++ b/java/io/nayuki/qrcodegen/DataTooLongException.java @@ -0,0 +1,55 @@ +/* + * QR Code generator library (Java) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.qrcodegen; + + +/** + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + *
    + *
  • Decrease the error correction level if it was greater than {@code Ecc.LOW}.

  • + *
  • If the advanced {@code encodeSegments()} function with 6 arguments was called, then increase the + * maxVersion argument if it was less than {@link QrCode#MAX_VERSION}. (This advice does not apply to the + * other factory functions because they search all versions up to {@code QrCode.MAX_VERSION}.)

  • + *
  • Split the text data into better or optimal segments in order to reduce the number of + * bits required. (See {@link QrSegmentAdvanced#makeSegmentsOptimally(String,QrCode.Ecc,int,int) + * QrSegmentAdvanced.makeSegmentsOptimally()}.)

  • + *
  • Change the text or binary data to be shorter.

  • + *
  • Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).

  • + *
  • Propagate the error upward to the caller/user.

  • + *
+ * @see QrCode#encodeText(String, QrCode.Ecc) + * @see QrCode#encodeBinary(byte[], QrCode.Ecc) + * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc) + * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc, int, int, int, boolean) + */ +public class DataTooLongException extends IllegalArgumentException { + + public DataTooLongException() {} + + + public DataTooLongException(String msg) { + super(msg); + } + +} diff --git a/python/qrcodegen.py b/python/qrcodegen.py index ca9bdf4..84c22df 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -888,3 +888,17 @@ class _BitBuffer(list): def _get_bit(x, i): """Returns true iff the i'th bit of x is set to 1.""" return (x >> i) & 1 != 0 + + + +class DataTooLongError(ValueError): + """Raised when the supplied data does not fit any QR Code version. Ways to handle this exception include: + - Decrease the error correction level if it was greater than Ecc.LOW. + - If the encode_segments() function was called with a maxversion argument, then increase + it if it was less than QrCode.MAX_VERSION. (This advice does not apply to the other + factory functions because they search all versions up to QrCode.MAX_VERSION.) + - Split the text data into better or optimal segments in order to reduce the number of bits required. + - Change the text or binary data to be shorter. + - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). + - Propagate the error upward to the caller/user.""" + pass diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b2753a3..8f863ac 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1220,6 +1220,34 @@ impl BitBuffer { /*---- Miscellaneous values ----*/ +/// The error type when the supplied data does not fit any QR Code version. +/// +/// Ways to handle this exception include: +/// +/// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`. +/// - If the `encode_segments_advanced()` function was called, then increase the maxversion +/// argument if it was less than `QrCode_MAX_VERSION`. (This advice does not apply to the +/// other factory functions because they search all versions up to `QrCode_MAX_VERSION`.) +/// - Split the text data into better or optimal segments in order to reduce the number of bits required. +/// - Change the text or binary data to be shorter. +/// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). +/// - Propagate the error upward to the caller/user. +#[derive(Debug, Clone)] +pub struct DataTooLong(String); + +impl std::error::Error for DataTooLong { + fn description(&self) -> &str { + &self.0 + } +} + +impl std::fmt::Display for DataTooLong { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(&self.0) + } +} + + /// A number between 1 and 40 (inclusive). #[derive(Copy, Clone)] pub struct Version(u8); From 8da8107cc293e2aadea065ad92e082423feb83fd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 02:42:04 +0000 Subject: [PATCH 562/810] Updated QrCode.encodeSegments() to throw DataTooLongException - in Java, C++, Python, Rust code. Rust code also changes return types from Option to Result. Updated Java and Rust documentation. --- cpp/QrCode.cpp | 12 ++++++++++-- java/io/nayuki/qrcodegen/QrCode.java | 19 ++++++++++++------- python/qrcodegen.py | 5 ++++- rust/src/lib.rs | 28 +++++++++++++++++----------- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 842c72d..3b7f289 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -74,8 +74,16 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, dataUsedBits = QrSegment::getTotalBits(segs, 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 - throw std::length_error("Data too long"); + if (version >= maxVersion) { // All versions in the range could not fit the given data + std::ostringstream sb; + if (dataUsedBits == -1) + sb << "Segment too long"; + else { + sb << "Data length = " << dataUsedBits << " bits, "; + sb << "Max capacity = " << dataCapacityBits << " bits"; + } + throw data_too_long(sb.str()); + } } if (dataUsedBits == -1) throw std::logic_error("Assertion error"); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index e689955..f904a68 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -64,7 +64,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the text * @throws NullPointerException if the text or error correction level is {@code null} - * @throws IllegalArgumentException if the text fails to fit in the + * @throws DataTooLongException if the text fails to fit in the * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeText(String text, Ecc ecl) { @@ -84,7 +84,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the data * @throws NullPointerException if the data or error correction level is {@code null} - * @throws IllegalArgumentException if the data fails to fit in the + * @throws DataTooLongException if the data fails to fit in the * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeBinary(byte[] data, Ecc ecl) { @@ -109,7 +109,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if the segments fail to fit in the + * @throws DataTooLongException if the segments fail to fit in the * largest version QR Code at the ECL, which means they are too long */ public static QrCode encodeSegments(List segs, Ecc ecl) { @@ -137,8 +137,9 @@ public final class QrCode { * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * or −1 ≤ mask ≤ 7 is violated; or if the segments fail to - * fit in the maxVersion QR Code at the ECL, which means they are too long + * or −1 ≤ mask ≤ 7 is violated + * @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); @@ -153,8 +154,12 @@ public final class QrCode { dataUsedBits = QrSegment.getTotalBits(segs, 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 - throw new IllegalArgumentException("Data too long"); + if (version >= maxVersion) { // All versions in the range could not fit the given data + String msg = "Segment too long"; + if (dataUsedBits != -1) + msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(msg); + } } assert dataUsedBits != -1; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 84c22df..b623d26 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -124,7 +124,10 @@ class QrCode(object): if datausedbits is not None and 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 - raise ValueError("Data too long") + msg = "Segment too long" + if datausedbits is not None: + msg = "Data length = {} bits, Max capacity = {} bits".format(datausedbits, datacapacitybits) + raise DataTooLongError(msg) if datausedbits is None: raise AssertionError() diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 8f863ac..394a334 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -146,9 +146,9 @@ impl QrCode { /// QR Code version is automatically chosen for the output. The ECC level of the result may be higher than /// the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the + /// Returns a wrapped `QrCode` if successful, or `Err` if the /// data is too long to fit in any version at the given ECC level. - pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Option { + pub fn encode_text(text: &str, ecl: QrCodeEcc) -> Result { let chrs: Vec = text.chars().collect(); let segs: Vec = QrSegment::make_segments(&chrs); QrCode::encode_segments(&segs, ecl) @@ -161,9 +161,9 @@ impl QrCode { /// bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. /// The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the + /// Returns a wrapped `QrCode` if successful, or `Err` if the /// data is too long to fit in any version at the given ECC level. - pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Option { + pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Result { let segs: Vec = vec![QrSegment::make_bytes(data)]; QrCode::encode_segments(&segs, ecl) } @@ -180,9 +180,9 @@ impl 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 `encode_text()` and `encode_binary()`. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the + /// Returns a wrapped `QrCode` if successful, or `Err` if the /// data is too long to fit in any version at the given ECC level. - pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Option { + pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Result { QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) } @@ -199,10 +199,10 @@ impl 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 `encode_text()` and `encode_binary()`. /// - /// Returns a wrapped `QrCode` if successful, or `None` if the data is too + /// Returns a wrapped `QrCode` if successful, or `Err` if the data is too /// long to fit in any version in the given range at the given ECC level. pub fn encode_segments_advanced(segs: &[QrSegment], mut ecl: QrCodeEcc, - minversion: Version, maxversion: Version, mask: Option, boostecl: bool) -> Option { + minversion: Version, maxversion: Version, mask: Option, boostecl: bool) -> Result { assert!(minversion.value() <= maxversion.value(), "Invalid value"); // Find the minimal version number to use @@ -211,14 +211,20 @@ impl QrCode { loop { // Number of data bits available let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; - if let Some(n) = QrSegment::get_total_bits(segs, version) { + let dataused: Option = QrSegment::get_total_bits(segs, version); + if let Some(n) = dataused { if n <= datacapacitybits { datausedbits = n; break; // This version number is found to be suitable } } if version.value() >= maxversion.value() { // All versions in the range could not fit the given data - return None; + let msg: String = match dataused { + None => String::from("Segment too long"), + Some(n) => format!("Data length = {} bits, Max capacity = {} bits", + n, datacapacitybits), + }; + return Err(DataTooLong(msg)); } version = Version::new(version.value() + 1); } @@ -263,7 +269,7 @@ impl QrCode { } // Create the QR Code object - Some(QrCode::encode_codewords(version, ecl, &datacodewords, mask)) + Ok(QrCode::encode_codewords(version, ecl, &datacodewords, mask)) } From c36c4a28eb508a3ebe07a740cd32d5d5c855c6a1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 02:42:35 +0000 Subject: [PATCH 563/810] Updated Java, C++, Python, Rust worker programs to catch the new exception; removed error message string comparisons. --- cpp/QrCodeGeneratorWorker.cpp | 6 +----- java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java | 4 +--- python/qrcodegen-worker.py | 4 +--- rust/examples/qrcodegen-worker.rs | 4 ++-- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp index b4669ba..2295593 100644 --- a/cpp/QrCodeGeneratorWorker.cpp +++ b/cpp/QrCodeGeneratorWorker.cpp @@ -91,11 +91,7 @@ int main() { std::cout << (qr.getModule(x, y) ? 1 : 0) << std::endl; } - } catch (const std::length_error &ex) { - if (strcmp(ex.what(), "Data too long") != 0) { - std::cerr << ex.what() << std::endl; - return EXIT_FAILURE; - } + } catch (const qrcodegen::data_too_long &ex) { std::cout << -1 << std::endl; } std::cout << std::flush; diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java index e9d7ecc..801b1b3 100644 --- a/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java +++ b/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java @@ -91,9 +91,7 @@ public final class QrCodeGeneratorWorker { System.out.println(qr.getModule(x, y) ? 1 : 0); } - } catch (IllegalArgumentException e) { - if (!e.getMessage().equals("Data too long")) - throw e; + } catch (DataTooLongException e) { System.out.println(-1); } System.out.flush(); diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py index c52548d..421b876 100644 --- a/python/qrcodegen-worker.py +++ b/python/qrcodegen-worker.py @@ -68,9 +68,7 @@ def main(): for x in range(qr.get_size()): print(1 if qr.get_module(x, y) else 0) - except ValueError as e: - if e.args[0] != "Data too long": - raise + except qrcodegen.DataTooLongError: print(-1) sys.stdout.flush() diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs index f35ec80..a6c3ca3 100644 --- a/rust/examples/qrcodegen-worker.rs +++ b/rust/examples/qrcodegen-worker.rs @@ -79,7 +79,7 @@ fn main() { match QrCode::encode_segments_advanced(&segs, ECC_LEVELS[errcorlvl as usize], Version::new(minversion as u8), Version::new(maxversion as u8), msk, boostecl != 0) { - Some(qr) => { + Ok(qr) => { // Print grid of modules println!("{}", qr.version().value()); for y in 0 .. qr.size() { @@ -88,7 +88,7 @@ fn main() { } } }, - None => println!("-1"), + Err(_) => println!("-1"), } use std::io::Write; std::io::stdout().flush().unwrap(); From 025400e70635f1b1c73669dc5d8a9bd3d90108fc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 02:42:49 +0000 Subject: [PATCH 564/810] Tweaked a package-private Rust function to not be public. --- rust/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 394a334..0a59396 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1189,7 +1189,7 @@ impl QrSegmentMode { // 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]. - pub fn num_char_count_bits(&self, ver: Version) -> u8 { + fn num_char_count_bits(&self, ver: Version) -> u8 { use QrSegmentMode::*; (match *self { Numeric => [10, 12, 14], From a14de3d959565ec53ece52ba616918487fc6182b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 03:47:04 +0000 Subject: [PATCH 565/810] Renamed colorX, colorY local variables in QrCode.getPenaltyScore() to just color, in all language versions. --- c/qrcodegen.c | 12 ++++++------ cpp/QrCode.cpp | 12 ++++++------ java/io/nayuki/qrcodegen/QrCode.java | 12 ++++++------ javascript/qrcodegen.js | 12 ++++++------ python/qrcodegen.py | 8 ++++---- rust/src/lib.rs | 12 ++++++------ typescript/qrcodegen.ts | 12 ++++++------ 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 79bf810..62fd04b 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -632,10 +632,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in row having same color for (int y = 0; y < qrsize; y++) { - bool colorX = false; + bool color = false; for (int x = 0, runX = -1; x < qrsize; x++) { - if (x == 0 || getModule(qrcode, x, y) != colorX) { - colorX = getModule(qrcode, x, y); + if (x == 0 || getModule(qrcode, x, y) != color) { + color = getModule(qrcode, x, y); runX = 1; } else { runX++; @@ -648,10 +648,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } // Adjacent modules in column having same color for (int x = 0; x < qrsize; x++) { - bool colorY = false; + bool color = false; for (int y = 0, runY = -1; y < qrsize; y++) { - if (y == 0 || getModule(qrcode, x, y) != colorY) { - colorY = getModule(qrcode, x, y); + if (y == 0 || getModule(qrcode, x, y) != color) { + color = getModule(qrcode, x, y); runY = 1; } else { runY++; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 3b7f289..0d51cb6 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -430,10 +430,10 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in row having same color for (int y = 0; y < size; y++) { - bool colorX = false; + bool color = false; for (int x = 0, runX = -1; x < size; x++) { - if (x == 0 || module(x, y) != colorX) { - colorX = module(x, y); + if (x == 0 || module(x, y) != color) { + color = module(x, y); runX = 1; } else { runX++; @@ -446,10 +446,10 @@ long QrCode::getPenaltyScore() const { } // Adjacent modules in column having same color for (int x = 0; x < size; x++) { - bool colorY = false; + bool color = false; for (int y = 0, runY = -1; y < size; y++) { - if (y == 0 || module(x, y) != colorY) { - colorY = module(x, y); + if (y == 0 || module(x, y) != color) { + color = module(x, y); runY = 1; } else { runY++; diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index f904a68..fd73760 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -597,10 +597,10 @@ public final class QrCode { // Adjacent modules in row having same color for (int y = 0; y < size; y++) { - boolean colorX = false; + boolean color = false; for (int x = 0, runX = 0; x < size; x++) { - if (x == 0 || modules[y][x] != colorX) { - colorX = modules[y][x]; + if (x == 0 || modules[y][x] != color) { + color = modules[y][x]; runX = 1; } else { runX++; @@ -613,10 +613,10 @@ public final class QrCode { } // Adjacent modules in column having same color for (int x = 0; x < size; x++) { - boolean colorY = false; + boolean color = false; for (int y = 0, runY = 0; y < size; y++) { - if (y == 0 || modules[y][x] != colorY) { - colorY = modules[y][x]; + if (y == 0 || modules[y][x] != color) { + color = modules[y][x]; runY = 1; } else { runY++; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 16531a5..fda02bd 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -429,9 +429,9 @@ var qrcodegen = new function() { // Adjacent modules in row having same color for (var y = 0; y < size; y++) { - for (var x = 0, runX, colorX; x < size; x++) { - if (x == 0 || modules[y][x] != colorX) { - colorX = modules[y][x]; + for (var x = 0, runX, color; x < size; x++) { + if (x == 0 || modules[y][x] != color) { + color = modules[y][x]; runX = 1; } else { runX++; @@ -444,9 +444,9 @@ var qrcodegen = new function() { } // Adjacent modules in column having same color for (var x = 0; x < size; x++) { - for (var y = 0, runY, colorY; y < size; y++) { - if (y == 0 || modules[y][x] != colorY) { - colorY = modules[y][x]; + for (var y = 0, runY, color; y < size; y++) { + if (y == 0 || modules[y][x] != color) { + color = modules[y][x]; runY = 1; } else { runY++; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index b623d26..9a1bcb4 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -465,8 +465,8 @@ class QrCode(object): # Adjacent modules in row having same color for y in range(size): for x in range(size): - if x == 0 or modules[y][x] != colorx: - colorx = modules[y][x] + if x == 0 or modules[y][x] != color: + color = modules[y][x] runx = 1 else: runx += 1 @@ -477,8 +477,8 @@ class QrCode(object): # Adjacent modules in column having same color for x in range(size): for y in range(size): - if y == 0 or modules[y][x] != colory: - colory = modules[y][x] + if y == 0 or modules[y][x] != color: + color = modules[y][x] runy = 1 else: runy += 1 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0a59396..e556b50 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -647,11 +647,11 @@ impl QrCode { // Adjacent modules in row having same color for y in 0 .. size { - let mut colorx = false; + let mut color = false; let mut runx: i32 = 0; for x in 0 .. size { - if x == 0 || self.module(x, y) != colorx { - colorx = self.module(x, y); + if x == 0 || self.module(x, y) != color { + color = self.module(x, y); runx = 1; } else { runx += 1; @@ -665,11 +665,11 @@ impl QrCode { } // Adjacent modules in column having same color for x in 0 .. size { - let mut colory = false; + let mut color = false; let mut runy: i32 = 0; for y in 0 .. size { - if y == 0 || self.module(x, y) != colory { - colory = self.module(x, y); + if y == 0 || self.module(x, y) != color { + color = self.module(x, y); runy = 1; } else { runy += 1; diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index fe17bcd..199e151 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -512,9 +512,9 @@ namespace qrcodegen { // Adjacent modules in row having same color for (let y = 0; y < this.size; y++) { - for (let x = 0, runX = 0, colorX = false; x < this.size; x++) { - if (x == 0 || this.modules[y][x] != colorX) { - colorX = this.modules[y][x]; + for (let x = 0, runX = 0, color = false; x < this.size; x++) { + if (x == 0 || this.modules[y][x] != color) { + color = this.modules[y][x]; runX = 1; } else { runX++; @@ -527,9 +527,9 @@ namespace qrcodegen { } // Adjacent modules in column having same color for (let x = 0; x < this.size; x++) { - for (let y = 0, runY = 0, colorY = false; y < this.size; y++) { - if (y == 0 || this.modules[y][x] != colorY) { - colorY = this.modules[y][x]; + for (let y = 0, runY = 0, color = false; y < this.size; y++) { + if (y == 0 || this.modules[y][x] != color) { + color = this.modules[y][x]; runY = 1; } else { runY++; From 111b20b2b99423731dc33bbb983662aa99408d98 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 05:21:34 +0000 Subject: [PATCH 566/810] Tweaked logic in QrCode.getPenaltyScore() for future expansion, without changing behavior, in all languages. --- c/qrcodegen.c | 10 ++++++---- cpp/QrCode.cpp | 10 ++++++---- java/io/nayuki/qrcodegen/QrCode.java | 10 ++++++---- javascript/qrcodegen.js | 12 ++++++++---- python/qrcodegen.py | 8 ++++++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 12 ++++++++---- 7 files changed, 42 insertions(+), 24 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 62fd04b..0aa39c9 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -633,8 +633,9 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in row having same color for (int y = 0; y < qrsize; y++) { bool color = false; - for (int x = 0, runX = -1; x < qrsize; x++) { - if (x == 0 || getModule(qrcode, x, y) != color) { + int runX = 0; + for (int x = 0; x < qrsize; x++) { + if (getModule(qrcode, x, y) != color) { color = getModule(qrcode, x, y); runX = 1; } else { @@ -649,8 +650,9 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in column having same color for (int x = 0; x < qrsize; x++) { bool color = false; - for (int y = 0, runY = -1; y < qrsize; y++) { - if (y == 0 || getModule(qrcode, x, y) != color) { + int runY = 0; + for (int y = 0; y < qrsize; y++) { + if (getModule(qrcode, x, y) != color) { color = getModule(qrcode, x, y); runY = 1; } else { diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 0d51cb6..7e71887 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -431,8 +431,9 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in row having same color for (int y = 0; y < size; y++) { bool color = false; - for (int x = 0, runX = -1; x < size; x++) { - if (x == 0 || module(x, y) != color) { + int runX = 0; + for (int x = 0; x < size; x++) { + if (module(x, y) != color) { color = module(x, y); runX = 1; } else { @@ -447,8 +448,9 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in column having same color for (int x = 0; x < size; x++) { bool color = false; - for (int y = 0, runY = -1; y < size; y++) { - if (y == 0 || module(x, y) != color) { + int runY = 0; + for (int y = 0; y < size; y++) { + if (module(x, y) != color) { color = module(x, y); runY = 1; } else { diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index fd73760..2e327b6 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -598,8 +598,9 @@ public final class QrCode { // Adjacent modules in row having same color for (int y = 0; y < size; y++) { boolean color = false; - for (int x = 0, runX = 0; x < size; x++) { - if (x == 0 || modules[y][x] != color) { + int runX = 0; + for (int x = 0; x < size; x++) { + if (modules[y][x] != color) { color = modules[y][x]; runX = 1; } else { @@ -614,8 +615,9 @@ public final class QrCode { // Adjacent modules in column having same color for (int x = 0; x < size; x++) { boolean color = false; - for (int y = 0, runY = 0; y < size; y++) { - if (y == 0 || modules[y][x] != color) { + int runY = 0; + for (int y = 0; y < size; y++) { + if (modules[y][x] != color) { color = modules[y][x]; runY = 1; } else { diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index fda02bd..18ae79e 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -429,8 +429,10 @@ var qrcodegen = new function() { // Adjacent modules in row having same color for (var y = 0; y < size; y++) { - for (var x = 0, runX, color; x < size; x++) { - if (x == 0 || modules[y][x] != color) { + var color = false; + var runX = 0; + for (var x = 0; x < size; x++) { + if (modules[y][x] != color) { color = modules[y][x]; runX = 1; } else { @@ -444,8 +446,10 @@ var qrcodegen = new function() { } // Adjacent modules in column having same color for (var x = 0; x < size; x++) { - for (var y = 0, runY, color; y < size; y++) { - if (y == 0 || modules[y][x] != color) { + var color = false; + var runY = 0; + for (var y = 0; y < size; y++) { + if (modules[y][x] != color) { color = modules[y][x]; runY = 1; } else { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 9a1bcb4..1459644 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -464,8 +464,10 @@ class QrCode(object): # Adjacent modules in row having same color for y in range(size): + color = False + runx = 0 for x in range(size): - if x == 0 or modules[y][x] != color: + if modules[y][x] != color: color = modules[y][x] runx = 1 else: @@ -476,8 +478,10 @@ class QrCode(object): result += 1 # Adjacent modules in column having same color for x in range(size): + color = False + runy = 0 for y in range(size): - if y == 0 or modules[y][x] != color: + if modules[y][x] != color: color = modules[y][x] runy = 1 else: diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e556b50..9a2c2ae 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -650,7 +650,7 @@ impl QrCode { let mut color = false; let mut runx: i32 = 0; for x in 0 .. size { - if x == 0 || self.module(x, y) != color { + if self.module(x, y) != color { color = self.module(x, y); runx = 1; } else { @@ -668,7 +668,7 @@ impl QrCode { let mut color = false; let mut runy: i32 = 0; for y in 0 .. size { - if y == 0 || self.module(x, y) != color { + if self.module(x, y) != color { color = self.module(x, y); runy = 1; } else { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 199e151..45d5c1b 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -512,8 +512,10 @@ namespace qrcodegen { // Adjacent modules in row having same color for (let y = 0; y < this.size; y++) { - for (let x = 0, runX = 0, color = false; x < this.size; x++) { - if (x == 0 || this.modules[y][x] != color) { + let color = false; + let runX = 0; + for (let x = 0; x < this.size; x++) { + if (this.modules[y][x] != color) { color = this.modules[y][x]; runX = 1; } else { @@ -527,8 +529,10 @@ namespace qrcodegen { } // Adjacent modules in column having same color for (let x = 0; x < this.size; x++) { - for (let y = 0, runY = 0, color = false; y < this.size; y++) { - if (y == 0 || this.modules[y][x] != color) { + let color = false; + let runY = 0; + for (let y = 0; y < this.size; y++) { + if (this.modules[y][x] != color) { color = this.modules[y][x]; runY = 1; } else { From 5ac0e2a938f98e4e3d0155db558b0a48909a0dea Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 05:24:21 +0000 Subject: [PATCH 567/810] Inverted some if-else statements in QrCode.getPenaltyScore() without changing behavior, in all languages. --- c/qrcodegen.c | 16 ++++++++-------- cpp/QrCode.cpp | 16 ++++++++-------- java/io/nayuki/qrcodegen/QrCode.java | 16 ++++++++-------- javascript/qrcodegen.js | 16 ++++++++-------- python/qrcodegen.py | 16 ++++++++-------- rust/src/lib.rs | 16 ++++++++-------- typescript/qrcodegen.ts | 16 ++++++++-------- 7 files changed, 56 insertions(+), 56 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 0aa39c9..dc3d99b 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -635,15 +635,15 @@ static long getPenaltyScore(const uint8_t qrcode[]) { bool color = false; int runX = 0; for (int x = 0; x < qrsize; x++) { - if (getModule(qrcode, x, y) != color) { - color = getModule(qrcode, x, y); - runX = 1; - } else { + if (getModule(qrcode, x, y) == color) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; + } else { + color = getModule(qrcode, x, y); + runX = 1; } } } @@ -652,15 +652,15 @@ static long getPenaltyScore(const uint8_t qrcode[]) { bool color = false; int runY = 0; for (int y = 0; y < qrsize; y++) { - if (getModule(qrcode, x, y) != color) { - color = getModule(qrcode, x, y); - runY = 1; - } else { + if (getModule(qrcode, x, y) == color) { runY++; if (runY == 5) result += PENALTY_N1; else if (runY > 5) result++; + } else { + color = getModule(qrcode, x, y); + runY = 1; } } } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 7e71887..d91343b 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -433,15 +433,15 @@ long QrCode::getPenaltyScore() const { bool color = false; int runX = 0; for (int x = 0; x < size; x++) { - if (module(x, y) != color) { - color = module(x, y); - runX = 1; - } else { + if (module(x, y) == color) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; + } else { + color = module(x, y); + runX = 1; } } } @@ -450,15 +450,15 @@ long QrCode::getPenaltyScore() const { bool color = false; int runY = 0; for (int y = 0; y < size; y++) { - if (module(x, y) != color) { - color = module(x, y); - runY = 1; - } else { + if (module(x, y) == color) { runY++; if (runY == 5) result += PENALTY_N1; else if (runY > 5) result++; + } else { + color = module(x, y); + runY = 1; } } } diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index 2e327b6..fc2c708 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -600,15 +600,15 @@ public final class QrCode { boolean color = false; int runX = 0; for (int x = 0; x < size; x++) { - if (modules[y][x] != color) { - color = modules[y][x]; - runX = 1; - } else { + if (modules[y][x] == color) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; + } else { + color = modules[y][x]; + runX = 1; } } } @@ -617,15 +617,15 @@ public final class QrCode { boolean color = false; int runY = 0; for (int y = 0; y < size; y++) { - if (modules[y][x] != color) { - color = modules[y][x]; - runY = 1; - } else { + if (modules[y][x] == color) { runY++; if (runY == 5) result += PENALTY_N1; else if (runY > 5) result++; + } else { + color = modules[y][x]; + runY = 1; } } } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 18ae79e..398c850 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -432,15 +432,15 @@ var qrcodegen = new function() { var color = false; var runX = 0; for (var x = 0; x < size; x++) { - if (modules[y][x] != color) { - color = modules[y][x]; - runX = 1; - } else { + if (modules[y][x] == color) { runX++; if (runX == 5) result += QrCode.PENALTY_N1; else if (runX > 5) result++; + } else { + color = modules[y][x]; + runX = 1; } } } @@ -449,15 +449,15 @@ var qrcodegen = new function() { var color = false; var runY = 0; for (var y = 0; y < size; y++) { - if (modules[y][x] != color) { - color = modules[y][x]; - runY = 1; - } else { + if (modules[y][x] == color) { runY++; if (runY == 5) result += QrCode.PENALTY_N1; else if (runY > 5) result++; + } else { + color = modules[y][x]; + runY = 1; } } } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 1459644..c32f02c 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -467,29 +467,29 @@ class QrCode(object): color = False runx = 0 for x in range(size): - if modules[y][x] != color: - color = modules[y][x] - runx = 1 - else: + if modules[y][x] == color: runx += 1 if runx == 5: result += QrCode._PENALTY_N1 elif runx > 5: result += 1 + else: + color = modules[y][x] + runx = 1 # Adjacent modules in column having same color for x in range(size): color = False runy = 0 for y in range(size): - if modules[y][x] != color: - color = modules[y][x] - runy = 1 - else: + if modules[y][x] == color: runy += 1 if runy == 5: result += QrCode._PENALTY_N1 elif runy > 5: result += 1 + else: + color = modules[y][x] + runy = 1 # 2*2 blocks of modules having same color for y in range(size - 1): diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9a2c2ae..305939f 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -650,16 +650,16 @@ impl QrCode { let mut color = false; let mut runx: i32 = 0; for x in 0 .. size { - if self.module(x, y) != color { - color = self.module(x, y); - runx = 1; - } else { + if self.module(x, y) == color { runx += 1; if runx == 5 { result += PENALTY_N1; } else if runx > 5 { result += 1; } + } else { + color = self.module(x, y); + runx = 1; } } } @@ -668,16 +668,16 @@ impl QrCode { let mut color = false; let mut runy: i32 = 0; for y in 0 .. size { - if self.module(x, y) != color { - color = self.module(x, y); - runy = 1; - } else { + if self.module(x, y) == color { runy += 1; if runy == 5 { result += PENALTY_N1; } else if runy > 5 { result += 1; } + } else { + color = self.module(x, y); + runy = 1; } } } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 45d5c1b..5ad0dbf 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -515,15 +515,15 @@ namespace qrcodegen { let color = false; let runX = 0; for (let x = 0; x < this.size; x++) { - if (this.modules[y][x] != color) { - color = this.modules[y][x]; - runX = 1; - } else { + if (this.modules[y][x] == color) { runX++; if (runX == 5) result += QrCode.PENALTY_N1; else if (runX > 5) result++; + } else { + color = this.modules[y][x]; + runX = 1; } } } @@ -532,15 +532,15 @@ namespace qrcodegen { let color = false; let runY = 0; for (let y = 0; y < this.size; y++) { - if (this.modules[y][x] != color) { - color = this.modules[y][x]; - runY = 1; - } else { + if (this.modules[y][x] == color) { runY++; if (runY == 5) result += QrCode.PENALTY_N1; else if (runY > 5) result++; + } else { + color = this.modules[y][x]; + runY = 1; } } } From af872343c0e9740f1fd260b90352c3f4594ae3d3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 26 Oct 2018 06:53:12 +0000 Subject: [PATCH 568/810] Completely rewrote the algorithm for detecting finder-like patterns, making it more accurate and compliant with the QR Code specification, in all languages. --- c/qrcodegen.c | 65 ++++++++++++++------- cpp/QrCode.cpp | 52 ++++++++++------- cpp/QrCode.hpp | 12 ++++ java/io/nayuki/qrcodegen/QrCode.java | 57 ++++++++++++------- javascript/qrcodegen.js | 57 ++++++++++++------- python/qrcodegen.py | 46 +++++++++------ rust/src/lib.rs | 84 ++++++++++++++++++++-------- typescript/qrcodegen.ts | 57 ++++++++++++------- 8 files changed, 292 insertions(+), 138 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index dc3d99b..0772f65 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -72,6 +72,8 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); static long getPenaltyScore(const uint8_t qrcode[]); +static void addRunToHistory(unsigned char run, unsigned char history[7]); +static bool hasFinderLikePattern(unsigned char runHistory[7]); testable bool getModule(const uint8_t qrcode[], int x, int y); testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); @@ -630,10 +632,11 @@ static long getPenaltyScore(const uint8_t qrcode[]) { int qrsize = qrcodegen_getSize(qrcode); long result = 0; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < qrsize; y++) { + unsigned char runHistory[7] = {0}; bool color = false; - int runX = 0; + unsigned char runX = 0; for (int x = 0; x < qrsize; x++) { if (getModule(qrcode, x, y) == color) { runX++; @@ -642,15 +645,24 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runX > 5) result++; } else { + addRunToHistory(runX, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = getModule(qrcode, x, y); runX = 1; } } + addRunToHistory(runX, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < qrsize; x++) { + unsigned char runHistory[7] = {0}; bool color = false; - int runY = 0; + unsigned char runY = 0; for (int y = 0; y < qrsize; y++) { if (getModule(qrcode, x, y) == color) { runY++; @@ -659,10 +671,18 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runY > 5) result++; } else { + addRunToHistory(runY, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = getModule(qrcode, x, y); runY = 1; } } + addRunToHistory(runY, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } // 2*2 blocks of modules having same color @@ -676,23 +696,6 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } } - // Finder-like pattern in rows - for (int y = 0; y < qrsize; y++) { - for (int x = 0, bits = 0; x < qrsize; x++) { - bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Finder-like pattern in columns - for (int x = 0; x < qrsize; x++) { - for (int y = 0, bits = 0; y < qrsize; y++) { - bits = ((bits << 1) & 0x7FF) | (getModule(qrcode, x, y) ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Balance of black and white modules int black = 0; for (int y = 0; y < qrsize; y++) { @@ -709,6 +712,26 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } +// Inserts the given value to the front of the given array, which shifts over the +// existing values and deletes the last value. A helper function for getPenaltyScore(). +static void addRunToHistory(unsigned char run, unsigned char history[7]) { + memmove(&history[1], &history[0], 6 * sizeof(history[0])); + history[0] = run; +} + + +// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and +// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). +// Must only be called immediately after a run of white modules has ended. +static bool hasFinderLikePattern(unsigned char runHistory[7]) { + unsigned char n = runHistory[1]; + // The maximum QR Code size is 177, hence the run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && (runHistory[0] >= n * 4 || runHistory[6] >= n * 4); +} + + /*---- Basic QR Code information ----*/ diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index d91343b..de23a7f 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -428,8 +428,9 @@ int QrCode::handleConstructorMasking(int mask) { long QrCode::getPenaltyScore() const { long result = 0; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < size; y++) { + std::deque runHistory(7, 0); bool color = false; int runX = 0; for (int x = 0; x < size; x++) { @@ -440,13 +441,22 @@ long QrCode::getPenaltyScore() const { else if (runX > 5) result++; } else { + addRunToHistory(runX, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = module(x, y); runX = 1; } } + addRunToHistory(runX, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { + std::deque runHistory(7, 0); bool color = false; int runY = 0; for (int y = 0; y < size; y++) { @@ -457,10 +467,18 @@ long QrCode::getPenaltyScore() const { else if (runY > 5) result++; } else { + addRunToHistory(runY, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = module(x, y); runY = 1; } } + addRunToHistory(runY, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } // 2*2 blocks of modules having same color @@ -474,23 +492,6 @@ long QrCode::getPenaltyScore() const { } } - // Finder-like pattern in rows - for (int y = 0; y < size; y++) { - for (int x = 0, bits = 0; x < size; x++) { - bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Finder-like pattern in columns - for (int x = 0; x < size; x++) { - for (int y = 0, bits = 0; y < size; y++) { - bits = ((bits << 1) & 0x7FF) | (module(x, y) ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Balance of black and white modules int black = 0; for (const vector &row : modules) { @@ -544,6 +545,19 @@ int QrCode::getNumDataCodewords(int ver, Ecc ecl) { } +void QrCode::addRunToHistory(int run, std::deque &history) { + history.pop_back(); + history.push_front(run); +} + + +bool QrCode::hasFinderLikePattern(const std::deque &runHistory) { + int n = runHistory.at(1); + return n > 0 && runHistory.at(2) == n && runHistory.at(4) == n && runHistory.at(5) == n + && runHistory.at(3) == n * 3 && std::max(runHistory.at(0), runHistory.at(6)) >= n * 4; +} + + bool QrCode::getBit(long x, int i) { return ((x >> i) & 1) != 0; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index df2fcde..2f69843 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -24,6 +24,7 @@ #pragma once #include +#include #include #include #include "QrSegment.hpp" @@ -278,6 +279,17 @@ class QrCode final { private: static int getNumDataCodewords(int ver, Ecc ecl); + // Inserts the given value to the front of the given array, which shifts over the + // existing values and deletes the last value. A helper function for getPenaltyScore(). + private: static void addRunToHistory(int run, std::deque &history); + + + // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). + // Must only be called immediately after a run of white modules has ended. + private: static bool hasFinderLikePattern(const std::deque &runHistory); + + // Returns true iff the i'th bit of x is set to 1. private: static bool getBit(long x, int i); diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/io/nayuki/qrcodegen/QrCode.java index fc2c708..ce28b10 100644 --- a/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/io/nayuki/qrcodegen/QrCode.java @@ -595,8 +595,9 @@ public final class QrCode { private int getPenaltyScore() { int result = 0; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < size; y++) { + int[] runHistory = new int[7]; boolean color = false; int runX = 0; for (int x = 0; x < size; x++) { @@ -607,13 +608,22 @@ public final class QrCode { else if (runX > 5) result++; } else { + addRunToHistory(runX, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = modules[y][x]; runX = 1; } } + addRunToHistory(runX, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { + int[] runHistory = new int[7]; boolean color = false; int runY = 0; for (int y = 0; y < size; y++) { @@ -624,10 +634,18 @@ public final class QrCode { else if (runY > 5) result++; } else { + addRunToHistory(runY, runHistory); + if (!color && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = modules[y][x]; runY = 1; } } + addRunToHistory(runY, runHistory); + if (color) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } // 2*2 blocks of modules having same color @@ -641,23 +659,6 @@ public final class QrCode { } } - // Finder-like pattern in rows - for (int y = 0; y < size; y++) { - for (int x = 0, bits = 0; x < size; x++) { - bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0); - if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Finder-like pattern in columns - for (int x = 0; x < size; x++) { - for (int y = 0, bits = 0; y < size; y++) { - bits = ((bits << 1) & 0b11111111111) | (modules[y][x] ? 1 : 0); - if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - // Balance of black and white modules int black = 0; for (boolean[] row : modules) { @@ -734,6 +735,24 @@ public final class QrCode { } + // Inserts the given value to the front of the given array, which shifts over the + // existing values and deletes the last value. A helper function for getPenaltyScore(). + private static void addRunToHistory(int run, int[] history) { + System.arraycopy(history, 0, history, 1, history.length - 1); + history[0] = run; + } + + + // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). + // Must only be called immediately after a run of white modules has ended. + private static boolean hasFinderLikePattern(int[] runHistory) { + int n = runHistory[1]; + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + } + + // 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; diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 398c850..b40bfd3 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -427,8 +427,9 @@ var qrcodegen = new function() { function getPenaltyScore() { var result = 0; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for (var y = 0; y < size; y++) { + var runHistory = [0,0,0,0,0,0,0]; var color = false; var runX = 0; for (var x = 0; x < size; x++) { @@ -439,13 +440,22 @@ var qrcodegen = new function() { else if (runX > 5) result++; } else { + QrCode.addRunToHistory(runX, runHistory); + if (!color && QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; color = modules[y][x]; runX = 1; } } + QrCode.addRunToHistory(runX, runHistory); + if (color) + QrCode.addRunToHistory(0, runHistory); // Dummy run of white + if (QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for (var x = 0; x < size; x++) { + var runHistory = [0,0,0,0,0,0,0]; var color = false; var runY = 0; for (var y = 0; y < size; y++) { @@ -456,10 +466,18 @@ var qrcodegen = new function() { else if (runY > 5) result++; } else { + QrCode.addRunToHistory(runY, runHistory); + if (!color && QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; color = modules[y][x]; runY = 1; } } + QrCode.addRunToHistory(runY, runHistory); + if (color) + QrCode.addRunToHistory(0, runHistory); // Dummy run of white + if (QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; } // 2*2 blocks of modules having same color @@ -473,23 +491,6 @@ var qrcodegen = new function() { } } - // Finder-like pattern in rows - for (var y = 0; y < size; y++) { - for (var x = 0, bits = 0; x < size; x++) { - bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); - if (x >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += QrCode.PENALTY_N3; - } - } - // Finder-like pattern in columns - for (var x = 0; x < size; x++) { - for (var y = 0, bits = 0; y < size; y++) { - bits = ((bits << 1) & 0x7FF) | (modules[y][x] ? 1 : 0); - if (y >= 10 && (bits == 0x05D || bits == 0x5D0)) // Needs 11 bits accumulated - result += QrCode.PENALTY_N3; - } - } - // Balance of black and white modules var black = 0; modules.forEach(function(row) { @@ -666,6 +667,24 @@ var qrcodegen = new function() { }; + // Inserts the given value to the front of the given array, which shifts over the + // existing values and deletes the last value. A helper function for getPenaltyScore(). + QrCode.addRunToHistory = function(run, history) { + history.pop(); + history.unshift(run); + }; + + + // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). + // Must only be called immediately after a run of white modules has ended. + QrCode.hasFinderLikePattern = function(runHistory) { + var n = runHistory[1]; + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + }; + + /*---- Constants and tables for QrCode ----*/ var MIN_VERSION = 1; // The minimum version number supported in the QR Code Model 2 standard diff --git a/python/qrcodegen.py b/python/qrcodegen.py index c32f02c..1a3f417 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -21,7 +21,7 @@ # Software. # -import itertools, re, sys +import collections, itertools, re, sys """ @@ -462,8 +462,9 @@ class QrCode(object): size = self._size modules = self._modules - # Adjacent modules in row having same color + # Adjacent modules in row having same color, and finder-like patterns for y in range(size): + runhistory = collections.deque([0] * 7, 7) color = False runx = 0 for x in range(size): @@ -474,10 +475,19 @@ class QrCode(object): elif runx > 5: result += 1 else: + runhistory.appendleft(runx) + if not color and QrCode.has_finder_like_pattern(runhistory): + result += QrCode._PENALTY_N3 color = modules[y][x] runx = 1 - # Adjacent modules in column having same color + runhistory.appendleft(runx) + if color: + runhistory.appendleft(0) # Dummy run of white + if QrCode.has_finder_like_pattern(runhistory): + result += QrCode._PENALTY_N3 + # Adjacent modules in column having same color, and finder-like patterns for x in range(size): + runhistory = collections.deque([0] * 7, 7) color = False runy = 0 for y in range(size): @@ -488,8 +498,16 @@ class QrCode(object): elif runy > 5: result += 1 else: + runhistory.appendleft(runy) + if not color and QrCode.has_finder_like_pattern(runhistory): + result += QrCode._PENALTY_N3 color = modules[y][x] runy = 1 + runhistory.appendleft(runy) + if color: + runhistory.appendleft(0) # Dummy run of white + if QrCode.has_finder_like_pattern(runhistory): + result += QrCode._PENALTY_N3 # 2*2 blocks of modules having same color for y in range(size - 1): @@ -497,21 +515,6 @@ class QrCode(object): if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: result += QrCode._PENALTY_N2 - # Finder-like pattern in rows - for y in range(size): - bits = 0 - for x in range(size): - bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) - if x >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated - result += QrCode._PENALTY_N3 - # Finder-like pattern in columns - for x in range(size): - bits = 0 - for y in range(size): - bits = ((bits << 1) & 0x7FF) | (1 if modules[y][x] else 0) - if y >= 10 and bits in (0x05D, 0x5D0): # Needs 11 bits accumulated - result += QrCode._PENALTY_N3 - # Balance of black and white modules black = sum((1 if cell else 0) for row in modules for cell in row) total = size**2 # Note that size is odd, so black/total != 1/2 @@ -564,6 +567,13 @@ class QrCode(object): * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] + @staticmethod + def has_finder_like_pattern(runhistory): + n = runhistory[1] + return n > 0 and n == runhistory[2] == runhistory[4] == runhistory[5] \ + and runhistory[3] == n * 3 and max(runhistory[0], runhistory[6]) >= n * 4 + + # ---- Constants and tables ---- MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 305939f..1b8ff44 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -645,8 +645,9 @@ impl QrCode { let mut result: i32 = 0; let size: i32 = self.size; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for y in 0 .. size { + let mut runhistory = RunHistory::new(); let mut color = false; let mut runx: i32 = 0; for x in 0 .. size { @@ -658,13 +659,25 @@ impl QrCode { result += 1; } } else { + runhistory.add_run(runx); + if !color && runhistory.has_finder_like_pattern() { + result += PENALTY_N3; + } color = self.module(x, y); runx = 1; } } + runhistory.add_run(runx); + if color { + runhistory.add_run(0); // Dummy run of white + } + if runhistory.has_finder_like_pattern() { + result += PENALTY_N3; + } } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for x in 0 .. size { + let mut runhistory = RunHistory::new(); let mut color = false; let mut runy: i32 = 0; for y in 0 .. size { @@ -676,10 +689,21 @@ impl QrCode { result += 1; } } else { + runhistory.add_run(runy); + if !color && runhistory.has_finder_like_pattern() { + result += PENALTY_N3; + } color = self.module(x, y); runy = 1; } } + runhistory.add_run(runy); + if color { + runhistory.add_run(0); // Dummy run of white + } + if runhistory.has_finder_like_pattern() { + result += PENALTY_N3; + } } // 2*2 blocks of modules having same color @@ -694,27 +718,6 @@ impl QrCode { } } - // Finder-like pattern in rows - for y in 0 .. size { - let mut bits: u32 = 0; - for x in 0 .. size { - bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32); - if x >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - } - // Finder-like pattern in columns - for x in 0 .. size { - let mut bits: u32 = 0; - for y in 0 .. size { - bits = ((bits << 1) & 0x7FF) | (self.module(x, y) as u32); - if y >= 10 && (bits == 0x05D || bits == 0x5D0) { // Needs 11 bits accumulated - result += PENALTY_N3; - } - } - } - // Balance of black and white modules let mut black: i32 = 0; for color in &self.modules { @@ -939,6 +942,41 @@ impl ReedSolomonGenerator { +/*---- RunHistory functionality ----*/ + +struct RunHistory(std::collections::VecDeque); + + +impl RunHistory { + + fn new() -> Self { + let mut temp = std::collections::VecDeque::::new(); + temp.resize(7, 0); + RunHistory(temp) + } + + + // Inserts the given value to the front of this array, which shifts over the existing + // values and deletes the last value. A helper function for get_penalty_score(). + fn add_run(&mut self, run: i32) { + self.0.pop_back(); + self.0.push_front(run); + } + + + // Tests whether this run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for get_penalty_score(). + // Must only be called immediately after a run of white modules has ended. + fn has_finder_like_pattern(&self) -> bool { + let n = self.0[1]; + n > 0 && self.0[2] == n && self.0[4] == n && self.0[5] == n + && self.0[3] == n * 3 && std::cmp::max(self.0[0], self.0[6]) >= n * 4 + } + +} + + + /*---- QrSegment functionality ----*/ /// A segment of character/binary/control data in a QR Code symbol. diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 5ad0dbf..9c3d4ff 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -510,8 +510,9 @@ namespace qrcodegen { private getPenaltyScore(): int { let result: int = 0; - // Adjacent modules in row having same color + // Adjacent modules in row having same color, and finder-like patterns for (let y = 0; y < this.size; y++) { + let runHistory = [0,0,0,0,0,0,0]; let color = false; let runX = 0; for (let x = 0; x < this.size; x++) { @@ -522,13 +523,22 @@ namespace qrcodegen { else if (runX > 5) result++; } else { + QrCode.addRunToHistory(runX, runHistory); + if (!color && QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; color = this.modules[y][x]; runX = 1; } } + QrCode.addRunToHistory(runX, runHistory); + if (color) + QrCode.addRunToHistory(0, runHistory); // Dummy run of white + if (QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; } - // Adjacent modules in column having same color + // Adjacent modules in column having same color, and finder-like patterns for (let x = 0; x < this.size; x++) { + let runHistory = [0,0,0,0,0,0,0]; let color = false; let runY = 0; for (let y = 0; y < this.size; y++) { @@ -539,10 +549,18 @@ namespace qrcodegen { else if (runY > 5) result++; } else { + QrCode.addRunToHistory(runY, runHistory); + if (!color && QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; color = this.modules[y][x]; runY = 1; } } + QrCode.addRunToHistory(runY, runHistory); + if (color) + QrCode.addRunToHistory(0, runHistory); // Dummy run of white + if (QrCode.hasFinderLikePattern(runHistory)) + result += QrCode.PENALTY_N3; } // 2*2 blocks of modules having same color @@ -556,23 +574,6 @@ namespace qrcodegen { } } - // Finder-like pattern in rows - for (let y = 0; y < this.size; y++) { - for (let x = 0, bits = 0; x < this.size; x++) { - bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0); - if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated - result += QrCode.PENALTY_N3; - } - } - // Finder-like pattern in columns - for (let x = 0; x < this.size; x++) { - for (let y = 0, bits = 0; y < this.size; y++) { - bits = ((bits << 1) & 0b11111111111) | (this.modules[y][x] ? 1 : 0); - if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) // Needs 11 bits accumulated - result += QrCode.PENALTY_N3; - } - } - // Balance of black and white modules let black: int = 0; for (let row of this.modules) { @@ -636,6 +637,24 @@ namespace qrcodegen { } + // Inserts the given value to the front of the given array, which shifts over the + // existing values and deletes the last value. A helper function for getPenaltyScore(). + private static addRunToHistory(run: int, history: Array): void { + history.pop(); + history.unshift(run); + } + + + // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). + // Must only be called immediately after a run of white modules has ended. + private static hasFinderLikePattern(runHistory: Array): boolean { + const n: int = runHistory[1]; + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + } + + /*-- Constants and tables --*/ // The minimum version number supported in the QR Code Model 2 standard. From 52b885fae14d7e6f06473c6b2cbb7fb09e7aae46 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 27 Oct 2018 02:14:52 +0000 Subject: [PATCH 569/810] Updated Java code - changed QrSegmentAdvanced.makeSegmentsOptimally() to throw DataTooLongException and synchronize logic and Javadoc with QrCode class, changed Javadoc of DataTooLongException. --- .../io/nayuki/qrcodegen/DataTooLongException.java | 8 +++++--- java/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 15 ++++++++++----- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/java/io/nayuki/qrcodegen/DataTooLongException.java b/java/io/nayuki/qrcodegen/DataTooLongException.java index 4baadfc..17f8d5e 100644 --- a/java/io/nayuki/qrcodegen/DataTooLongException.java +++ b/java/io/nayuki/qrcodegen/DataTooLongException.java @@ -28,9 +28,10 @@ package io.nayuki.qrcodegen; * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: *
    *
  • Decrease the error correction level if it was greater than {@code Ecc.LOW}.

  • - *
  • If the advanced {@code encodeSegments()} function with 6 arguments was called, then increase the - * maxVersion argument if it was less than {@link QrCode#MAX_VERSION}. (This advice does not apply to the - * other factory functions because they search all versions up to {@code QrCode.MAX_VERSION}.)

  • + *
  • If the advanced {@code encodeSegments()} function with 6 arguments or the + * {@code makeSegmentsOptimally()} function was called, then increase the maxVersion argument + * if it was less than {@link QrCode#MAX_VERSION}. (This advice does not apply to the other + * factory functions because they search all versions up to {@code QrCode.MAX_VERSION}.)

  • *
  • Split the text data into better or optimal segments in order to reduce the number of * bits required. (See {@link QrSegmentAdvanced#makeSegmentsOptimally(String,QrCode.Ecc,int,int) * QrSegmentAdvanced.makeSegmentsOptimally()}.)

  • @@ -42,6 +43,7 @@ package io.nayuki.qrcodegen; * @see QrCode#encodeBinary(byte[], QrCode.Ecc) * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc) * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc, int, int, int, boolean) + * @see QrSegmentAdvanced#makeSegmentsOptimally(String, io.nayuki.qrcodegen.QrCode.Ecc, int, int) */ public class DataTooLongException extends IllegalArgumentException { diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java index c435a92..1efe7b5 100644 --- a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java +++ b/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java @@ -57,8 +57,8 @@ public final class QrSegmentAdvanced { * @return a new mutable list (not {@code null}) of segments (not {@code null}) * containing the text, minimizing the bit length with respect to the constraints * @throws NullPointerException if the text or error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * is violated, or if the data is too long to fit in a QR Code at maxVersion at ECL + * @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) { // Check arguments @@ -70,7 +70,7 @@ public final class QrSegmentAdvanced { // Iterate through version numbers, and make tentative segments List segs = null; int[] codePoints = toCodePoints(text); - for (int version = minVersion; version <= maxVersion; version++) { + for (int version = minVersion; ; version++) { if (version == minVersion || version == 10 || version == 27) segs = makeSegmentsOptimally(codePoints, version); assert segs != null; @@ -79,9 +79,14 @@ public final class QrSegmentAdvanced { int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; int dataUsedBits = QrSegment.getTotalBits(segs, version); if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - return segs; + return segs; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given text + String msg = "Segment too long"; + if (dataUsedBits != -1) + msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(msg); + } } - throw new IllegalArgumentException("Data too long"); } From 5483653b53f6efe24d7dcc8d3ac56c215aefc8a9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 27 Oct 2018 03:02:36 +0000 Subject: [PATCH 570/810] Moved Java source files into new subdirectories for Maven standard layout. --- java/{ => src/main/java}/io/nayuki/qrcodegen/BitBuffer.java | 0 .../main/java}/io/nayuki/qrcodegen/DataTooLongException.java | 0 java/{ => src/main/java}/io/nayuki/qrcodegen/QrCode.java | 0 .../main/java}/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java | 0 .../main/java}/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java | 0 java/{ => src/main/java}/io/nayuki/qrcodegen/QrSegment.java | 0 .../main/java}/io/nayuki/qrcodegen/QrSegmentAdvanced.java | 0 java/{ => src/main/java}/io/nayuki/qrcodegen/package-info.java | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename java/{ => src/main/java}/io/nayuki/qrcodegen/BitBuffer.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/DataTooLongException.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/QrCode.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/QrSegment.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/QrSegmentAdvanced.java (100%) rename java/{ => src/main/java}/io/nayuki/qrcodegen/package-info.java (100%) diff --git a/java/io/nayuki/qrcodegen/BitBuffer.java b/java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java similarity index 100% rename from java/io/nayuki/qrcodegen/BitBuffer.java rename to java/src/main/java/io/nayuki/qrcodegen/BitBuffer.java diff --git a/java/io/nayuki/qrcodegen/DataTooLongException.java b/java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java similarity index 100% rename from java/io/nayuki/qrcodegen/DataTooLongException.java rename to java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java diff --git a/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java similarity index 100% rename from java/io/nayuki/qrcodegen/QrCode.java rename to java/src/main/java/io/nayuki/qrcodegen/QrCode.java diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java similarity index 100% rename from java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java rename to java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java diff --git a/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java similarity index 100% rename from java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java rename to java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java diff --git a/java/io/nayuki/qrcodegen/QrSegment.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegment.java similarity index 100% rename from java/io/nayuki/qrcodegen/QrSegment.java rename to java/src/main/java/io/nayuki/qrcodegen/QrSegment.java diff --git a/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java b/java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java similarity index 100% rename from java/io/nayuki/qrcodegen/QrSegmentAdvanced.java rename to java/src/main/java/io/nayuki/qrcodegen/QrSegmentAdvanced.java diff --git a/java/io/nayuki/qrcodegen/package-info.java b/java/src/main/java/io/nayuki/qrcodegen/package-info.java similarity index 100% rename from java/io/nayuki/qrcodegen/package-info.java rename to java/src/main/java/io/nayuki/qrcodegen/package-info.java From 313b93d5b030b29c6c6a9679ada7d60a2827e401 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 28 Oct 2018 18:27:12 +0000 Subject: [PATCH 571/810] Tweaked JavaScript code to use String.substring() instead of the deprecated non-standard substr(). --- javascript/qrcodegen.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index b40bfd3..5123072 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -805,7 +805,7 @@ var qrcodegen = new function() { var bb = new BitBuffer(); for (var i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration var n = Math.min(digits.length - i, 3); - bb.appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1); + bb.appendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1); i += n; } return new this(this.Mode.NUMERIC, digits.length, bb); @@ -945,7 +945,7 @@ var qrcodegen = new function() { if (str.charAt(i) != "%") result.push(str.charCodeAt(i)); else { - result.push(parseInt(str.substr(i + 1, 2), 16)); + result.push(parseInt(str.substring(i + 1, i + 3), 16)); i += 2; } } From a24466089b6decf2f4118e06e78f038c137ad1f6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 1 Nov 2018 17:52:48 +0000 Subject: [PATCH 572/810] Tweaked TypeScript code to change 'let' declarations to 'const' wherever possible; but avoiding variables where objects are internally mutated (i.e. strict C++/Rust-style constness). --- typescript/qrcodegen.ts | 86 ++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 9c3d4ff..2bb3e71 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -59,7 +59,7 @@ namespace qrcodegen { // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the // ecl argument if it can be done without increasing the version. public static encodeText(text: string, ecl: QrCode.Ecc): QrCode { - let segs: Array = qrcodegen.QrSegment.makeSegments(text); + const segs: Array = qrcodegen.QrSegment.makeSegments(text); return QrCode.encodeSegments(segs, ecl); } @@ -69,7 +69,7 @@ namespace qrcodegen { // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. public static encodeBinary(data: Array, ecl: QrCode.Ecc): QrCode { - let seg: QrSegment = qrcodegen.QrSegment.makeBytes(data); + const seg: QrSegment = qrcodegen.QrSegment.makeBytes(data); return QrCode.encodeSegments([seg], ecl); } @@ -97,8 +97,8 @@ namespace qrcodegen { let version: int; let dataUsedBits: int; for (version = minVersion; ; version++) { - let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available - let usedBits: number = QrSegment.getTotalBits(segs, version); + const dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available + const usedBits: number = QrSegment.getTotalBits(segs, version); if (usedBits <= dataCapacityBits) { dataUsedBits = usedBits; break; // This version number is found to be suitable @@ -108,24 +108,24 @@ namespace qrcodegen { } // Increase the error correction level while the data still fits in the current version number - for (let newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high + for (const newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } // Concatenate all segments to create the data bit string let bb = new BitBuffer(); - for (let seg of segs) { + for (const seg of segs) { bb.appendBits(seg.mode.modeBits, 4); bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - for (let b of seg.getData()) + for (const b of seg.getData()) bb.push(b); } if (bb.length != dataUsedBits) throw "Assertion error"; // Add terminator and pad up to a byte if applicable - let dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; + const dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; if (bb.length > dataCapacityBits) throw "Assertion error"; bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); @@ -202,7 +202,7 @@ namespace qrcodegen { // Compute ECC, draw modules this.drawFunctionPatterns(); - let allCodewords: Array = this.addEccAndInterleave(dataCodewords); + const allCodewords: Array = this.addEccAndInterleave(dataCodewords); this.drawCodewords(allCodewords); // Do masking @@ -211,7 +211,7 @@ namespace qrcodegen { for (let i = 0; i < 8; i++) { this.drawFormatBits(i); this.applyMask(i); - let penalty: int = this.getPenaltyScore(); + const penalty: int = this.getPenaltyScore(); if (penalty < minPenalty) { mask = i; minPenalty = penalty; @@ -248,7 +248,7 @@ namespace qrcodegen { public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) throw "Value out of range"; - let width: int = (this.size + border * 2) * scale; + const width: int = (this.size + border * 2) * scale; canvas.width = width; canvas.height = width; let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; @@ -299,8 +299,8 @@ namespace qrcodegen { this.drawFinderPattern(3, this.size - 4); // Draw numerous alignment patterns - let alignPatPos: Array = this.getAlignmentPatternPositions(); - let numAlign: int = alignPatPos.length; + const alignPatPos: Array = this.getAlignmentPatternPositions(); + const numAlign: int = alignPatPos.length; for (let i = 0; i < numAlign; i++) { for (let j = 0; j < numAlign; j++) { // Don't draw on the three finder corners @@ -361,9 +361,9 @@ namespace qrcodegen { // Draw two copies for (let i = 0; i < 18; i++) { - let bt: boolean = getBit(bits, i); - let a: int = this.size - 11 + i % 3; - let b: int = Math.floor(i / 3); + const bt: boolean = getBit(bits, i); + const a: int = this.size - 11 + i % 3; + const b: int = Math.floor(i / 3); this.setFunctionModule(a, b, bt); this.setFunctionModule(b, a, bt); } @@ -375,9 +375,9 @@ namespace qrcodegen { private drawFinderPattern(x: int, y: int): void { for (let dy = -4; dy <= 4; dy++) { for (let dx = -4; dx <= 4; dx++) { - let dist: int = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm - let xx: int = x + dx; - let yy: int = y + dy; + const dist: int = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm + const xx: int = x + dx; + const yy: int = y + dy; if (0 <= xx && xx < this.size && 0 <= yy && yy < this.size) this.setFunctionModule(xx, yy, dist != 2 && dist != 4); } @@ -414,19 +414,19 @@ namespace qrcodegen { throw "Invalid argument"; // Calculate parameter numbers - let numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; - let blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver]; - let rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8); - let numShortBlocks: int = numBlocks - rawCodewords % numBlocks; - let shortBlockLen: int = Math.floor(rawCodewords / numBlocks); + const numBlocks: int = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + const blockEccLen: int = QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver]; + const rawCodewords: int = Math.floor(QrCode.getNumRawDataModules(ver) / 8); + const numShortBlocks: int = numBlocks - rawCodewords % numBlocks; + const shortBlockLen: int = Math.floor(rawCodewords / numBlocks); // Split data into blocks and append ECC to each block let blocks: Array> = []; - let rs = new ReedSolomonGenerator(blockEccLen); + const rs = new ReedSolomonGenerator(blockEccLen); for (let i = 0, k = 0; i < numBlocks; i++) { let dat: Array = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); k += dat.length; - let ecc: Array = rs.getRemainder(dat); + const ecc: Array = rs.getRemainder(dat); if (i < numShortBlocks) dat.push(0); blocks.push(dat.concat(ecc)); @@ -459,9 +459,9 @@ namespace qrcodegen { right = 5; for (let vert = 0; vert < this.size; vert++) { // Vertical counter for (let j = 0; j < 2; j++) { - let x: int = right - j; // Actual x coordinate - let upward: boolean = ((right + 1) & 2) == 0; - let y: int = upward ? this.size - 1 - vert : vert; // Actual y coordinate + const x: int = right - j; // Actual x coordinate + const upward: boolean = ((right + 1) & 2) == 0; + const y: int = upward ? this.size - 1 - vert : vert; // Actual y coordinate if (!this.isFunction[y][x] && i < data.length * 8) { this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); i++; @@ -566,7 +566,7 @@ namespace qrcodegen { // 2*2 blocks of modules having same color for (let y = 0; y < this.size - 1; y++) { for (let x = 0; x < this.size - 1; x++) { - let color: boolean = this.modules[y][x]; + const color: boolean = this.modules[y][x]; if ( color == this.modules[y][x + 1] && color == this.modules[y + 1][x] && color == this.modules[y + 1][x + 1]) @@ -576,15 +576,15 @@ namespace qrcodegen { // Balance of black and white modules let black: int = 0; - for (let row of this.modules) { - for (let color of row) { + for (const row of this.modules) { + for (const color of row) { if (color) black++; } } - let total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 + const total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - let k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; + const k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; result += k * QrCode.PENALTY_N4; return result; } @@ -599,8 +599,8 @@ namespace qrcodegen { if (this.version == 1) return []; else { - let numAlign: int = Math.floor(this.version / 7) + 2; - let step: int = (this.version == 32) ? 26 : + const numAlign: int = Math.floor(this.version / 7) + 2; + const step: int = (this.version == 32) ? 26 : Math.ceil((this.size - 13) / (numAlign*2 - 2)) * 2; let result: Array = [6]; for (let pos = this.size - 7; result.length < numAlign; pos -= step) @@ -618,7 +618,7 @@ namespace qrcodegen { throw "Version number out of range"; let result: int = (16 * ver + 128) * ver + 64; if (ver >= 2) { - let numAlign: int = Math.floor(ver / 7) + 2; + const numAlign: int = Math.floor(ver / 7) + 2; result -= (25 * numAlign - 10) * numAlign - 55; if (ver >= 7) result -= 36; @@ -718,7 +718,7 @@ namespace qrcodegen { // can be converted to UTF-8 bytes and encoded as a byte mode segment. public static makeBytes(data: Array): QrSegment { let bb = new BitBuffer(); - for (let b of data) + for (const b of data) bb.appendBits(b, 8); return new QrSegment(QrSegment.Mode.BYTE, data.length, bb); } @@ -730,7 +730,7 @@ namespace qrcodegen { throw "String contains non-numeric characters"; let bb = new BitBuffer(); for (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration - let n: int = Math.min(digits.length - i, 3); + const n: int = Math.min(digits.length - i, 3); bb.appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1); i += n; } @@ -827,8 +827,8 @@ namespace qrcodegen { // the given version. The result is infinity if a segment has too many characters to fit its length field. public static getTotalBits(segs: Array, version: int): number { let result: number = 0; - for (let seg of segs) { - let ccbits: int = seg.mode.numCharCountBits(version); + for (const seg of segs) { + const ccbits: int = seg.mode.numCharCountBits(version); if (seg.numChars >= (1 << ccbits)) return Infinity; // The segment's length doesn't fit the field's bit width result += 4 + ccbits + seg.bitData.length; @@ -922,8 +922,8 @@ namespace qrcodegen { public getRemainder(data: Array): Array { // Compute the remainder by performing polynomial division let result: Array = this.coefficients.map(_ => 0); - for (let b of data) { - let factor: byte = b ^ (result.shift() as int); + for (const b of data) { + const factor: byte = b ^ (result.shift() as int); result.push(0); for (let i = 0; i < result.length; i++) result[i] ^= ReedSolomonGenerator.multiply(this.coefficients[i], factor); From 08886d2a3ed29b299ea6bcbe45f17a873579545b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 1 Nov 2018 18:00:02 +0000 Subject: [PATCH 573/810] Simplified a bit of code in JavaScript, TypeScript, Python. --- javascript/qrcodegen.js | 5 +++-- python/qrcodegen.py | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 5123072..3fa1a20 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -997,8 +997,9 @@ var qrcodegen = new function() { data.forEach(function(b) { var factor = b ^ result.shift(); result.push(0); - for (var i = 0; i < result.length; i++) - result[i] ^= ReedSolomonGenerator.multiply(coefficients[i], factor); + coefficients.forEach(function(coef, i) { + result[i] ^= ReedSolomonGenerator.multiply(coef, factor); + }); }); return result; }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 1a3f417..8d82837 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -870,8 +870,8 @@ class _ReedSolomonGenerator(object): for b in data: factor = b ^ result.pop(0) result.append(0) - for i in range(len(result)): - result[i] ^= _ReedSolomonGenerator._multiply(self.coefficients[i], factor) + for (i, coef) in enumerate(self.coefficients): + result[i] ^= _ReedSolomonGenerator._multiply(coef, factor) return result diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 2bb3e71..d990d84 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -925,8 +925,8 @@ namespace qrcodegen { for (const b of data) { const factor: byte = b ^ (result.shift() as int); result.push(0); - for (let i = 0; i < result.length; i++) - result[i] ^= ReedSolomonGenerator.multiply(this.coefficients[i], factor); + this.coefficients.forEach((coef, i) => + result[i] ^= ReedSolomonGenerator.multiply(coef, factor)); } return result; } From 2cfcd5e8595bfcc7d9573e6107d826ae2d368d95 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 1 Nov 2018 18:00:22 +0000 Subject: [PATCH 574/810] Tweaked some names and types in TypeScript code. --- typescript/qrcodegen.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d990d84..d0d7d61 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -361,11 +361,11 @@ namespace qrcodegen { // Draw two copies for (let i = 0; i < 18; i++) { - const bt: boolean = getBit(bits, i); + const color: boolean = getBit(bits, i); const a: int = this.size - 11 + i % 3; const b: int = Math.floor(i / 3); - this.setFunctionModule(a, b, bt); - this.setFunctionModule(b, a, bt); + this.setFunctionModule(a, b, color); + this.setFunctionModule(b, a, color); } } @@ -923,7 +923,7 @@ namespace qrcodegen { // Compute the remainder by performing polynomial division let result: Array = this.coefficients.map(_ => 0); for (const b of data) { - const factor: byte = b ^ (result.shift() as int); + const factor: byte = b ^ (result.shift() as byte); result.push(0); this.coefficients.forEach((coef, i) => result[i] ^= ReedSolomonGenerator.multiply(coef, factor)); From 8e54f6ffa656520ca85d4d58b7970a3e35bf0bed Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 2 Nov 2018 00:37:35 +0000 Subject: [PATCH 575/810] Added a working Maven pom.xml file, in order to release the library on The Central Repository. The POM content was made with tremendous help from these reference documentation: - https://maven.apache.org/pom.html - https://central.sonatype.org/pages/apache-maven.html --- java/pom.xml | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 java/pom.xml diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 0000000..9945163 --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,94 @@ + + + 4.0.0 + + io.nayuki + qrcodegen + 1.4.0 + jar + + UTF-8 + 1.8 + 1.8 + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + + + QR Code generator library + High quality QR Code generator library + https://www.nayuki.io/page/qr-code-generator-library + 2016 + + + The MIT License + https://opensource.org/licenses/MIT + repo + + + + + Project Nayuki + me@nayuki.io + https://www.nayuki.io/ + + + + + scm:git:git://github.com/nayuki/QR-Code-generator.git + scm:git:ssh://github.com:nayuki/QR-Code-generator.git + https://github.com/nayuki/QR-Code-generator/tree/master/java + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + From 86149fa1b2b1e3e78ce723ab41aa82bd5a1e885f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 2 Nov 2018 00:40:17 +0000 Subject: [PATCH 576/810] Updated Python and Rust package version number. --- python/setup.py | 2 +- rust/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/setup.py b/python/setup.py index c67d31c..f4a6db1 100644 --- a/python/setup.py +++ b/python/setup.py @@ -27,7 +27,7 @@ import setuptools setuptools.setup( name = "qrcodegen", description = "High quality QR Code generator library for Python 2 and 3", - version = "1.3.0", + version = "1.4.0", platforms = "OS Independent", license = "MIT License", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c01d154..d98c4bb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qrcodegen" -version = "1.3.1" +version = "1.4.0" authors = ["Project Nayuki"] description = "High-quality QR Code generator library" homepage = "https://www.nayuki.io/page/qr-code-generator-library" From 76de28378e84b27e016d1cf6ccaeffd0e41bb311 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 4 Nov 2018 18:56:47 +0000 Subject: [PATCH 577/810] Added a const to C code for safety. --- c/qrcodegen.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 0772f65..18408eb 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -73,7 +73,7 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); static long getPenaltyScore(const uint8_t qrcode[]); static void addRunToHistory(unsigned char run, unsigned char history[7]); -static bool hasFinderLikePattern(unsigned char runHistory[7]); +static bool hasFinderLikePattern(const unsigned char runHistory[7]); testable bool getModule(const uint8_t qrcode[], int x, int y); testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); @@ -723,7 +723,7 @@ static void addRunToHistory(unsigned char run, unsigned char history[7]) { // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). // Must only be called immediately after a run of white modules has ended. -static bool hasFinderLikePattern(unsigned char runHistory[7]) { +static bool hasFinderLikePattern(const unsigned char runHistory[7]) { unsigned char n = runHistory[1]; // The maximum QR Code size is 177, hence the run length n <= 177. // Arithmetic is promoted to int, so n*4 will not overflow. From 2359d6824340ae2b4d4b2735c70f62af90acac63 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 4 Nov 2018 19:16:28 +0000 Subject: [PATCH 578/810] Tweaked C++ code to inline handleConstructorMasking() because the mask field is private instead of public const. --- cpp/QrCode.cpp | 46 ++++++++++++++++++++++------------------------ cpp/QrCode.hpp | 6 ------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index de23a7f..1b82517 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -139,11 +139,31 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) modules = vector >(size, vector(size)); // Initially all white isFunction = vector >(size, vector(size)); - // Compute ECC, draw modules, do masking + // Compute ECC, draw modules drawFunctionPatterns(); const vector allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this->mask = handleConstructorMasking(mask); + + // Do masking + if (mask == -1) { // Automatically choose best mask + long minPenalty = LONG_MAX; + for (int i = 0; i < 8; i++) { + drawFormatBits(i); + applyMask(i); + long penalty = getPenaltyScore(); + if (penalty < minPenalty) { + mask = i; + minPenalty = penalty; + } + applyMask(i); // Undoes the mask due to XOR + } + } + if (mask < 0 || mask > 7) + throw std::logic_error("Assertion error"); + this->mask = mask; + drawFormatBits(mask); // Overwrite old format bits + applyMask(mask); // Apply the final choice of mask + isFunction.clear(); isFunction.shrink_to_fit(); } @@ -403,28 +423,6 @@ void QrCode::applyMask(int mask) { } -int QrCode::handleConstructorMasking(int mask) { - if (mask == -1) { // Automatically choose best mask - long minPenalty = LONG_MAX; - for (int i = 0; i < 8; i++) { - drawFormatBits(i); - applyMask(i); - long penalty = getPenaltyScore(); - if (penalty < minPenalty) { - mask = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } - } - if (mask < 0 || mask > 7) - throw std::logic_error("Assertion error"); - drawFormatBits(mask); // Overwrite old format bits - applyMask(mask); // Apply the final choice of mask - return mask; // The caller shall assign this value to the final-declared field -} - - long QrCode::getPenaltyScore() const { long result = 0; diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 2f69843..8e4970e 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -247,12 +247,6 @@ class QrCode final { private: void applyMask(int mask); - // A messy helper function for the constructors. 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 mask); - - // 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: long getPenaltyScore() const; From 22319bf90fc5747ef06e66056476d70377b0aae0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 4 Nov 2018 19:25:45 +0000 Subject: [PATCH 579/810] Tweaked Rust code to inline handle_constructor_masking(). --- rust/src/lib.rs | 51 ++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1b8ff44..0e177ee 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -280,7 +280,7 @@ impl QrCode { /// /// This is a low-level API that most users should not use directly. /// A mid-level API is the `encode_segments()` function. - pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mask: Option) -> Self { + pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mut mask: Option) -> Self { // Initialize fields let size: usize = (ver.value() as usize) * 4 + 17; let mut result = Self { @@ -292,11 +292,31 @@ impl QrCode { isfunction: vec![false; size * size], }; - // Compute ECC, draw modules, do masking + // Compute ECC, draw modules result.draw_function_patterns(); let allcodewords: Vec = result.add_ecc_and_interleave(datacodewords); result.draw_codewords(&allcodewords); - result.handle_constructor_masking(mask); + + // Do masking + if mask.is_none() { // Automatically choose best mask + let mut minpenalty: i32 = std::i32::MAX; + for i in 0u8 .. 8 { + let newmask = Mask::new(i); + result.draw_format_bits(newmask); + result.apply_mask(newmask); + let penalty: i32 = result.get_penalty_score(); + if penalty < minpenalty { + mask = Some(newmask); + minpenalty = penalty; + } + result.apply_mask(newmask); // Undoes the mask due to XOR + } + } + let mask: Mask = mask.unwrap(); + result.mask = mask; + result.draw_format_bits(mask); // Overwrite old format bits + result.apply_mask(mask); // Apply the final choice of mask + result.isfunction.clear(); result.isfunction.shrink_to_fit(); result @@ -614,31 +634,6 @@ impl QrCode { } - // A messy helper function for the constructors. 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. - fn handle_constructor_masking(&mut self, mut mask: Option) { - if mask.is_none() { // Automatically choose best mask - let mut minpenalty: i32 = std::i32::MAX; - for i in 0u8 .. 8 { - let newmask = Mask::new(i); - self.draw_format_bits(newmask); - self.apply_mask(newmask); - let penalty: i32 = self.get_penalty_score(); - if penalty < minpenalty { - mask = Some(newmask); - minpenalty = penalty; - } - self.apply_mask(newmask); // Undoes the mask due to XOR - } - } - let msk: Mask = mask.unwrap(); - self.draw_format_bits(msk); // Overwrite old format bits - self.apply_mask(msk); // Apply the final choice of mask - self.mask = msk; - } - - // 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. fn get_penalty_score(&self) -> i32 { From fd083f70e8b9de21e137d44801ee0d9d2d3b7e83 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 4 Nov 2018 19:26:33 +0000 Subject: [PATCH 580/810] Reordered calls to applyMask()/drawFormatBits() for conceptual clarity, without changing output (because masks don't affect format bits), in all language versions. --- c/qrcodegen.c | 4 ++-- cpp/QrCode.cpp | 4 ++-- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- javascript/qrcodegen.js | 4 ++-- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 4 ++-- typescript/qrcodegen.ts | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 18408eb..67c1917 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -264,8 +264,8 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz long minPenalty = LONG_MAX; for (int i = 0; i < 8; i++) { enum qrcodegen_Mask msk = (enum qrcodegen_Mask)i; - drawFormatBits(ecl, msk, qrcode); applyMask(tempBuffer, qrcode, msk); + drawFormatBits(ecl, msk, qrcode); long penalty = getPenaltyScore(qrcode); if (penalty < minPenalty) { mask = msk; @@ -275,8 +275,8 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz } } assert(0 <= (int)mask && (int)mask <= 7); - drawFormatBits(ecl, mask, qrcode); applyMask(tempBuffer, qrcode, mask); + drawFormatBits(ecl, mask, qrcode); return true; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 1b82517..5232ac6 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -148,8 +148,8 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) if (mask == -1) { // Automatically choose best mask long minPenalty = LONG_MAX; for (int i = 0; i < 8; i++) { - drawFormatBits(i); applyMask(i); + drawFormatBits(i); long penalty = getPenaltyScore(); if (penalty < minPenalty) { mask = i; @@ -161,8 +161,8 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) if (mask < 0 || mask > 7) throw std::logic_error("Assertion error"); this->mask = mask; - drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask + drawFormatBits(mask); // Overwrite old format bits isFunction.clear(); isFunction.shrink_to_fit(); diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ce28b10..68f7cc4 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -573,8 +573,8 @@ public final class QrCode { if (mask == -1) { // Automatically choose best mask int minPenalty = Integer.MAX_VALUE; for (int i = 0; i < 8; i++) { - drawFormatBits(i); applyMask(i); + drawFormatBits(i); int penalty = getPenaltyScore(); if (penalty < minPenalty) { mask = i; @@ -584,8 +584,8 @@ public final class QrCode { } } assert 0 <= mask && mask <= 7; - drawFormatBits(mask); // Overwrite old format bits 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 } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 3fa1a20..d03ea89 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -113,8 +113,8 @@ var qrcodegen = new function() { if (mask == -1) { // Automatically choose best mask var minPenalty = Infinity; for (var i = 0; i < 8; i++) { - drawFormatBits(i); applyMask(i); + drawFormatBits(i); var penalty = getPenaltyScore(); if (penalty < minPenalty) { mask = i; @@ -125,8 +125,8 @@ var qrcodegen = new function() { } if (mask < 0 || mask > 7) throw "Assertion error"; - drawFormatBits(mask); // Overwrite old format bits applyMask(mask); // Apply the final choice of mask + drawFormatBits(mask); // Overwrite old format bits isFunction = null; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 8d82837..e27615b 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -209,16 +209,16 @@ class QrCode(object): if mask == -1: # Automatically choose best mask minpenalty = 1 << 32 for i in range(8): - self._draw_format_bits(i) self._apply_mask(i) + self._draw_format_bits(i) penalty = self._get_penalty_score() if penalty < minpenalty: mask = i minpenalty = penalty self._apply_mask(i) # Undoes the mask due to XOR assert 0 <= mask <= 7 - self._draw_format_bits(mask) # Overwrite old format bits self._apply_mask(mask) # Apply the final choice of mask + self._draw_format_bits(mask) # Overwrite old format bits # 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), diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0e177ee..4fedaba 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -302,8 +302,8 @@ impl QrCode { let mut minpenalty: i32 = std::i32::MAX; for i in 0u8 .. 8 { let newmask = Mask::new(i); - result.draw_format_bits(newmask); result.apply_mask(newmask); + result.draw_format_bits(newmask); let penalty: i32 = result.get_penalty_score(); if penalty < minpenalty { mask = Some(newmask); @@ -314,8 +314,8 @@ impl QrCode { } let mask: Mask = mask.unwrap(); result.mask = mask; - result.draw_format_bits(mask); // Overwrite old format bits result.apply_mask(mask); // Apply the final choice of mask + result.draw_format_bits(mask); // Overwrite old format bits result.isfunction.clear(); result.isfunction.shrink_to_fit(); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index d0d7d61..2d6677e 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -209,8 +209,8 @@ namespace qrcodegen { if (mask == -1) { // Automatically choose best mask let minPenalty: int = 1000000000; for (let i = 0; i < 8; i++) { - this.drawFormatBits(i); this.applyMask(i); + this.drawFormatBits(i); const penalty: int = this.getPenaltyScore(); if (penalty < minPenalty) { mask = i; @@ -222,8 +222,8 @@ namespace qrcodegen { if (mask < 0 || mask > 7) throw "Assertion error"; this.mask = mask; - this.drawFormatBits(mask); // Overwrite old format bits this.applyMask(mask); // Apply the final choice of mask + this.drawFormatBits(mask); // Overwrite old format bits this.isFunction = []; } From 04e1942b16a32fc8f38513665a0c98c50e17ace7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 04:20:09 +0000 Subject: [PATCH 581/810] Tweaked a Javadoc comment. --- .../src/main/java/io/nayuki/qrcodegen/DataTooLongException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java b/java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java index 17f8d5e..0cbefc7 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java +++ b/java/src/main/java/io/nayuki/qrcodegen/DataTooLongException.java @@ -43,7 +43,7 @@ package io.nayuki.qrcodegen; * @see QrCode#encodeBinary(byte[], QrCode.Ecc) * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc) * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc, int, int, int, boolean) - * @see QrSegmentAdvanced#makeSegmentsOptimally(String, io.nayuki.qrcodegen.QrCode.Ecc, int, int) + * @see QrSegmentAdvanced#makeSegmentsOptimally(String, QrCode.Ecc, int, int) */ public class DataTooLongException extends IllegalArgumentException { From 2f4b0e8fd8749341c064bc73ab7d2f6fd4bf07a7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 04:37:27 +0000 Subject: [PATCH 582/810] Tweaked drawFormatBits() and drawDummyFormatBits() to use end-exclusive range in second copy for clarity. --- src/io/nayuki/fastqrcodegen/QrCode.java | 2 +- src/io/nayuki/fastqrcodegen/QrTemplate.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index dcd958f..091bec5 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -356,7 +356,7 @@ public final class QrCode { setModule(14 - i, 8, getBit(bits, i)); // Draw second copy - for (int i = 0; i <= 7; i++) + for (int i = 0; i < 8; i++) setModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setModule(8, size - 15 + i, getBit(bits, i)); diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 931d9b0..3ccfb40 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -149,7 +149,7 @@ final class QrTemplate { darkenFunctionModule(14 - i, 8, 0); // Draw second copy - for (int i = 0; i <= 7; i++) + for (int i = 0; i < 8; i++) darkenFunctionModule(size - 1 - i, 8, 0); for (int i = 8; i < 15; i++) darkenFunctionModule(8, size - 15 + i, 0); From df729db98bb9a4a7dbf1b18bb30891d634e820a4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 04:37:57 +0000 Subject: [PATCH 583/810] Added "data too long" exception class, changed code to make use of it, updated Javadoc comments. --- .../fastqrcodegen/DataTooLongException.java | 57 +++++++++++++++++++ src/io/nayuki/fastqrcodegen/QrCode.java | 19 ++++--- .../fastqrcodegen/QrCodeGeneratorWorker.java | 4 +- .../fastqrcodegen/QrSegmentAdvanced.java | 15 +++-- 4 files changed, 80 insertions(+), 15 deletions(-) create mode 100644 src/io/nayuki/fastqrcodegen/DataTooLongException.java diff --git a/src/io/nayuki/fastqrcodegen/DataTooLongException.java b/src/io/nayuki/fastqrcodegen/DataTooLongException.java new file mode 100644 index 0000000..c6e4444 --- /dev/null +++ b/src/io/nayuki/fastqrcodegen/DataTooLongException.java @@ -0,0 +1,57 @@ +/* + * Fast QR Code generator library + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/fast-qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.fastqrcodegen; + + +/** + * Thrown when the supplied data does not fit any QR Code version. Ways to handle this exception include: + *
      + *
    • Decrease the error correction level if it was greater than {@code Ecc.LOW}.

    • + *
    • If the advanced {@code encodeSegments()} function with 6 arguments or the + * {@code makeSegmentsOptimally()} function was called, then increase the maxVersion argument + * if it was less than {@link QrCode#MAX_VERSION}. (This advice does not apply to the other + * factory functions because they search all versions up to {@code QrCode.MAX_VERSION}.)

    • + *
    • Split the text data into better or optimal segments in order to reduce the number of + * bits required. (See {@link QrSegmentAdvanced#makeSegmentsOptimally(String,QrCode.Ecc,int,int) + * QrSegmentAdvanced.makeSegmentsOptimally()}.)

    • + *
    • Change the text or binary data to be shorter.

    • + *
    • Change the text to fit the character set of a particular segment mode (e.g. alphanumeric).

    • + *
    • Propagate the error upward to the caller/user.

    • + *
    + * @see QrCode#encodeText(String, QrCode.Ecc) + * @see QrCode#encodeBinary(byte[], QrCode.Ecc) + * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc) + * @see QrCode#encodeSegments(java.util.List, QrCode.Ecc, int, int, int, boolean) + * @see QrSegmentAdvanced#makeSegmentsOptimally(String, QrCode.Ecc, int, int) + */ +public class DataTooLongException extends IllegalArgumentException { + + public DataTooLongException() {} + + + public DataTooLongException(String msg) { + super(msg); + } + +} diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 091bec5..c24aae4 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -64,7 +64,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the text * @throws NullPointerException if the text or error correction level is {@code null} - * @throws IllegalArgumentException if the text fails to fit in the + * @throws DataTooLongException if the text fails to fit in the * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeText(String text, Ecc ecl) { @@ -84,7 +84,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the data * @throws NullPointerException if the data or error correction level is {@code null} - * @throws IllegalArgumentException if the data fails to fit in the + * @throws DataTooLongException if the data fails to fit in the * largest version QR Code at the ECL, which means it is too long */ public static QrCode encodeBinary(byte[] data, Ecc ecl) { @@ -109,7 +109,7 @@ public final class QrCode { * @param ecl the error correction level to use (not {@code null}) (boostable) * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} - * @throws IllegalArgumentException if the segments fail to fit in the + * @throws DataTooLongException if the segments fail to fit in the * largest version QR Code at the ECL, which means they are too long */ public static QrCode encodeSegments(List segs, Ecc ecl) { @@ -137,8 +137,9 @@ public final class QrCode { * @return a QR Code (not {@code null}) representing the segments * @throws NullPointerException if the list of segments, any segment, or the error correction level is {@code null} * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * or −1 ≤ mask ≤ 7 is violated; or if the segments fail to - * fit in the maxVersion QR Code at the ECL, which means they are too long + * or −1 ≤ mask ≤ 7 is violated + * @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); @@ -153,8 +154,12 @@ public final class QrCode { dataUsedBits = QrSegment.getTotalBits(segs, 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 - throw new IllegalArgumentException("Data too long"); + if (version >= maxVersion) { // All versions in the range could not fit the given data + String msg = "Segment too long"; + if (dataUsedBits != -1) + msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(msg); + } } assert dataUsedBits != -1; diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java index eb9dd36..fc0c9d1 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java @@ -91,9 +91,7 @@ public final class QrCodeGeneratorWorker { System.out.println(qr.getModule(x, y) ? 1 : 0); } - } catch (IllegalArgumentException e) { - if (!e.getMessage().equals("Data too long")) - throw e; + } catch (DataTooLongException e) { System.out.println(-1); } System.out.flush(); diff --git a/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java b/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java index 6cbdb7b..3cd5ae5 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java +++ b/src/io/nayuki/fastqrcodegen/QrSegmentAdvanced.java @@ -57,8 +57,8 @@ public final class QrSegmentAdvanced { * @return a new mutable list (not {@code null}) of segments (not {@code null}) * containing the text, minimizing the bit length with respect to the constraints * @throws NullPointerException if the text or error correction level is {@code null} - * @throws IllegalArgumentException if 1 ≤ minVersion ≤ maxVersion ≤ 40 - * is violated, or if the data is too long to fit in a QR Code at maxVersion at ECL + * @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) { // Check arguments @@ -70,7 +70,7 @@ public final class QrSegmentAdvanced { // Iterate through version numbers, and make tentative segments List segs = null; int[] codePoints = toCodePoints(text); - for (int version = minVersion; version <= maxVersion; version++) { + for (int version = minVersion; ; version++) { if (version == minVersion || version == 10 || version == 27) segs = makeSegmentsOptimally(codePoints, version); assert segs != null; @@ -79,9 +79,14 @@ public final class QrSegmentAdvanced { int dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; int dataUsedBits = QrSegment.getTotalBits(segs, version); if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) - return segs; + return segs; // This version number is found to be suitable + if (version >= maxVersion) { // All versions in the range could not fit the given text + String msg = "Segment too long"; + if (dataUsedBits != -1) + msg = String.format("Data length = %d bits, Max capacity = %d bits", dataUsedBits, dataCapacityBits); + throw new DataTooLongException(msg); + } } - throw new IllegalArgumentException("Data too long"); } From 8551314425dac9130a99c016d91360c72de63d7b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 04:38:11 +0000 Subject: [PATCH 584/810] Added/updated/deleted some comments. --- src/io/nayuki/fastqrcodegen/QrCode.java | 4 ++++ src/io/nayuki/fastqrcodegen/QrSegment.java | 6 +++--- src/io/nayuki/fastqrcodegen/QrTemplate.java | 5 ++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index c24aae4..32b2acf 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -197,6 +197,8 @@ public final class QrCode { /*---- 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; @@ -213,6 +215,8 @@ public final class QrCode { * −1), the resulting object still has a mask value between 0 and 7. */ public final int mask; + // Private grid of modules/pixels: + // The modules of this QR Code. Immutable after constructor finishes. Accessed through getModule(). private final int[] modules; diff --git a/src/io/nayuki/fastqrcodegen/QrSegment.java b/src/io/nayuki/fastqrcodegen/QrSegment.java index 86cce95..7ac0234 100644 --- a/src/io/nayuki/fastqrcodegen/QrSegment.java +++ b/src/io/nayuki/fastqrcodegen/QrSegment.java @@ -40,7 +40,8 @@ import java.util.Objects; *

    This segment class imposes no length restrictions, but QR Codes have restrictions. * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. * Any segment longer than this is meaningless for the purpose of generating QR Codes. - * This class can represent kanji mode segments, but provides no help in encoding them.

    + * This class can represent kanji mode segments, but provides no help in encoding them + * - see {@link QrSegmentAdvanced} for full kanji support.

    */ public final class QrSegment { @@ -224,7 +225,7 @@ public final class QrSegment { * Always zero or positive. Not the same as the data's bit length. */ public final int numChars; - // The data bits of this segment. Not null. Accessed through getData(). + // The data bits of this segment. Not null. final int[] data; // Requires 0 <= bitLength <= data.length * 32. @@ -273,7 +274,6 @@ public final class QrSegment { } - /*---- Constants ----*/ static final int[] ALPHANUMERIC_MAP; diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 3ccfb40..f933bd7 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -90,7 +90,8 @@ final class QrTemplate { final int[][] masks; final int[] dataOutputBitIndexes; - private int[] isFunction; // Discarded when constructor finishes + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + private int[] isFunction; private QrTemplate(int ver) { @@ -275,8 +276,6 @@ final class QrTemplate { } - /*---- Private static 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. From b2671166cec80ba8deda8077e30e4fbfc43254af Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 05:09:58 +0000 Subject: [PATCH 585/810] Renamed many variables in getPenaltyScore(), in preparation for future changes. --- src/io/nayuki/fastqrcodegen/QrCode.java | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 32b2acf..b3bac2a 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -484,36 +484,36 @@ public final class QrCode { // Iterate over adjacent pairs of rows for (int index = 0, downIndex = size, end = size * size; index < end; ) { - int bits = 0; - int downBits = 0; - int runColor = 0; - int runLen = 0; + int curRow = 0; + int nextRow = 0; + int color = 0; + int runX = 0; for (int x = 0; x < size; x++, index++, downIndex++) { // Adjacent modules having same color - int bit = getBit(modules[index >>> 5], index); - if (bit != runColor) { - runColor = bit; - runLen = 1; + int c = getBit(modules[index >>> 5], index); + if (c != color) { + color = c; + runX = 1; } else { - runLen++; - if (runLen == 5) + runX++; + if (runX == 5) result += PENALTY_N1; - else if (runLen > 5) + else if (runX > 5) result++; } - black += bit; - bits = ((bits & 0b1111111111) << 1) | bit; + black += c; + curRow = ((curRow & 0b1111111111) << 1) | c; if (downIndex < end) { - downBits = ((downBits & 1) << 1) | getBit(modules[downIndex >>> 5], downIndex); + nextRow = ((nextRow & 1) << 1) | getBit(modules[downIndex >>> 5], downIndex); // 2*2 blocks of modules having same color - if (x >= 1 && (downBits == 0 || downBits == 3) && downBits == (bits & 3)) + if (x >= 1 && (nextRow == 0 || nextRow == 3) && nextRow == (curRow & 3)) result += PENALTY_N2; } // Finder-like pattern - if (x >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) + if (x >= 10 && (curRow == 0b00001011101 || curRow == 0b10111010000)) result += PENALTY_N3; } } @@ -521,25 +521,25 @@ public final class QrCode { // Iterate over single columns for (int x = 0; x < size; x++) { int bits = 0; - int runColor = 0; - int runLen = 0; + int color = 0; + int runY = 0; for (int y = 0, index = x; y < size; y++, index += size) { // Adjacent modules having same color - int bit = getBit(modules[index >>> 5], index); - if (bit != runColor) { - runColor = bit; - runLen = 1; + int c = getBit(modules[index >>> 5], index); + if (c != color) { + color = c; + runY = 1; } else { - runLen++; - if (runLen == 5) + runY++; + if (runY == 5) result += PENALTY_N1; - else if (runLen > 5) + else if (runY > 5) result++; } // Finder-like pattern - bits = ((bits & 0b1111111111) << 1) | bit; + bits = ((bits & 0b1111111111) << 1) | c; if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) result += PENALTY_N3; } From f4f971f3847c6f9b9d498cd22e66ebbe850078d6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 05:10:33 +0000 Subject: [PATCH 586/810] Inverted some if-else statements in getPenaltyScore(). --- src/io/nayuki/fastqrcodegen/QrCode.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index b3bac2a..ccf474d 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -492,15 +492,15 @@ public final class QrCode { // Adjacent modules having same color int c = getBit(modules[index >>> 5], index); - if (c != color) { - color = c; - runX = 1; - } else { + if (c == color) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; + } else { + color = c; + runX = 1; } black += c; @@ -527,15 +527,15 @@ public final class QrCode { // Adjacent modules having same color int c = getBit(modules[index >>> 5], index); - if (c != color) { - color = c; - runY = 1; - } else { + if (c == color) { runY++; if (runY == 5) result += PENALTY_N1; else if (runY > 5) result++; + } else { + color = c; + runY = 1; } // Finder-like pattern From 655bb970ce1b2d658f3a7603532c8b78b0c0ccd2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 05:16:26 +0000 Subject: [PATCH 587/810] Completely rewrote the algorithm for detecting finder-like patterns, making it more accurate and compliant with the QR Code specification. --- src/io/nayuki/fastqrcodegen/QrCode.java | 52 ++++++++++++++++++------- 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index ccf474d..fe337ad 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -481,16 +481,16 @@ public final class QrCode { private int getPenaltyScore() { int result = 0; int black = 0; + int[] runHistory = new int[7]; // Iterate over adjacent pairs of rows for (int index = 0, downIndex = size, end = size * size; index < end; ) { int curRow = 0; int nextRow = 0; + Arrays.fill(runHistory, 0); int color = 0; int runX = 0; for (int x = 0; x < size; x++, index++, downIndex++) { - - // Adjacent modules having same color int c = getBit(modules[index >>> 5], index); if (c == color) { runX++; @@ -499,10 +499,12 @@ public final class QrCode { else if (runX > 5) result++; } else { + addRunToHistory(runX, runHistory); + if (color == 0 && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = c; runX = 1; } - black += c; curRow = ((curRow & 0b1111111111) << 1) | c; if (downIndex < end) { @@ -511,21 +513,20 @@ public final class QrCode { if (x >= 1 && (nextRow == 0 || nextRow == 3) && nextRow == (curRow & 3)) result += PENALTY_N2; } - - // Finder-like pattern - if (x >= 10 && (curRow == 0b00001011101 || curRow == 0b10111010000)) - result += PENALTY_N3; } + addRunToHistory(runX, runHistory); + if (color == 1) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } // Iterate over single columns for (int x = 0; x < size; x++) { - int bits = 0; + Arrays.fill(runHistory, 0); int color = 0; int runY = 0; for (int y = 0, index = x; y < size; y++, index += size) { - - // Adjacent modules having same color int c = getBit(modules[index >>> 5], index); if (c == color) { runY++; @@ -534,15 +535,18 @@ public final class QrCode { else if (runY > 5) result++; } else { + addRunToHistory(runY, runHistory); + if (color == 0 && hasFinderLikePattern(runHistory)) + result += PENALTY_N3; color = c; runY = 1; } - - // Finder-like pattern - bits = ((bits & 0b1111111111) << 1) | c; - if (y >= 10 && (bits == 0b00001011101 || bits == 0b10111010000)) - result += PENALTY_N3; } + addRunToHistory(runY, runHistory); + if (color == 1) + addRunToHistory(0, runHistory); // Dummy run of white + if (hasFinderLikePattern(runHistory)) + result += PENALTY_N3; } // Balance of black and white modules @@ -567,6 +571,24 @@ public final class QrCode { } + // Inserts the given value to the front of the given array, which shifts over the + // existing values and deletes the last value. A helper function for getPenaltyScore(). + private static void addRunToHistory(int run, int[] history) { + System.arraycopy(history, 0, history, 1, history.length - 1); + history[0] = run; + } + + + // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and + // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). + // Must only be called immediately after a run of white modules has ended. + private static boolean hasFinderLikePattern(int[] runHistory) { + int n = runHistory[1]; + return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n + && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + } + + // Returns 0 or 1 based on the i'th bit of x. static int getBit(int x, int i) { return (x >>> i) & 1; From 47541e1b29df1aad8010d81af144fc1bc44fae72 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 05:17:45 +0000 Subject: [PATCH 588/810] Simplified some code in getPenaltyScore(). --- src/io/nayuki/fastqrcodegen/QrCode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index fe337ad..2fd362d 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -506,11 +506,11 @@ public final class QrCode { runX = 1; } black += c; - curRow = ((curRow & 0b1111111111) << 1) | c; if (downIndex < end) { - nextRow = ((nextRow & 1) << 1) | getBit(modules[downIndex >>> 5], downIndex); + curRow = ((curRow << 1) | c) & 3; + nextRow = ((nextRow << 1) | getBit(modules[downIndex >>> 5], downIndex)) & 3; // 2*2 blocks of modules having same color - if (x >= 1 && (nextRow == 0 || nextRow == 3) && nextRow == (curRow & 3)) + if (x >= 1 && (curRow == 0 || curRow == 3) && curRow == nextRow) result += PENALTY_N2; } } From df55fd6504a367f0648e573424b3d844e838ed86 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 5 Nov 2018 05:18:03 +0000 Subject: [PATCH 589/810] Rearranged variables, updated comment. --- src/io/nayuki/fastqrcodegen/QrCode.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 2fd362d..7825632 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -485,11 +485,11 @@ public final class QrCode { // Iterate over adjacent pairs of rows for (int index = 0, downIndex = size, end = size * size; index < end; ) { - int curRow = 0; - int nextRow = 0; Arrays.fill(runHistory, 0); int color = 0; int runX = 0; + int curRow = 0; + int nextRow = 0; for (int x = 0; x < size; x++, index++, downIndex++) { int c = getBit(modules[index >>> 5], index); if (c == color) { @@ -589,7 +589,7 @@ public final class QrCode { } - // Returns 0 or 1 based on the i'th bit of x. + // Returns 0 or 1 based on the (i mod 32)'th bit of x. static int getBit(int x, int i) { return (x >>> i) & 1; } From 3a28ba7dfd6524e5fdf9b664a497ca61808fd727 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 4 Dec 2018 23:46:38 +0000 Subject: [PATCH 590/810] Added missing null check in C code after malloc. --- c/qrcodegen-worker.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c index 2fb0930..b57d30e 100644 --- a/c/qrcodegen-worker.c +++ b/c/qrcodegen-worker.c @@ -78,6 +78,10 @@ int main(void) { bool ok; if (isAscii) { char *text = malloc((length + 1) * sizeof(char)); + if (text == NULL) { + perror("malloc"); + return EXIT_FAILURE; + } for (int i = 0; i < length; i++) text[i] = (char)data[i]; text[length] = '\0'; From ba1e9bcd25510ce9f9fcd77068c1b68f21df8bf3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 3 Jan 2019 23:29:41 +0000 Subject: [PATCH 591/810] Readme: Updated copyright year. --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index e5f6507..2d367d3 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -192,7 +192,7 @@ Rust language: License ------- -Copyright © 2018 Project Nayuki. (MIT License) +Copyright © 2019 Project Nayuki. (MIT License) [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of From ec729bf2695300691f4b3a96ba5eaba083ebec00 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 3 Jan 2019 23:31:07 +0000 Subject: [PATCH 592/810] Moved an #include in C++ code for correctness. --- cpp/QrCode.cpp | 1 - cpp/QrCode.hpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 5232ac6..2679805 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #include #include "BitBuffer.hpp" #include "QrCode.hpp" diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 8e4970e..d496e2a 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include "QrSegment.hpp" From a072562461b232011157d28363b5e7bf8b8ee073 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 14 Feb 2019 01:47:14 +0000 Subject: [PATCH 593/810] Simplified an integer comparison in C code. --- c/qrcodegen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 67c1917..e229e18 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -853,7 +853,7 @@ testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars) { return -1; } assert(result >= 0); - if (result > (unsigned int)INT16_MAX) + if (result > INT16_MAX) return -1; return (int)result; } From 40d24f38aa0a8180b271b6c88be8633f842ed9d4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 14 Feb 2019 01:53:57 +0000 Subject: [PATCH 594/810] Fixed argument type of qrcodegen_encodeSegmentsAdvanced() mask in C code. --- c/qrcodegen.c | 4 ++-- c/qrcodegen.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index e229e18..3818499 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -195,13 +195,13 @@ testable void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[] bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, uint8_t tempBuffer[], uint8_t qrcode[]) { return qrcodegen_encodeSegmentsAdvanced(segs, len, ecl, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, -1, true, tempBuffer, qrcode); + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true, tempBuffer, qrcode); } // Public function - see documentation comment in header file. bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]) { assert(segs != NULL || len == 0); assert(qrcodegen_VERSION_MIN <= minVersion && minVersion <= maxVersion && maxVersion <= qrcodegen_VERSION_MAX); assert(0 <= (int)ecl && (int)ecl <= 3 && -1 <= (int)mask && (int)mask <= 7); diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 30f9db2..a99b94d 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -216,8 +216,8 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, * The smallest possible QR Code version within the given range is automatically * chosen for the output. Iff boostEcl is true, then the ECC level of the result * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). + * version. The mask is either between qrcodegen_Mask_0 to 7 to force that mask, or + * qrcodegen_Mask_AUTO to automatically choose an appropriate mask (which may be slow). * This function allows the user to create a custom sequence of segments that switches * between modes (such as alphanumeric and byte) to encode text in less space. * This is a low-level API; the high-level API is qrcodegen_encodeText() and qrcodegen_encodeBinary(). @@ -226,7 +226,7 @@ bool qrcodegen_encodeSegments(const struct qrcodegen_Segment segs[], size_t len, * But the qrcode array must not overlap tempBuffer or any segment's data buffer. */ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], size_t len, enum qrcodegen_Ecc ecl, - int minVersion, int maxVersion, int mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); + int minVersion, int maxVersion, enum qrcodegen_Mask mask, bool boostEcl, uint8_t tempBuffer[], uint8_t qrcode[]); /* From b2ff7ce7653fd88837934d6c9dadacb7e1a592f9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 23 Jun 2019 03:40:03 +0000 Subject: [PATCH 595/810] Replaced the finder-like pattern detection algorithm with a more sophisticated and accurate one, including documentation comments, only for the Java version of the library. This fixes nearly all the false negatives/positives in the previous implementation. --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 157 ++++++++++++++---- 1 file changed, 121 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 68f7cc4..8ce096f 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -596,10 +596,11 @@ public final class QrCode { int result = 0; // Adjacent modules in row having same color, and finder-like patterns + FinderPatternDetector det = new FinderPatternDetector(); for (int y = 0; y < size; y++) { - int[] runHistory = new int[7]; boolean color = false; int runX = 0; + det.reset(); for (int x = 0; x < size; x++) { if (modules[y][x] == color) { runX++; @@ -608,22 +609,16 @@ public final class QrCode { else if (runX > 5) result++; } else { - addRunToHistory(runX, runHistory); - if (!color && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; color = modules[y][x]; runX = 1; } + result += det.addModuleAndMatch(color) * PENALTY_N3; } - addRunToHistory(runX, runHistory); - if (color) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += det.terminateAndMatch() * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { - int[] runHistory = new int[7]; + det.reset(); boolean color = false; int runY = 0; for (int y = 0; y < size; y++) { @@ -634,18 +629,12 @@ public final class QrCode { else if (runY > 5) result++; } else { - addRunToHistory(runY, runHistory); - if (!color && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; color = modules[y][x]; runY = 1; } + result += det.addModuleAndMatch(color) * PENALTY_N3; } - addRunToHistory(runY, runHistory); - if (color) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += det.terminateAndMatch() * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -735,24 +724,6 @@ public final class QrCode { } - // Inserts the given value to the front of the given array, which shifts over the - // existing values and deletes the last value. A helper function for getPenaltyScore(). - private static void addRunToHistory(int run, int[] history) { - System.arraycopy(history, 0, history, 1, history.length - 1); - history[0] = run; - } - - - // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). - // Must only be called immediately after a run of white modules has ended. - private static boolean hasFinderLikePattern(int[] runHistory) { - int n = runHistory[1]; - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; - } - - // 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; @@ -911,4 +882,118 @@ public final class QrCode { } + + + /*---- Private helper class ----*/ + + /** + * Detects finder-like patterns in a line of modules, for the purpose of penalty score calculation. + * A finder-like pattern has alternating black and white modules with run length ratios of 1:1:3:1:1, + * such that the center run is black and this pattern is surrounded by at least a ratio + * of 4:1 white modules on one side and at least 1:1 white modules on the other side. + * The finite line of modules is conceptually padded with an infinite number of white modules on both sides. + * + * Here are graphic examples of the designed behavior, where '[' means start of line, + * ']' means end of line, '.' means white module, and '#' means black module: + * - [....#.###.#....] Two matches + * - [#.###.#] Two matches, because of infinite white border + * - [##..######..##] Two matches, with a scale of 2 + * - [#.###.#.#] One match, using the infinite white left border + * - [#.#.###.#.#] Zero matches, due to insufficient white modules surrounding the 1:1:3:1:1 pattern + * - [#.###.##] Zero matches, because the rightmost black bar is too long + * - [#.###.#.###.#] Two matches, with the matches overlapping and sharing modules + */ + private static final class FinderPatternDetector { + + /*-- Fields --*/ + + // Mutable running state + private boolean currentRunColor; // false = white, true = black + private int currentRunLength; // In modules, always positive + // runHistory[0] = length of most recently ended run, + // runHistory[1] = length of next older run of opposite color, etc. + // This array begins as all zeros. Zero is not a valid run length. + private int[] runHistory = new int[7]; + + + /*-- Methods --*/ + + /** + * Re-initializes this detector to the start of a row or column. + * This allows reuse of this object and its array, reducing memory allocation. + */ + public void reset() { + currentRunColor = false; + currentRunLength = QR_CODE_SIZE_MAX; // Add white border to initial run + Arrays.fill(runHistory, 0); + } + + + /** + * Tells this detector that the next module has the specified color, and returns + * the number of finder-like patterns detected due to processing the current module. + * The result is usually 0, but can be 1 or 2 only when transitioning from + * white to black (i.e. {@code currentRunColor == false && color == true}). + * @param color the color of the next module, where {@code true} denotes black and {@code false} is white + * @return either 0, 1, or 2 + */ + public int addModuleAndMatch(boolean color) { + if (color == currentRunColor) + currentRunLength++; + else { + addToHistory(currentRunLength); + currentRunColor = color; + currentRunLength = 1; + if (color) // Transitioning from white to black + return countCurrentMatches(); + } + return 0; + } + + + /** + * Tells this detector that the line of modules has ended, and + * returns the number of finder-like patterns detected at the end. + * After this, {@link #reset()} must be called before any other methods. + * @return either 0, 1, or 2 + */ + public int terminateAndMatch() { + if (currentRunColor) { // Terminate black run + addToHistory(currentRunLength); + currentRunLength = 0; + } + currentRunLength += QR_CODE_SIZE_MAX; // Add white border to final run + addToHistory(currentRunLength); + return countCurrentMatches(); + } + + + // Shifts the array back and puts the given value at the front. + private void addToHistory(int run) { + System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); + runHistory[0] = run; + } + + + // Can only be called immediately after a white run is added. + private int countCurrentMatches() { + int n = runHistory[1]; + assert n <= QR_CODE_SIZE_MAX * 3; + boolean core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; + if (core) { + return (runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); + } else + return 0; + } + + + /*-- Constant --*/ + + // This amount of padding is enough to guarantee at least 4 scaled + // white modules at any pattern scale that fits inside any QR Code. + private static final int QR_CODE_SIZE_MAX = QrCode.MAX_VERSION * 4 + 17; + + } + } From 1ca214499bc430e5bed5f35ac20562f0e8572ae9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Jul 2019 01:43:20 +0000 Subject: [PATCH 596/810] Renamed a local variable in all language versions (although the Java version has different logic). --- c/qrcodegen.c | 20 +++++++++---------- cpp/QrCode.cpp | 20 +++++++++---------- .../main/java/io/nayuki/qrcodegen/QrCode.java | 16 +++++++-------- javascript/qrcodegen.js | 20 +++++++++---------- python/qrcodegen.py | 20 +++++++++---------- rust/src/lib.rs | 20 +++++++++---------- typescript/qrcodegen.ts | 20 +++++++++---------- 7 files changed, 68 insertions(+), 68 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 3818499..17f8aaa 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -635,10 +635,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < qrsize; y++) { unsigned char runHistory[7] = {0}; - bool color = false; + bool runColor = false; unsigned char runX = 0; for (int x = 0; x < qrsize; x++) { - if (getModule(qrcode, x, y) == color) { + if (getModule(qrcode, x, y) == runColor) { runX++; if (runX == 5) result += PENALTY_N1; @@ -646,14 +646,14 @@ static long getPenaltyScore(const uint8_t qrcode[]) { result++; } else { addRunToHistory(runX, runHistory); - if (!color && hasFinderLikePattern(runHistory)) + if (!runColor && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = getModule(qrcode, x, y); + runColor = getModule(qrcode, x, y); runX = 1; } } addRunToHistory(runX, runHistory); - if (color) + if (runColor) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; @@ -661,10 +661,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < qrsize; x++) { unsigned char runHistory[7] = {0}; - bool color = false; + bool runColor = false; unsigned char runY = 0; for (int y = 0; y < qrsize; y++) { - if (getModule(qrcode, x, y) == color) { + if (getModule(qrcode, x, y) == runColor) { runY++; if (runY == 5) result += PENALTY_N1; @@ -672,14 +672,14 @@ static long getPenaltyScore(const uint8_t qrcode[]) { result++; } else { addRunToHistory(runY, runHistory); - if (!color && hasFinderLikePattern(runHistory)) + if (!runColor && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = getModule(qrcode, x, y); + runColor = getModule(qrcode, x, y); runY = 1; } } addRunToHistory(runY, runHistory); - if (color) + if (runColor) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 2679805..dba2cc5 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -428,10 +428,10 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < size; y++) { std::deque runHistory(7, 0); - bool color = false; + bool runColor = false; int runX = 0; for (int x = 0; x < size; x++) { - if (module(x, y) == color) { + if (module(x, y) == runColor) { runX++; if (runX == 5) result += PENALTY_N1; @@ -439,14 +439,14 @@ long QrCode::getPenaltyScore() const { result++; } else { addRunToHistory(runX, runHistory); - if (!color && hasFinderLikePattern(runHistory)) + if (!runColor && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = module(x, y); + runColor = module(x, y); runX = 1; } } addRunToHistory(runX, runHistory); - if (color) + if (runColor) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; @@ -454,10 +454,10 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { std::deque runHistory(7, 0); - bool color = false; + bool runColor = false; int runY = 0; for (int y = 0; y < size; y++) { - if (module(x, y) == color) { + if (module(x, y) == runColor) { runY++; if (runY == 5) result += PENALTY_N1; @@ -465,14 +465,14 @@ long QrCode::getPenaltyScore() const { result++; } else { addRunToHistory(runY, runHistory); - if (!color && hasFinderLikePattern(runHistory)) + if (!runColor && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = module(x, y); + runColor = module(x, y); runY = 1; } } addRunToHistory(runY, runHistory); - if (color) + if (runColor) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 8ce096f..07afd7d 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -598,41 +598,41 @@ public final class QrCode { // Adjacent modules in row having same color, and finder-like patterns FinderPatternDetector det = new FinderPatternDetector(); for (int y = 0; y < size; y++) { - boolean color = false; + boolean runClor = false; int runX = 0; det.reset(); for (int x = 0; x < size; x++) { - if (modules[y][x] == color) { + if (modules[y][x] == runClor) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; } else { - color = modules[y][x]; + runClor = modules[y][x]; runX = 1; } - result += det.addModuleAndMatch(color) * PENALTY_N3; + result += det.addModuleAndMatch(runClor) * PENALTY_N3; } result += det.terminateAndMatch() * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { det.reset(); - boolean color = false; + boolean runColor = false; int runY = 0; for (int y = 0; y < size; y++) { - if (modules[y][x] == color) { + if (modules[y][x] == runColor) { runY++; if (runY == 5) result += PENALTY_N1; else if (runY > 5) result++; } else { - color = modules[y][x]; + runColor = modules[y][x]; runY = 1; } - result += det.addModuleAndMatch(color) * PENALTY_N3; + result += det.addModuleAndMatch(runColor) * PENALTY_N3; } result += det.terminateAndMatch() * PENALTY_N3; } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index d03ea89..cf77be8 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -430,10 +430,10 @@ var qrcodegen = new function() { // Adjacent modules in row having same color, and finder-like patterns for (var y = 0; y < size; y++) { var runHistory = [0,0,0,0,0,0,0]; - var color = false; + var runColor = false; var runX = 0; for (var x = 0; x < size; x++) { - if (modules[y][x] == color) { + if (modules[y][x] == runColor) { runX++; if (runX == 5) result += QrCode.PENALTY_N1; @@ -441,14 +441,14 @@ var qrcodegen = new function() { result++; } else { QrCode.addRunToHistory(runX, runHistory); - if (!color && QrCode.hasFinderLikePattern(runHistory)) + if (!runColor && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; - color = modules[y][x]; + runColor = modules[y][x]; runX = 1; } } QrCode.addRunToHistory(runX, runHistory); - if (color) + if (runColor) QrCode.addRunToHistory(0, runHistory); // Dummy run of white if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; @@ -456,10 +456,10 @@ var qrcodegen = new function() { // Adjacent modules in column having same color, and finder-like patterns for (var x = 0; x < size; x++) { var runHistory = [0,0,0,0,0,0,0]; - var color = false; + var runColor = false; var runY = 0; for (var y = 0; y < size; y++) { - if (modules[y][x] == color) { + if (modules[y][x] == runColor) { runY++; if (runY == 5) result += QrCode.PENALTY_N1; @@ -467,14 +467,14 @@ var qrcodegen = new function() { result++; } else { QrCode.addRunToHistory(runY, runHistory); - if (!color && QrCode.hasFinderLikePattern(runHistory)) + if (!runColor && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; - color = modules[y][x]; + runColor = modules[y][x]; runY = 1; } } QrCode.addRunToHistory(runY, runHistory); - if (color) + if (runColor) QrCode.addRunToHistory(0, runHistory); // Dummy run of white if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index e27615b..40487da 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -465,10 +465,10 @@ class QrCode(object): # Adjacent modules in row having same color, and finder-like patterns for y in range(size): runhistory = collections.deque([0] * 7, 7) - color = False + runcolor = False runx = 0 for x in range(size): - if modules[y][x] == color: + if modules[y][x] == runcolor: runx += 1 if runx == 5: result += QrCode._PENALTY_N1 @@ -476,22 +476,22 @@ class QrCode(object): result += 1 else: runhistory.appendleft(runx) - if not color and QrCode.has_finder_like_pattern(runhistory): + if not runcolor and QrCode.has_finder_like_pattern(runhistory): result += QrCode._PENALTY_N3 - color = modules[y][x] + runcolor = modules[y][x] runx = 1 runhistory.appendleft(runx) - if color: + if runcolor: runhistory.appendleft(0) # Dummy run of white if QrCode.has_finder_like_pattern(runhistory): result += QrCode._PENALTY_N3 # Adjacent modules in column having same color, and finder-like patterns for x in range(size): runhistory = collections.deque([0] * 7, 7) - color = False + runcolor = False runy = 0 for y in range(size): - if modules[y][x] == color: + if modules[y][x] == runcolor: runy += 1 if runy == 5: result += QrCode._PENALTY_N1 @@ -499,12 +499,12 @@ class QrCode(object): result += 1 else: runhistory.appendleft(runy) - if not color and QrCode.has_finder_like_pattern(runhistory): + if not runcolor and QrCode.has_finder_like_pattern(runhistory): result += QrCode._PENALTY_N3 - color = modules[y][x] + runcolor = modules[y][x] runy = 1 runhistory.appendleft(runy) - if color: + if runcolor: runhistory.appendleft(0) # Dummy run of white if QrCode.has_finder_like_pattern(runhistory): result += QrCode._PENALTY_N3 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4fedaba..d337040 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -643,10 +643,10 @@ impl QrCode { // Adjacent modules in row having same color, and finder-like patterns for y in 0 .. size { let mut runhistory = RunHistory::new(); - let mut color = false; + let mut runcolor = false; let mut runx: i32 = 0; for x in 0 .. size { - if self.module(x, y) == color { + if self.module(x, y) == runcolor { runx += 1; if runx == 5 { result += PENALTY_N1; @@ -655,15 +655,15 @@ impl QrCode { } } else { runhistory.add_run(runx); - if !color && runhistory.has_finder_like_pattern() { + if !runcolor && runhistory.has_finder_like_pattern() { result += PENALTY_N3; } - color = self.module(x, y); + runcolor = self.module(x, y); runx = 1; } } runhistory.add_run(runx); - if color { + if runcolor { runhistory.add_run(0); // Dummy run of white } if runhistory.has_finder_like_pattern() { @@ -673,10 +673,10 @@ impl QrCode { // Adjacent modules in column having same color, and finder-like patterns for x in 0 .. size { let mut runhistory = RunHistory::new(); - let mut color = false; + let mut runcolor = false; let mut runy: i32 = 0; for y in 0 .. size { - if self.module(x, y) == color { + if self.module(x, y) == runcolor { runy += 1; if runy == 5 { result += PENALTY_N1; @@ -685,15 +685,15 @@ impl QrCode { } } else { runhistory.add_run(runy); - if !color && runhistory.has_finder_like_pattern() { + if !runcolor && runhistory.has_finder_like_pattern() { result += PENALTY_N3; } - color = self.module(x, y); + runcolor = self.module(x, y); runy = 1; } } runhistory.add_run(runy); - if color { + if runcolor { runhistory.add_run(0); // Dummy run of white } if runhistory.has_finder_like_pattern() { diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 2d6677e..7366757 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -513,10 +513,10 @@ namespace qrcodegen { // Adjacent modules in row having same color, and finder-like patterns for (let y = 0; y < this.size; y++) { let runHistory = [0,0,0,0,0,0,0]; - let color = false; + let runColor = false; let runX = 0; for (let x = 0; x < this.size; x++) { - if (this.modules[y][x] == color) { + if (this.modules[y][x] == runColor) { runX++; if (runX == 5) result += QrCode.PENALTY_N1; @@ -524,14 +524,14 @@ namespace qrcodegen { result++; } else { QrCode.addRunToHistory(runX, runHistory); - if (!color && QrCode.hasFinderLikePattern(runHistory)) + if (!runColor && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; - color = this.modules[y][x]; + runColor = this.modules[y][x]; runX = 1; } } QrCode.addRunToHistory(runX, runHistory); - if (color) + if (runColor) QrCode.addRunToHistory(0, runHistory); // Dummy run of white if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; @@ -539,10 +539,10 @@ namespace qrcodegen { // Adjacent modules in column having same color, and finder-like patterns for (let x = 0; x < this.size; x++) { let runHistory = [0,0,0,0,0,0,0]; - let color = false; + let runColor = false; let runY = 0; for (let y = 0; y < this.size; y++) { - if (this.modules[y][x] == color) { + if (this.modules[y][x] == runColor) { runY++; if (runY == 5) result += QrCode.PENALTY_N1; @@ -550,14 +550,14 @@ namespace qrcodegen { result++; } else { QrCode.addRunToHistory(runY, runHistory); - if (!color && QrCode.hasFinderLikePattern(runHistory)) + if (!runColor && QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; - color = this.modules[y][x]; + runColor = this.modules[y][x]; runY = 1; } } QrCode.addRunToHistory(runY, runHistory); - if (color) + if (runColor) QrCode.addRunToHistory(0, runHistory); // Dummy run of white if (QrCode.hasFinderLikePattern(runHistory)) result += QrCode.PENALTY_N3; From 6794ebefa7daa1cf11675e68154dc6d4fa1b4c3d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Jul 2019 01:59:24 +0000 Subject: [PATCH 597/810] Inlined the Java-version finder-like pattern detector into the penalty score calculation logic in a non-trivial way, keeping behavior identical but reducing {declarations, computations, comments, explanations}. --- .../main/java/io/nayuki/qrcodegen/QrCode.java | 172 +++++------------- 1 file changed, 48 insertions(+), 124 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 07afd7d..21b347a 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -596,31 +596,36 @@ public final class QrCode { int result = 0; // Adjacent modules in row having same color, and finder-like patterns - FinderPatternDetector det = new FinderPatternDetector(); + int[] runHistory = new int[7]; for (int y = 0; y < size; y++) { - boolean runClor = false; + boolean runColor = false; int runX = 0; - det.reset(); + Arrays.fill(runHistory, 0); + int padRun = size; // Add white border to initial run for (int x = 0; x < size; x++) { - if (modules[y][x] == runClor) { + if (modules[y][x] == runColor) { runX++; if (runX == 5) result += PENALTY_N1; else if (runX > 5) result++; } else { - runClor = modules[y][x]; + finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; + runColor = modules[y][x]; runX = 1; } - result += det.addModuleAndMatch(runClor) * PENALTY_N3; } - result += det.terminateAndMatch() * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { - det.reset(); boolean runColor = false; int runY = 0; + Arrays.fill(runHistory, 0); + int padRun = size; // Add white border to initial run for (int y = 0; y < size; y++) { if (modules[y][x] == runColor) { runY++; @@ -629,12 +634,15 @@ public final class QrCode { else if (runY > 5) result++; } else { + finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = modules[y][x]; runY = 1; } - result += det.addModuleAndMatch(runColor) * PENALTY_N3; } - result += det.terminateAndMatch() * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -724,6 +732,36 @@ public final class QrCode { } + // 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) { + int n = runHistory[1]; + 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); + } + + + // 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 + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); + } + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { + 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; @@ -882,118 +920,4 @@ public final class QrCode { } - - - /*---- Private helper class ----*/ - - /** - * Detects finder-like patterns in a line of modules, for the purpose of penalty score calculation. - * A finder-like pattern has alternating black and white modules with run length ratios of 1:1:3:1:1, - * such that the center run is black and this pattern is surrounded by at least a ratio - * of 4:1 white modules on one side and at least 1:1 white modules on the other side. - * The finite line of modules is conceptually padded with an infinite number of white modules on both sides. - * - * Here are graphic examples of the designed behavior, where '[' means start of line, - * ']' means end of line, '.' means white module, and '#' means black module: - * - [....#.###.#....] Two matches - * - [#.###.#] Two matches, because of infinite white border - * - [##..######..##] Two matches, with a scale of 2 - * - [#.###.#.#] One match, using the infinite white left border - * - [#.#.###.#.#] Zero matches, due to insufficient white modules surrounding the 1:1:3:1:1 pattern - * - [#.###.##] Zero matches, because the rightmost black bar is too long - * - [#.###.#.###.#] Two matches, with the matches overlapping and sharing modules - */ - private static final class FinderPatternDetector { - - /*-- Fields --*/ - - // Mutable running state - private boolean currentRunColor; // false = white, true = black - private int currentRunLength; // In modules, always positive - // runHistory[0] = length of most recently ended run, - // runHistory[1] = length of next older run of opposite color, etc. - // This array begins as all zeros. Zero is not a valid run length. - private int[] runHistory = new int[7]; - - - /*-- Methods --*/ - - /** - * Re-initializes this detector to the start of a row or column. - * This allows reuse of this object and its array, reducing memory allocation. - */ - public void reset() { - currentRunColor = false; - currentRunLength = QR_CODE_SIZE_MAX; // Add white border to initial run - Arrays.fill(runHistory, 0); - } - - - /** - * Tells this detector that the next module has the specified color, and returns - * the number of finder-like patterns detected due to processing the current module. - * The result is usually 0, but can be 1 or 2 only when transitioning from - * white to black (i.e. {@code currentRunColor == false && color == true}). - * @param color the color of the next module, where {@code true} denotes black and {@code false} is white - * @return either 0, 1, or 2 - */ - public int addModuleAndMatch(boolean color) { - if (color == currentRunColor) - currentRunLength++; - else { - addToHistory(currentRunLength); - currentRunColor = color; - currentRunLength = 1; - if (color) // Transitioning from white to black - return countCurrentMatches(); - } - return 0; - } - - - /** - * Tells this detector that the line of modules has ended, and - * returns the number of finder-like patterns detected at the end. - * After this, {@link #reset()} must be called before any other methods. - * @return either 0, 1, or 2 - */ - public int terminateAndMatch() { - if (currentRunColor) { // Terminate black run - addToHistory(currentRunLength); - currentRunLength = 0; - } - currentRunLength += QR_CODE_SIZE_MAX; // Add white border to final run - addToHistory(currentRunLength); - return countCurrentMatches(); - } - - - // Shifts the array back and puts the given value at the front. - private void addToHistory(int run) { - System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); - runHistory[0] = run; - } - - - // Can only be called immediately after a white run is added. - private int countCurrentMatches() { - int n = runHistory[1]; - assert n <= QR_CODE_SIZE_MAX * 3; - boolean core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; - if (core) { - return (runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) - + (runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); - } else - return 0; - } - - - /*-- Constant --*/ - - // This amount of padding is enough to guarantee at least 4 scaled - // white modules at any pattern scale that fits inside any QR Code. - private static final int QR_CODE_SIZE_MAX = QrCode.MAX_VERSION * 4 + 17; - - } - } From c5ad557eea19771a30d592399980e0da1bfa24e1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Jul 2019 03:30:51 +0000 Subject: [PATCH 598/810] Updated the finder pattern detector logic in the other 6 language versions to match Java code. --- c/qrcodegen.c | 81 +++++++++++++++++-------------- cpp/QrCode.cpp | 60 +++++++++++++---------- cpp/QrCode.hpp | 18 +++---- javascript/qrcodegen.js | 65 ++++++++++++++++--------- python/qrcodegen.py | 53 ++++++++++++--------- rust/src/lib.rs | 103 ++++++++++++++++++---------------------- typescript/qrcodegen.ts | 69 +++++++++++++++------------ 7 files changed, 248 insertions(+), 201 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 17f8aaa..84b47c8 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -72,8 +72,9 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]); static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qrcodegen_Mask mask); static long getPenaltyScore(const uint8_t qrcode[]); -static void addRunToHistory(unsigned char run, unsigned char history[7]); -static bool hasFinderLikePattern(const unsigned char runHistory[7]); +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7]); testable bool getModule(const uint8_t qrcode[], int x, int y); testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); @@ -634,9 +635,10 @@ static long getPenaltyScore(const uint8_t qrcode[]) { // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < qrsize; y++) { - unsigned char runHistory[7] = {0}; bool runColor = false; - unsigned char runX = 0; + int runX = 0; + int runHistory[7] = {0}; + int padRun = qrsize; // Add white border to initial run for (int x = 0; x < qrsize; x++) { if (getModule(qrcode, x, y) == runColor) { runX++; @@ -645,24 +647,22 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runX > 5) result++; } else { - addRunToHistory(runX, runHistory); - if (!runColor && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; runColor = getModule(qrcode, x, y); runX = 1; } } - addRunToHistory(runX, runHistory); - if (runColor) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory, qrsize) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < qrsize; x++) { - unsigned char runHistory[7] = {0}; bool runColor = false; - unsigned char runY = 0; + int runY = 0; + int runHistory[7] = {0}; + int padRun = qrsize; // Add white border to initial run for (int y = 0; y < qrsize; y++) { if (getModule(qrcode, x, y) == runColor) { runY++; @@ -671,18 +671,15 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runY > 5) result++; } else { - addRunToHistory(runY, runHistory); - if (!runColor && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; runColor = getModule(qrcode, x, y); runY = 1; } } - addRunToHistory(runY, runHistory); - if (runColor) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory, qrsize) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -712,23 +709,35 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } -// Inserts the given value to the front of the given array, which shifts over the -// existing values and deletes the last value. A helper function for getPenaltyScore(). -static void addRunToHistory(unsigned char run, unsigned char history[7]) { - memmove(&history[1], &history[0], 6 * sizeof(history[0])); - history[0] = run; +// Can only be called immediately after a white run is added, and +// returns either 0, 1, or 2. A helper function for getPenaltyScore(). +static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { + int n = runHistory[1]; + assert(n <= qrsize * 3); + bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; + // The maximum QR Code size is 177, hence the black run length n <= 177. + // Arithmetic is promoted to int, so n*4 will not overflow. + return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); } -// Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and -// surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). -// Must only be called immediately after a run of white modules has ended. -static bool hasFinderLikePattern(const unsigned char runHistory[7]) { - unsigned char n = runHistory[1]; - // The maximum QR Code size is 177, hence the run length n <= 177. - // Arithmetic is promoted to int, so n*4 will not overflow. - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && (runHistory[0] >= n * 4 || runHistory[6] >= n * 4); +// Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). +static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { + if (currentRunColor) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += qrsize; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory, qrsize); +} + + +// Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7]) { + memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); + runHistory[0] = currentRunLength; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index dba2cc5..9880c54 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -427,9 +427,10 @@ long QrCode::getPenaltyScore() const { // Adjacent modules in row having same color, and finder-like patterns for (int y = 0; y < size; y++) { - std::deque runHistory(7, 0); bool runColor = false; int runX = 0; + std::array runHistory = {}; + int padRun = size; // Add white border to initial run for (int x = 0; x < size; x++) { if (module(x, y) == runColor) { runX++; @@ -438,24 +439,22 @@ long QrCode::getPenaltyScore() const { else if (runX > 5) result++; } else { - addRunToHistory(runX, runHistory); - if (!runColor && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = module(x, y); runX = 1; } } - addRunToHistory(runX, runHistory); - if (runColor) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { - std::deque runHistory(7, 0); bool runColor = false; int runY = 0; + std::array runHistory = {}; + int padRun = size; // Add white border to initial run for (int y = 0; y < size; y++) { if (module(x, y) == runColor) { runY++; @@ -464,18 +463,15 @@ long QrCode::getPenaltyScore() const { else if (runY > 5) result++; } else { - addRunToHistory(runY, runHistory); - if (!runColor && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = module(x, y); runY = 1; } } - addRunToHistory(runY, runHistory); - if (runColor) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -542,16 +538,30 @@ int QrCode::getNumDataCodewords(int ver, Ecc ecl) { } -void QrCode::addRunToHistory(int run, std::deque &history) { - history.pop_back(); - history.push_front(run); +int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { + int n = runHistory.at(1); + if (n > size * 3) + throw std::logic_error("Assertion error"); + bool core = n > 0 && runHistory.at(2) == n && runHistory.at(3) == n * 3 && runHistory.at(4) == n && runHistory.at(5) == n; + return (core && runHistory.at(0) >= n * 4 && runHistory.at(6) >= n ? 1 : 0) + + (core && runHistory.at(6) >= n * 4 && runHistory.at(0) >= n ? 1 : 0); } -bool QrCode::hasFinderLikePattern(const std::deque &runHistory) { - int n = runHistory.at(1); - return n > 0 && runHistory.at(2) == n && runHistory.at(4) == n && runHistory.at(5) == n - && runHistory.at(3) == n * 3 && std::max(runHistory.at(0), runHistory.at(6)) >= n * 4; +int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { + if (currentRunColor) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); +} + + +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) { + std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); + runHistory.at(0) = currentRunLength; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index d496e2a..6196355 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -23,8 +23,8 @@ #pragma once +#include #include -#include #include #include #include @@ -274,15 +274,17 @@ class QrCode final { private: static int getNumDataCodewords(int ver, Ecc ecl); - // Inserts the given value to the front of the given array, which shifts over the - // existing values and deletes the last value. A helper function for getPenaltyScore(). - private: static void addRunToHistory(int run, std::deque &history); + // 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(const std::array &runHistory) const; - // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). - // Must only be called immediately after a run of white modules has ended. - private: static bool hasFinderLikePattern(const std::deque &runHistory); + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private: int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const; + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private: static void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory); // Returns true iff the i'th bit of x is set to 1. diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index cf77be8..40aff87 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -429,9 +429,10 @@ var qrcodegen = new function() { // Adjacent modules in row having same color, and finder-like patterns for (var y = 0; y < size; y++) { - var runHistory = [0,0,0,0,0,0,0]; var runColor = false; var runX = 0; + var runHistory = [0,0,0,0,0,0,0]; + var padRun = size; for (var x = 0; x < size; x++) { if (modules[y][x] == runColor) { runX++; @@ -440,24 +441,22 @@ var qrcodegen = new function() { else if (runX > 5) result++; } else { - QrCode.addRunToHistory(runX, runHistory); - if (!runColor && QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + QrCode.finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = modules[y][x]; runX = 1; } } - QrCode.addRunToHistory(runX, runHistory); - if (runColor) - QrCode.addRunToHistory(0, runHistory); // Dummy run of white - if (QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (var x = 0; x < size; x++) { - var runHistory = [0,0,0,0,0,0,0]; var runColor = false; var runY = 0; + var runHistory = [0,0,0,0,0,0,0]; + var padRun = size; for (var y = 0; y < size; y++) { if (modules[y][x] == runColor) { runY++; @@ -466,18 +465,15 @@ var qrcodegen = new function() { else if (runY > 5) result++; } else { - QrCode.addRunToHistory(runY, runHistory); - if (!runColor && QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + QrCode.finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (!runColor) + result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = modules[y][x]; runY = 1; } } - QrCode.addRunToHistory(runY, runHistory); - if (runColor) - QrCode.addRunToHistory(0, runHistory); // Dummy run of white - if (QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3; } // 2*2 blocks of modules having same color @@ -525,6 +521,30 @@ var qrcodegen = new function() { } + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + function finderPenaltyCountPatterns(runHistory) { + var n = runHistory[1]; + if (n > size * 3) + throw "Assertion error"; + var 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); + } + + + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + function finderPenaltyTerminateAndCount(currentRunColor, currentRunLength, runHistory) { + if (currentRunColor) { // Terminate black run + QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); + } + + // Returns true iff the i'th bit of x is set to 1. function getBit(x, i) { return ((x >>> i) & 1) != 0; @@ -667,11 +687,10 @@ var qrcodegen = new function() { }; - // Inserts the given value to the front of the given array, which shifts over the - // existing values and deletes the last value. A helper function for getPenaltyScore(). - QrCode.addRunToHistory = function(run, history) { - history.pop(); - history.unshift(run); + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + QrCode.finderPenaltyAddHistory = function(currentRunLength, runHistory) { + runHistory.pop(); + runHistory.unshift(currentRunLength); }; diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 40487da..3dd8eca 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -464,9 +464,10 @@ class QrCode(object): # Adjacent modules in row having same color, and finder-like patterns for y in range(size): - runhistory = collections.deque([0] * 7, 7) runcolor = False runx = 0 + runhistory = collections.deque([0] * 7, 7) + padrun = size # Add white border to initial run for x in range(size): if modules[y][x] == runcolor: runx += 1 @@ -475,21 +476,19 @@ class QrCode(object): elif runx > 5: result += 1 else: - runhistory.appendleft(runx) - if not runcolor and QrCode.has_finder_like_pattern(runhistory): - result += QrCode._PENALTY_N3 + runhistory.appendleft(runx + padrun) + padrun = 0 + if not runcolor: + result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3 runcolor = modules[y][x] runx = 1 - runhistory.appendleft(runx) - if runcolor: - runhistory.appendleft(0) # Dummy run of white - if QrCode.has_finder_like_pattern(runhistory): - result += QrCode._PENALTY_N3 + result += self._finder_penalty_terminate_and_count(runcolor, runx + padrun, runhistory) * QrCode._PENALTY_N3 # Adjacent modules in column having same color, and finder-like patterns for x in range(size): - runhistory = collections.deque([0] * 7, 7) runcolor = False runy = 0 + runhistory = collections.deque([0] * 7, 7) + padrun = size # Add white border to initial run for y in range(size): if modules[y][x] == runcolor: runy += 1 @@ -498,16 +497,13 @@ class QrCode(object): elif runy > 5: result += 1 else: - runhistory.appendleft(runy) - if not runcolor and QrCode.has_finder_like_pattern(runhistory): - result += QrCode._PENALTY_N3 + runhistory.appendleft(runy + padrun) + padrun = 0 + if not runcolor: + result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3 runcolor = modules[y][x] runy = 1 - runhistory.appendleft(runy) - if runcolor: - runhistory.appendleft(0) # Dummy run of white - if QrCode.has_finder_like_pattern(runhistory): - result += QrCode._PENALTY_N3 + result += self._finder_penalty_terminate_and_count(runcolor, runy + padrun, runhistory) * QrCode._PENALTY_N3 # 2*2 blocks of modules having same color for y in range(size - 1): @@ -567,11 +563,24 @@ class QrCode(object): * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] - @staticmethod - def has_finder_like_pattern(runhistory): + # Can only be called immediately after a white run is added, and + # returns either 0, 1, or 2. A helper function for _get_penalty_score(). + def _finder_penalty_count_patterns(self, runhistory): n = runhistory[1] - return n > 0 and n == runhistory[2] == runhistory[4] == runhistory[5] \ - and runhistory[3] == n * 3 and max(runhistory[0], runhistory[6]) >= n * 4 + assert n <= self._size * 3 + core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3 + return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \ + + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0) + + + # Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score(). + def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory): + if currentruncolor: # Terminate black run + runhistory.appendleft(currentrunlength) + currentrunlength = 0 + currentrunlength += self._size # Add white border to final run + runhistory.appendleft(currentrunlength) + return self._finder_penalty_count_patterns(runhistory) # ---- Constants and tables ---- diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d337040..28b5514 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -642,9 +642,10 @@ impl QrCode { // Adjacent modules in row having same color, and finder-like patterns for y in 0 .. size { - let mut runhistory = RunHistory::new(); let mut runcolor = false; let mut runx: i32 = 0; + let mut runhistory = [0i32; 7]; + let mut padrun = size; // Add white border to initial run for x in 0 .. size { if self.module(x, y) == runcolor { runx += 1; @@ -654,27 +655,23 @@ impl QrCode { result += 1; } } else { - runhistory.add_run(runx); - if !runcolor && runhistory.has_finder_like_pattern() { - result += PENALTY_N3; + QrCode::finder_penalty_add_history(runx + padrun, &mut runhistory); + padrun = 0; + if !runcolor { + result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3; } runcolor = self.module(x, y); runx = 1; } } - runhistory.add_run(runx); - if runcolor { - runhistory.add_run(0); // Dummy run of white - } - if runhistory.has_finder_like_pattern() { - result += PENALTY_N3; - } + result += self.finder_penalty_terminate_and_count(runcolor, runx + padrun, &mut runhistory) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for x in 0 .. size { - let mut runhistory = RunHistory::new(); let mut runcolor = false; let mut runy: i32 = 0; + let mut runhistory = [0i32; 7]; + let mut padrun = size; // Add white border to initial run for y in 0 .. size { if self.module(x, y) == runcolor { runy += 1; @@ -684,21 +681,16 @@ impl QrCode { result += 1; } } else { - runhistory.add_run(runy); - if !runcolor && runhistory.has_finder_like_pattern() { - result += PENALTY_N3; + QrCode::finder_penalty_add_history(runy + padrun, &mut runhistory); + padrun = 0; + if !runcolor { + result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3; } runcolor = self.module(x, y); runy = 1; } } - runhistory.add_run(runy); - if runcolor { - runhistory.add_run(0); // Dummy run of white - } - if runhistory.has_finder_like_pattern() { - result += PENALTY_N3; - } + result += self.finder_penalty_terminate_and_count(runcolor, runy + padrun, &mut runhistory) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -780,6 +772,38 @@ impl QrCode { table[ecl.ordinal()][ver.value() as usize] as usize } + + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for get_penalty_score(). + fn finder_penalty_count_patterns(&self, runhistory: &[i32;7]) -> i32 { + let n = runhistory[1]; + assert!(n <= self.size * 3); + let core = n > 0 && runhistory[2] == n && runhistory[3] == n * 3 && runhistory[4] == n && runhistory[5] == n; + return if core && runhistory[0] >= n * 4 && runhistory[6] >= n { 1 } else { 0 } + + if core && runhistory[6] >= n * 4 && runhistory[0] >= n { 1 } else { 0 }; + } + + + // Must be called at the end of a line (row or column) of modules. A helper function for get_penalty_score(). + fn finder_penalty_terminate_and_count(&self, currentruncolor: bool, mut currentrunlength: i32, runhistory: &mut [i32;7]) -> i32 { + if currentruncolor { // Terminate black run + QrCode::finder_penalty_add_history(currentrunlength, runhistory); + currentrunlength = 0; + } + currentrunlength += self.size; // Add white border to final run + QrCode::finder_penalty_add_history(currentrunlength, runhistory); + self.finder_penalty_count_patterns(runhistory) + } + + + // Pushes the given value to the front and drops the last value. A helper function for get_penalty_score(). + fn finder_penalty_add_history(currentrunlength: i32, runhistory: &mut [i32;7]) { + for i in (0 .. runhistory.len()-1).rev() { + runhistory[i + 1] = runhistory[i]; + } + runhistory[0] = currentrunlength; + } + } @@ -937,41 +961,6 @@ impl ReedSolomonGenerator { -/*---- RunHistory functionality ----*/ - -struct RunHistory(std::collections::VecDeque); - - -impl RunHistory { - - fn new() -> Self { - let mut temp = std::collections::VecDeque::::new(); - temp.resize(7, 0); - RunHistory(temp) - } - - - // Inserts the given value to the front of this array, which shifts over the existing - // values and deletes the last value. A helper function for get_penalty_score(). - fn add_run(&mut self, run: i32) { - self.0.pop_back(); - self.0.push_front(run); - } - - - // Tests whether this run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for get_penalty_score(). - // Must only be called immediately after a run of white modules has ended. - fn has_finder_like_pattern(&self) -> bool { - let n = self.0[1]; - n > 0 && self.0[2] == n && self.0[4] == n && self.0[5] == n - && self.0[3] == n * 3 && std::cmp::max(self.0[0], self.0[6]) >= n * 4 - } - -} - - - /*---- QrSegment functionality ----*/ /// A segment of character/binary/control data in a QR Code symbol. diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 7366757..3a3b03a 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -512,9 +512,10 @@ namespace qrcodegen { // Adjacent modules in row having same color, and finder-like patterns for (let y = 0; y < this.size; y++) { - let runHistory = [0,0,0,0,0,0,0]; let runColor = false; let runX = 0; + let runHistory = [0,0,0,0,0,0,0]; + let padRun = this.size; for (let x = 0; x < this.size; x++) { if (this.modules[y][x] == runColor) { runX++; @@ -523,24 +524,22 @@ namespace qrcodegen { else if (runX > 5) result++; } else { - QrCode.addRunToHistory(runX, runHistory); - if (!runColor && QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + QrCode.finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (!runColor) + result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = this.modules[y][x]; runX = 1; } } - QrCode.addRunToHistory(runX, runHistory); - if (runColor) - QrCode.addRunToHistory(0, runHistory); // Dummy run of white - if (QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + result += this.finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (let x = 0; x < this.size; x++) { - let runHistory = [0,0,0,0,0,0,0]; let runColor = false; let runY = 0; + let runHistory = [0,0,0,0,0,0,0]; + let padRun = this.size; for (let y = 0; y < this.size; y++) { if (this.modules[y][x] == runColor) { runY++; @@ -549,18 +548,15 @@ namespace qrcodegen { else if (runY > 5) result++; } else { - QrCode.addRunToHistory(runY, runHistory); - if (!runColor && QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + QrCode.finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (!runColor) + result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = this.modules[y][x]; runY = 1; } } - QrCode.addRunToHistory(runY, runHistory); - if (runColor) - QrCode.addRunToHistory(0, runHistory); // Dummy run of white - if (QrCode.hasFinderLikePattern(runHistory)) - result += QrCode.PENALTY_N3; + result += this.finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3; } // 2*2 blocks of modules having same color @@ -637,21 +633,34 @@ namespace qrcodegen { } - // Inserts the given value to the front of the given array, which shifts over the - // existing values and deletes the last value. A helper function for getPenaltyScore(). - private static addRunToHistory(run: int, history: Array): void { - history.pop(); - history.unshift(run); + // Can only be called immediately after a white run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + private finderPenaltyCountPatterns(runHistory: Array): int { + const n: int = runHistory[1]; + if (n > this.size * 3) + throw "Assertion error"; + const core: boolean = 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); } - // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). - // Must only be called immediately after a run of white modules has ended. - private static hasFinderLikePattern(runHistory: Array): boolean { - const n: int = runHistory[1]; - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array): int { + if (currentRunColor) { // Terminate black run + QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += this.size; // Add white border to final run + QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + return this.finderPenaltyCountPatterns(runHistory); + } + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private static finderPenaltyAddHistory(currentRunLength: int, runHistory: Array): void { + runHistory.pop(); + runHistory.unshift(currentRunLength); } From 8fbfc087d24e67cff76e40a102e6b337ce0b6534 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 6 Jul 2019 05:18:23 +0000 Subject: [PATCH 599/810] Updated Java path in Python batch tester due to Maven subdirectories (commit 5483653b53f6). --- python/qrcodegen-batch-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index 3acf236..fe35b84 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -32,7 +32,7 @@ if sys.version_info.major < 3: CHILD_PROGRAMS = [ ["python2", "../python/qrcodegen-worker.py"], # Python 2 program ["python3", "../python/qrcodegen-worker.py"], # Python 3 program - ["java", "-cp", "../java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program + ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["../c/qrcodegen-worker"], # C program ["../cpp/QrCodeGeneratorWorker"], # C++ program ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program From 07417015042dcfa2a1177b7d79a245deb6fe83ad Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 02:08:30 +0000 Subject: [PATCH 600/810] Added simple build script for TypeScript. --- typescript/build.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 typescript/build.sh diff --git a/typescript/build.sh b/typescript/build.sh new file mode 100644 index 0000000..8d046b4 --- /dev/null +++ b/typescript/build.sh @@ -0,0 +1,24 @@ +# +# Build script for QR Code generator (TypeScript) +# +# Copyright (c) Project Nayuki. (MIT License) +# https://www.nayuki.io/page/qr-code-generator-library +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of +# this software and associated documentation files (the "Software"), to deal in +# the Software without restriction, including without limitation the rights to +# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +# the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# - The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# - The Software is provided "as is", without warranty of any kind, express or +# implied, including but not limited to the warranties of merchantability, +# fitness for a particular purpose and noninfringement. In no event shall the +# authors or copyright holders be liable for any claim, damages or other +# liability, whether in an action of contract, tort or otherwise, arising from, +# out of or in connection with the Software or the use or other dealings in the +# Software. +# + +tsc --strict --target ES6 qrcodegen.ts qrcodegen-demo.ts From 9c337e5329de5b0436182f411bdda5515237f8b5 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 02:49:30 +0000 Subject: [PATCH 601/810] Simplified Python worker program, mainly due to bytearray. --- python/qrcodegen-worker.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py index 421b876..b159bb6 100644 --- a/python/qrcodegen-worker.py +++ b/python/qrcodegen-worker.py @@ -26,7 +26,6 @@ # Software. # -from __future__ import print_function import sys import qrcodegen py3 = sys.version_info.major >= 3 @@ -43,7 +42,7 @@ def main(): length = read_int() if length == -1: break - data = [read_int() for _ in range(length)] + data = bytearray(read_int() for _ in range(length)) # Read encoding parameters errcorlvl = read_int() @@ -54,11 +53,9 @@ def main(): # Make segments for encoding if all((b < 128) for b in data): # Is ASCII - segs = qrcodegen.QrSegment.make_segments("".join(chr(b) for b in data)) - elif py3: - segs = [qrcodegen.QrSegment.make_bytes(bytes(data))] + segs = qrcodegen.QrSegment.make_segments(data.decode("ASCII")) else: - segs = [qrcodegen.QrSegment.make_bytes("".join(chr(b) for b in data))] + segs = [qrcodegen.QrSegment.make_bytes(data)] try: # Try to make QR Code symbol qr = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) From 5ec3109cb9023c8f03be1faea9a825cfb1f65e25 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 02:57:14 +0000 Subject: [PATCH 602/810] Added TypeScript command line worker program, updated its build script. --- python/qrcodegen-batch-test.py | 1 + typescript/build.sh | 7 +- typescript/qrcodegen-worker.ts | 130 +++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 typescript/qrcodegen-worker.ts diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index fe35b84..bce48bf 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -33,6 +33,7 @@ CHILD_PROGRAMS = [ ["python2", "../python/qrcodegen-worker.py"], # Python 2 program ["python3", "../python/qrcodegen-worker.py"], # Python 3 program ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program + ["node", "../typescript/qrcodegen-worker.js"], # TypeScript program ["../c/qrcodegen-worker"], # C program ["../cpp/QrCodeGeneratorWorker"], # C++ program ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program diff --git a/typescript/build.sh b/typescript/build.sh index 8d046b4..53eb740 100644 --- a/typescript/build.sh +++ b/typescript/build.sh @@ -21,4 +21,9 @@ # Software. # -tsc --strict --target ES6 qrcodegen.ts qrcodegen-demo.ts +tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-demo.ts + +if [ '!' -d node_modules ]; then + npm install @types/node +fi +tsc --strict --target ES2017 --outFile qrcodegen-worker.js qrcodegen.ts qrcodegen-worker.ts diff --git a/typescript/qrcodegen-worker.ts b/typescript/qrcodegen-worker.ts new file mode 100644 index 0000000..92a728c --- /dev/null +++ b/typescript/qrcodegen-worker.ts @@ -0,0 +1,130 @@ +/* + * QR Code generator test worker (TypeScript) + * + * This program reads data and encoding parameters from standard input and writes + * QR Code bitmaps to standard output. The I/O format is one integer per line. + * Run with no command line arguments. The program is intended for automated + * batch testing of end-to-end functionality of this QR Code generator library. + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +async function main(): Promise { + while (true) { + // Read data or exit + const length: number = await input.readInt(); + if (length == -1) + break; + let data: Array = []; + for (let i = 0; i < length; i++) + data.push(await input.readInt()); + + // Read encoding parameters + const errCorLvl : number = await input.readInt(); + const minVersion: number = await input.readInt(); + const maxVersion: number = await input.readInt(); + const mask : number = await input.readInt(); + const boostEcl : number = await input.readInt(); + + // Make segments for encoding + let segs: Array; + if (data.every(b => b < 128)) { // Is ASCII + const s: string = data.map(b => String.fromCharCode(b)).join(""); + segs = qrcodegen.QrSegment.makeSegments(s); + } else + segs = [qrcodegen.QrSegment.makeBytes(data)]; + + try { // Try to make QR Code symbol + const qr = qrcodegen.QrCode.encodeSegments( + segs, ECC_LEVELS[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0); + // Print grid of modules + await printLine(qr.version); + for (let y = 0; y < qr.size; y++) { + for (let x = 0; x < qr.size; x++) + await printLine(qr.getModule(x, y) ? 1 : 0); + } + + } catch (e) { + if (e == "Data too long") + await printLine(-1); + } + } +} + + +namespace input { + + let queue: Array = []; + let callback: ((line:string)=>void)|null = null; + + const readline = require("readline"); + let reader = readline.createInterface({ + input: process.stdin, + terminal: false, + }); + reader.on("line", (line: string) => { + queue.push(line); + if (callback !== null) { + callback(queue.shift() as string); + callback = null; + } + }); + + + async function readLine(): Promise { + return new Promise(resolve => { + if (callback !== null) + throw "Illegal state"; + if (queue.length > 0) + resolve(queue.shift() as string); + else + callback = resolve; + }); + } + + + export async function readInt(): Promise { + let s = await readLine(); + if (!/^-?\d+$/.test(s)) + throw "Invalid number syntax"; + return parseInt(s, 10); + } + +} + + +async function printLine(x: Object): Promise { + return new Promise(resolve => + process.stdout.write(x + "\n", "utf-8", ()=>resolve())); +} + + +const ECC_LEVELS: Array = [ + qrcodegen.QrCode.Ecc.LOW, + qrcodegen.QrCode.Ecc.MEDIUM, + qrcodegen.QrCode.Ecc.QUARTILE, + qrcodegen.QrCode.Ecc.HIGH, +]; + + +main(); From bf03981d7e25fce3a63080de66077c99733093ef Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 03:11:13 +0000 Subject: [PATCH 603/810] Tweaked batch tester to avoid Python children generating .pyc files. --- python/qrcodegen-batch-test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index bce48bf..f0d3663 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -30,8 +30,8 @@ if sys.version_info.major < 3: CHILD_PROGRAMS = [ - ["python2", "../python/qrcodegen-worker.py"], # Python 2 program - ["python3", "../python/qrcodegen-worker.py"], # Python 3 program + ["python2", "-B", "../python/qrcodegen-worker.py"], # Python 2 program + ["python3", "-B", "../python/qrcodegen-worker.py"], # Python 3 program ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["node", "../typescript/qrcodegen-worker.js"], # TypeScript program ["../c/qrcodegen-worker"], # C program From cc9176cdbef7ac925bda06bb0e0227debde346d7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 03:12:18 +0000 Subject: [PATCH 604/810] Removed a blank line for stylistic consistency. --- .../src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java | 1 - 1 file changed, 1 deletion(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java index 801b1b3..4cf9de3 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java @@ -81,7 +81,6 @@ public final class QrCodeGeneratorWorker { else 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); // Print grid of modules From b5aaadf75842c1fac49c8acc8a3eb988651f1001 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 16:54:00 +0000 Subject: [PATCH 605/810] Demoted ReedSolomonGenerator from a class to a set of functions, and changed some names and comments, in all languages except C. This reduces code verbosity but doesn't change public APIs or visible behavior. The code organization is similar to the finder-like-pattern-detector feature. --- cpp/QrCode.cpp | 107 ++++++------ cpp/QrCode.hpp | 59 ++----- .../main/java/io/nayuki/qrcodegen/QrCode.java | 156 +++++++----------- javascript/qrcodegen.js | 134 +++++++-------- python/qrcodegen.py | 114 ++++++------- rust/src/lib.rs | 131 +++++++-------- typescript/qrcodegen.ts | 141 +++++++--------- 7 files changed, 361 insertions(+), 481 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 9880c54..56a3e3f 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -345,11 +345,11 @@ vector QrCode::addEccAndInterleave(const vector &data) const { // Split data into blocks and append ECC to each block vector > blocks; - const ReedSolomonGenerator rs(blockEccLen); + const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); k += dat.size(); - const vector ecc = rs.getRemainder(dat); + const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); if (i < numShortBlocks) dat.push_back(0); dat.insert(dat.end(), ecc.cbegin(), ecc.cend()); @@ -538,6 +538,57 @@ int QrCode::getNumDataCodewords(int ver, Ecc ecl) { } +vector QrCode::reedSolomonComputeDivisor(int degree) { + if (degree < 1 || degree > 255) + throw std::domain_error("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + vector result(degree); + result.at(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). + uint8_t root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (size_t j = 0; j < result.size(); j++) { + result.at(j) = reedSolomonMultiply(result.at(j), root); + if (j + 1 < result.size()) + result.at(j) ^= result.at(j + 1); + } + root = reedSolomonMultiply(root, 0x02); + } + return result; +} + + +vector QrCode::reedSolomonComputeRemainder(const vector &data, const vector &divisor) { + vector result(divisor.size()); + for (uint8_t b : data) { // Polynomial division + uint8_t factor = b ^ result.at(0); + result.erase(result.begin()); + result.push_back(0); + for (size_t j = 0; j < result.size(); j++) + result.at(j) ^= reedSolomonMultiply(divisor.at(j), factor); + } + return result; +} + + +uint8_t QrCode::reedSolomonMultiply(uint8_t x, uint8_t y) { + // Russian peasant multiplication + int z = 0; + for (int i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >> 7) * 0x11D); + z ^= ((y >> i) & 1) * x; + } + if (z >> 8 != 0) + throw std::logic_error("Assertion error"); + return static_cast(z); +} + + int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) const { int n = runHistory.at(1); if (n > size * 3) @@ -597,58 +648,6 @@ const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { }; -QrCode::ReedSolomonGenerator::ReedSolomonGenerator(int degree) : - coefficients() { - if (degree < 1 || degree > 255) - throw std::domain_error("Degree out of range"); - - // Start with the monomial x^0 - coefficients.resize(degree); - coefficients.at(degree - 1) = 1; - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - uint8_t root = 1; - for (int i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (size_t j = 0; j < coefficients.size(); j++) { - coefficients.at(j) = multiply(coefficients.at(j), root); - if (j + 1 < coefficients.size()) - coefficients.at(j) ^= coefficients.at(j + 1); - } - root = multiply(root, 0x02); - } -} - - -vector QrCode::ReedSolomonGenerator::getRemainder(const vector &data) const { - // Compute the remainder by performing polynomial division - vector result(coefficients.size()); - for (uint8_t b : data) { - uint8_t factor = b ^ result.at(0); - result.erase(result.begin()); - result.push_back(0); - for (size_t j = 0; j < result.size(); j++) - result.at(j) ^= multiply(coefficients.at(j), factor); - } - return result; -} - - -uint8_t QrCode::ReedSolomonGenerator::multiply(uint8_t x, uint8_t y) { - // Russian peasant multiplication - int z = 0; - for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); - z ^= ((y >> i) & 1) * x; - } - if (z >> 8 != 0) - throw std::logic_error("Assertion error"); - return static_cast(z); -} - - data_too_long::data_too_long(const std::string &msg) : std::length_error(msg) {} diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 6196355..8ad519f 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -274,6 +274,20 @@ class QrCode final { private: static int getNumDataCodewords(int ver, Ecc ecl); + // 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 std::vector reedSolomonComputeDivisor(int degree); + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private: static std::vector reedSolomonComputeRemainder(const std::vector &data, const std::vector &divisor); + + + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. + private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); + + // 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(const std::array &runHistory) const; @@ -310,51 +324,6 @@ class QrCode final { private: static const std::int8_t ECC_CODEWORDS_PER_BLOCK[4][41]; private: static const std::int8_t NUM_ERROR_CORRECTION_BLOCKS[4][41]; - - - /*---- Private helper class ----*/ - - /* - * Computes the Reed-Solomon error correction codewords for a sequence of data codewords - * at a given degree. Objects are immutable, and the state only depends on the degree. - * This class exists because each data block in a QR Code shares the same the divisor polynomial. - */ - private: class ReedSolomonGenerator final { - - /*-- Immutable field --*/ - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - private: std::vector coefficients; - - - /*-- Constructor --*/ - - /* - * Creates a Reed-Solomon ECC generator for the given degree. This could be implemented - * as a lookup table over all possible parameter values, instead of as an algorithm. - */ - public: explicit ReedSolomonGenerator(int degree); - - - /*-- Method --*/ - - /* - * Computes and returns the Reed-Solomon error correction codewords for the given - * sequence of data codewords. The returned object is always a new byte array. - * This method does not alter this object's state (because it is immutable). - */ - public: std::vector getRemainder(const std::vector &data) const; - - - /*-- Static function --*/ - - // Returns the product of the two given field elements modulo GF(2^8/0x11D). - // All inputs are valid. This could be implemented as a 256*256 lookup table. - private: static std::uint8_t multiply(std::uint8_t x, std::uint8_t y); - - }; - }; diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 21b347a..b82ee1f 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -483,12 +483,12 @@ public final class QrCode { // Split data into blocks and append ECC to each block byte[][] blocks = new byte[numBlocks][]; - ReedSolomonGenerator rs = new ReedSolomonGenerator(blockEccLen); + byte[] rsDiv = reedSolomonComputeDivisor(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { byte[] dat = Arrays.copyOfRange(data, k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); k += dat.length; byte[] block = Arrays.copyOf(dat, shortBlockLen + 1); - byte[] ecc = rs.getRemainder(dat); + byte[] ecc = reedSolomonComputeRemainder(dat, rsDiv); System.arraycopy(ecc, 0, block, block.length - blockEccLen, ecc.length); blocks[i] = block; } @@ -722,6 +722,64 @@ public final class QrCode { } + // 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) { + if (degree < 1 || degree > 255) + throw new IllegalArgumentException("Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // 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). + int root = 1; + for (int i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (int j = 0; j < result.length; j++) { + result[j] = (byte)reedSolomonMultiply(result[j] & 0xFF, root); + if (j + 1 < result.length) + result[j] ^= result[j + 1]; + } + root = reedSolomonMultiply(root, 0x02); + } + 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); + Objects.requireNonNull(divisor); + byte[] result = new byte[divisor.length]; + for (byte b : data) { // Polynomial division + int factor = (b ^ result[0]) & 0xFF; + System.arraycopy(result, 1, result, 0, result.length - 1); + result[result.length - 1] = 0; + for (int i = 0; i < result.length; i++) + result[i] ^= reedSolomonMultiply(divisor[i] & 0xFF, factor); + } + 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; + } + + // 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. @@ -826,98 +884,4 @@ public final class QrCode { } } - - - /*---- Private helper class ----*/ - - /** - * Computes the Reed-Solomon error correction codewords for a sequence of data codewords - * at a given degree. Objects are immutable, and the state only depends on the degree. - * This class exists because each data block in a QR Code shares the same the divisor polynomial. - */ - private static final class ReedSolomonGenerator { - - /*-- Field --*/ - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - private final byte[] coefficients; - - - /*-- Constructor --*/ - - /** - * Constructs a Reed-Solomon ECC generator for the specified degree. This could be implemented - * as a lookup table over all possible parameter values, instead of as an algorithm. - * @param degree the divisor polynomial degree, which must be between 1 and 255 (inclusive) - * @throws IllegalArgumentException if degree < 1 or degree > 255 - */ - public ReedSolomonGenerator(int degree) { - if (degree < 1 || degree > 255) - throw new IllegalArgumentException("Degree out of range"); - - // Start with the monomial x^0 - coefficients = new byte[degree]; - coefficients[degree - 1] = 1; - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - int root = 1; - for (int i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (int j = 0; j < coefficients.length; j++) { - coefficients[j] = (byte)multiply(coefficients[j] & 0xFF, root); - if (j + 1 < coefficients.length) - coefficients[j] ^= coefficients[j + 1]; - } - root = multiply(root, 0x02); - } - } - - - /*-- Method --*/ - - /** - * Computes and returns the Reed-Solomon error correction codewords for the specified - * sequence of data codewords. The returned object is always a new byte array. - * This method does not alter this object's state (because it is immutable). - * @param data the sequence of data codewords - * @return the Reed-Solomon error correction codewords - * @throws NullPointerException if the data is {@code null} - */ - public byte[] getRemainder(byte[] data) { - Objects.requireNonNull(data); - - // Compute the remainder by performing polynomial division - byte[] result = new byte[coefficients.length]; - for (byte b : data) { - int factor = (b ^ result[0]) & 0xFF; - System.arraycopy(result, 1, result, 0, result.length - 1); - result[result.length - 1] = 0; - for (int i = 0; i < result.length; i++) - result[i] ^= multiply(coefficients[i] & 0xFF, factor); - } - return result; - } - - - /*-- Static function --*/ - - // 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 multiply(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; - } - - } - } diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 40aff87..503e5ec 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -339,11 +339,11 @@ var qrcodegen = new function() { // Split data into blocks and append ECC to each block var blocks = []; - var rs = new ReedSolomonGenerator(blockEccLen); + var rsDiv = QrCode.reedSolomonComputeDivisor(blockEccLen); for (var i = 0, k = 0; i < numBlocks; i++) { var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); k += dat.length; - var ecc = rs.getRemainder(dat); + var ecc = QrCode.reedSolomonComputeRemainder(dat, rsDiv); if (i < numShortBlocks) dat.push(0); blocks.push(dat.concat(ecc)); @@ -687,6 +687,66 @@ var qrcodegen = new function() { }; + // 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. + QrCode.reedSolomonComputeDivisor = function(degree) { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + var result = []; + for (var i = 0; i < degree - 1; i++) + result.push(0); + result.push(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). + var root = 1; + for (var i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (var j = 0; j < result.length; j++) { + result[j] = QrCode.reedSolomonMultiply(result[j], root); + if (j + 1 < result.length) + result[j] ^= result[j + 1]; + } + root = QrCode.reedSolomonMultiply(root, 0x02); + } + return result; + }; + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + QrCode.reedSolomonComputeRemainder = function(data, divisor) { + var result = divisor.map(function() { return 0; }); + data.forEach(function(b) { // Polynomial division + var factor = b ^ result.shift(); + result.push(0); + divisor.forEach(function(coef, i) { + result[i] ^= QrCode.reedSolomonMultiply(coef, factor); + }); + }); + 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. + QrCode.reedSolomonMultiply = function(x, y) { + if (x >>> 8 != 0 || y >>> 8 != 0) + throw "Byte out of range"; + // Russian peasant multiplication + var z = 0; + for (var i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if (z >>> 8 != 0) + throw "Assertion error"; + return z; + }; + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). QrCode.finderPenaltyAddHistory = function(currentRunLength, runHistory) { runHistory.pop(); @@ -972,76 +1032,6 @@ var qrcodegen = new function() { } - - /* - * A private helper class that computes the Reed-Solomon error correction codewords for a sequence of - * data codewords at a given degree. Objects are immutable, and the state only depends on the degree. - * This class exists because each data block in a QR Code shares the same the divisor polynomial. - * This constructor creates a Reed-Solomon ECC generator for the given degree. This could be implemented - * as a lookup table over all possible parameter values, instead of as an algorithm. - */ - function ReedSolomonGenerator(degree) { - if (degree < 1 || degree > 255) - throw "Degree out of range"; - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - var coefficients = []; - - // Start with the monomial x^0 - for (var i = 0; i < degree - 1; i++) - coefficients.push(0); - coefficients.push(1); - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - var root = 1; - for (var i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (var j = 0; j < coefficients.length; j++) { - coefficients[j] = ReedSolomonGenerator.multiply(coefficients[j], root); - if (j + 1 < coefficients.length) - coefficients[j] ^= coefficients[j + 1]; - } - root = ReedSolomonGenerator.multiply(root, 0x02); - } - - // Computes and returns the Reed-Solomon error correction codewords for the given - // sequence of data codewords. The returned object is always a new byte array. - // This method does not alter this object's state (because it is immutable). - this.getRemainder = function(data) { - // Compute the remainder by performing polynomial division - var result = coefficients.map(function() { return 0; }); - data.forEach(function(b) { - var factor = b ^ result.shift(); - result.push(0); - coefficients.forEach(function(coef, i) { - result[i] ^= ReedSolomonGenerator.multiply(coef, factor); - }); - }); - return result; - }; - } - - // This static function 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. - ReedSolomonGenerator.multiply = function(x, y) { - if (x >>> 8 != 0 || y >>> 8 != 0) - throw "Byte out of range"; - // Russian peasant multiplication - var z = 0; - for (var i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >>> 7) * 0x11D); - z ^= ((y >>> i) & 1) * x; - } - if (z >>> 8 != 0) - throw "Assertion error"; - return z; - }; - - - /* * A private helper class that represents an appendable sequence of bits (0s and 1s). * Mainly used by QrSegment. This constructor creates an empty bit buffer (length 0). diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 3dd8eca..e1e8fff 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -396,12 +396,12 @@ class QrCode(object): # Split data into blocks and append ECC to each block blocks = [] - rs = _ReedSolomonGenerator(blockecclen) + rsdiv = QrCode._reed_solomon_compute_divisor(blockecclen) k = 0 for i in range(numblocks): dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] k += len(dat) - ecc = rs.get_remainder(dat) + ecc = QrCode._reed_solomon_compute_remainder(dat, rsdiv) if i < numshortblocks: dat.append(0) blocks.append(dat + ecc) @@ -563,6 +563,57 @@ class QrCode(object): * QrCode._NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver] + @staticmethod + def _reed_solomon_compute_divisor(degree): + """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.""" + if degree < 1 or degree > 255: + raise ValueError("Degree out of range") + # Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + result = [0] * (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). + root = 1 + for _ in range(degree): # Unused variable i + # Multiply the current product by (x - r^i) + for j in range(degree): + result[j] = QrCode._reed_solomon_multiply(result[j], root) + if j + 1 < degree: + result[j] ^= result[j + 1] + root = QrCode._reed_solomon_multiply(root, 0x02) + return result + + + @staticmethod + def _reed_solomon_compute_remainder(data, divisor): + """Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.""" + result = [0] * len(divisor) + for b in data: # Polynomial division + factor = b ^ result.pop(0) + result.append(0) + for (i, coef) in enumerate(divisor): + result[i] ^= QrCode._reed_solomon_multiply(coef, factor) + return result + + + @staticmethod + def _reed_solomon_multiply(x, y): + """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.""" + if x >> 8 != 0 or y >> 8 != 0: + raise ValueError("Byte out of range") + # Russian peasant multiplication + z = 0 + for i in reversed(range(8)): + z = (z << 1) ^ ((z >> 7) * 0x11D) + z ^= ((y >> i) & 1) * x + assert z >> 8 == 0 + return z + + # Can only be called immediately after a white run is added, and # returns either 0, 1, or 2. A helper function for _get_penalty_score(). def _finder_penalty_count_patterns(self, runhistory): @@ -841,64 +892,7 @@ class QrSegment(object): -# ---- Private helper classes ---- - -class _ReedSolomonGenerator(object): - """Computes the Reed-Solomon error correction codewords for a sequence of data codewords - at a given degree. Objects are immutable, and the state only depends on the degree. - This class exists because each data block in a QR Code shares the same the divisor polynomial.""" - - def __init__(self, degree): - """Creates a Reed-Solomon ECC generator for the given degree. This could be implemented - as a lookup table over all possible parameter values, instead of as an algorithm.""" - if degree < 1 or degree > 255: - raise ValueError("Degree out of range") - - # Start with the monomial x^0 - self.coefficients = [0] * (degree - 1) + [1] - - # Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - # drop the highest term, and store the rest of the coefficients in order of descending powers. - # Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - root = 1 - for _ in range(degree): # Unused variable i - # Multiply the current product by (x - r^i) - for j in range(degree): - self.coefficients[j] = _ReedSolomonGenerator._multiply(self.coefficients[j], root) - if j + 1 < degree: - self.coefficients[j] ^= self.coefficients[j + 1] - root = _ReedSolomonGenerator._multiply(root, 0x02) - - - def get_remainder(self, data): - """Computes and returns the Reed-Solomon error correction codewords for the given - sequence of data codewords. The returned object is always a new byte list. - This method does not alter this object's state (because it is immutable).""" - # Compute the remainder by performing polynomial division - result = [0] * len(self.coefficients) - for b in data: - factor = b ^ result.pop(0) - result.append(0) - for (i, coef) in enumerate(self.coefficients): - result[i] ^= _ReedSolomonGenerator._multiply(coef, factor) - return result - - - @staticmethod - def _multiply(x, y): - """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.""" - if x >> 8 != 0 or y >> 8 != 0: - raise ValueError("Byte out of range") - # Russian peasant multiplication - z = 0 - for i in reversed(range(8)): - z = (z << 1) ^ ((z >> 7) * 0x11D) - z ^= ((y >> i) & 1) * x - assert z >> 8 == 0 - return z - - +# ---- Private helper class ---- class _BitBuffer(list): """An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.""" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 28b5514..d71a979 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -550,12 +550,12 @@ impl QrCode { // Split data into blocks and append ECC to each block let mut blocks = Vec::>::with_capacity(numblocks); - let rs = ReedSolomonGenerator::new(blockecclen); + let rsdiv: Vec = QrCode::reed_solomon_compute_divisor(blockecclen); let mut k: usize = 0; for i in 0 .. numblocks { let mut dat = data[k .. k + shortblocklen - blockecclen + ((i >= numshortblocks) as usize)].to_vec(); k += dat.len(); - let ecc: Vec = rs.get_remainder(&dat); + let ecc: Vec = QrCode::reed_solomon_compute_remainder(&dat, &rsdiv); if i < numshortblocks { dat.push(0); } @@ -773,6 +773,60 @@ impl QrCode { } + // 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. + fn reed_solomon_compute_divisor(degree: usize) -> Vec { + assert!(1 <= degree && degree <= 255, "Degree out of range"); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + let mut result = vec![0u8; degree - 1]; + result.push(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). + let mut root: u8 = 1; + for _ in 0 .. degree { // Unused variable i + // Multiply the current product by (x - r^i) + for j in 0 .. degree { + result[j] = QrCode::reed_solomon_multiply(result[j], root); + if j + 1 < result.len() { + result[j] ^= result[j + 1]; + } + } + root = QrCode::reed_solomon_multiply(root, 0x02); + } + result + } + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + fn reed_solomon_compute_remainder(data: &[u8], divisor: &[u8]) -> Vec { + let mut result = vec![0u8; divisor.len()]; + for b in data { // Polynomial division + let factor: u8 = b ^ result.remove(0); + result.push(0); + for (x, y) in result.iter_mut().zip(divisor.iter()) { + *x ^= QrCode::reed_solomon_multiply(*y, factor); + } + } + 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. + fn reed_solomon_multiply(x: u8, y: u8) -> u8 { + // Russian peasant multiplication + let mut z: u8 = 0; + for i in (0 .. 8).rev() { + z = (z << 1) ^ ((z >> 7) * 0x1D); + z ^= ((y >> i) & 1) * x; + } + z + } + + // Can only be called immediately after a white run is added, and // returns either 0, 1, or 2. A helper function for get_penalty_score(). fn finder_penalty_count_patterns(&self, runhistory: &[i32;7]) -> i32 { @@ -888,79 +942,6 @@ impl QrCodeEcc { -/*---- ReedSolomonGenerator functionality ----*/ - -// Computes the Reed-Solomon error correction codewords for a sequence of data codewords -// at a given degree. Objects are immutable, and the state only depends on the degree. -// This struct and impl exist because each data block in a QR Code shares the same the divisor polynomial. -struct ReedSolomonGenerator { - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - coefficients: Vec, - -} - - -impl ReedSolomonGenerator { - - // Creates a Reed-Solomon ECC generator for the given degree. This could be implemented - // as a lookup table over all possible parameter values, instead of as an algorithm. - fn new(degree: usize) -> Self { - assert!(1 <= degree && degree <= 255, "Degree out of range"); - // Start with the monomial x^0 - let mut coefs = vec![0u8; degree - 1]; - coefs.push(1); - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - let mut root: u8 = 1; - for _ in 0 .. degree { // Unused variable i - // Multiply the current product by (x - r^i) - for j in 0 .. degree { - coefs[j] = ReedSolomonGenerator::multiply(coefs[j], root); - if j + 1 < coefs.len() { - coefs[j] ^= coefs[j + 1]; - } - } - root = ReedSolomonGenerator::multiply(root, 0x02); - } - Self { coefficients: coefs } - } - - - // Computes and returns the Reed-Solomon error correction codewords for the given sequence of data codewords. - fn get_remainder(&self, data: &[u8]) -> Vec { - // Compute the remainder by performing polynomial division - let mut result = vec![0u8; self.coefficients.len()]; - for b in data { - let factor: u8 = b ^ result.remove(0); - result.push(0); - for (x, y) in result.iter_mut().zip(self.coefficients.iter()) { - *x ^= ReedSolomonGenerator::multiply(*y, factor); - } - } - 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. - fn multiply(x: u8, y: u8) -> u8 { - // Russian peasant multiplication - let mut z: u8 = 0; - for i in (0 .. 8).rev() { - z = (z << 1) ^ ((z >> 7) * 0x1D); - z ^= ((y >> i) & 1) * x; - } - z - } - -} - - - /*---- QrSegment functionality ----*/ /// A segment of character/binary/control data in a QR Code symbol. diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 3a3b03a..f43e85e 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -422,11 +422,11 @@ namespace qrcodegen { // Split data into blocks and append ECC to each block let blocks: Array> = []; - const rs = new ReedSolomonGenerator(blockEccLen); + const rsDiv: Array = QrCode.reedSolomonComputeDivisor(blockEccLen); for (let i = 0, k = 0; i < numBlocks; i++) { let dat: Array = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); k += dat.length; - const ecc: Array = rs.getRemainder(dat); + const ecc: Array = QrCode.reedSolomonComputeRemainder(dat, rsDiv); if (i < numShortBlocks) dat.push(0); blocks.push(dat.concat(ecc)); @@ -633,6 +633,65 @@ namespace qrcodegen { } + // 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 reedSolomonComputeDivisor(degree: int): Array { + if (degree < 1 || degree > 255) + throw "Degree out of range"; + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + let result: Array = []; + for (let i = 0; i < degree - 1; i++) + result.push(0); + result.push(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). + let root = 1; + for (let i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (let j = 0; j < result.length; j++) { + result[j] = QrCode.reedSolomonMultiply(result[j], root); + if (j + 1 < result.length) + result[j] ^= result[j + 1]; + } + root = QrCode.reedSolomonMultiply(root, 0x02); + } + return result; + } + + + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + private static reedSolomonComputeRemainder(data: Array, divisor: Array): Array { + let result: Array = divisor.map(_ => 0); + for (const b of data) { // Polynomial division + const factor: byte = b ^ (result.shift() as byte); + result.push(0); + divisor.forEach((coef, i) => + result[i] ^= QrCode.reedSolomonMultiply(coef, factor)); + } + 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 reedSolomonMultiply(x: byte, y: byte): byte { + if (x >>> 8 != 0 || y >>> 8 != 0) + throw "Byte out of range"; + // Russian peasant multiplication + let z: int = 0; + for (let i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11D); + z ^= ((y >>> i) & 1) * x; + } + if (z >>> 8 != 0) + throw "Assertion error"; + return z as byte; + } + + // Can only be called immediately after a white run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private finderPenaltyCountPatterns(runHistory: Array): int { @@ -883,83 +942,7 @@ namespace qrcodegen { - /*---- Private helper classes ----*/ - - /* - * Computes the Reed-Solomon error correction codewords for a sequence of data codewords - * at a given degree. Objects are immutable, and the state only depends on the degree. - * This class exists because each data block in a QR Code shares the same the divisor polynomial. - */ - class ReedSolomonGenerator { - - // Coefficients of the divisor polynomial, stored from highest to lowest power, excluding the leading term which - // is always 1. For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - private readonly coefficients: Array = []; - - - // Creates a Reed-Solomon ECC generator for the given degree. This could be implemented - // as a lookup table over all possible parameter values, instead of as an algorithm. - public constructor(degree: int) { - if (degree < 1 || degree > 255) - throw "Degree out of range"; - let coefs = this.coefficients; - - // Start with the monomial x^0 - for (let i = 0; i < degree - 1; i++) - coefs.push(0); - coefs.push(1); - - // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. - // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). - let root = 1; - for (let i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (let j = 0; j < coefs.length; j++) { - coefs[j] = ReedSolomonGenerator.multiply(coefs[j], root); - if (j + 1 < coefs.length) - coefs[j] ^= coefs[j + 1]; - } - root = ReedSolomonGenerator.multiply(root, 0x02); - } - } - - - // Computes and returns the Reed-Solomon error correction codewords for the given - // sequence of data codewords. The returned object is always a new byte array. - // This method does not alter this object's state (because it is immutable). - public getRemainder(data: Array): Array { - // Compute the remainder by performing polynomial division - let result: Array = this.coefficients.map(_ => 0); - for (const b of data) { - const factor: byte = b ^ (result.shift() as byte); - result.push(0); - this.coefficients.forEach((coef, i) => - result[i] ^= ReedSolomonGenerator.multiply(coef, factor)); - } - 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 multiply(x: byte, y: byte): byte { - if (x >>> 8 != 0 || y >>> 8 != 0) - throw "Byte out of range"; - // Russian peasant multiplication - let z: int = 0; - for (let i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >>> 7) * 0x11D); - z ^= ((y >>> i) & 1) * x; - } - if (z >>> 8 != 0) - throw "Assertion error"; - return z as byte; - } - - } - - + /*---- Private helper class ----*/ /* * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. From 76127b8bfe368a3f1ff90444ff693b4bb7af28af Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 17:09:51 +0000 Subject: [PATCH 606/810] Renamed functions and variables, and updated comments, thus synchronizing the C language version with the previous changeset. --- c/qrcodegen-test.c | 48 +++++++++++++++++++++++----------------------- c/qrcodegen.c | 43 +++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 45 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index fb91b75..db48b11 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -49,9 +49,9 @@ void appendBitsToBuffer(unsigned int val, int numBits, uint8_t buffer[], int *bi void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ecc ecl, uint8_t result[]); int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); int getNumRawDataModules(int version); -void calcReedSolomonGenerator(int degree, uint8_t result[]); -void calcReedSolomonRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]); -uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); +void reedSolomonComputeDivisor(int degree, uint8_t result[]); +void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]); +uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); void initializeFunctionModules(int version, uint8_t qrcode[]); int getAlignmentPatternPositions(int version, uint8_t result[7]); bool getModule(const uint8_t qrcode[], int x, int y); @@ -116,12 +116,12 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e // Split data into blocks and append ECC to each block uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*)); uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t)); - calcReedSolomonGenerator(blockEccLen, generator); + reedSolomonComputeDivisor(blockEccLen, generator); for (int i = 0, k = 0; i < numBlocks; i++) { uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t)); int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); memcpy(block, &data[k], datLen * sizeof(uint8_t)); - calcReedSolomonRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); + reedSolomonComputeRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); k += datLen; blocks[i] = block; } @@ -235,19 +235,19 @@ static void testGetNumRawDataModules(void) { } -static void testCalcReedSolomonGenerator(void) { +static void testReedSolomonComputeDivisor(void) { uint8_t generator[30]; - calcReedSolomonGenerator(1, generator); + reedSolomonComputeDivisor(1, generator); assert(generator[0] == 0x01); numTestCases++; - calcReedSolomonGenerator(2, generator); + reedSolomonComputeDivisor(2, generator); assert(generator[0] == 0x03); assert(generator[1] == 0x02); numTestCases++; - calcReedSolomonGenerator(5, generator); + reedSolomonComputeDivisor(5, generator); assert(generator[0] == 0x1F); assert(generator[1] == 0xC6); assert(generator[2] == 0x3F); @@ -255,7 +255,7 @@ static void testCalcReedSolomonGenerator(void) { assert(generator[4] == 0x74); numTestCases++; - calcReedSolomonGenerator(30, generator); + reedSolomonComputeDivisor(30, generator); assert(generator[ 0] == 0xD4); assert(generator[ 1] == 0xF6); assert(generator[ 5] == 0xC0); @@ -268,13 +268,13 @@ static void testCalcReedSolomonGenerator(void) { } -static void testCalcReedSolomonRemainder(void) { +static void testReedSolomonComputeRemainder(void) { { uint8_t data[1]; uint8_t generator[3]; uint8_t remainder[ARRAY_LENGTH(generator)]; - calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); - calcReedSolomonRemainder(data, 0, generator, ARRAY_LENGTH(generator), remainder); + reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); + reedSolomonComputeRemainder(data, 0, generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == 0); assert(remainder[1] == 0); assert(remainder[2] == 0); @@ -284,8 +284,8 @@ static void testCalcReedSolomonRemainder(void) { uint8_t data[2] = {0, 1}; uint8_t generator[4]; uint8_t remainder[ARRAY_LENGTH(generator)]; - calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); - calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); + reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == generator[0]); assert(remainder[1] == generator[1]); assert(remainder[2] == generator[2]); @@ -296,8 +296,8 @@ static void testCalcReedSolomonRemainder(void) { uint8_t data[5] = {0x03, 0x3A, 0x60, 0x12, 0xC7}; uint8_t generator[5]; uint8_t remainder[ARRAY_LENGTH(generator)]; - calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); - calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); + reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[0] == 0xCB); assert(remainder[1] == 0x36); assert(remainder[2] == 0x16); @@ -315,8 +315,8 @@ static void testCalcReedSolomonRemainder(void) { }; uint8_t generator[30]; uint8_t remainder[ARRAY_LENGTH(generator)]; - calcReedSolomonGenerator(ARRAY_LENGTH(generator), generator); - calcReedSolomonRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); + reedSolomonComputeDivisor(ARRAY_LENGTH(generator), generator); + reedSolomonComputeRemainder(data, ARRAY_LENGTH(data), generator, ARRAY_LENGTH(generator), remainder); assert(remainder[ 0] == 0xCE); assert(remainder[ 1] == 0xF0); assert(remainder[ 2] == 0x31); @@ -333,7 +333,7 @@ static void testCalcReedSolomonRemainder(void) { } -static void testFiniteFieldMultiply(void) { +static void testReedSolomonMultiply(void) { const uint8_t cases[][3] = { {0x00, 0x00, 0x00}, {0x01, 0x01, 0x01}, @@ -354,7 +354,7 @@ static void testFiniteFieldMultiply(void) { }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { const uint8_t *tc = cases[i]; - assert(finiteFieldMultiply(tc[0], tc[1]) == tc[2]); + assert(reedSolomonMultiply(tc[0], tc[1]) == tc[2]); numTestCases++; } } @@ -1054,9 +1054,9 @@ int main(void) { testAddEccAndInterleave(); testGetNumDataCodewords(); testGetNumRawDataModules(); - testCalcReedSolomonGenerator(); - testCalcReedSolomonRemainder(); - testFiniteFieldMultiply(); + testReedSolomonComputeDivisor(); + testReedSolomonComputeRemainder(); + testReedSolomonMultiply(); testInitializeFunctionModulesEtc(); testGetAlignmentPatternPositions(); testGetSetModule(); diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 84b47c8..d899803 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -58,10 +58,10 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec testable int getNumDataCodewords(int version, enum qrcodegen_Ecc ecl); testable int getNumRawDataModules(int ver); -testable void calcReedSolomonGenerator(int degree, uint8_t result[]); -testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]); +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]); -testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y); +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); testable void initializeFunctionModules(int version, uint8_t qrcode[]); static void drawWhiteFunctionModules(uint8_t qrcode[], int version); @@ -301,13 +301,13 @@ testable void addEccAndInterleave(uint8_t data[], int version, enum qrcodegen_Ec // Split data into blocks, calculate ECC, and interleave // (not concatenate) the bytes into a single sequence - uint8_t generator[qrcodegen_REED_SOLOMON_DEGREE_MAX]; - calcReedSolomonGenerator(blockEccLen, generator); + uint8_t rsdiv[qrcodegen_REED_SOLOMON_DEGREE_MAX]; + reedSolomonComputeDivisor(blockEccLen, rsdiv); const uint8_t *dat = data; for (int i = 0; i < numBlocks; i++) { int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); uint8_t *ecc = &data[dataLen]; // Temporary storage - calcReedSolomonRemainder(dat, datLen, generator, blockEccLen, ecc); + reedSolomonComputeRemainder(dat, datLen, rsdiv, blockEccLen, ecc); for (int j = 0, k = i; j < datLen; j++, k += numBlocks) { // Copy data if (j == shortBlockDataLen) k -= numShortBlocks; @@ -350,43 +350,44 @@ testable int getNumRawDataModules(int ver) { /*---- Reed-Solomon ECC generator functions ----*/ -// Calculates the Reed-Solomon generator polynomial of the given degree, storing in result[0 : degree]. -testable void calcReedSolomonGenerator(int degree, uint8_t result[]) { - // Start with the monomial x^0 +// Computes a Reed-Solomon ECC generator polynomial for the given degree, storing in result[0 : degree]. +// This could be implemented as a lookup table over all possible parameter values, instead of as an algorithm. +testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. memset(result, 0, degree * sizeof(result[0])); - result[degree - 1] = 1; + 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}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. + // 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). uint8_t root = 1; for (int i = 0; i < degree; i++) { // Multiply the current product by (x - r^i) for (int j = 0; j < degree; j++) { - result[j] = finiteFieldMultiply(result[j], root); + result[j] = reedSolomonMultiply(result[j], root); if (j + 1 < degree) result[j] ^= result[j + 1]; } - root = finiteFieldMultiply(root, 0x02); + root = reedSolomonMultiply(root, 0x02); } } -// Calculates the remainder of the polynomial data[0 : dataLen] when divided by the generator[0 : degree], where all -// polynomials are in big endian and the generator has an implicit leading 1 term, storing the result in result[0 : degree]. -testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, +// Computes the Reed-Solomon error correction codeword for the given data and divisor polynomials. +// The remainder when data[0 : dataLen] is divided by divisor[0 : degree] is stored in result[0 : degree]. +// All polynomials are in big endian, and the generator has an implicit leading 1 term. +testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]) { - - // Perform polynomial division assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); memset(result, 0, degree * sizeof(result[0])); - for (int i = 0; i < dataLen; i++) { + for (int i = 0; i < dataLen; i++) { // Polynomial division uint8_t factor = data[i] ^ result[0]; memmove(&result[0], &result[1], (degree - 1) * sizeof(result[0])); result[degree - 1] = 0; for (int j = 0; j < degree; j++) - result[j] ^= finiteFieldMultiply(generator[j], factor); + result[j] ^= reedSolomonMultiply(generator[j], factor); } } @@ -395,7 +396,7 @@ testable void calcReedSolomonRemainder(const uint8_t data[], int dataLen, // Returns the product of the two given field elements modulo GF(2^8/0x11D). // All inputs are valid. This could be implemented as a 256*256 lookup table. -testable uint8_t finiteFieldMultiply(uint8_t x, uint8_t y) { +testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { // Russian peasant multiplication uint8_t z = 0; for (int i = 7; i >= 0; i--) { From b6aa563812b8e5f6e0465c4591b40ef8ad64ca64 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 17:10:36 +0000 Subject: [PATCH 607/810] Tweaked a comment in QrCode.reedSolomonComputeDivisor() in 4 language versions. --- javascript/qrcodegen.js | 2 +- python/qrcodegen.py | 2 +- rust/src/lib.rs | 2 +- typescript/qrcodegen.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js index 503e5ec..043c2d0 100644 --- a/javascript/qrcodegen.js +++ b/javascript/qrcodegen.js @@ -693,7 +693,7 @@ var qrcodegen = new function() { if (degree < 1 || degree > 255) throw "Degree out of range"; // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. var result = []; for (var i = 0; i < degree - 1; i++) result.push(0); diff --git a/python/qrcodegen.py b/python/qrcodegen.py index e1e8fff..69e0c0a 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -570,7 +570,7 @@ class QrCode(object): if degree < 1 or degree > 255: raise ValueError("Degree out of range") # Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. result = [0] * (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}), diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d71a979..4a2748a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -778,7 +778,7 @@ impl QrCode { fn reed_solomon_compute_divisor(degree: usize) -> Vec { assert!(1 <= degree && degree <= 255, "Degree out of range"); // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. let mut result = vec![0u8; degree - 1]; result.push(1); // Start off with the monomial x^0 diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index f43e85e..c77f9b4 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -639,7 +639,7 @@ namespace qrcodegen { if (degree < 1 || degree > 255) throw "Degree out of range"; // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. let result: Array = []; for (let i = 0; i < degree - 1; i++) result.push(0); From ee5e4ca121c1038810edd5641a5d7ca07cad12da Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 17:10:57 +0000 Subject: [PATCH 608/810] Tweaked a few comments. --- python/qrcodegen.py | 6 +++--- rust/src/lib.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 69e0c0a..0b567b2 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -614,9 +614,9 @@ class QrCode(object): return z - # Can only be called immediately after a white run is added, and - # returns either 0, 1, or 2. A helper function for _get_penalty_score(). def _finder_penalty_count_patterns(self, runhistory): + """Can only be called immediately after a white run is added, and + returns either 0, 1, or 2. A helper function for _get_penalty_score().""" n = runhistory[1] assert n <= self._size * 3 core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3 @@ -624,8 +624,8 @@ class QrCode(object): + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0) - # Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score(). def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory): + """Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score().""" if currentruncolor: # Terminate black run runhistory.appendleft(currentrunlength) currentrunlength = 0 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 4a2748a..37ccb4c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -814,8 +814,8 @@ impl QrCode { } - // 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. + // Returns the product of the two given field elements modulo GF(2^8/0x11D). + // All inputs are valid. This could be implemented as a 256*256 lookup table. fn reed_solomon_multiply(x: u8, y: u8) -> u8 { // Russian peasant multiplication let mut z: u8 = 0; From b7c9ccfff91c750b0f8cf686de40cc784063c421 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 17:11:28 +0000 Subject: [PATCH 609/810] Simplified and clarified a few bits of code, without changing behavior. --- cpp/QrCode.cpp | 4 ++-- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 2 +- python/qrcodegen.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 56a3e3f..4630752 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -569,8 +569,8 @@ vector QrCode::reedSolomonComputeRemainder(const vector &data, uint8_t factor = b ^ result.at(0); result.erase(result.begin()); result.push_back(0); - for (size_t j = 0; j < result.size(); j++) - result.at(j) ^= reedSolomonMultiply(divisor.at(j), factor); + for (size_t i = 0; i < result.size(); i++) + result.at(i) ^= reedSolomonMultiply(divisor.at(i), factor); } return result; } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index b82ee1f..f7d8ab7 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -768,7 +768,7 @@ public final class QrCode { // 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; + assert x >> 8 == 0 && y >> 8 == 0; // Russian peasant multiplication int z = 0; for (int i = 7; i >= 0; i--) { diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 0b567b2..6ada1aa 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -567,7 +567,7 @@ class QrCode(object): def _reed_solomon_compute_divisor(degree): """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.""" - if degree < 1 or degree > 255: + if not (1 <= degree <= 255): raise ValueError("Degree out of range") # Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. From 8407d3783952b3cea4d68ba7cf321f3af463b632 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 14 Jul 2019 17:15:03 +0000 Subject: [PATCH 610/810] Updated package version numbers. --- java/pom.xml | 2 +- python/setup.py | 2 +- rust/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index 9945163..d36e826 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,7 +4,7 @@ io.nayuki qrcodegen - 1.4.0 + 1.5.0 jar UTF-8 diff --git a/python/setup.py b/python/setup.py index f4a6db1..834a5e7 100644 --- a/python/setup.py +++ b/python/setup.py @@ -27,7 +27,7 @@ import setuptools setuptools.setup( name = "qrcodegen", description = "High quality QR Code generator library for Python 2 and 3", - version = "1.4.0", + version = "1.5.0", platforms = "OS Independent", license = "MIT License", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d98c4bb..001b52d 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qrcodegen" -version = "1.4.0" +version = "1.5.0" authors = ["Project Nayuki"] description = "High-quality QR Code generator library" homepage = "https://www.nayuki.io/page/qr-code-generator-library" From 907813a82caf61e266ed3365d70d9843dec73e55 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 15 Jul 2019 23:16:50 +0000 Subject: [PATCH 611/810] Fixed reStructuredText syntax for describing the Python package. --- python/setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/setup.py b/python/setup.py index 834a5e7..97d53f8 100644 --- a/python/setup.py +++ b/python/setup.py @@ -88,6 +88,8 @@ Install this package by downloading the source code ZIP file from PyPI_, or by r Examples: +:: + from qrcodegen import * # Simple operation From 1e24fcf67a0d876f3d6e1b1a88a8313e3beee62f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 16 Jul 2019 00:07:10 +0000 Subject: [PATCH 612/810] Tweaked TypeScript code to make it compatible with ECMAScript 5. Main change is demoting class BitBuffer into plain arrays. This makes the TypeScript code architecture diverge slightly from the other 5 supported OOP languages. --- typescript/qrcodegen-demo.ts | 2 +- typescript/qrcodegen.ts | 69 +++++++++++++++--------------------- 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/typescript/qrcodegen-demo.ts b/typescript/qrcodegen-demo.ts index 55c54b5..64d468a 100644 --- a/typescript/qrcodegen-demo.ts +++ b/typescript/qrcodegen-demo.ts @@ -29,7 +29,7 @@ namespace app { function initialize(): void { let elems = document.querySelectorAll("input[type=number], textarea"); for (let el of elems) { - if (!el.id.startsWith("version-")) + if (el.id.indexOf("version-") != 0) (el as any).oninput = redrawQrCode; } elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index c77f9b4..3e436b6 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -114,10 +114,10 @@ namespace qrcodegen { } // Concatenate all segments to create the data bit string - let bb = new BitBuffer(); + let bb: Array = [] for (const seg of segs) { - bb.appendBits(seg.mode.modeBits, 4); - bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); + appendBits(seg.mode.modeBits, 4, bb); + appendBits(seg.numChars, seg.mode.numCharCountBits(version), bb); for (const b of seg.getData()) bb.push(b); } @@ -128,14 +128,14 @@ namespace qrcodegen { const dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; if (bb.length > dataCapacityBits) throw "Assertion error"; - bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); - bb.appendBits(0, (8 - bb.length % 8) % 8); + appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb); + appendBits(0, (8 - bb.length % 8) % 8, bb); if (bb.length % 8 != 0) throw "Assertion error"; // Pad with alternating bytes until data capacity is reached for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); + appendBits(padByte, 8, bb); // Pack bits into bytes in big endian let dataCodewords: Array = []; @@ -757,6 +757,16 @@ namespace qrcodegen { } + // Appends the given number of low-order bits of the given value + // to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. + function appendBits(val: int, len: int, bb: Array): void { + if (len < 0 || len > 31 || val >>> len != 0) + throw "Value out of range"; + for (let i = len - 1; i >= 0; i--) // Append bit by bit + bb.push((val >>> i) & 1); + } + + // Returns true iff the i'th bit of x is set to 1. function getBit(x: int, i: int): boolean { return ((x >>> i) & 1) != 0; @@ -785,9 +795,9 @@ namespace qrcodegen { // byte mode. All input byte arrays are acceptable. Any text string // can be converted to UTF-8 bytes and encoded as a byte mode segment. public static makeBytes(data: Array): QrSegment { - let bb = new BitBuffer(); + let bb: Array = [] for (const b of data) - bb.appendBits(b, 8); + appendBits(b, 8, bb); return new QrSegment(QrSegment.Mode.BYTE, data.length, bb); } @@ -796,10 +806,10 @@ namespace qrcodegen { public static makeNumeric(digits: string): QrSegment { if (!this.NUMERIC_REGEX.test(digits)) throw "String contains non-numeric characters"; - let bb = new BitBuffer(); + let bb: Array = [] for (let i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration const n: int = Math.min(digits.length - i, 3); - bb.appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1); + appendBits(parseInt(digits.substr(i, n), 10), n * 3 + 1, bb); i += n; } return new QrSegment(QrSegment.Mode.NUMERIC, digits.length, bb); @@ -812,15 +822,15 @@ namespace qrcodegen { public static makeAlphanumeric(text: string): QrSegment { if (!this.ALPHANUMERIC_REGEX.test(text)) throw "String contains unencodable characters in alphanumeric mode"; - let bb = new BitBuffer(); + let bb: Array = [] let i: int; for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2 let temp: int = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); - bb.appendBits(temp, 11); + appendBits(temp, 11, bb); } if (i < text.length) // 1 character remaining - bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); + appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb); return new QrSegment(QrSegment.Mode.ALPHANUMERIC, text.length, bb); } @@ -843,17 +853,17 @@ namespace qrcodegen { // Returns a segment representing an Extended Channel Interpretation // (ECI) designator with the given assignment value. public static makeEci(assignVal: int): QrSegment { - let bb = new BitBuffer(); + let bb: Array = [] if (assignVal < 0) throw "ECI assignment value out of range"; else if (assignVal < (1 << 7)) - bb.appendBits(assignVal, 8); + appendBits(assignVal, 8, bb); else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(assignVal, 14); + appendBits(2, 2, bb); + appendBits(assignVal, 14, bb); } else if (assignVal < 1000000) { - bb.appendBits(6, 3); - bb.appendBits(assignVal, 21); + appendBits(6, 3, bb); + appendBits(assignVal, 21, bb); } else throw "ECI assignment value out of range"; return new QrSegment(QrSegment.Mode.ECI, 0, bb); @@ -940,27 +950,6 @@ namespace qrcodegen { } - - - /*---- Private helper class ----*/ - - /* - * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. - * The implicit constructor creates an empty bit buffer (length 0). - */ - class BitBuffer extends Array { - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. - public appendBits(val: int, len: int): void { - if (len < 0 || len > 31 || val >>> len != 0) - throw "Value out of range"; - for (let i = len - 1; i >= 0; i--) // Append bit by bit - this.push((val >>> i) & 1); - } - - } - } From f759146df37f4920e554c5a261c943a03e5c4e27 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 16 Jul 2019 00:19:38 +0000 Subject: [PATCH 613/810] Deleted hand-written JavaScript code in favor of compiling from TypeScript, updated directories and readme. --- Readme.markdown | 22 +- javascript/qrcodegen-demo.js | 166 --- javascript/qrcodegen.js | 1055 ----------------- .../build.sh | 0 .../qrcodegen-demo.ts | 0 .../qrcodegen-js-demo.html | 0 .../qrcodegen-worker.ts | 0 .../qrcodegen.ts | 0 8 files changed, 2 insertions(+), 1241 deletions(-) delete mode 100644 javascript/qrcodegen-demo.js delete mode 100644 javascript/qrcodegen.js rename {typescript => typescript-javascript}/build.sh (100%) rename {typescript => typescript-javascript}/qrcodegen-demo.ts (100%) rename {javascript => typescript-javascript}/qrcodegen-js-demo.html (100%) rename {typescript => typescript-javascript}/qrcodegen-worker.ts (100%) rename {typescript => typescript-javascript}/qrcodegen.ts (100%) diff --git a/Readme.markdown b/Readme.markdown index 2d367d3..653dbbc 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -15,7 +15,7 @@ Features Core features: -* Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust +* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, C++, C, Rust * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (JavaScript and TypeScript only) @@ -62,7 +62,7 @@ Java language: } } -JavaScript language: +TypeScript/JavaScript languages: // Name abbreviated for the sake of these examples here var QRC = qrcodegen.QrCode; @@ -80,24 +80,6 @@ JavaScript language: } } -TypeScript language: - - // Simple operation - let qr0: qrcodegen.QrCode = qrcodegen.QrCode.encodeText( - "Hello, world!", qrcodegen.QrCode.Ecc.MEDIUM); - let svg: string = qr0.toSvgString(4); - - // Manual operation - let segs: Array = - qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); - let qr1: qrcodegen.QrCode = qrcodegen.QrCode.encodeSegments( - segs, qrcodegen.QrCode.Ecc.HIGH, 5, 5, 2, false); - for (let y = 0; y < qr1.size; y++) { - for (let x = 0; x < qr1.size; x++) { - (... paint qr1.getModule(x, y) ...) - } - } - Python language: from qrcodegen import * diff --git a/javascript/qrcodegen-demo.js b/javascript/qrcodegen-demo.js deleted file mode 100644 index 4e23ba0..0000000 --- a/javascript/qrcodegen-demo.js +++ /dev/null @@ -1,166 +0,0 @@ -/* - * QR Code generator demo (JavaScript) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -"use strict"; - - -var app = new function() { - - function initialize() { - var elems = document.querySelectorAll("input[type=number], textarea"); - for (var i = 0; i < elems.length; i++) { - if (elems[i].id.indexOf("version-") != 0) - elems[i].oninput = redrawQrCode; - } - elems = document.querySelectorAll("input[type=radio], input[type=checkbox]"); - for (var i = 0; i < elems.length; i++) - elems[i].onchange = redrawQrCode; - redrawQrCode(); - } - - - function redrawQrCode() { - // Show/hide rows based on bitmap/vector image output - var bitmapOutput = document.getElementById("output-format-bitmap").checked; - var scaleRow = document.getElementById("scale-row"); - var svgXmlRow = document.getElementById("svg-xml-row"); - if (bitmapOutput) { - scaleRow.style.removeProperty("display"); - svgXmlRow.style.display = "none"; - } else { - scaleRow.style.display = "none"; - svgXmlRow.style.removeProperty("display"); - } - var svgXml = document.getElementById("svg-xml-output"); - svgXml.value = ""; - - // Reset output images in case of early termination - var canvas = document.getElementById("qrcode-canvas"); - var svg = document.getElementById("qrcode-svg"); - canvas.style.display = "none"; - svg.style.display = "none"; - - // Returns a QrCode.Ecc object based on the radio buttons in the HTML form. - function getInputErrorCorrectionLevel() { - if (document.getElementById("errcorlvl-medium").checked) - return qrcodegen.QrCode.Ecc.MEDIUM; - else if (document.getElementById("errcorlvl-quartile").checked) - return qrcodegen.QrCode.Ecc.QUARTILE; - else if (document.getElementById("errcorlvl-high").checked) - return qrcodegen.QrCode.Ecc.HIGH; - else // In case no radio button is depressed - return qrcodegen.QrCode.Ecc.LOW; - } - - // Get form inputs and compute QR Code - var ecl = getInputErrorCorrectionLevel(); - var text = document.getElementById("text-input").value; - var segs = qrcodegen.QrSegment.makeSegments(text); - var minVer = parseInt(document.getElementById("version-min-input").value, 10); - var maxVer = parseInt(document.getElementById("version-max-input").value, 10); - var mask = parseInt(document.getElementById("mask-input").value, 10); - var boostEcc = document.getElementById("boost-ecc-input").checked; - var qr = qrcodegen.QrCode.encodeSegments(segs, ecl, minVer, maxVer, mask, boostEcc); - - // Draw image output - var border = parseInt(document.getElementById("border-input").value, 10); - if (border < 0 || border > 100) - return; - if (bitmapOutput) { - var scale = parseInt(document.getElementById("scale-input").value, 10); - if (scale <= 0 || scale > 30) - return; - qr.drawCanvas(scale, border, canvas); - canvas.style.removeProperty("display"); - } else { - var code = qr.toSvgString(border); - svg.setAttribute("viewBox", / viewBox="([^"]*)"/.exec(code)[1]); - svg.querySelector("path").setAttribute("d", / d="([^"]*)"/.exec(code)[1]); - svg.style.removeProperty("display"); - svgXml.value = qr.toSvgString(border); - } - - // Returns a string to describe the given list of segments. - function describeSegments(segs) { - if (segs.length == 0) - return "none"; - else if (segs.length == 1) { - var mode = segs[0].mode; - var Mode = qrcodegen.QrSegment.Mode; - if (mode == Mode.NUMERIC ) return "numeric"; - if (mode == Mode.ALPHANUMERIC) return "alphanumeric"; - if (mode == Mode.BYTE ) return "byte"; - if (mode == Mode.KANJI ) return "kanji"; - return "unknown"; - } else - return "multiple"; - } - - // Returns the number of Unicode code points in the given UTF-16 string. - function countUnicodeChars(str) { - var result = 0; - for (var i = 0; i < str.length; i++, result++) { - var c = str.charCodeAt(i); - if (c < 0xD800 || c >= 0xE000) - continue; - else if (0xD800 <= c && c < 0xDC00 && i + 1 < str.length) { // High surrogate - i++; - var d = str.charCodeAt(i); - if (0xDC00 <= d && d < 0xE000) // Low surrogate - continue; - } - throw "Invalid UTF-16 string"; - } - return result; - } - - // Show the QR Code symbol's statistics as a string - var stats = "QR Code version = " + qr.version + ", "; - stats += "mask pattern = " + qr.mask + ", "; - stats += "character count = " + countUnicodeChars(text) + ",\n"; - stats += "encoding mode = " + describeSegments(segs) + ", "; - stats += "error correction = level " + "LMQH".charAt(qr.errorCorrectionLevel.ordinal) + ", "; - stats += "data bits = " + qrcodegen.QrSegment.getTotalBits(segs, qr.version) + "."; - document.getElementById("statistics-output").textContent = stats; - } - - - this.handleVersionMinMax = function(which) { - var minElem = document.getElementById("version-min-input"); - var maxElem = document.getElementById("version-max-input"); - var minVal = parseInt(minElem.value, 10); - var maxVal = parseInt(maxElem.value, 10); - minVal = Math.max(Math.min(minVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); - maxVal = Math.max(Math.min(maxVal, qrcodegen.QrCode.MAX_VERSION), qrcodegen.QrCode.MIN_VERSION); - if (which == "min" && minVal > maxVal) - maxVal = minVal; - else if (which == "max" && maxVal < minVal) - minVal = maxVal; - minElem.value = minVal.toString(); - maxElem.value = maxVal.toString(); - redrawQrCode(); - } - - - initialize(); -} diff --git a/javascript/qrcodegen.js b/javascript/qrcodegen.js deleted file mode 100644 index 043c2d0..0000000 --- a/javascript/qrcodegen.js +++ /dev/null @@ -1,1055 +0,0 @@ -/* - * QR Code generator library (JavaScript) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -"use strict"; - - -/* - * Module "qrcodegen", public members: - * - Class QrCode: - * - Function encodeText(str text, QrCode.Ecc ecl) -> QrCode - * - Function encodeBinary(list data, QrCode.Ecc ecl) -> QrCode - * - Function encodeSegments(list segs, QrCode.Ecc ecl, - * int minVersion=1, int maxVersion=40, mask=-1, boostEcl=true) -> QrCode - * - Constants int MIN_VERSION, MAX_VERSION - * - Constructor QrCode(int version, QrCode.Ecc ecl, list dataCodewords, int mask) - * - Fields int version, size, mask - * - Field QrCode.Ecc errorCorrectionLevel - * - Method getModule(int x, int y) -> bool - * - Method drawCanvas(int scale, int border, HTMLCanvasElement canvas) -> void - * - Method toSvgString(int border) -> str - * - Enum Ecc: - * - Constants LOW, MEDIUM, QUARTILE, HIGH - * - Field int ordinal - * - Class QrSegment: - * - Function makeBytes(list data) -> QrSegment - * - Function makeNumeric(str data) -> QrSegment - * - Function makeAlphanumeric(str data) -> QrSegment - * - Function makeSegments(str text) -> list - * - Function makeEci(int assignVal) -> QrSegment - * - Constructor QrSegment(QrSegment.Mode mode, int numChars, list bitData) - * - Field QrSegment.Mode mode - * - Field int numChars - * - Method getData() -> list - * - Constants RegExp NUMERIC_REGEX, ALPHANUMERIC_REGEX - * - Enum Mode: - * - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI - */ -var qrcodegen = new function() { - - /*---- QR Code symbol class ----*/ - - /* - * A class that represents a QR Code symbol, which is a type of two-dimension barcode. - * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and white cells. - * The class provides static factory functions to create a QR Code from text or binary data. - * The class covers the QR Code Model 2 specification, supporting all versions (sizes) - * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. - * - * Ways to create a QR Code object: - * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary(). - * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments(). - * - Low level: Custom-make the array of data codeword bytes (including - * segment headers and final padding, excluding error correction codewords), - * supply the appropriate version number, and call the QrCode() constructor. - * (Note that all ways require supplying the desired error correction level.) - * - * This constructor creates a new QR Code with the given 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 encodeSegments() function. - */ - this.QrCode = function(version, errCorLvl, dataCodewords, mask) { - - /*---- Constructor (low level) ----*/ - - // Check scalar arguments - if (version < MIN_VERSION || version > MAX_VERSION) - throw "Version value out of range"; - if (mask < -1 || mask > 7) - throw "Mask value out of range"; - if (!(errCorLvl instanceof Ecc)) - throw "QrCode.Ecc expected"; - var size = version * 4 + 17; - - // Initialize both grids to be size*size arrays of Boolean false - var row = []; - for (var i = 0; i < size; i++) - row.push(false); - var modules = []; // Initially all white - var isFunction = []; - for (var i = 0; i < size; i++) { - modules .push(row.slice()); - isFunction.push(row.slice()); - } - - // Compute ECC, draw modules - drawFunctionPatterns(); - var allCodewords = addEccAndInterleave(dataCodewords); - drawCodewords(allCodewords); - - // Do masking - if (mask == -1) { // Automatically choose best mask - var minPenalty = Infinity; - for (var i = 0; i < 8; i++) { - applyMask(i); - drawFormatBits(i); - var penalty = getPenaltyScore(); - if (penalty < minPenalty) { - mask = i; - minPenalty = penalty; - } - applyMask(i); // Undoes the mask due to XOR - } - } - if (mask < 0 || mask > 7) - throw "Assertion error"; - applyMask(mask); // Apply the final choice of mask - drawFormatBits(mask); // Overwrite old format bits - - isFunction = null; - - - /*---- Read-only instance properties ----*/ - - // The version number of this QR Code, which is between 1 and 40 (inclusive). - // This determines the size of this barcode. - Object.defineProperty(this, "version", {value:version}); - - // The width and height of this QR Code, measured in modules, between - // 21 and 177 (inclusive). This is equal to version * 4 + 17. - Object.defineProperty(this, "size", {value:size}); - - // The error correction level used in this QR Code. - Object.defineProperty(this, "errorCorrectionLevel", {value:errCorLvl}); - - // 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. - Object.defineProperty(this, "mask", {value:mask}); - - - /*---- Accessor methods ----*/ - - // Returns the color of the module (pixel) at the given coordinates, which is false - // for white or true for black. The top left corner has the coordinates (x=0, y=0). - // If the given coordinates are out of bounds, then false (white) is returned. - this.getModule = function(x, y) { - return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; - }; - - - /*---- Public instance methods ----*/ - - // Draws this QR Code, with the given module scale and border modules, onto the given HTML - // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. - // The drawn image is be purely black and white, and fully opaque. - // The scale must be a positive integer and the border must be a non-negative integer. - this.drawCanvas = function(scale, border, canvas) { - if (scale <= 0 || border < 0) - throw "Value out of range"; - var width = (size + border * 2) * scale; - canvas.width = width; - canvas.height = width; - var ctx = canvas.getContext("2d"); - for (var y = -border; y < size + border; y++) { - for (var x = -border; x < size + border; x++) { - ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF"; - ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); - } - } - }; - - // Returns a string of SVG code for an image depicting this QR Code, with the given number - // of border modules. The string always uses Unix newlines (\n), regardless of the platform. - this.toSvgString = function(border) { - if (border < 0) - throw "Border must be non-negative"; - var parts = []; - for (var y = 0; y < size; y++) { - for (var x = 0; x < size; x++) { - if (this.getModule(x, y)) - parts.push("M" + (x + border) + "," + (y + border) + "h1v1h-1z"); - } - } - return '\n' + - '\n' + - '\n' + - '\t\n' + - '\t\n' + - '\n'; - }; - - - /*---- Private helper methods for constructor: Drawing function modules ----*/ - - // Reads this object's version field, and draws and marks all function modules. - function drawFunctionPatterns() { - // Draw horizontal and vertical timing patterns - for (var i = 0; i < size; i++) { - 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 - var alignPatPos = getAlignmentPatternPositions(); - var numAlign = alignPatPos.length; - for (var i = 0; i < numAlign; i++) { - for (var 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)) - 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. - function drawFormatBits(mask) { - // Calculate error correction code and pack bits - var data = errCorLvl.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 - var rem = data; - for (var i = 0; i < 10; i++) - rem = (rem << 1) ^ ((rem >>> 9) * 0x537); - var bits = (data << 10 | rem) ^ 0x5412; // uint15 - if (bits >>> 15 != 0) - throw "Assertion error"; - - // Draw first copy - for (var i = 0; i <= 5; i++) - setFunctionModule(8, i, getBit(bits, i)); - setFunctionModule(8, 7, getBit(bits, 6)); - setFunctionModule(8, 8, getBit(bits, 7)); - setFunctionModule(7, 8, getBit(bits, 8)); - for (var i = 9; i < 15; i++) - setFunctionModule(14 - i, 8, getBit(bits, i)); - - // Draw second copy - for (var i = 0; i < 8; i++) - setFunctionModule(size - 1 - i, 8, getBit(bits, i)); - for (var i = 8; i < 15; i++) - 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. - function drawVersion() { - if (version < 7) - return; - - // Calculate error correction code and pack bits - var rem = version; // version is uint6, in the range [7, 40] - for (var i = 0; i < 12; i++) - rem = (rem << 1) ^ ((rem >>> 11) * 0x1F25); - var bits = version << 12 | rem; // uint18 - if (bits >>> 18 != 0) - throw "Assertion error"; - - // Draw two copies - for (var i = 0; i < 18; i++) { - var bit = getBit(bits, i); - var a = size - 11 + i % 3; - var b = Math.floor(i / 3); - setFunctionModule(a, b, bit); - 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. - function drawFinderPattern(x, y) { - for (var dy = -4; dy <= 4; dy++) { - for (var dx = -4; dx <= 4; dx++) { - var dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm - var xx = x + dx, yy = y + dy; - if (0 <= xx && xx < size && 0 <= yy && yy < size) - setFunctionModule(xx, yy, dist != 2 && dist != 4); - } - } - } - - - // Draws a 5*5 alignment pattern, with the center module - // at (x, y). All modules must be in bounds. - function drawAlignmentPattern(x, y) { - for (var dy = -2; dy <= 2; dy++) { - for (var dx = -2; dx <= 2; dx++) - 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. - function setFunctionModule(x, y, 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. - function addEccAndInterleave(data) { - if (data.length != QrCode.getNumDataCodewords(version, errCorLvl)) - throw "Invalid argument"; - - // Calculate parameter numbers - var numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[errCorLvl.ordinal][version]; - var blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK [errCorLvl.ordinal][version]; - var rawCodewords = Math.floor(QrCode.getNumRawDataModules(version) / 8); - var numShortBlocks = numBlocks - rawCodewords % numBlocks; - var shortBlockLen = Math.floor(rawCodewords / numBlocks); - - // Split data into blocks and append ECC to each block - var blocks = []; - var rsDiv = QrCode.reedSolomonComputeDivisor(blockEccLen); - for (var i = 0, k = 0; i < numBlocks; i++) { - var dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); - k += dat.length; - var ecc = QrCode.reedSolomonComputeRemainder(dat, rsDiv); - if (i < numShortBlocks) - dat.push(0); - blocks.push(dat.concat(ecc)); - } - - // Interleave (not concatenate) the bytes from every block into a single sequence - var result = []; - for (var i = 0; i < blocks[0].length; i++) { - for (var j = 0; j < blocks.length; j++) { - // Skip the padding byte in short blocks - if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) - result.push(blocks[j][i]); - } - } - if (result.length != rawCodewords) - throw "Assertion error"; - 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. - function drawCodewords(data) { - if (data.length != Math.floor(QrCode.getNumRawDataModules(version) / 8)) - throw "Invalid argument"; - var i = 0; // Bit index into the data - // Do the funny zigzag scan - for (var right = size - 1; right >= 1; right -= 2) { // Index of right column in each column pair - if (right == 6) - right = 5; - for (var vert = 0; vert < size; vert++) { // Vertical counter - for (var j = 0; j < 2; j++) { - var x = right - j; // Actual x coordinate - var upward = ((right + 1) & 2) == 0; - var y = upward ? size - 1 - vert : vert; // Actual y coordinate - if (!isFunction[y][x] && i < data.length * 8) { - modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); - i++; - } - // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method - } - } - } - if (i != data.length * 8) - throw "Assertion error"; - } - - - // 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. - function applyMask(mask) { - if (mask < 0 || mask > 7) - throw "Mask value out of range"; - for (var y = 0; y < size; y++) { - for (var x = 0; x < size; x++) { - var invert; - switch (mask) { - 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 = (Math.floor(x / 3) + Math.floor(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 "Assertion error"; - } - if (!isFunction[y][x] && invert) - modules[y][x] = !modules[y][x]; - } - } - } - - - // 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. - function getPenaltyScore() { - var result = 0; - - // Adjacent modules in row having same color, and finder-like patterns - for (var y = 0; y < size; y++) { - var runColor = false; - var runX = 0; - var runHistory = [0,0,0,0,0,0,0]; - var padRun = size; - for (var x = 0; x < size; x++) { - if (modules[y][x] == runColor) { - runX++; - if (runX == 5) - result += QrCode.PENALTY_N1; - else if (runX > 5) - result++; - } else { - QrCode.finderPenaltyAddHistory(runX + padRun, runHistory); - padRun = 0; - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; - runColor = modules[y][x]; - runX = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3; - } - // Adjacent modules in column having same color, and finder-like patterns - for (var x = 0; x < size; x++) { - var runColor = false; - var runY = 0; - var runHistory = [0,0,0,0,0,0,0]; - var padRun = size; - for (var y = 0; y < size; y++) { - if (modules[y][x] == runColor) { - runY++; - if (runY == 5) - result += QrCode.PENALTY_N1; - else if (runY > 5) - result++; - } else { - QrCode.finderPenaltyAddHistory(runY + padRun, runHistory); - padRun = 0; - if (!runColor) - result += finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; - runColor = modules[y][x]; - runY = 1; - } - } - result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3; - } - - // 2*2 blocks of modules having same color - for (var y = 0; y < size - 1; y++) { - for (var x = 0; x < size - 1; x++) { - var color = modules[y][x]; - if ( color == modules[y][x + 1] && - color == modules[y + 1][x] && - color == modules[y + 1][x + 1]) - result += QrCode.PENALTY_N2; - } - } - - // Balance of black and white modules - var black = 0; - modules.forEach(function(row) { - row.forEach(function(color) { - if (color) - black++; - }); - }); - var total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - var k = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; - result += k * QrCode.PENALTY_N4; - return result; - } - - - // 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 integers. - function getAlignmentPatternPositions() { - if (version == 1) - return []; - else { - var numAlign = Math.floor(version / 7) + 2; - var step = (version == 32) ? 26 : - Math.ceil((size - 13) / (numAlign*2 - 2)) * 2; - var result = [6]; - for (var pos = size - 7; result.length < numAlign; pos -= step) - result.splice(1, 0, pos); - return result; - } - } - - - // Can only be called immediately after a white run is added, and - // returns either 0, 1, or 2. A helper function for getPenaltyScore(). - function finderPenaltyCountPatterns(runHistory) { - var n = runHistory[1]; - if (n > size * 3) - throw "Assertion error"; - var 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); - } - - - // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). - function finderPenaltyTerminateAndCount(currentRunColor, currentRunLength, runHistory) { - if (currentRunColor) { // Terminate black run - QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); - currentRunLength = 0; - } - currentRunLength += size; // Add white border to final run - QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); - return finderPenaltyCountPatterns(runHistory); - } - - - // Returns true iff the i'th bit of x is set to 1. - function getBit(x, i) { - return ((x >>> i) & 1) != 0; - } - }; - - - /*---- Static factory functions (high level) for QrCode ----*/ - - /* - * Returns a QR Code representing the given Unicode text string at the given error correction level. - * As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer - * Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible - * QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the - * ecl argument if it can be done without increasing the version. - */ - this.QrCode.encodeText = function(text, ecl) { - var segs = qrcodegen.QrSegment.makeSegments(text); - return this.encodeSegments(segs, ecl); - }; - - - /* - * Returns a QR Code representing the given binary data at the given error correction level. - * This function always encodes using the binary segment mode, not any text mode. The maximum number of - * bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. - * The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. - */ - this.QrCode.encodeBinary = function(data, ecl) { - var seg = qrcodegen.QrSegment.makeBytes(data); - return this.encodeSegments([seg], ecl); - }; - - - /*---- Static factory functions (mid level) for QrCode ----*/ - - /* - * Returns a QR Code representing the given segments with the given encoding parameters. - * The smallest possible QR Code version within the given range is automatically - * chosen for the output. Iff boostEcl is true, then the ECC level of the result - * may be higher than the ecl argument if it can be done without increasing the - * version. The mask number is either between 0 to 7 (inclusive) to force that - * mask, or -1 to automatically choose an appropriate mask (which may be slow). - * This function allows the user to create a custom sequence of segments that switches - * between modes (such as alphanumeric and byte) to encode text in less space. - * This is a mid-level API; the high-level API is encodeText() and encodeBinary(). - */ - this.QrCode.encodeSegments = function(segs, ecl, minVersion, maxVersion, mask, boostEcl) { - if (minVersion == undefined) minVersion = MIN_VERSION; - if (maxVersion == undefined) maxVersion = MAX_VERSION; - if (mask == undefined) mask = -1; - if (boostEcl == undefined) boostEcl = true; - if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) - throw "Invalid value"; - - // Find the minimal version number to use - var version, dataUsedBits; - for (version = minVersion; ; version++) { - var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available - dataUsedBits = qrcodegen.QrSegment.getTotalBits(segs, version); - if (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 - throw "Data too long"; - } - - // Increase the error correction level while the data still fits in the current version number - [this.Ecc.MEDIUM, this.Ecc.QUARTILE, this.Ecc.HIGH].forEach(function(newEcl) { // From low to high - if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) - ecl = newEcl; - }); - - // Concatenate all segments to create the data bit string - var bb = new BitBuffer(); - segs.forEach(function(seg) { - bb.appendBits(seg.mode.modeBits, 4); - bb.appendBits(seg.numChars, seg.mode.numCharCountBits(version)); - seg.getData().forEach(function(bit) { - bb.push(bit); - }); - }); - if (bb.length != dataUsedBits) - throw "Assertion error"; - - // Add terminator and pad up to a byte if applicable - var dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; - if (bb.length > dataCapacityBits) - throw "Assertion error"; - bb.appendBits(0, Math.min(4, dataCapacityBits - bb.length)); - bb.appendBits(0, (8 - bb.length % 8) % 8); - if (bb.length % 8 != 0) - throw "Assertion error"; - - // Pad with alternating bytes until data capacity is reached - for (var padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) - bb.appendBits(padByte, 8); - - // Pack bits into bytes in big endian - var dataCodewords = []; - while (dataCodewords.length * 8 < bb.length) - dataCodewords.push(0); - bb.forEach(function(bit, i) { - dataCodewords[i >>> 3] |= bit << (7 - (i & 7)); - }); - - // Create the QR Code object - return new this(version, ecl, dataCodewords, mask); - }; - - - /*---- Private static helper functions for QrCode ----*/ - - var QrCode = {}; // Private object to assign properties to. Not the same object as 'this.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. - QrCode.getNumRawDataModules = function(ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) - throw "Version number out of range"; - var result = (16 * ver + 128) * ver + 64; - if (ver >= 2) { - var numAlign = Math.floor(ver / 7) + 2; - result -= (25 * numAlign - 10) * numAlign - 55; - if (ver >= 7) - result -= 36; - } - return result; - }; - - - // 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. - QrCode.getNumDataCodewords = function(ver, ecl) { - return Math.floor(QrCode.getNumRawDataModules(ver) / 8) - - QrCode.ECC_CODEWORDS_PER_BLOCK [ecl.ordinal][ver] * - QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; - }; - - - // 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. - QrCode.reedSolomonComputeDivisor = function(degree) { - if (degree < 1 || degree > 255) - throw "Degree out of range"; - // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. - var result = []; - for (var i = 0; i < degree - 1; i++) - result.push(0); - result.push(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). - var root = 1; - for (var i = 0; i < degree; i++) { - // Multiply the current product by (x - r^i) - for (var j = 0; j < result.length; j++) { - result[j] = QrCode.reedSolomonMultiply(result[j], root); - if (j + 1 < result.length) - result[j] ^= result[j + 1]; - } - root = QrCode.reedSolomonMultiply(root, 0x02); - } - return result; - }; - - - // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. - QrCode.reedSolomonComputeRemainder = function(data, divisor) { - var result = divisor.map(function() { return 0; }); - data.forEach(function(b) { // Polynomial division - var factor = b ^ result.shift(); - result.push(0); - divisor.forEach(function(coef, i) { - result[i] ^= QrCode.reedSolomonMultiply(coef, factor); - }); - }); - 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. - QrCode.reedSolomonMultiply = function(x, y) { - if (x >>> 8 != 0 || y >>> 8 != 0) - throw "Byte out of range"; - // Russian peasant multiplication - var z = 0; - for (var i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >>> 7) * 0x11D); - z ^= ((y >>> i) & 1) * x; - } - if (z >>> 8 != 0) - throw "Assertion error"; - return z; - }; - - - // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - QrCode.finderPenaltyAddHistory = function(currentRunLength, runHistory) { - runHistory.pop(); - runHistory.unshift(currentRunLength); - }; - - - // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). - // Must only be called immediately after a run of white modules has ended. - QrCode.hasFinderLikePattern = function(runHistory) { - var n = runHistory[1]; - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; - }; - - - /*---- Constants and tables for QrCode ----*/ - - var MIN_VERSION = 1; // The minimum version number supported in the QR Code Model 2 standard - var MAX_VERSION = 40; // The maximum version number supported in the QR Code Model 2 standard - Object.defineProperty(this.QrCode, "MIN_VERSION", {value:MIN_VERSION}); - Object.defineProperty(this.QrCode, "MAX_VERSION", {value:MAX_VERSION}); - - // For use in getPenaltyScore(), when evaluating which mask is best. - QrCode.PENALTY_N1 = 3; - QrCode.PENALTY_N2 = 3; - QrCode.PENALTY_N3 = 40; - QrCode.PENALTY_N4 = 10; - - QrCode.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 - [null, 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 - [null, 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 - [null, 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 - [null, 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 - ]; - - QrCode.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 - [null, 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 - [null, 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 - [null, 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 - [null, 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. Immutable. - */ - this.QrCode.Ecc = { - LOW : new Ecc(0, 1), // The QR Code can tolerate about 7% erroneous codewords - MEDIUM : new Ecc(1, 0), // The QR Code can tolerate about 15% erroneous codewords - QUARTILE: new Ecc(2, 3), // The QR Code can tolerate about 25% erroneous codewords - HIGH : new Ecc(3, 2), // The QR Code can tolerate about 30% erroneous codewords - }; - - - // Private constructor. - function Ecc(ord, fb) { - // (Public) In the range 0 to 3 (unsigned 2-bit integer) - Object.defineProperty(this, "ordinal", {value:ord}); - - // (Package-private) In the range 0 to 3 (unsigned 2-bit integer) - Object.defineProperty(this, "formatBits", {value:fb}); - } - - - - /*---- Data segment class ----*/ - - /* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this class are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment.makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment() constructor with appropriate values. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - * This constructor creates a QR Code segment with the given attributes and data. - * The character count (numChars) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is cloned and stored. - */ - this.QrSegment = function(mode, numChars, bitData) { - /*---- Constructor (low level) ----*/ - if (numChars < 0 || !(mode instanceof Mode)) - throw "Invalid argument"; - - // The data bits of this segment. Accessed through getData(). - bitData = bitData.slice(); // Make defensive copy - - // The mode indicator of this segment. - Object.defineProperty(this, "mode", {value:mode}); - - // The length of this segment's unencoded data. Measured in characters for - // numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - // Always zero or positive. Not the same as the data's bit length. - Object.defineProperty(this, "numChars", {value:numChars}); - - // Returns a new copy of the data bits of this segment. - this.getData = function() { - return bitData.slice(); // Make defensive copy - }; - }; - - - /*---- Static factory functions (mid level) for QrSegment ----*/ - - /* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte arrays are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ - this.QrSegment.makeBytes = function(data) { - var bb = new BitBuffer(); - data.forEach(function(b) { - bb.appendBits(b, 8); - }); - return new this(this.Mode.BYTE, data.length, bb); - }; - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - this.QrSegment.makeNumeric = function(digits) { - if (!this.NUMERIC_REGEX.test(digits)) - throw "String contains non-numeric characters"; - var bb = new BitBuffer(); - for (var i = 0; i < digits.length; ) { // Consume up to 3 digits per iteration - var n = Math.min(digits.length - i, 3); - bb.appendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1); - i += n; - } - return new this(this.Mode.NUMERIC, digits.length, bb); - }; - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - this.QrSegment.makeAlphanumeric = function(text) { - if (!this.ALPHANUMERIC_REGEX.test(text)) - throw "String contains unencodable characters in alphanumeric mode"; - var bb = new BitBuffer(); - var i; - for (i = 0; i + 2 <= text.length; i += 2) { // Process groups of 2 - var temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; - temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); - bb.appendBits(temp, 11); - } - if (i < text.length) // 1 character remaining - bb.appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6); - return new this(this.Mode.ALPHANUMERIC, text.length, bb); - }; - - - /* - * Returns a new mutable list of zero or more segments to represent the given Unicode text string. - * The result may use various segment modes and switch modes to optimize the length of the bit stream. - */ - this.QrSegment.makeSegments = function(text) { - // Select the most efficient segment encoding automatically - if (text == "") - return []; - else if (this.NUMERIC_REGEX.test(text)) - return [this.makeNumeric(text)]; - else if (this.ALPHANUMERIC_REGEX.test(text)) - return [this.makeAlphanumeric(text)]; - else - return [this.makeBytes(toUtf8ByteArray(text))]; - }; - - - /* - * Returns a segment representing an Extended Channel Interpretation - * (ECI) designator with the given assignment value. - */ - this.QrSegment.makeEci = function(assignVal) { - var bb = new BitBuffer(); - if (assignVal < 0) - throw "ECI assignment value out of range"; - else if (assignVal < (1 << 7)) - bb.appendBits(assignVal, 8); - else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(assignVal, 14); - } else if (assignVal < 1000000) { - bb.appendBits(6, 3); - bb.appendBits(assignVal, 21); - } else - throw "ECI assignment value out of range"; - return new this(this.Mode.ECI, 0, bb); - }; - - - // (Package-private) Calculates and returns the number of bits needed to encode the given segments at the - // given version. The result is infinity if a segment has too many characters to fit its length field. - this.QrSegment.getTotalBits = function(segs, version) { - var result = 0; - for (var i = 0; i < segs.length; i++) { - var seg = segs[i]; - var ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1 << ccbits)) - return Infinity; // The segment's length doesn't fit the field's bit width - result += 4 + ccbits + seg.getData().length; - } - return result; - }; - - - /*---- Constants for QrSegment ----*/ - - var QrSegment = {}; // Private object to assign properties to. Not the same object as 'this.QrSegment'. - - // (Public) Describes precisely all strings that are encodable in numeric mode. - // To test whether a string s is encodable: var ok = NUMERIC_REGEX.test(s); - // A string is encodable iff each character is in the range 0 to 9. - this.QrSegment.NUMERIC_REGEX = /^[0-9]*$/; - - // (Public) Describes precisely all strings that are encodable in alphanumeric mode. - // To test whether a string s is encodable: var ok = ALPHANUMERIC_REGEX.test(s); - // A string is encodable iff each character is in the following set: 0 to 9, A to Z - // (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - this.QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; - - // (Private) The set of all legal characters in alphanumeric mode, - // where each character value maps to the index in the string. - QrSegment.ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - - - /*---- Public helper enumeration ----*/ - - /* - * Describes how a segment's data bits are interpreted. Immutable. - */ - this.QrSegment.Mode = { // Constants - NUMERIC : new Mode(0x1, [10, 12, 14]), - ALPHANUMERIC: new Mode(0x2, [ 9, 11, 13]), - BYTE : new Mode(0x4, [ 8, 16, 16]), - KANJI : new Mode(0x8, [ 8, 10, 12]), - ECI : new Mode(0x7, [ 0, 0, 0]), - }; - - - // Private constructor. - function Mode(mode, ccbits) { - // (Package-private) The mode indicator bits, which is a uint4 value (range 0 to 15). - Object.defineProperty(this, "modeBits", {value:mode}); - - // (Package-private) 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]. - this.numCharCountBits = function(ver) { - return ccbits[Math.floor((ver + 7) / 17)]; - }; - } - - - - /*---- Private helper functions and classes ----*/ - - // Returns a new array of bytes representing the given string encoded in UTF-8. - function toUtf8ByteArray(str) { - str = encodeURI(str); - var result = []; - for (var i = 0; i < str.length; i++) { - if (str.charAt(i) != "%") - result.push(str.charCodeAt(i)); - else { - result.push(parseInt(str.substring(i + 1, i + 3), 16)); - i += 2; - } - } - return result; - } - - - /* - * A private helper class that represents an appendable sequence of bits (0s and 1s). - * Mainly used by QrSegment. This constructor creates an empty bit buffer (length 0). - */ - function BitBuffer() { - Array.call(this); - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. - this.appendBits = function(val, len) { - if (len < 0 || len > 31 || val >>> len != 0) - throw "Value out of range"; - for (var i = len - 1; i >= 0; i--) // Append bit by bit - this.push((val >>> i) & 1); - }; - } - - BitBuffer.prototype = Object.create(Array.prototype); - BitBuffer.prototype.constructor = BitBuffer; - -}; diff --git a/typescript/build.sh b/typescript-javascript/build.sh similarity index 100% rename from typescript/build.sh rename to typescript-javascript/build.sh diff --git a/typescript/qrcodegen-demo.ts b/typescript-javascript/qrcodegen-demo.ts similarity index 100% rename from typescript/qrcodegen-demo.ts rename to typescript-javascript/qrcodegen-demo.ts diff --git a/javascript/qrcodegen-js-demo.html b/typescript-javascript/qrcodegen-js-demo.html similarity index 100% rename from javascript/qrcodegen-js-demo.html rename to typescript-javascript/qrcodegen-js-demo.html diff --git a/typescript/qrcodegen-worker.ts b/typescript-javascript/qrcodegen-worker.ts similarity index 100% rename from typescript/qrcodegen-worker.ts rename to typescript-javascript/qrcodegen-worker.ts diff --git a/typescript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts similarity index 100% rename from typescript/qrcodegen.ts rename to typescript-javascript/qrcodegen.ts From 1207d89c332eebeb0f63562433b2a2a158ce3cb0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 17:08:54 +0000 Subject: [PATCH 614/810] Added another feature point to the readme. --- Readme.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.markdown b/Readme.markdown index 653dbbc..998c849 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -19,6 +19,7 @@ Core features: * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (JavaScript and TypeScript only) +* Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License From f8f01c4d4e930a77e2e237dbbaf3b2c627319de0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 19:12:00 +0000 Subject: [PATCH 615/810] Refactored logic in QrTemplate and ReedSolomonGenerator into new Memozier class. --- src/io/nayuki/fastqrcodegen/Memoizer.java | 88 +++++++++++++++++++ src/io/nayuki/fastqrcodegen/QrCode.java | 4 +- src/io/nayuki/fastqrcodegen/QrTemplate.java | 51 +---------- .../fastqrcodegen/ReedSolomonGenerator.java | 53 +---------- 4 files changed, 94 insertions(+), 102 deletions(-) create mode 100644 src/io/nayuki/fastqrcodegen/Memoizer.java diff --git a/src/io/nayuki/fastqrcodegen/Memoizer.java b/src/io/nayuki/fastqrcodegen/Memoizer.java new file mode 100644 index 0000000..cbb7455 --- /dev/null +++ b/src/io/nayuki/fastqrcodegen/Memoizer.java @@ -0,0 +1,88 @@ +/* + * Fast QR Code generator library + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/fast-qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +package io.nayuki.fastqrcodegen; + +import java.lang.ref.SoftReference; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + + +// A thread-safe cache based on soft references. +final class Memoizer { + + private final Function function; + private Map> cache = new HashMap<>(); + private Set pending = new HashSet<>(); + + + // Creates a memoizer based on the given function that takes one input to compute an output. + public Memoizer(Function func) { + function = func; + } + + + // Computes function.apply(arg) or returns a cached copy of a previous call. + public R get(T arg) { + while (true) { + synchronized(this) { + if (cache.containsKey(arg)) { + SoftReference ref = cache.get(arg); + R result = ref.get(); + if (result != null) + return result; + cache.remove(arg); + } + // Now cache.containsKey(arg) == false + + if (!pending.contains(arg)) { + pending.add(arg); + break; + } + + try { + this.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + try { + R result = function.apply(arg); + synchronized(this) { + cache.put(arg, new SoftReference<>(result)); + } + return result; + } finally { + synchronized(this) { + pending.remove(arg); + this.notifyAll(); + } + } + } + +} diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 7825632..e25f377 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -248,7 +248,7 @@ public final class QrCode { errorCorrectionLevel = Objects.requireNonNull(ecl); Objects.requireNonNull(dataCodewords); - QrTemplate tpl = QrTemplate.getInstance(ver); + QrTemplate tpl = QrTemplate.MEMOIZER.get(ver); modules = tpl.template.clone(); // Compute ECC, draw modules, do masking @@ -407,7 +407,7 @@ public final class QrCode { // Split data into blocks, calculate ECC, and interleave // (not concatenate) the bytes into a single sequence byte[] result = new byte[rawCodewords]; - ReedSolomonGenerator rs = ReedSolomonGenerator.getInstance(blockEccLen); + ReedSolomonGenerator rs = ReedSolomonGenerator.MEMOIZER.get(blockEccLen); byte[] ecc = new byte[blockEccLen]; // Temporary storage per iteration for (int i = 0, k = 0; i < numBlocks; i++) { int datLen = shortBlockDataLen + (i < numShortBlocks ? 0 : 1); diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index f933bd7..b2e435a 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -25,59 +25,12 @@ package io.nayuki.fastqrcodegen; import static io.nayuki.fastqrcodegen.QrCode.MAX_VERSION; import static io.nayuki.fastqrcodegen.QrCode.MIN_VERSION; -import java.lang.ref.SoftReference; final class QrTemplate { - /*---- Factory members ----*/ - - public static QrTemplate getInstance(int version) { - if (version < MIN_VERSION || version > MAX_VERSION) - throw new IllegalArgumentException("Version out of range"); - - while (true) { - synchronized(cache) { - SoftReference ref = cache[version]; - if (ref != null) { - QrTemplate result = ref.get(); - if (result != null) - return result; - cache[version] = null; - } - - if (!isPending[version]) { - isPending[version] = true; - break; - } - - try { - cache.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - - try { - QrTemplate tpl = new QrTemplate(version); - synchronized(cache) { - cache[version] = new SoftReference<>(tpl); - } - return tpl; - } finally { - synchronized(cache) { - isPending[version] = false; - cache.notifyAll(); - } - } - } - - - @SuppressWarnings("unchecked") - private static final SoftReference[] cache = new SoftReference[MAX_VERSION + 1]; - - private static final boolean[] isPending = new boolean[MAX_VERSION + 1]; + public static final Memoizer MEMOIZER + = new Memoizer<>(QrTemplate::new); diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index aef45c0..697bc81 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -23,63 +23,14 @@ package io.nayuki.fastqrcodegen; -import java.lang.ref.SoftReference; import java.util.Arrays; import java.util.Objects; final class ReedSolomonGenerator { - /*---- Factory members ----*/ - - public static ReedSolomonGenerator getInstance(int degree) { - if (degree < 1 || degree > MAX_DEGREE) - throw new IllegalArgumentException("Degree out of range"); - - while (true) { - synchronized(cache) { - SoftReference ref = cache[degree]; - if (ref != null) { - ReedSolomonGenerator result = ref.get(); - if (result != null) - return result; - cache[degree] = null; - } - - if (!isPending[degree]) { - isPending[degree] = true; - break; - } - - try { - cache.wait(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - } - - try { - ReedSolomonGenerator rs = new ReedSolomonGenerator(degree); - synchronized(cache) { - cache[degree] = new SoftReference<>(rs); - } - return rs; - } finally { - synchronized(cache) { - isPending[degree] = false; - cache.notifyAll(); - } - } - } - - - private static final int MAX_DEGREE = 30; - - @SuppressWarnings("unchecked") - private static final SoftReference[] cache = new SoftReference[MAX_DEGREE + 1]; - - private static final boolean[] isPending = new boolean[MAX_DEGREE + 1]; + public static final Memoizer MEMOIZER + = new Memoizer<>(ReedSolomonGenerator::new); From fa7a0926729a297cce99e670e12fc1b1449fb26f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 19:13:19 +0000 Subject: [PATCH 616/810] Deleted some section comments. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 3 --- src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index b2e435a..c8da8d9 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -33,9 +33,6 @@ final class QrTemplate { = new Memoizer<>(QrTemplate::new); - - /*---- Instance members ----*/ - private final int version; private final int size; diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index 697bc81..971aa02 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -33,9 +33,6 @@ final class ReedSolomonGenerator { = new Memoizer<>(ReedSolomonGenerator::new); - - /*---- Instance members ----*/ - // A table of size 256 * degree, where polynomialMultiply[i][j] = multiply(i, coefficients[j]). // 'coefficients' is the temporary array representing the coefficients of the divisor polynomial, // stored from highest to lowest power, excluding the leading term which is always 1. @@ -90,9 +87,6 @@ final class ReedSolomonGenerator { } - - /*---- Constant members ----*/ - // 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 multiply(int x, int y) { From 960b9cd32dd9c8fb8580f57c5cdee0f6aa1b181b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 19:13:46 +0000 Subject: [PATCH 617/810] Deleted some static imports. --- src/io/nayuki/fastqrcodegen/QrTemplate.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index c8da8d9..98cdeb7 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -23,9 +23,6 @@ package io.nayuki.fastqrcodegen; -import static io.nayuki.fastqrcodegen.QrCode.MAX_VERSION; -import static io.nayuki.fastqrcodegen.QrCode.MIN_VERSION; - final class QrTemplate { @@ -45,7 +42,7 @@ final class QrTemplate { private QrTemplate(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw new IllegalArgumentException("Version out of range"); version = ver; size = version * 4 + 17; @@ -249,7 +246,7 @@ final class QrTemplate { // 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. static int getNumRawDataModules(int ver) { - if (ver < MIN_VERSION || ver > MAX_VERSION) + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw new IllegalArgumentException("Version number out of range"); int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { From 6d6e0f3fded0d5ac0f0e69fd2e8a759c340873ab Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 23:54:55 +0000 Subject: [PATCH 618/810] Added and updated many comments, almost all at the member level (rarely within functions), some with original wording, some synchronizing with the main qrcodegen project. --- src/io/nayuki/fastqrcodegen/BitBuffer.java | 45 +++++-------------- src/io/nayuki/fastqrcodegen/QrCode.java | 13 +++--- src/io/nayuki/fastqrcodegen/QrTemplate.java | 21 ++++++--- .../fastqrcodegen/ReedSolomonGenerator.java | 18 ++++---- 4 files changed, 42 insertions(+), 55 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/BitBuffer.java b/src/io/nayuki/fastqrcodegen/BitBuffer.java index 18e1027..fafafd5 100644 --- a/src/io/nayuki/fastqrcodegen/BitBuffer.java +++ b/src/io/nayuki/fastqrcodegen/BitBuffer.java @@ -27,24 +27,20 @@ import java.util.Arrays; import java.util.Objects; -/** - * An appendable sequence of bits (0s and 1s). Mainly used by {@link QrSegment}. - */ +// An appendable sequence of bits (0s and 1s), mainly used by QrSegment. final class BitBuffer { /*---- Fields ----*/ - int[] data; + int[] data; // In each 32-bit word, bits are filled from top down. - int bitLength; + int bitLength; // Always non-negative. /*---- Constructor ----*/ - /** - * Constructs an empty bit buffer (length 0). - */ + // Creates an empty bit buffer. public BitBuffer() { data = new int[64]; bitLength = 0; @@ -54,10 +50,7 @@ final class BitBuffer { /*---- Methods ----*/ - /** - * Returns the length of this sequence, which is a non-negative value. - * @return the length of this sequence - */ + // Returns the bit at the given index, yielding 0 or 1. public int getBit(int index) { if (index < 0 || index >= bitLength) throw new IndexOutOfBoundsException(); @@ -65,11 +58,8 @@ final class BitBuffer { } - /** - * Returns an array representing this buffer's bits packed into bytes - * in big endian. The current bit length must be a multiple of 8. - * @return a new byte array (not {@code null}) representing this bit sequence - */ + // Returns a new array representing this buffer's bits packed into + // bytes in big endian. The current bit length must be a multiple of 8. public byte[] getBytes() { if (bitLength % 8 != 0) throw new IllegalStateException("Data is not a whole number of bytes"); @@ -80,15 +70,8 @@ final class BitBuffer { } - /** - * Appends the specified number of low-order bits of the specified value to this - * buffer. Requires 0 ≤ len ≤ 31 and 0 ≤ val < 2len. - * @param val the value to append - * @param len the number of low-order bits in the value to take - * @throws IllegalArgumentException if the value or number of bits is out of range - * @throws IllegalStateException if appending the data - * would make bitLength exceed Integer.MAX_VALUE - */ + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. public void appendBits(int val, int len) { if (len < 0 || len > 31 || val >>> len != 0) throw new IllegalArgumentException("Value out of range"); @@ -114,14 +97,8 @@ final class BitBuffer { } - /** - * Appends the specified sequence of bits to this buffer. - * Requires 0 ≤ len ≤ 32 × vals.length. - * @param vals the sequence of bits to append (not {@code null}) - * @param len the number of prefix bits to read from the array - * @throws IllegalStateException if appending the data - * would make bitLength exceed Integer.MAX_VALUE - */ + // Appends to this buffer the sequence of bits represented by the given + // word array and given bit length. Requires 0 <= len <= 32 * vals.length. public void appendBits(int[] vals, int len) { Objects.requireNonNull(vals); if (len == 0) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index e25f377..31c9a6e 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -215,9 +215,8 @@ public final class QrCode { * −1), the resulting object still has a mask value between 0 and 7. */ public final int mask; - // Private grid of modules/pixels: - - // The modules of this QR Code. Immutable after constructor finishes. Accessed through getModule(). + // Private grid of modules of this QR Code, packed tightly into bits. + // Immutable after constructor finishes. Accessed through getModule(). private final int[] modules; @@ -424,8 +423,8 @@ public final class QrCode { } - // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire - // data area of this QR Code symbol. Function modules need to be marked off before this is called. + // Draws the given sequence of 8-bit codewords (data and error correction) + // onto the entire data area of this QR Code, based on the given bit indexes. private void drawCodewords(int[] dataOutputBitIndexes, byte[] allCodewords) { Objects.requireNonNull(dataOutputBitIndexes); Objects.requireNonNull(allCodewords); @@ -443,7 +442,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 symbol needs exactly one (not zero, two, etc.) mask applied. + // QR Code needs exactly one (not zero, two, etc.) mask applied. private void applyMask(int[] mask) { if (mask.length != modules.length) throw new IllegalArgumentException(); @@ -453,7 +452,7 @@ 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. + // method is called. The 'mask' 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[][] masks, int mask) { if (mask == -1) { // Automatically choose best mask diff --git a/src/io/nayuki/fastqrcodegen/QrTemplate.java b/src/io/nayuki/fastqrcodegen/QrTemplate.java index 98cdeb7..b774cce 100644 --- a/src/io/nayuki/fastqrcodegen/QrTemplate.java +++ b/src/io/nayuki/fastqrcodegen/QrTemplate.java @@ -24,23 +24,28 @@ package io.nayuki.fastqrcodegen; +// Stores the parts of a QR Code that depend only on the version number, +// and does not depend on the data or error correction level or mask. final class QrTemplate { + // Use this memoizer to get instances of this class. public static final Memoizer MEMOIZER = new Memoizer<>(QrTemplate::new); - private final int version; - private final int size; + private final int version; // In the range [1, 40]. + private final int size; // Derived from version. - final int[] template; - final int[][] masks; - final int[] dataOutputBitIndexes; + final int[] template; // Length and values depend on version. + final int[][] masks; // masks.length == 8, and masks[i].length == template.length. + final int[] dataOutputBitIndexes; // Length and values depend on version. // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + // Otherwise when the constructor is running, isFunction.length == template.length. private int[] isFunction; + // Creates a QR Code template for the given version number. private QrTemplate(int ver) { if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) throw new IllegalArgumentException("Version out of range"); @@ -56,6 +61,7 @@ final class QrTemplate { } + // 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++) { @@ -101,7 +107,7 @@ final class QrTemplate { darkenFunctionModule(size - 1 - i, 8, 0); for (int i = 8; i < 15; i++) darkenFunctionModule(8, size - 15 + i, 0); - darkenFunctionModule(8, size - 8, 1); + darkenFunctionModule(8, size - 8, 1); // Always black } @@ -153,6 +159,7 @@ final class QrTemplate { } + // Computes and returns a new array of masks, based on this object's various fields. private int[][] generateMasks() { int[][] result = new int[8][template.length]; for (int mask = 0; mask < result.length; mask++) { @@ -180,6 +187,7 @@ final class QrTemplate { } + // Computes and returns an array of bit indexes, based on this object's various fields. private int[] generateZigzagScan() { int[] result = new int[getNumRawDataModules(version) / 8 * 8]; int i = 0; // Bit index into the data @@ -203,6 +211,7 @@ final class QrTemplate { } + // Returns the value of the bit at the given coordinates in the given grid. private int getModule(int[] grid, int x, int y) { assert 0 <= x && x < size; assert 0 <= y && y < size; diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index 971aa02..cb2def7 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -27,29 +27,31 @@ import java.util.Arrays; import java.util.Objects; +// Computes Reed-Solomon error correction codewords for given data codewords. final class ReedSolomonGenerator { + // Use this memoizer to get instances of this class. public static final Memoizer MEMOIZER = new Memoizer<>(ReedSolomonGenerator::new); // A table of size 256 * degree, where polynomialMultiply[i][j] = multiply(i, coefficients[j]). - // 'coefficients' is the temporary array representing the coefficients of the divisor polynomial, - // stored from highest to lowest power, excluding the leading term which is always 1. - // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. + // 'coefficients' is the temporary array computed in the constructor. private byte[][] polynomialMultiply; + // Creates a Reed-Solomon ECC generator polynomial for the given degree. private ReedSolomonGenerator(int degree) { if (degree < 1 || degree > 255) throw new IllegalArgumentException("Degree out of range"); - // Start with the monomial x^0 + // The divisor polynomial, whose coefficients are stored from highest to lowest power. + // For example, x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. byte[] coefficients = new byte[degree]; - coefficients[degree - 1] = 1; + coefficients[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}), - // drop the highest term, and store the rest of the coefficients in order of descending powers. + // 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). int root = 1; for (int i = 0; i < degree; i++) { @@ -70,15 +72,15 @@ final class ReedSolomonGenerator { } + // Returns the error correction codeword for the given data polynomial and this divisor polynomial. public void getRemainder(byte[] data, int dataOff, int dataLen, byte[] result) { Objects.requireNonNull(data); Objects.requireNonNull(result); int degree = polynomialMultiply[0].length; assert result.length == degree; - // Compute the remainder by performing polynomial division Arrays.fill(result, (byte)0); - for (int i = dataOff, dataEnd = dataOff + dataLen; i < dataEnd; i++) { + for (int i = dataOff, dataEnd = dataOff + dataLen; i < dataEnd; i++) { // Polynomial division byte[] table = polynomialMultiply[(data[i] ^ result[0]) & 0xFF]; for (int j = 0; j < degree - 1; j++) result[j] = (byte)(result[j + 1] ^ table[j]); From afc12dc9d22fa08ad91654c2023253247a238a87 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 23:56:02 +0000 Subject: [PATCH 619/810] Synchronized some code with the parent project, without changing behavior. --- src/io/nayuki/fastqrcodegen/QrCode.java | 4 ++-- src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java | 1 - src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 31c9a6e..6d44c47 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -458,8 +458,8 @@ public final class QrCode { if (mask == -1) { // Automatically choose best mask int minPenalty = Integer.MAX_VALUE; for (int i = 0; i < 8; i++) { - drawFormatBits(i); applyMask(masks[i]); + drawFormatBits(i); int penalty = getPenaltyScore(); if (penalty < minPenalty) { mask = i; @@ -469,8 +469,8 @@ public final class QrCode { } } assert 0 <= mask && mask <= 7; - drawFormatBits(mask); // Overwrite old format bits applyMask(masks[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 } diff --git a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java index fc0c9d1..5bf9b30 100644 --- a/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java +++ b/src/io/nayuki/fastqrcodegen/QrCodeGeneratorWorker.java @@ -81,7 +81,6 @@ public final class QrCodeGeneratorWorker { else 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); // Print grid of modules diff --git a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java index cb2def7..a4b77be 100644 --- a/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java +++ b/src/io/nayuki/fastqrcodegen/ReedSolomonGenerator.java @@ -92,7 +92,7 @@ final class ReedSolomonGenerator { // 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 multiply(int x, int y) { - assert x >>> 8 == 0 && y >>> 8 == 0; + assert x >> 8 == 0 && y >> 8 == 0; // Russian peasant multiplication int z = 0; for (int i = 7; i >= 0; i--) { From 4671dca0ad45c3d0915e0c0cd9453da4afd51409 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 20 Jul 2019 23:56:18 +0000 Subject: [PATCH 620/810] Tweaked a bit of code for simplicity. --- src/io/nayuki/fastqrcodegen/QrCode.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 6d44c47..0093a27 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -377,13 +377,10 @@ public final class QrCode { private void setModule(int x, int y, int black) { assert 0 <= x && x < size; assert 0 <= y && y < size; + assert black == 0 || black == 1; int i = y * size + x; - if (black == 0) - modules[i >>> 5] &= ~(1 << i); - else if (black == 1) - modules[i >>> 5] |= 1 << i; - else - throw new IllegalArgumentException(); + modules[i >>> 5] &= ~(1 << i); + modules[i >>> 5] |= black << i; } From 42c357ae1c73633a73b26ac6de7f297e00a78956 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 21 Jul 2019 00:01:11 +0000 Subject: [PATCH 621/810] Renamed a local variable. --- src/io/nayuki/fastqrcodegen/QrCode.java | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 0093a27..1928014 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -482,13 +482,13 @@ public final class QrCode { // Iterate over adjacent pairs of rows for (int index = 0, downIndex = size, end = size * size; index < end; ) { Arrays.fill(runHistory, 0); - int color = 0; + int runColor = 0; int runX = 0; int curRow = 0; int nextRow = 0; for (int x = 0; x < size; x++, index++, downIndex++) { int c = getBit(modules[index >>> 5], index); - if (c == color) { + if (c == runColor) { runX++; if (runX == 5) result += PENALTY_N1; @@ -496,9 +496,9 @@ public final class QrCode { result++; } else { addRunToHistory(runX, runHistory); - if (color == 0 && hasFinderLikePattern(runHistory)) + if (runColor == 0 && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = c; + runColor = c; runX = 1; } black += c; @@ -511,7 +511,7 @@ public final class QrCode { } } addRunToHistory(runX, runHistory); - if (color == 1) + if (runColor == 1) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; @@ -520,11 +520,11 @@ public final class QrCode { // Iterate over single columns for (int x = 0; x < size; x++) { Arrays.fill(runHistory, 0); - int color = 0; + int runColor = 0; int runY = 0; for (int y = 0, index = x; y < size; y++, index += size) { int c = getBit(modules[index >>> 5], index); - if (c == color) { + if (c == runColor) { runY++; if (runY == 5) result += PENALTY_N1; @@ -532,14 +532,14 @@ public final class QrCode { result++; } else { addRunToHistory(runY, runHistory); - if (color == 0 && hasFinderLikePattern(runHistory)) + if (runColor == 0 && hasFinderLikePattern(runHistory)) result += PENALTY_N3; - color = c; + runColor = c; runY = 1; } } addRunToHistory(runY, runHistory); - if (color == 1) + if (runColor == 1) addRunToHistory(0, runHistory); // Dummy run of white if (hasFinderLikePattern(runHistory)) result += PENALTY_N3; From f05a8f9098bd1bbad12b04bf87d783bb4f192c99 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 21 Jul 2019 00:04:52 +0000 Subject: [PATCH 622/810] Replaced the finder-like pattern detection algorithm with a more sophisticated and accurate one, synchronizing with the parent project. --- src/io/nayuki/fastqrcodegen/QrCode.java | 68 ++++++++++++++----------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index 1928014..b629391 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -481,9 +481,10 @@ public final class QrCode { // Iterate over adjacent pairs of rows for (int index = 0, downIndex = size, end = size * size; index < end; ) { - Arrays.fill(runHistory, 0); int runColor = 0; int runX = 0; + Arrays.fill(runHistory, 0); + int padRun = size; // Add white border to initial run int curRow = 0; int nextRow = 0; for (int x = 0; x < size; x++, index++, downIndex++) { @@ -495,9 +496,10 @@ public final class QrCode { else if (runX > 5) result++; } else { - addRunToHistory(runX, runHistory); - if (runColor == 0 && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runX + padRun, runHistory); + padRun = 0; + if (runColor == 0) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = c; runX = 1; } @@ -510,18 +512,15 @@ public final class QrCode { result += PENALTY_N2; } } - addRunToHistory(runX, runHistory); - if (runColor == 1) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3; } // Iterate over single columns for (int x = 0; x < size; x++) { - Arrays.fill(runHistory, 0); int runColor = 0; int runY = 0; + Arrays.fill(runHistory, 0); + int padRun = size; // Add white border to initial run for (int y = 0, index = x; y < size; y++, index += size) { int c = getBit(modules[index >>> 5], index); if (c == runColor) { @@ -531,18 +530,15 @@ public final class QrCode { else if (runY > 5) result++; } else { - addRunToHistory(runY, runHistory); - if (runColor == 0 && hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + finderPenaltyAddHistory(runY + padRun, runHistory); + padRun = 0; + if (runColor == 0) + result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = c; runY = 1; } } - addRunToHistory(runY, runHistory); - if (runColor == 1) - addRunToHistory(0, runHistory); // Dummy run of white - if (hasFinderLikePattern(runHistory)) - result += PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3; } // Balance of black and white modules @@ -567,21 +563,33 @@ public final class QrCode { } - // Inserts the given value to the front of the given array, which shifts over the - // existing values and deletes the last value. A helper function for getPenaltyScore(). - private static void addRunToHistory(int run, int[] history) { - System.arraycopy(history, 0, history, 1, history.length - 1); - history[0] = run; + // 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) { + int n = runHistory[1]; + 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); } - // Tests whether the given run history has the pattern of ratio 1:1:3:1:1 in the middle, and - // surrounded by at least 4 on either or both ends. A helper function for getPenaltyScore(). - // Must only be called immediately after a run of white modules has ended. - private static boolean hasFinderLikePattern(int[] runHistory) { - int n = runHistory[1]; - return n > 0 && runHistory[2] == n && runHistory[4] == n && runHistory[5] == n - && runHistory[3] == n * 3 && Math.max(runHistory[0], runHistory[6]) >= n * 4; + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + private int finderPenaltyTerminateAndCount(int currentRunColor, int currentRunLength, int[] runHistory) { + if (currentRunColor == 1) { // Terminate black run + finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += size; // Add white border to final run + finderPenaltyAddHistory(currentRunLength, runHistory); + return finderPenaltyCountPatterns(runHistory); + } + + + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { + System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); + runHistory[0] = currentRunLength; } From 1edf83109fabbbe6c6a2aa044bdd63540078c2eb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 21 Jul 2019 17:18:03 +0000 Subject: [PATCH 623/810] Updated readme text, synchronized package-info Javadoc text with the readme. --- Readme.markdown | 7 ++++--- src/io/nayuki/fastqrcodegen/package-info.java | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index bd70143..1f83e40 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -5,9 +5,9 @@ Fast QR Code generator library Introduction ------------ -This Java library generates QR Code symbols, and its design is optimized for speed. It contrasts with another QR library by the same author which is slow but which optimizes for clarity and conciseness. The functionality of this library and its API are nearly identical to the slow library, but it runs anywhere from 1.5× to 6× as fast. +This Java library generates QR Code symbols, and its design is optimized for speed. It contrasts with another QR library by the same author which is slow but which optimizes for clarity and conciseness. The functionality of this library and its API are nearly identical to the slow library, but it runs anywhere from 1.5× to 10× as fast. -Home page for the fast library (design explanation, benchmarks): https://www.nayuki.io/page/fast-qr-code-generator-library +Home page for the fast library (design explanation, benchmarks): [https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library) Home page for the slow library (live demo, QR Code introduction, competitor comparisons): [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) @@ -22,6 +22,7 @@ Core features: * Encodes numeric and special-alphanumeric text in less space than general text * Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes * Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts +* Detects finder-like penalty patterns more accurately than other implementations * Open source code under the permissive MIT License Manual parameters: @@ -59,7 +60,7 @@ Examples License ------- -Copyright © 2018 Project Nayuki. (MIT License) +Copyright © 2019 Project Nayuki. (MIT License) [https://www.nayuki.io/page/fast-qr-code-generator-library](https://www.nayuki.io/page/fast-qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of diff --git a/src/io/nayuki/fastqrcodegen/package-info.java b/src/io/nayuki/fastqrcodegen/package-info.java index e70dc6c..50ddb0f 100644 --- a/src/io/nayuki/fastqrcodegen/package-info.java +++ b/src/io/nayuki/fastqrcodegen/package-info.java @@ -8,11 +8,12 @@ *

    Features

    *

    Core features:

    *
      - *
    • Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust

    • - *
    • Significantly shorter code but more documentation comments compared to competing libraries

    • *
    • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

    • *
    • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

    • *
    • Encodes numeric and special-alphanumeric text in less space than general text

    • + *
    • Encodes Japanese Unicode text in kanji mode to save a lot of space compared to UTF-8 bytes

    • + *
    • Computes optimal segment mode switching for text with mixed numeric/alphanumeric/general/kanji parts

    • + *
    • Detects finder-like penalty patterns more accurately than other implementations

    • *
    • Open source code under the permissive MIT License

    • *
    *

    Manual parameters:

    From c3479c004321f53735471c8717eb19564a4f9722 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 14:59:37 +0000 Subject: [PATCH 624/810] In C++ version, added explicit casts from int/long to uint32_t for first argument of calls to BitBuffer.appendBits(). --- cpp/QrCode.cpp | 4 ++-- cpp/QrCodeGeneratorDemo.cpp | 2 +- cpp/QrSegment.cpp | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 4630752..faff7ae 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -96,8 +96,8 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, // Concatenate all segments to create the data bit string BitBuffer bb; for (const QrSegment &seg : segs) { - bb.appendBits(seg.getMode().getModeBits(), 4); - bb.appendBits(seg.getNumChars(), seg.getMode().numCharCountBits(version)); + bb.appendBits(static_cast(seg.getMode().getModeBits()), 4); + bb.appendBits(static_cast(seg.getNumChars()), seg.getMode().numCharCountBits(version)); bb.insert(bb.end(), seg.getData().begin(), seg.getData().end()); } if (bb.size() != static_cast(dataUsedBits)) diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 1286433..d8ee821 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -153,7 +153,7 @@ static void doSegmentDemo() { }; qrcodegen::BitBuffer bb; for (int c : kanjiChars) - bb.appendBits(c, 13); + bb.appendBits(static_cast(c), 13); const QrCode qr5 = QrCode::encodeSegments( {QrSegment(QrSegment::Mode::KANJI, kanjiChars.size(), bb)}, QrCode::Ecc::LOW); diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index 64d77f7..a12f273 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -81,13 +81,13 @@ QrSegment QrSegment::makeNumeric(const char *digits) { accumData = accumData * 10 + (c - '0'); accumCount++; if (accumCount == 3) { - bb.appendBits(accumData, 10); + bb.appendBits(static_cast(accumData), 10); accumData = 0; accumCount = 0; } } if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(accumData, accumCount * 3 + 1); + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); } @@ -104,13 +104,13 @@ QrSegment QrSegment::makeAlphanumeric(const char *text) { accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); accumCount++; if (accumCount == 2) { - bb.appendBits(accumData, 11); + bb.appendBits(static_cast(accumData), 11); accumData = 0; accumCount = 0; } } if (accumCount > 0) // 1 character remaining - bb.appendBits(accumData, 6); + bb.appendBits(static_cast(accumData), 6); return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); } @@ -138,13 +138,13 @@ QrSegment QrSegment::makeEci(long assignVal) { if (assignVal < 0) throw std::domain_error("ECI assignment value out of range"); else if (assignVal < (1 << 7)) - bb.appendBits(assignVal, 8); + bb.appendBits(static_cast(assignVal), 8); else if (assignVal < (1 << 14)) { bb.appendBits(2, 2); - bb.appendBits(assignVal, 14); + bb.appendBits(static_cast(assignVal), 14); } else if (assignVal < 1000000L) { bb.appendBits(6, 3); - bb.appendBits(assignVal, 21); + bb.appendBits(static_cast(assignVal), 21); } else throw std::domain_error("ECI assignment value out of range"); return QrSegment(Mode::ECI, 0, std::move(bb)); From 1fb40bc11354bf216fc031809af560778a44dc0e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 15:00:00 +0000 Subject: [PATCH 625/810] In C++ version, added explicit integer casts for second argument of calls to BitBuffer.appendBits(). --- cpp/QrCode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index faff7ae..c849f44 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -107,8 +107,8 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; if (bb.size() > dataCapacityBits) throw std::logic_error("Assertion error"); - bb.appendBits(0, std::min(4, dataCapacityBits - bb.size())); - bb.appendBits(0, (8 - bb.size() % 8) % 8); + bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); + bb.appendBits(0, (8 - static_cast(bb.size() % 8)) % 8); if (bb.size() % 8 != 0) throw std::logic_error("Assertion error"); From 419b5ae2d79665dcf198aed2cb5cb11a747af180 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 15:02:22 +0000 Subject: [PATCH 626/810] In C++ version, added some more explicit casts for integer signedness and width. --- cpp/QrCode.cpp | 6 +++--- cpp/QrCodeGeneratorWorker.cpp | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index c849f44..9eef0e4 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -104,7 +104,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, throw std::logic_error("Assertion error"); // Add terminator and pad up to a byte if applicable - size_t dataCapacityBits = getNumDataCodewords(version, ecl) * 8; + size_t dataCapacityBits = static_cast(getNumDataCodewords(version, ecl)) * 8; if (bb.size() > dataCapacityBits) throw std::logic_error("Assertion error"); bb.appendBits(0, std::min(4, static_cast(dataCapacityBits - bb.size()))); @@ -235,7 +235,7 @@ void QrCode::drawFunctionPatterns() { // Draw numerous alignment patterns const vector alignPatPos = getAlignmentPatternPositions(); - int numAlign = alignPatPos.size(); + int numAlign = static_cast(alignPatPos.size()); for (int i = 0; i < numAlign; i++) { for (int j = 0; j < numAlign; j++) { // Don't draw on the three finder corners @@ -348,7 +348,7 @@ vector QrCode::addEccAndInterleave(const vector &data) const { const vector rsDiv = reedSolomonComputeDivisor(blockEccLen); for (int i = 0, k = 0; i < numBlocks; i++) { vector dat(data.cbegin() + k, data.cbegin() + (k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1))); - k += dat.size(); + k += static_cast(dat.size()); const vector ecc = reedSolomonComputeRemainder(dat, rsDiv); if (i < numShortBlocks) dat.push_back(0); diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp index 2295593..073107f 100644 --- a/cpp/QrCodeGeneratorWorker.cpp +++ b/cpp/QrCodeGeneratorWorker.cpp @@ -26,6 +26,7 @@ * Software. */ +#include #include #include #include @@ -83,7 +84,7 @@ int main() { try { // Try to make QR Code symbol const QrCode qr = QrCode::encodeSegments(segs, - ECC_LEVELS.at(errCorLvl), minVersion, maxVersion, mask, boostEcl == 1); + ECC_LEVELS.at(static_cast(errCorLvl)), minVersion, maxVersion, mask, boostEcl == 1); // Print grid of modules std::cout << qr.getVersion() << std::endl; for (int y = 0; y < qr.getSize(); y++) { From a8a91e0d38e4af6f2a6d666a14bdf8d218726221 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 15:18:18 +0000 Subject: [PATCH 627/810] In C++ version, fixed all remaining implicit integer signedness conversions with the help of GCC's "-Wsign-conversion" and "-Wconversion". --- cpp/QrCode.cpp | 32 ++++++++++++++++++-------------- cpp/QrCodeGeneratorDemo.cpp | 2 +- cpp/QrSegment.cpp | 2 +- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 9eef0e4..e6a287d 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -135,8 +135,9 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) if (mask < -1 || mask > 7) throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; - modules = vector >(size, vector(size)); // Initially all white - isFunction = vector >(size, vector(size)); + size_t sz = static_cast(size); + modules = vector >(sz, vector(sz)); // Initially all white + isFunction = vector >(sz, vector(sz)); // Compute ECC, draw modules drawFunctionPatterns(); @@ -235,9 +236,9 @@ void QrCode::drawFunctionPatterns() { // Draw numerous alignment patterns const vector alignPatPos = getAlignmentPatternPositions(); - int numAlign = static_cast(alignPatPos.size()); - for (int i = 0; i < numAlign; i++) { - for (int j = 0; j < numAlign; j++) { + size_t numAlign = alignPatPos.size(); + for (size_t i = 0; i < numAlign; i++) { + for (size_t 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))) drawAlignmentPattern(alignPatPos.at(i), alignPatPos.at(j)); @@ -322,13 +323,15 @@ void QrCode::drawAlignmentPattern(int x, int y) { void QrCode::setFunctionModule(int x, int y, bool isBlack) { - modules.at(y).at(x) = isBlack; - isFunction.at(y).at(x) = true; + size_t ux = static_cast(x); + size_t uy = static_cast(y); + modules.at(uy).at(ux) = isBlack; + isFunction.at(uy).at(ux) = true; } bool QrCode::module(int x, int y) const { - return modules.at(y).at(x); + return modules.at(static_cast(y)).at(static_cast(x)); } @@ -382,9 +385,9 @@ void QrCode::drawCodewords(const vector &data) { right = 5; for (int vert = 0; vert < size; vert++) { // Vertical counter for (int j = 0; j < 2; j++) { - int x = right - j; // Actual x coordinate + size_t x = static_cast(right - j); // Actual x coordinate bool upward = ((right + 1) & 2) == 0; - int y = upward ? size - 1 - vert : vert; // Actual y coordinate + size_t y = static_cast(upward ? size - 1 - vert : vert); // Actual y coordinate if (!isFunction.at(y).at(x) && i < data.size() * 8) { modules.at(y).at(x) = getBit(data.at(i >> 3), 7 - static_cast(i & 7)); i++; @@ -402,8 +405,9 @@ void QrCode::drawCodewords(const vector &data) { void QrCode::applyMask(int mask) { if (mask < 0 || mask > 7) throw std::domain_error("Mask value out of range"); - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { + size_t sz = static_cast(size); + for (size_t y = 0; y < sz; y++) { + for (size_t x = 0; x < sz; x++) { bool invert; switch (mask) { case 0: invert = (x + y) % 2 == 0; break; @@ -543,8 +547,8 @@ vector QrCode::reedSolomonComputeDivisor(int degree) { throw std::domain_error("Degree out of range"); // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - vector result(degree); - result.at(degree - 1) = 1; // Start off with the monomial x^0 + vector result(static_cast(degree)); + result.at(result.size() - 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. diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index d8ee821..8284cfa 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -155,7 +155,7 @@ static void doSegmentDemo() { for (int c : kanjiChars) bb.appendBits(static_cast(c), 13); const QrCode qr5 = QrCode::encodeSegments( - {QrSegment(QrSegment::Mode::KANJI, kanjiChars.size(), bb)}, + {QrSegment(QrSegment::Mode::KANJI, static_cast(kanjiChars.size()), bb)}, QrCode::Ecc::LOW); printQr(qr5); } diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp index a12f273..45ea843 100644 --- a/cpp/QrSegment.cpp +++ b/cpp/QrSegment.cpp @@ -101,7 +101,7 @@ QrSegment QrSegment::makeAlphanumeric(const char *text) { const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); if (temp == nullptr) throw std::domain_error("String contains unencodable characters in alphanumeric mode"); - accumData = accumData * 45 + (temp - ALPHANUMERIC_CHARSET); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); accumCount++; if (accumCount == 2) { bb.appendBits(static_cast(accumData), 11); From 9312480978d70003a2051e766ee2208cd00eac69 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 16:31:22 +0000 Subject: [PATCH 628/810] In C version, added many explicit integer type casts for signedness, with the help of GCC's "-Wsign-conversion" and "-Wconversion". --- c/qrcodegen-demo.c | 2 +- c/qrcodegen-test.c | 34 +++++++++++++++++----------------- c/qrcodegen-worker.c | 10 +++++----- c/qrcodegen.c | 34 ++++++++++++++++++---------------- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/c/qrcodegen-demo.c b/c/qrcodegen-demo.c index ef44108..872f48b 100644 --- a/c/qrcodegen-demo.c +++ b/c/qrcodegen-demo.c @@ -224,7 +224,7 @@ static void doSegmentDemo(void) { uint8_t *segBuf = calloc(qrcodegen_calcSegmentBufferSize(qrcodegen_Mode_KANJI, len), sizeof(uint8_t)); struct qrcodegen_Segment seg; seg.mode = qrcodegen_Mode_KANJI; - seg.numChars = len; + seg.numChars = (int)len; seg.bitLength = 0; for (size_t i = 0; i < len; i++) { for (int j = 12; j >= 0; j--, seg.bitLength++) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index db48b11..30bb318 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -114,13 +114,13 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e int shortBlockLen = rawCodewords / numBlocks; // Split data into blocks and append ECC to each block - uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*)); - uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t)); + uint8_t **blocks = malloc((size_t)numBlocks * sizeof(uint8_t*)); + uint8_t *generator = malloc((size_t)blockEccLen * sizeof(uint8_t)); reedSolomonComputeDivisor(blockEccLen, generator); for (int i = 0, k = 0; i < numBlocks; i++) { - uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t)); + uint8_t *block = malloc((size_t)(shortBlockLen + 1) * sizeof(uint8_t)); int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); - memcpy(block, &data[k], datLen * sizeof(uint8_t)); + memcpy(block, &data[k], (size_t)datLen * sizeof(uint8_t)); reedSolomonComputeRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); k += datLen; blocks[i] = block; @@ -128,7 +128,7 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e free(generator); // Interleave (not concatenate) the bytes from every block into a single sequence - uint8_t *result = malloc(rawCodewords * sizeof(uint8_t)); + uint8_t *result = malloc((size_t)rawCodewords * sizeof(uint8_t)); for (int i = 0, k = 0; i < shortBlockLen + 1; i++) { for (int j = 0; j < numBlocks; j++) { // Skip the padding byte in short blocks @@ -149,18 +149,18 @@ static void testAddEccAndInterleave(void) { for (int version = 1; version <= 40; version++) { for (int ecl = 0; ecl < 4; ecl++) { int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); - uint8_t *pureData = malloc(dataLen * sizeof(uint8_t)); + uint8_t *pureData = malloc((size_t)dataLen * sizeof(uint8_t)); for (int i = 0; i < dataLen; i++) - pureData[i] = rand() % 256; + pureData[i] = (uint8_t)(rand() % 256); uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl); int dataAndEccLen = getNumRawDataModules(version) / 8; - uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t)); - memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); - uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t)); + uint8_t *paddedData = malloc((size_t)dataAndEccLen * sizeof(uint8_t)); + memcpy(paddedData, pureData, (size_t)dataLen * sizeof(uint8_t)); + uint8_t *actualOutput = malloc((size_t)dataAndEccLen * sizeof(uint8_t)); addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); - assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); + assert(memcmp(actualOutput, expectOutput, (size_t)dataAndEccLen * sizeof(uint8_t)) == 0); free(pureData); free(expectOutput); free(paddedData); @@ -362,7 +362,7 @@ static void testReedSolomonMultiply(void) { static void testInitializeFunctionModulesEtc(void) { for (int ver = 1; ver <= 40; ver++) { - uint8_t *qrcode = malloc(qrcodegen_BUFFER_LEN_FOR_VERSION(ver) * sizeof(uint8_t)); + uint8_t *qrcode = malloc((size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(ver) * sizeof(uint8_t)); assert(qrcode != NULL); initializeFunctionModules(ver, qrcode); @@ -718,7 +718,7 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, cases[i][0]) == cases[i][1]); + assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, (size_t)cases[i][0]) == cases[i][1]); numTestCases++; } } @@ -748,7 +748,7 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, cases[i][0]) == cases[i][1]); + assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, (size_t)cases[i][0]) == cases[i][1]); numTestCases++; } } @@ -777,7 +777,7 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, cases[i][0]) == cases[i][1]); + assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, (size_t)cases[i][0]) == cases[i][1]); numTestCases++; } } @@ -805,7 +805,7 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, cases[i][0]) == cases[i][1]); + assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, (size_t)cases[i][0]) == cases[i][1]); numTestCases++; } } @@ -1049,7 +1049,7 @@ static void testGetTotalBits(void) { /*---- Main runner ----*/ int main(void) { - srand(time(NULL)); + srand((unsigned int)time(NULL)); testAppendBitsToBuffer(); testAddEccAndInterleave(); testGetNumDataCodewords(); diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c index b57d30e..b41a037 100644 --- a/c/qrcodegen-worker.c +++ b/c/qrcodegen-worker.c @@ -47,7 +47,7 @@ int main(void) { // Read data bytes bool isAscii = true; - uint8_t *data = malloc(length * sizeof(uint8_t)); + uint8_t *data = malloc((size_t)length * sizeof(uint8_t)); if (data == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -67,8 +67,8 @@ int main(void) { // Allocate memory for QR Code int bufferLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - uint8_t *qrcode = malloc(bufferLen * sizeof(uint8_t)); - uint8_t *tempBuffer = malloc(bufferLen * sizeof(uint8_t)); + uint8_t *qrcode = malloc((size_t)bufferLen * sizeof(uint8_t)); + uint8_t *tempBuffer = malloc((size_t)bufferLen * sizeof(uint8_t)); if (qrcode == NULL || tempBuffer == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -77,7 +77,7 @@ int main(void) { // Try to make QR Code symbol bool ok; if (isAscii) { - char *text = malloc((length + 1) * sizeof(char)); + char *text = malloc((size_t)(length + 1) * sizeof(char)); if (text == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -89,7 +89,7 @@ int main(void) { minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); free(text); } else if (length <= bufferLen) { - memcpy(tempBuffer, data, length * sizeof(data[0])); + memcpy(tempBuffer, data, (size_t)length * sizeof(data[0])); ok = qrcodegen_encodeBinary(tempBuffer, (size_t)length, qrcode, (enum qrcodegen_Ecc)errCorLvl, minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); } else diff --git a/c/qrcodegen.c b/c/qrcodegen.c index d899803..612409a 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -132,7 +132,7 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode size_t textLen = strlen(text); if (textLen == 0) return qrcodegen_encodeSegmentsAdvanced(NULL, 0, ecl, minVersion, maxVersion, mask, boostEcl, tempBuffer, qrcode); - size_t bufLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + size_t bufLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); struct qrcodegen_Segment seg; if (qrcodegen_isNumeric(text)) { @@ -228,14 +228,16 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz } // Concatenate all segments to create the data bit string - memset(qrcode, 0, qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); + memset(qrcode, 0, (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(version) * sizeof(qrcode[0])); int bitLen = 0; for (size_t i = 0; i < len; i++) { const struct qrcodegen_Segment *seg = &segs[i]; - appendBitsToBuffer((int)seg->mode, 4, qrcode, &bitLen); - appendBitsToBuffer(seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); - for (int j = 0; j < seg->bitLength; j++) - appendBitsToBuffer((seg->data[j >> 3] >> (7 - (j & 7))) & 1, 1, qrcode, &bitLen); + appendBitsToBuffer((unsigned int)seg->mode, 4, qrcode, &bitLen); + appendBitsToBuffer((unsigned int)seg->numChars, numCharCountBits(seg->mode, version), qrcode, &bitLen); + for (int j = 0; j < seg->bitLength; j++) { + int bit = (seg->data[j >> 3] >> (7 - (j & 7))) & 1; + appendBitsToBuffer((unsigned int)bit, 1, qrcode, &bitLen); + } } assert(bitLen == dataUsedBits); @@ -356,7 +358,7 @@ testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array {255, 8, 93}. - memset(result, 0, degree * sizeof(result[0])); + memset(result, 0, (size_t)degree * sizeof(result[0])); 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}), @@ -381,10 +383,10 @@ testable void reedSolomonComputeDivisor(int degree, uint8_t result[]) { testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, const uint8_t generator[], int degree, uint8_t result[]) { assert(1 <= degree && degree <= qrcodegen_REED_SOLOMON_DEGREE_MAX); - memset(result, 0, degree * sizeof(result[0])); + memset(result, 0, (size_t)degree * sizeof(result[0])); for (int i = 0; i < dataLen; i++) { // Polynomial division uint8_t factor = data[i] ^ result[0]; - memmove(&result[0], &result[1], (degree - 1) * sizeof(result[0])); + memmove(&result[0], &result[1], (size_t)(degree - 1) * sizeof(result[0])); result[degree - 1] = 0; for (int j = 0; j < degree; j++) result[j] ^= reedSolomonMultiply(generator[j], factor); @@ -400,7 +402,7 @@ testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { // Russian peasant multiplication uint8_t z = 0; for (int i = 7; i >= 0; i--) { - z = (z << 1) ^ ((z >> 7) * 0x11D); + z = (uint8_t)((z << 1) ^ ((z >> 7) * 0x11D)); z ^= ((y >> i) & 1) * x; } return z; @@ -415,7 +417,7 @@ testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { testable void initializeFunctionModules(int version, uint8_t qrcode[]) { // Initialize QR Code int qrsize = version * 4 + 17; - memset(qrcode, 0, ((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); + memset(qrcode, 0, (size_t)((qrsize * qrsize + 7) / 8 + 1) * sizeof(qrcode[0])); qrcode[0] = (uint8_t)qrsize; // Fill horizontal and vertical timing patterns @@ -551,7 +553,7 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { int step = (version == 32) ? 26 : (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) - result[i] = pos; + result[i] = (uint8_t)pos; result[0] = 6; return numAlign; } @@ -962,16 +964,16 @@ struct qrcodegen_Segment qrcodegen_makeEci(long assignVal, uint8_t buf[]) { assert(false); else if (assignVal < (1 << 7)) { memset(buf, 0, 1 * sizeof(buf[0])); - appendBitsToBuffer(assignVal, 8, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)assignVal, 8, buf, &result.bitLength); } else if (assignVal < (1 << 14)) { memset(buf, 0, 2 * sizeof(buf[0])); appendBitsToBuffer(2, 2, buf, &result.bitLength); - appendBitsToBuffer(assignVal, 14, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)assignVal, 14, buf, &result.bitLength); } else if (assignVal < 1000000L) { memset(buf, 0, 3 * sizeof(buf[0])); appendBitsToBuffer(6, 3, buf, &result.bitLength); - appendBitsToBuffer(assignVal >> 10, 11, buf, &result.bitLength); - appendBitsToBuffer(assignVal & 0x3FF, 10, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal >> 10), 11, buf, &result.bitLength); + appendBitsToBuffer((unsigned int)(assignVal & 0x3FF), 10, buf, &result.bitLength); } else assert(false); result.data = buf; From 68d4cb52173d97c369248999f80eca7e01696ec8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:07:09 +0000 Subject: [PATCH 629/810] Clarified C qrcodegen-test addEccAndInterleaveReference() so that its internals use size_t instead of int. --- c/qrcodegen-test.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 30bb318..0db1b29 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -107,30 +107,30 @@ static void testAppendBitsToBuffer(void) { // Ported from the Java version of the code. static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, enum qrcodegen_Ecc ecl) { // Calculate parameter numbers - int numBlocks = NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; - int blockEccLen = ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; - int rawCodewords = getNumRawDataModules(version) / 8; - int numShortBlocks = numBlocks - rawCodewords % numBlocks; - int shortBlockLen = rawCodewords / numBlocks; + size_t numBlocks = (size_t)NUM_ERROR_CORRECTION_BLOCKS[(int)ecl][version]; + size_t blockEccLen = (size_t)ECC_CODEWORDS_PER_BLOCK[(int)ecl][version]; + size_t rawCodewords = (size_t)getNumRawDataModules(version) / 8; + size_t numShortBlocks = numBlocks - rawCodewords % numBlocks; + size_t shortBlockLen = rawCodewords / numBlocks; // Split data into blocks and append ECC to each block - uint8_t **blocks = malloc((size_t)numBlocks * sizeof(uint8_t*)); - uint8_t *generator = malloc((size_t)blockEccLen * sizeof(uint8_t)); - reedSolomonComputeDivisor(blockEccLen, generator); - for (int i = 0, k = 0; i < numBlocks; i++) { - uint8_t *block = malloc((size_t)(shortBlockLen + 1) * sizeof(uint8_t)); - int datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); - memcpy(block, &data[k], (size_t)datLen * sizeof(uint8_t)); - reedSolomonComputeRemainder(&data[k], datLen, generator, blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); + uint8_t **blocks = malloc(numBlocks * sizeof(uint8_t*)); + uint8_t *generator = malloc(blockEccLen * sizeof(uint8_t)); + reedSolomonComputeDivisor((int)blockEccLen, generator); + for (size_t i = 0, k = 0; i < numBlocks; i++) { + uint8_t *block = malloc((shortBlockLen + 1) * sizeof(uint8_t)); + size_t datLen = shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1); + memcpy(block, &data[k], datLen * sizeof(uint8_t)); + reedSolomonComputeRemainder(&data[k], (int)datLen, generator, (int)blockEccLen, &block[shortBlockLen + 1 - blockEccLen]); k += datLen; blocks[i] = block; } free(generator); // Interleave (not concatenate) the bytes from every block into a single sequence - uint8_t *result = malloc((size_t)rawCodewords * sizeof(uint8_t)); - for (int i = 0, k = 0; i < shortBlockLen + 1; i++) { - for (int j = 0; j < numBlocks; j++) { + uint8_t *result = malloc(rawCodewords * sizeof(uint8_t)); + for (size_t i = 0, k = 0; i < shortBlockLen + 1; i++) { + for (size_t j = 0; j < numBlocks; j++) { // Skip the padding byte in short blocks if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) { result[k] = blocks[j][i]; @@ -138,7 +138,7 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e } } } - for (int i = 0; i < numBlocks; i++) + for (size_t i = 0; i < numBlocks; i++) free(blocks[i]); free(blocks); return result; From ae0ff71ff1066a09afa5ff000f4d6561ff46e7b9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:26:56 +0000 Subject: [PATCH 630/810] Clarified C qrcodegen-test testAddEccAndInterleave() so that its internals use size_t instead of int. --- c/qrcodegen-test.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 0db1b29..0992a41 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -148,19 +148,19 @@ static uint8_t *addEccAndInterleaveReference(const uint8_t *data, int version, e static void testAddEccAndInterleave(void) { for (int version = 1; version <= 40; version++) { for (int ecl = 0; ecl < 4; ecl++) { - int dataLen = getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); - uint8_t *pureData = malloc((size_t)dataLen * sizeof(uint8_t)); - for (int i = 0; i < dataLen; i++) + size_t dataLen = (size_t)getNumDataCodewords(version, (enum qrcodegen_Ecc)ecl); + uint8_t *pureData = malloc(dataLen * sizeof(uint8_t)); + for (size_t i = 0; i < dataLen; i++) pureData[i] = (uint8_t)(rand() % 256); uint8_t *expectOutput = addEccAndInterleaveReference(pureData, version, (enum qrcodegen_Ecc)ecl); - int dataAndEccLen = getNumRawDataModules(version) / 8; - uint8_t *paddedData = malloc((size_t)dataAndEccLen * sizeof(uint8_t)); - memcpy(paddedData, pureData, (size_t)dataLen * sizeof(uint8_t)); - uint8_t *actualOutput = malloc((size_t)dataAndEccLen * sizeof(uint8_t)); + size_t dataAndEccLen = (size_t)getNumRawDataModules(version) / 8; + uint8_t *paddedData = malloc(dataAndEccLen * sizeof(uint8_t)); + memcpy(paddedData, pureData, dataLen * sizeof(uint8_t)); + uint8_t *actualOutput = malloc(dataAndEccLen * sizeof(uint8_t)); addEccAndInterleave(paddedData, version, (enum qrcodegen_Ecc)ecl, actualOutput); - assert(memcmp(actualOutput, expectOutput, (size_t)dataAndEccLen * sizeof(uint8_t)) == 0); + assert(memcmp(actualOutput, expectOutput, dataAndEccLen * sizeof(uint8_t)) == 0); free(pureData); free(expectOutput); free(paddedData); From a492346364a40eb931865a5d0e550d6137ff5657 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:30:40 +0000 Subject: [PATCH 631/810] Clarified C qrcodegen-worker so that two internal variables use size_t instead of int. --- c/qrcodegen-worker.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c index b41a037..51678bb 100644 --- a/c/qrcodegen-worker.c +++ b/c/qrcodegen-worker.c @@ -39,20 +39,24 @@ int main(void) { while (true) { // Read data length or exit - int length; - if (scanf("%d", &length) != 1) - return EXIT_FAILURE; - if (length == -1) - break; + size_t length; + { + int temp; + if (scanf("%d", &temp) != 1) + return EXIT_FAILURE; + if (temp == -1) + break; + length = (size_t)temp; + } // Read data bytes bool isAscii = true; - uint8_t *data = malloc((size_t)length * sizeof(uint8_t)); + uint8_t *data = malloc(length * sizeof(uint8_t)); if (data == NULL) { perror("malloc"); return EXIT_FAILURE; } - for (int i = 0; i < length; i++) { + for (size_t i = 0; i < length; i++) { int b; if (scanf("%d", &b) != 1) return EXIT_FAILURE; @@ -66,9 +70,9 @@ int main(void) { return EXIT_FAILURE; // Allocate memory for QR Code - int bufferLen = qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - uint8_t *qrcode = malloc((size_t)bufferLen * sizeof(uint8_t)); - uint8_t *tempBuffer = malloc((size_t)bufferLen * sizeof(uint8_t)); + size_t bufferLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); + uint8_t *qrcode = malloc(bufferLen * sizeof(uint8_t)); + uint8_t *tempBuffer = malloc(bufferLen * sizeof(uint8_t)); if (qrcode == NULL || tempBuffer == NULL) { perror("malloc"); return EXIT_FAILURE; @@ -77,20 +81,20 @@ int main(void) { // Try to make QR Code symbol bool ok; if (isAscii) { - char *text = malloc((size_t)(length + 1) * sizeof(char)); + char *text = malloc((length + 1) * sizeof(char)); if (text == NULL) { perror("malloc"); return EXIT_FAILURE; } - for (int i = 0; i < length; i++) + for (size_t i = 0; i < length; i++) text[i] = (char)data[i]; text[length] = '\0'; ok = qrcodegen_encodeText(text, tempBuffer, qrcode, (enum qrcodegen_Ecc)errCorLvl, minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); free(text); } else if (length <= bufferLen) { - memcpy(tempBuffer, data, (size_t)length * sizeof(data[0])); - ok = qrcodegen_encodeBinary(tempBuffer, (size_t)length, qrcode, (enum qrcodegen_Ecc)errCorLvl, + memcpy(tempBuffer, data, length * sizeof(data[0])); + ok = qrcodegen_encodeBinary(tempBuffer, length, qrcode, (enum qrcodegen_Ecc)errCorLvl, minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); } else ok = false; From 42f753cfcf7d5d5540c51203b944aaf5c4646d78 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:31:19 +0000 Subject: [PATCH 632/810] Fixed indentation in a line of C code. --- c/qrcodegen-worker.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c index 51678bb..dc16888 100644 --- a/c/qrcodegen-worker.c +++ b/c/qrcodegen-worker.c @@ -59,7 +59,7 @@ int main(void) { for (size_t i = 0; i < length; i++) { int b; if (scanf("%d", &b) != 1) - return EXIT_FAILURE; + return EXIT_FAILURE; data[i] = (uint8_t)b; isAscii &= 0 < b && b < 128; } From 1a254cf67fc2e9cb62c883b60b68775bfbf43232 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:44:19 +0000 Subject: [PATCH 633/810] Refactored C qrcodegen-test's testCalcSegmentBitLength() to add a test case struct. --- c/qrcodegen-test.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 0992a41..4648dcf 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -694,8 +694,12 @@ static void testCalcSegmentBufferSize(void) { static void testCalcSegmentBitLength(void) { + struct TestCase { + size_t numChars; + int result; + }; { - const int cases[][2] = { + const struct TestCase CASES[] = { {0, 0}, {1, 4}, {2, 7}, @@ -717,13 +721,13 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 2, -1}, {INT_MAX / 1, -1}, }; - for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, (size_t)cases[i][0]) == cases[i][1]); + for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { - const int cases[][2] = { + const struct TestCase CASES[] = { {0, 0}, {1, 6}, {2, 11}, @@ -747,13 +751,13 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 2, -1}, {INT_MAX / 1, -1}, }; - for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, (size_t)cases[i][0]) == cases[i][1]); + for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { - const int cases[][2] = { + const struct TestCase CASES[] = { {0, 0}, {1, 8}, {2, 16}, @@ -776,13 +780,13 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 2, -1}, {INT_MAX / 1, -1}, }; - for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, (size_t)cases[i][0]) == cases[i][1]); + for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, CASES[i].numChars) == CASES[i].result); numTestCases++; } } { - const int cases[][2] = { + const struct TestCase CASES[] = { {0, 0}, {1, 13}, {2, 26}, @@ -804,8 +808,8 @@ static void testCalcSegmentBitLength(void) { {INT_MAX / 2, -1}, {INT_MAX / 1, -1}, }; - for (size_t i = 0; i < ARRAY_LENGTH(cases); i++) { - assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, (size_t)cases[i][0]) == cases[i][1]); + for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { + assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, CASES[i].numChars) == CASES[i].result); numTestCases++; } } From 0ebd221a2727fce6b7506ff6f6764f5bef14a3e4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:56:28 +0000 Subject: [PATCH 634/810] Updated C test suite for an internal function because the parameter is size_t and not int. (The function and test were introduced in commit 08108ee6d8ae.) --- c/qrcodegen-test.c | 42 +++++++++++++++++++----------------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 4648dcf..37612d7 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -717,9 +717,10 @@ static void testCalcSegmentBitLength(void) { {9832, -1}, {12000, -1}, {28453, -1}, - {INT_MAX / 3, -1}, - {INT_MAX / 2, -1}, - {INT_MAX / 1, -1}, + {SIZE_MAX / 6, -1}, + {SIZE_MAX / 3, -1}, + {SIZE_MAX / 2, -1}, + {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_NUMERIC, CASES[i].numChars) == CASES[i].result); @@ -745,11 +746,10 @@ static void testCalcSegmentBitLength(void) { {5959, -1}, {12000, -1}, {28453, -1}, - {INT_MAX / 5, -1}, - {INT_MAX / 4, -1}, - {INT_MAX / 3, -1}, - {INT_MAX / 2, -1}, - {INT_MAX / 1, -1}, + {SIZE_MAX / 10, -1}, + {SIZE_MAX / 5, -1}, + {SIZE_MAX / 2, -1}, + {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_ALPHANUMERIC, CASES[i].numChars) == CASES[i].result); @@ -771,14 +771,11 @@ static void testCalcSegmentBitLength(void) { {5957, -1}, {12000, -1}, {28453, -1}, - {INT_MAX / 8 + 1, -1}, - {INT_MAX / 7, -1}, - {INT_MAX / 6, -1}, - {INT_MAX / 5, -1}, - {INT_MAX / 4, -1}, - {INT_MAX / 3, -1}, - {INT_MAX / 2, -1}, - {INT_MAX / 1, -1}, + {SIZE_MAX / 15, -1}, + {SIZE_MAX / 12, -1}, + {SIZE_MAX / 7, -1}, + {SIZE_MAX / 3, -1}, + {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_BYTE, CASES[i].numChars) == CASES[i].result); @@ -800,13 +797,12 @@ static void testCalcSegmentBitLength(void) { {2522, -1}, {12000, -1}, {28453, -1}, - {INT_MAX / 13 + 1, -1}, - {INT_MAX / 12, -1}, - {INT_MAX / 9, -1}, - {INT_MAX / 4, -1}, - {INT_MAX / 3, -1}, - {INT_MAX / 2, -1}, - {INT_MAX / 1, -1}, + {SIZE_MAX / 25, -1}, + {SIZE_MAX / 20, -1}, + {SIZE_MAX / 11, -1}, + {SIZE_MAX / 4, -1}, + {SIZE_MAX / 2, -1}, + {SIZE_MAX / 1, -1}, }; for (size_t i = 0; i < ARRAY_LENGTH(CASES); i++) { assert(calcSegmentBitLength(qrcodegen_Mode_KANJI, CASES[i].numChars) == CASES[i].result); From 5efbdc48223d4922f052327c93ea509bce92061d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 22 Jul 2019 17:57:06 +0000 Subject: [PATCH 635/810] Added whitespace to align some code. --- cpp/QrCode.cpp | 2 +- typescript/qrcodegen.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index e6a287d..e169174 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -325,7 +325,7 @@ void QrCode::drawAlignmentPattern(int x, int y) { void QrCode::setFunctionModule(int x, int y, bool isBlack) { size_t ux = static_cast(x); size_t uy = static_cast(y); - modules.at(uy).at(ux) = isBlack; + modules .at(uy).at(ux) = isBlack; isFunction.at(uy).at(ux) = true; } diff --git a/typescript/qrcodegen.ts b/typescript/qrcodegen.ts index 3e436b6..a7f0ab4 100644 --- a/typescript/qrcodegen.ts +++ b/typescript/qrcodegen.ts @@ -26,9 +26,9 @@ namespace qrcodegen { - type bit = number; + type bit = number; type byte = number; - type int = number; + type int = number; /*---- QR Code symbol class ----*/ From 588d1a6b8727920919c9848471abdf21891a0c5f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 18:01:16 +0000 Subject: [PATCH 636/810] Tweaked HTML application to add warning message if scripts aren't loaded. --- typescript-javascript/qrcodegen-demo.ts | 2 ++ typescript-javascript/qrcodegen-js-demo.html | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/typescript-javascript/qrcodegen-demo.ts b/typescript-javascript/qrcodegen-demo.ts index 64d468a..fef368e 100644 --- a/typescript-javascript/qrcodegen-demo.ts +++ b/typescript-javascript/qrcodegen-demo.ts @@ -27,6 +27,8 @@ namespace app { function initialize(): void { + (document.getElementById("loading") as HTMLElement).style.display = "none"; + (document.getElementById("loaded") as HTMLElement).style.removeProperty("display"); let elems = document.querySelectorAll("input[type=number], textarea"); for (let el of elems) { if (el.id.indexOf("version-") != 0) diff --git a/typescript-javascript/qrcodegen-js-demo.html b/typescript-javascript/qrcodegen-js-demo.html index afaa90f..ff8ecf7 100644 --- a/typescript-javascript/qrcodegen-js-demo.html +++ b/typescript-javascript/qrcodegen-js-demo.html @@ -51,7 +51,12 @@

    QR Code generator demo library (JavaScript)

    -
    +
    +

    Loading application...

    +

    (Are the JavaScript files missing?)

    +

    (The JavaScript code needs to be compiled from the TypeScript code.)

    +
    + From e20059b9cadd0240d3334bf79021b97538ff7b5b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 18:08:40 +0000 Subject: [PATCH 637/810] Renamed demo HTML and TypeScript files. --- typescript-javascript/build.sh | 2 +- .../{qrcodegen-js-demo.html => qrcodegen-input-demo.html} | 2 +- .../{qrcodegen-demo.ts => qrcodegen-input-demo.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename typescript-javascript/{qrcodegen-js-demo.html => qrcodegen-input-demo.html} (98%) rename typescript-javascript/{qrcodegen-demo.ts => qrcodegen-input-demo.ts} (100%) diff --git a/typescript-javascript/build.sh b/typescript-javascript/build.sh index 53eb740..c98b65d 100644 --- a/typescript-javascript/build.sh +++ b/typescript-javascript/build.sh @@ -21,7 +21,7 @@ # Software. # -tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-demo.ts +tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-input-demo.ts if [ '!' -d node_modules ]; then npm install @types/node diff --git a/typescript-javascript/qrcodegen-js-demo.html b/typescript-javascript/qrcodegen-input-demo.html similarity index 98% rename from typescript-javascript/qrcodegen-js-demo.html rename to typescript-javascript/qrcodegen-input-demo.html index ff8ecf7..652f7c1 100644 --- a/typescript-javascript/qrcodegen-js-demo.html +++ b/typescript-javascript/qrcodegen-input-demo.html @@ -126,7 +126,7 @@
    - +

    Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

    diff --git a/typescript-javascript/qrcodegen-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts similarity index 100% rename from typescript-javascript/qrcodegen-demo.ts rename to typescript-javascript/qrcodegen-input-demo.ts From e9f71336924e74ea2063d1cd9ca8bc8d6f34c524 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 18:09:42 +0000 Subject: [PATCH 638/810] Indented the HTML file fully. --- .../qrcodegen-input-demo.html | 218 +++++++++--------- 1 file changed, 109 insertions(+), 109 deletions(-) diff --git a/typescript-javascript/qrcodegen-input-demo.html b/typescript-javascript/qrcodegen-input-demo.html index 652f7c1..f4d5828 100644 --- a/typescript-javascript/qrcodegen-input-demo.html +++ b/typescript-javascript/qrcodegen-input-demo.html @@ -22,113 +22,113 @@ --> - - - QR Code generator library demo (JavaScript) - - - - -

    QR Code generator demo library (JavaScript)

    -
    -

    Loading application...

    -

    (Are the JavaScript files missing?)

    -

    (The JavaScript code needs to be compiled from the TypeScript code.)

    -
    - - - - -
    -

    Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

    - + + + QR Code generator library demo (JavaScript) + + + + +

    QR Code generator demo library (JavaScript)

    +
    +

    Loading application...

    +

    (Are the JavaScript files missing?)

    +

    (The JavaScript code needs to be compiled from the TypeScript code.)

    +
    + + + + +
    +

    Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

    + From cf612ec2dc1921ddd81d5d83b59f24813f022418 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 18:11:21 +0000 Subject: [PATCH 639/810] Changed internal name of the HTML demo program. --- typescript-javascript/qrcodegen-input-demo.html | 6 +++--- typescript-javascript/qrcodegen-input-demo.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/typescript-javascript/qrcodegen-input-demo.html b/typescript-javascript/qrcodegen-input-demo.html index f4d5828..d136412 100644 --- a/typescript-javascript/qrcodegen-input-demo.html +++ b/typescript-javascript/qrcodegen-input-demo.html @@ -1,5 +1,5 @@ + + + + + QR Code generator output demo (JavaScript) + + + + +

    QR Code generator output demo (JavaScript)

    +
    +

    Loading application...

    +

    (Are the JavaScript files missing?)

    +

    (The JavaScript code needs to be compiled from the TypeScript code.)

    +
    + + +
    +

    Copyright © Project Nayuki – https://www.nayuki.io/page/qr-code-generator-library

    + + diff --git a/typescript-javascript/qrcodegen-output-demo.ts b/typescript-javascript/qrcodegen-output-demo.ts new file mode 100644 index 0000000..ffc4fbc --- /dev/null +++ b/typescript-javascript/qrcodegen-output-demo.ts @@ -0,0 +1,220 @@ +/* + * QR Code generator output demo (TypeScript) + * + * Copyright (c) Project Nayuki. (MIT License) + * https://www.nayuki.io/page/qr-code-generator-library + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * - The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * - The Software is provided "as is", without warranty of any kind, express or + * implied, including but not limited to the warranties of merchantability, + * fitness for a particular purpose and noninfringement. In no event shall the + * authors or copyright holders be liable for any claim, damages or other + * liability, whether in an action of contract, tort or otherwise, arising from, + * out of or in connection with the Software or the use or other dealings in the + * Software. + */ + +"use strict"; + + +namespace app { + + let outputElem = document.getElementById("output") as HTMLElement; + + + // The main application program. + function main(): void { + while (outputElem.firstChild !== null) + outputElem.removeChild(outputElem.firstChild); + doBasicDemo(); + doVarietyDemo(); + doSegmentDemo(); + doMaskDemo(); + } + + + // Creates a single QR Code, then appends it to the document. + function doBasicDemo(): void { + appendHeading("Basic"); + const text: string = "Hello, world!"; // User-supplied Unicode text + const errCorLvl: qrcodegen.QrCode.Ecc = qrcodegen.QrCode.Ecc.LOW; // Error correction level + const qr: qrcodegen.QrCode = qrcodegen.QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol + qr.drawCanvas(10, 4, appendCanvas("hello-world-QR")); // Draw it on screen + } + + + // Creates a variety of QR Codes that exercise different features of the library, and appends each one to the document. + function doVarietyDemo(): void { + appendHeading("Variety"); + let qr: qrcodegen.QrCode; + const QrCode = qrcodegen.QrCode; // Abbreviation + + // Numeric mode encoding (3.33 bits per digit) + qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); + qr.drawCanvas(13, 1, appendCanvas("pi-digits-QR")); + + // Alphanumeric mode encoding (5.5 bits per character) + qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); + qr.drawCanvas(10, 2, appendCanvas("alphanumeric-QR")); + + // Unicode text as UTF-8 + qr = QrCode.encodeText("\u3053\u3093\u306B\u3061wa\u3001\u4E16\u754C\uFF01 \u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE); + qr.drawCanvas(10, 3, appendCanvas("unicode-QR")); + + // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) + qr = QrCode.encodeText( + "Alice was beginning to get very tired of sitting by her sister on the bank, " + + "and of having nothing to do: once or twice she had peeped into the book her sister was reading, " + + "but it had no pictures or conversations in it, 'and what is the use of a book,' thought Alice " + + "'without pictures or conversations?' So she was considering in her own mind (as well as she could, " + + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); + qr.drawCanvas(6, 10, appendCanvas("alice-wonderland-QR")); + } + + + // Creates QR Codes with manually specified segments for better compactness. + function doSegmentDemo(): void { + appendHeading("Segment"); + let qr: qrcodegen.QrCode; + let segs: Array; + const QrCode = qrcodegen.QrCode; // Abbreviation + const QrSegment = qrcodegen.QrSegment; // Abbreviation + + // Illustration "silver" + const silver0: string = "THE SQUARE ROOT OF 2 IS 1."; + const silver1: string = "41421356237309504880168872420969807856967187537694807317667973799"; + qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); + qr.drawCanvas(10, 3, appendCanvas("sqrt2-monolithic-QR")); + + segs = [ + QrSegment.makeAlphanumeric(silver0), + QrSegment.makeNumeric(silver1)]; + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr.drawCanvas(10, 3, appendCanvas("sqrt2-segmented-QR")); + + // Illustration "golden" + const golden0: string = "Golden ratio \u03C6 = 1."; + const golden1: string = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; + const golden2: string = "......"; + qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); + qr.drawCanvas(8, 5, appendCanvas("phi-monolithic-QR")); + + segs = [ + QrSegment.makeBytes(toUtf8ByteArray(golden0)), + QrSegment.makeNumeric(golden1), + QrSegment.makeAlphanumeric(golden2)]; + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr.drawCanvas(8, 5, appendCanvas("phi-segmented-QR")); + + // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters + const madoka: string = "\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F"; + qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); + qr.drawCanvas(9, 4, appendCanvas("madoka-utf8-QR")); + + const kanjiCharBits: Array = [ // Kanji mode encoding (13 bits per character) + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, + 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + ]; + segs = [new QrSegment(QrSegment.Mode.KANJI, kanjiCharBits.length / 13, kanjiCharBits)]; + qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); + qr.drawCanvas(9, 4, appendCanvas("madoka-kanji-QR")); + } + + + // Creates QR Codes with the same size and contents but different mask patterns. + function doMaskDemo(): void { + appendHeading("Mask"); + let qr: qrcodegen.QrCode; + let segs: Array; + const QrCode = qrcodegen.QrCode; // Abbreviation + + // Project Nayuki URL + segs = qrcodegen.QrSegment.makeSegments("https://www.nayuki.io/"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask + qr.drawCanvas(8, 6, appendCanvas("project-nayuki-automask-QR")); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 + qr.drawCanvas(8, 6, appendCanvas("project-nayuki-mask3-QR")); + + // Chinese text as UTF-8 + segs = qrcodegen.QrSegment.makeSegments("\u7DAD\u57FA\u767E\u79D1\uFF08Wikipedia\uFF0C\u8046\u807Di/\u02CCw\u026Ak\u1D7B\u02C8pi\u02D0di.\u0259/\uFF09\u662F\u4E00" + + "\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08\u756B"); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 + qr.drawCanvas(10, 3, appendCanvas("unicode-mask0-QR")); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 + qr.drawCanvas(10, 3, appendCanvas("unicode-mask1-QR")); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 + qr.drawCanvas(10, 3, appendCanvas("unicode-mask5-QR")); + qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 + qr.drawCanvas(10, 3, appendCanvas("unicode-mask7-QR")); + } + + + function appendHeading(text: string): void { + let h2 = outputElem.appendChild(document.createElement("h2")); + h2.textContent = text; + } + + + function appendCanvas(caption: string): HTMLCanvasElement { + let p = outputElem.appendChild(document.createElement("p")); + p.textContent = caption + ":"; + let result = document.createElement("canvas"); + outputElem.appendChild(result); + return result; + } + + + function toUtf8ByteArray(str: string): Array { + str = encodeURI(str); + let result: Array = []; + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) != "%") + result.push(str.charCodeAt(i)); + else { + result.push(parseInt(str.substr(i + 1, 2), 16)); + i += 2; + } + } + return result; + } + + + main(); + +} From c22fc2f8f69fbb568d8b038c7d1cf4b5a1807f5d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 19:03:49 +0000 Subject: [PATCH 643/810] Tweaked readme text slightly. --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index 998c849..8484a04 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, C++, C, Rust * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (JavaScript and TypeScript only) +* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (TypeScript/JavaScript only) * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License From 20e904578eba802f2d71515180b9585c98b5a5dc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 30 Jul 2019 20:44:38 +0000 Subject: [PATCH 644/810] Updated readme text in multiple programming language versions. --- Readme.markdown | 2 +- java/src/main/java/io/nayuki/qrcodegen/package-info.java | 2 +- python/setup.py | 2 +- rust/Readme.markdown | 3 ++- rust/src/lib.rs | 3 ++- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 8484a04..c220958 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -15,7 +15,7 @@ Features Core features: -* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, C++, C, Rust +* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (TypeScript/JavaScript only) diff --git a/java/src/main/java/io/nayuki/qrcodegen/package-info.java b/java/src/main/java/io/nayuki/qrcodegen/package-info.java index 805a4d7..124b438 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/package-info.java +++ b/java/src/main/java/io/nayuki/qrcodegen/package-info.java @@ -7,7 +7,7 @@ *

    Features

    *

    Core features:

    *
      - *
    • Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust

    • + *
    • Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C

    • *
    • Significantly shorter code but more documentation comments compared to competing libraries

    • *
    • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

    • *
    • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

    • diff --git a/python/setup.py b/python/setup.py index 97d53f8..5bffaaf 100644 --- a/python/setup.py +++ b/python/setup.py @@ -66,7 +66,7 @@ Features Core features: -* Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust +* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol, SVG XML string diff --git a/rust/Readme.markdown b/rust/Readme.markdown index 2b56474..7a20c4a 100644 --- a/rust/Readme.markdown +++ b/rust/Readme.markdown @@ -15,10 +15,11 @@ Features Core features: -* Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust +* Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard * Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 37ccb4c..55c5df5 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -35,10 +35,11 @@ //! //! Core features: //! -//! - Available in 7 programming languages, all with nearly equal functionality: Java, JavaScript, TypeScript, Python, C++, C, Rust +//! - Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C //! - Significantly shorter code but more documentation comments compared to competing libraries //! - Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard //! - Output formats: Raw modules/pixels of the QR symbol, SVG XML string +//! - Detects finder-like penalty patterns more accurately than other implementations //! - Encodes numeric and special-alphanumeric text in less space than general text //! - Open source code under the permissive MIT License //! From 50d00ebf76c66a4ea436a3841a023cae12715786 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 8 Aug 2019 06:58:20 +0000 Subject: [PATCH 645/810] Simplified Rust code using ?-operator on Option (Rust 1.22). --- rust/src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 55c5df5..eef02d1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1128,10 +1128,7 @@ impl QrSegment { if seg.numchars >= 1 << ccbits { return None; // The segment's length doesn't fit the field's bit width } - match result.checked_add(4 + (ccbits as usize) + seg.data.len()) { - None => return None, // The sum will overflow a usize type - Some(val) => result = val, - } + result = result.checked_add(4 + (ccbits as usize) + seg.data.len())?; } Some(result) } From 7eac8beffeb935de5a2fdd65656d8cb500b0abda Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 9 Aug 2019 23:00:13 +0000 Subject: [PATCH 646/810] Updated C and C++ makefiles to exclude warnings and sanitizers by default, instead recommending extra flags for manually doing a diagnostic/debug build. --- c/Makefile | 11 +++++------ cpp/Makefile | 11 +++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/c/Makefile b/c/Makefile index 99f926b..fd0c367 100644 --- a/c/Makefile +++ b/c/Makefile @@ -28,12 +28,11 @@ # - CC: The C compiler, such as gcc or clang. # - CFLAGS: Any extra user-specified compiler flags (can be blank). -# Mandatory compiler flags -CFLAGS += -std=c99 -# Diagnostics. Adding '-fsanitize=address' is helpful for most versions of Clang and newer versions of GCC. -CFLAGS += -Wall -fsanitize=undefined -# Optimization level -CFLAGS += -O1 +# Recommended compiler flags: +CFLAGS += -std=c99 -O + +# Extra flags for diagnostics: +# CFLAGS += -g -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -fsanitize=undefined,address # ---- Controlling make ---- diff --git a/cpp/Makefile b/cpp/Makefile index 858a1bf..1d32943 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -28,12 +28,11 @@ # - CXX: The C++ compiler, such as g++ or clang++. # - CXXFLAGS: Any extra user-specified compiler flags (can be blank). -# Mandatory compiler flags -CXXFLAGS += -std=c++11 -# Diagnostics. Adding '-fsanitize=address' is helpful for most versions of Clang and newer versions of GCC. -CXXFLAGS += -Wall -fsanitize=undefined -# Optimization level -CXXFLAGS += -O1 +# Recommended compiler flags: +CXXFLAGS += -std=c++11 -O + +# Extra flags for diagnostics: +# CXXFLAGS += -g -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -fsanitize=undefined,address # ---- Controlling make ---- From 1424d9f3320f2755dc8c2014f52959354cd6c87b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 10 Aug 2019 02:36:56 +0000 Subject: [PATCH 647/810] Simplified a bit of TypeScript and Rust code using for-each looping. --- rust/src/lib.rs | 4 ++-- typescript-javascript/qrcodegen.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index eef02d1..147b542 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -567,10 +567,10 @@ impl QrCode { // Interleave (not concatenate) the bytes from every block into a single sequence let mut result = Vec::::with_capacity(rawcodewords); for i in 0 .. shortblocklen + 1 { - for j in 0 .. numblocks { + for (j, block) in blocks.iter().enumerate() { // Skip the padding byte in short blocks if i != shortblocklen - blockecclen || j >= numshortblocks { - result.push(blocks[j][i]); + result.push(block[i]); } } } diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index a7f0ab4..00e7827 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -435,11 +435,11 @@ namespace qrcodegen { // Interleave (not concatenate) the bytes from every block into a single sequence let result: Array = []; for (let i = 0; i < blocks[0].length; i++) { - for (let j = 0; j < blocks.length; j++) { + blocks.forEach((block, j) => { // Skip the padding byte in short blocks if (i != shortBlockLen - blockEccLen || j >= numShortBlocks) - result.push(blocks[j][i]); - } + result.push(block[i]); + }); } if (result.length != rawCodewords) throw "Assertion error"; From 78ee34f9a6edf01138bbbd501413c011443c17bd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 10 Aug 2019 02:39:13 +0000 Subject: [PATCH 648/810] Simplified some Rust methods to be pass-by-copy-value instead of pass-by-reference, thanks to clippy (linter)'s suggestions. --- rust/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 147b542..7cfd653 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -917,9 +917,9 @@ pub enum QrCodeEcc { impl QrCodeEcc { // Returns an unsigned 2-bit integer (in the range 0 to 3). - fn ordinal(&self) -> usize { + fn ordinal(self) -> usize { use QrCodeEcc::*; - match *self { + match self { Low => 0, Medium => 1, Quartile => 2, @@ -929,9 +929,9 @@ impl QrCodeEcc { // Returns an unsigned 2-bit integer (in the range 0 to 3). - fn format_bits(&self) -> u32 { + fn format_bits(self) -> u32 { use QrCodeEcc::*; - match *self { + match self { Low => 1, Medium => 0, Quartile => 3, @@ -1176,9 +1176,9 @@ impl QrSegmentMode { // Returns an unsigned 4-bit integer value (range 0 to 15) // representing the mode indicator bits for this mode object. - fn mode_bits(&self) -> u32 { + fn mode_bits(self) -> u32 { use QrSegmentMode::*; - match *self { + match self { Numeric => 0x1, Alphanumeric => 0x2, Byte => 0x4, @@ -1190,9 +1190,9 @@ impl QrSegmentMode { // 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]. - fn num_char_count_bits(&self, ver: Version) -> u8 { + fn num_char_count_bits(self, ver: Version) -> u8 { use QrSegmentMode::*; - (match *self { + (match self { Numeric => [10, 12, 14], Alphanumeric => [ 9, 11, 13], Byte => [ 8, 16, 16], @@ -1269,7 +1269,7 @@ impl Version { } /// Returns the value, which is in the range [1, 40]. - pub fn value(&self) -> u8 { + pub fn value(self) -> u8 { self.0 } } @@ -1289,7 +1289,7 @@ impl Mask { } /// Returns the value, which is in the range [0, 7]. - pub fn value(&self) -> u8 { + pub fn value(self) -> u8 { self.0 } } From 621a77757eecfbcc77ac87c0af496b6e8d366c7f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 10 Aug 2019 02:41:40 +0000 Subject: [PATCH 649/810] Simplified miscellaneous Rust code, due to clippy linting. --- rust/examples/qrcodegen-worker.rs | 9 ++++----- rust/src/lib.rs | 10 +++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs index a6c3ca3..6726d9b 100644 --- a/rust/examples/qrcodegen-worker.rs +++ b/rust/examples/qrcodegen-worker.rs @@ -66,13 +66,12 @@ fn main() { assert!(boostecl >> 1 == 0); // Make segments for encoding - let segs: Vec; - if isascii { + let segs: Vec = if isascii { let chrs: Vec = std::str::from_utf8(&data).unwrap().chars().collect(); - segs = QrSegment::make_segments(&chrs); + QrSegment::make_segments(&chrs) } else { - segs = vec![QrSegment::make_bytes(&data)]; - } + vec![QrSegment::make_bytes(&data)] + }; // Try to make QR Code symbol let msk = if mask == -1 { None } else { Some(Mask::new(mask as u8)) }; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7cfd653..67afbb9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -834,8 +834,8 @@ impl QrCode { let n = runhistory[1]; assert!(n <= self.size * 3); let core = n > 0 && runhistory[2] == n && runhistory[3] == n * 3 && runhistory[4] == n && runhistory[5] == n; - return if core && runhistory[0] >= n * 4 && runhistory[6] >= n { 1 } else { 0 } - + if core && runhistory[6] >= n * 4 && runhistory[0] >= n { 1 } else { 0 }; + ( i32::from(core && runhistory[0] >= n * 4 && runhistory[6] >= n) + + i32::from(core && runhistory[6] >= n * 4 && runhistory[0] >= n)) } @@ -1088,11 +1088,7 @@ impl QrSegment { /// The character count (numchars) must agree with the mode and /// the bit buffer length, but the constraint isn't checked. pub fn new(mode: QrSegmentMode, numchars: usize, data: Vec) -> Self { - Self { - mode: mode, - numchars: numchars, - data: data, - } + Self { mode, numchars, data } } From 6431ef47fd756f2f6d5b10fdb0e1bd7888d42009 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 10 Aug 2019 02:47:22 +0000 Subject: [PATCH 650/810] Tweaked Rust code to have safer integer lossless upcasts, thanks to clippy's diagnostics. --- rust/examples/qrcodegen-worker.rs | 6 +++--- rust/src/lib.rs | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs index 6726d9b..da9a0a3 100644 --- a/rust/examples/qrcodegen-worker.rs +++ b/rust/examples/qrcodegen-worker.rs @@ -47,7 +47,7 @@ fn main() { let mut data = Vec::::with_capacity(length as usize); for _ in 0 .. length { let b: i16 = read_int(); - assert_eq!((b as u8) as i16, b, "Byte value out of range"); + assert_eq!(i16::from(b as u8), b, "Byte value out of range"); data.push(b as u8); } let isascii: bool = data.iter().all(|b| *b < 128); @@ -59,9 +59,9 @@ fn main() { let mask = read_int(); let boostecl = read_int(); assert!(0 <= errcorlvl && errcorlvl <= 3); - assert!((qrcodegen::QrCode_MIN_VERSION.value() as i16) <= minversion + assert!(i16::from(qrcodegen::QrCode_MIN_VERSION.value()) <= minversion && minversion <= maxversion - && maxversion <= (qrcodegen::QrCode_MAX_VERSION.value() as i16)); + && maxversion <= i16::from(qrcodegen::QrCode_MAX_VERSION.value())); assert!(-1 <= mask && mask <= 7); assert!(boostecl >> 1 == 0); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 67afbb9..e6bf3bb 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -442,7 +442,7 @@ impl QrCode { // Calculate error correction code and pack bits let size: i32 = self.size; // errcorrlvl is uint2, mask is uint3 - let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | (mask.value() as u32); + let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value()); let mut rem: u32 = data; for _ in 0 .. 10 { rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -480,11 +480,11 @@ impl QrCode { } // Calculate error correction code and pack bits - let mut rem: u32 = self.version.value() as u32; // version is uint6, in the range [7, 40] + let mut rem: u32 = u32::from(self.version.value()); // version is uint6, in the range [7, 40] for _ in 0 .. 12 { rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); } - let bits: u32 = (self.version.value() as u32) << 12 | rem; // uint18 + let bits: u32 = u32::from(self.version.value()) << 12 | rem; // uint18 assert!(bits >> 18 == 0, "Assertion error"); // Draw two copies @@ -596,7 +596,7 @@ impl QrCode { let upward: bool = (right + 1) & 2 == 0; let y: i32 = if upward { self.size - 1 - vert } else { vert }; // Actual y coordinate if !self.isfunction[(y * self.size + x) as usize] && i < data.len() * 8 { - *self.module_mut(x, y) = get_bit(data[i >> 3] as u32, 7 - ((i & 7) as i32)); + *self.module_mut(x, y) = get_bit(u32::from(data[i >> 3]), 7 - ((i & 7) as i32)); i += 1; } // If this QR Code has any remainder bits (0 to 7), they were assigned as @@ -729,9 +729,9 @@ impl QrCode { if ver == 1 { vec![] } else { - let numalign: i32 = (ver as i32) / 7 + 2; + let numalign: i32 = i32::from(ver) / 7 + 2; let step: i32 = if ver == 32 { 26 } else - {((ver as i32)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; + {(i32::from(ver)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; let mut result: Vec = (0 .. numalign - 1).map( |i| self.size - 7 - i * step).collect(); result.push(6); @@ -986,7 +986,7 @@ impl QrSegment { pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); for b in data { - bb.append_bits(*b as u32, 8); + bb.append_bits(u32::from(*b), 8); } QrSegment::new(QrSegmentMode::Byte, data.len(), bb.0) } @@ -1215,7 +1215,7 @@ impl BitBuffer { /// Requires len ≤ 31 and val < 2len. pub fn append_bits(&mut self, val: u32, len: u8) { assert!(len <= 31 && (val >> len) == 0, "Value out of range"); - self.0.extend((0 .. len as i32).rev().map(|i| get_bit(val, i))); // Append bit by bit + self.0.extend((0 .. i32::from(len)).rev().map(|i| get_bit(val, i))); // Append bit by bit } } From c5d4709e0e6c7d719143ab9f41ccc5dbb54f4349 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 10 Sep 2019 19:25:21 +0000 Subject: [PATCH 651/810] Tweaked a C++ integer cast. --- cpp/QrCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index e169174..4c8bdfc 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -287,7 +287,7 @@ void QrCode::drawVersion() { int rem = version; // version is uint6, in the range [7, 40] for (int i = 0; i < 12; i++) rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - long bits = (long)version << 12 | rem; // uint18 + long bits = static_cast(version) << 12 | rem; // uint18 if (bits >> 18 != 0) throw std::logic_error("Assertion error"); From eb115a99314f34426014ac8456fd1bf65900fd54 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 12 Sep 2019 16:24:41 +0000 Subject: [PATCH 652/810] Tweaked Rust code to convert more integer casts from 'as' to 'from'. --- rust/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index e6bf3bb..01583d9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -266,7 +266,7 @@ impl QrCode { // Pack bits into bytes in big endian let mut datacodewords = vec![0u8; bb.0.len() / 8]; for (i, bit) in bb.0.iter().enumerate() { - datacodewords[i >> 3] |= (*bit as u8) << (7 - (i & 7)); + datacodewords[i >> 3] |= u8::from(*bit) << (7 - (i & 7)); } // Create the QR Code object @@ -283,7 +283,7 @@ impl QrCode { /// A mid-level API is the `encode_segments()` function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mut mask: Option) -> Self { // Initialize fields - let size: usize = (ver.value() as usize) * 4 + 17; + let size: usize = usize::from(ver.value()) * 4 + 17; let mut result = Self { version: ver, size: size as i32, @@ -554,7 +554,7 @@ impl QrCode { let rsdiv: Vec = QrCode::reed_solomon_compute_divisor(blockecclen); let mut k: usize = 0; for i in 0 .. numblocks { - let mut dat = data[k .. k + shortblocklen - blockecclen + ((i >= numshortblocks) as usize)].to_vec(); + let mut dat = data[k .. k + shortblocklen - blockecclen + usize::from(i >= numshortblocks)].to_vec(); k += dat.len(); let ecc: Vec = QrCode::reed_solomon_compute_remainder(&dat, &rsdiv); if i < numshortblocks { @@ -709,7 +709,7 @@ impl QrCode { // Balance of black and white modules let mut black: i32 = 0; for color in &self.modules { - black += *color as i32; + black += i32::from(*color); } let total: i32 = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% @@ -745,7 +745,7 @@ impl QrCode { // 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. fn get_num_raw_data_modules(ver: Version) -> usize { - let ver = ver.value() as usize; + let ver = usize::from(ver.value()); let mut result: usize = (16 * ver + 128) * ver + 64; if ver >= 2 { let numalign: usize = ver / 7 + 2; @@ -770,7 +770,7 @@ impl QrCode { // Returns an entry from the given table based on the given values. fn table_get(table: &'static [[i8; 41]; 4], ver: Version, ecl: QrCodeEcc) -> usize { - table[ecl.ordinal()][ver.value() as usize] as usize + table[ecl.ordinal()][usize::from(ver.value())] as usize } From 6007d92aaaff8b55fcb0a8cac45e9c8e6248995d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 12 Sep 2019 17:19:39 +0000 Subject: [PATCH 653/810] Tweaked Rust code to reduce redundant type declarations. --- rust/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 01583d9..291411c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -283,7 +283,7 @@ impl QrCode { /// A mid-level API is the `encode_segments()` function. pub fn encode_codewords(ver: Version, ecl: QrCodeEcc, datacodewords: &[u8], mut mask: Option) -> Self { // Initialize fields - let size: usize = usize::from(ver.value()) * 4 + 17; + let size = usize::from(ver.value()) * 4 + 17; let mut result = Self { version: ver, size: size as i32, @@ -729,7 +729,7 @@ impl QrCode { if ver == 1 { vec![] } else { - let numalign: i32 = i32::from(ver) / 7 + 2; + let numalign = i32::from(ver) / 7 + 2; let step: i32 = if ver == 32 { 26 } else {(i32::from(ver)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; let mut result: Vec = (0 .. numalign - 1).map( From 765527895c6fe4b4e5aa7fa0da31c570a004bbe2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 12 Sep 2019 17:25:32 +0000 Subject: [PATCH 654/810] Tightened some Rust variable scopes, simplified a bit of code. --- rust/src/lib.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 291411c..f65cc72 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -440,14 +440,15 @@ impl QrCode { // based on the given mask and this object's error correction level field. fn draw_format_bits(&mut self, mask: Mask) { // Calculate error correction code and pack bits - let size: i32 = self.size; - // errcorrlvl is uint2, mask is uint3 - let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value()); - let mut rem: u32 = data; - for _ in 0 .. 10 { - rem = (rem << 1) ^ ((rem >> 9) * 0x537); - } - let bits: u32 = (data << 10 | rem) ^ 0x5412; // uint15 + let bits: u32 = { + // errcorrlvl is uint2, mask is uint3 + let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value()); + let mut rem: u32 = data; + for _ in 0 .. 10 { + rem = (rem << 1) ^ ((rem >> 9) * 0x537); + } + (data << 10 | rem) ^ 0x5412 // uint15 + }; assert_eq!(bits >> 15, 0, "Assertion error"); // Draw first copy @@ -462,6 +463,7 @@ impl QrCode { } // Draw second copy + let size: i32 = self.size; for i in 0 .. 8 { self.set_function_module(size - 1 - i, 8, get_bit(bits, i)); } @@ -480,11 +482,14 @@ impl QrCode { } // Calculate error correction code and pack bits - let mut rem: u32 = u32::from(self.version.value()); // version is uint6, in the range [7, 40] - for _ in 0 .. 12 { - rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); - } - let bits: u32 = u32::from(self.version.value()) << 12 | rem; // uint18 + let bits: u32 = { + let data = u32::from(self.version.value()); // uint6, in the range [7, 40] + let mut rem: u32 = data; + for _ in 0 .. 12 { + rem = (rem << 1) ^ ((rem >> 11) * 0x1F25); + } + data << 12 | rem // uint18 + }; assert!(bits >> 18 == 0, "Assertion error"); // Draw two copies From 9fdd05e64d6c9c6d0509c3d746de3d2358359237 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 12 Sep 2019 17:26:51 +0000 Subject: [PATCH 655/810] Simplified some pieces of Rust code. --- rust/src/lib.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f65cc72..0dce0cc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -208,15 +208,13 @@ impl QrCode { // Find the minimal version number to use let mut version = minversion; - let datausedbits: usize; - loop { + let datausedbits: usize = loop { // Number of data bits available let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let dataused: Option = QrSegment::get_total_bits(segs, version); if let Some(n) = dataused { if n <= datacapacitybits { - datausedbits = n; - break; // This version number is found to be suitable + break n; // This version number is found to be suitable } } if version.value() >= maxversion.value() { // All versions in the range could not fit the given data @@ -228,7 +226,7 @@ impl QrCode { return Err(DataTooLong(msg)); } version = Version::new(version.value() + 1); - } + }; // Increase the error correction level while the data still fits in the current version number for newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { // From low to high @@ -559,8 +557,9 @@ impl QrCode { let rsdiv: Vec = QrCode::reed_solomon_compute_divisor(blockecclen); let mut k: usize = 0; for i in 0 .. numblocks { - let mut dat = data[k .. k + shortblocklen - blockecclen + usize::from(i >= numshortblocks)].to_vec(); - k += dat.len(); + let datlen: usize = shortblocklen - blockecclen + usize::from(i >= numshortblocks); + let mut dat = data[k .. k + datlen].to_vec(); + k += datlen; let ecc: Vec = QrCode::reed_solomon_compute_remainder(&dat, &rsdiv); if i < numshortblocks { dat.push(0); @@ -712,10 +711,7 @@ impl QrCode { } // Balance of black and white modules - let mut black: i32 = 0; - for color in &self.modules { - black += i32::from(*color); - } + let black: i32 = self.modules.iter().copied().map(i32::from).sum(); let total: i32 = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% let k: i32 = ((black * 20 - total * 10).abs() + total - 1) / total - 1; From 2136d88ac8c2aa6f4147e8bd93d904c6e7cd4c39 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 13 Sep 2019 03:44:56 +0000 Subject: [PATCH 656/810] Tweaked Rust code to use inclusive-range for-loops where appropriate. --- rust/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0dce0cc..35bc609 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -504,8 +504,8 @@ impl QrCode { // Draws a 9*9 finder pattern including the border separator, // with the center module at (x, y). Modules can be out of bounds. fn draw_finder_pattern(&mut self, x: i32, y: i32) { - for dy in -4 .. 5 { - for dx in -4 .. 5 { + for dy in -4 ..= 4 { + for dx in -4 ..= 4 { let xx: i32 = x + dx; let yy: i32 = y + dy; if 0 <= xx && xx < self.size && 0 <= yy && yy < self.size { @@ -520,8 +520,8 @@ impl QrCode { // Draws a 5*5 alignment pattern, with the center module // at (x, y). All modules must be in bounds. fn draw_alignment_pattern(&mut self, x: i32, y: i32) { - for dy in -2 .. 3 { - for dx in -2 .. 3 { + for dy in -2 ..= 2 { + for dx in -2 ..= 2 { self.set_function_module(x + dx, y + dy, std::cmp::max(dx.abs(), dy.abs()) != 1); } } @@ -570,7 +570,7 @@ impl QrCode { // Interleave (not concatenate) the bytes from every block into a single sequence let mut result = Vec::::with_capacity(rawcodewords); - for i in 0 .. shortblocklen + 1 { + for i in 0 ..= shortblocklen { for (j, block) in blocks.iter().enumerate() { // Skip the padding byte in short blocks if i != shortblocklen - blockecclen || j >= numshortblocks { From e83ac1cd628ec69c40db3906619aae87be958fe9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 13 Sep 2019 04:02:23 +0000 Subject: [PATCH 657/810] Simplified and clarified various pieces of Rust code. --- rust/src/lib.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 35bc609..9d6ad62 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -165,7 +165,7 @@ impl QrCode { /// Returns a wrapped `QrCode` if successful, or `Err` if the /// data is too long to fit in any version at the given ECC level. pub fn encode_binary(data: &[u8], ecl: QrCodeEcc) -> Result { - let segs: Vec = vec![QrSegment::make_bytes(data)]; + let segs: [QrSegment; 1] = [QrSegment::make_bytes(data)]; QrCode::encode_segments(&segs, ecl) } @@ -212,20 +212,18 @@ impl QrCode { // Number of data bits available let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; let dataused: Option = QrSegment::get_total_bits(segs, version); - if let Some(n) = dataused { - if n <= datacapacitybits { - break n; // This version number is found to be suitable - } - } - if version.value() >= maxversion.value() { // All versions in the range could not fit the given data + if dataused.map_or(false, |n| n <= datacapacitybits) { + break dataused.unwrap(); // This version number is found to be suitable + } else if version.value() >= maxversion.value() { // All versions in the range could not fit the given data let msg: String = match dataused { None => String::from("Segment too long"), Some(n) => format!("Data length = {} bits, Max capacity = {} bits", n, datacapacitybits), }; return Err(DataTooLong(msg)); + } else { + version = Version::new(version.value() + 1); } - version = Version::new(version.value() + 1); }; // Increase the error correction level while the data still fits in the current version number @@ -1262,7 +1260,7 @@ impl Version { /// Panics if the number is outside the range [1, 40]. pub fn new(ver: u8) -> Self { assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range"); - Version(ver) + Self(ver) } /// Returns the value, which is in the range [1, 40]. @@ -1282,7 +1280,7 @@ impl Mask { /// Panics if the number is outside the range [0, 7]. pub fn new(mask: u8) -> Self { assert!(mask <= 7, "Mask value out of range"); - Mask(mask) + Self(mask) } /// Returns the value, which is in the range [0, 7]. From 585e80e4f50a84a65b311bc2a8caf0cd6b55fd34 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 13 Sep 2019 04:03:32 +0000 Subject: [PATCH 658/810] Slightly simplified Rust code so that for-loops and lambdas capture variables as value-copy instead of reference. --- rust/src/lib.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 9d6ad62..1c38e41 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -227,9 +227,9 @@ impl QrCode { }; // Increase the error correction level while the data still fits in the current version number - for newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { // From low to high - if boostecl && datausedbits <= QrCode::get_num_data_codewords(version, *newecl) * 8 { - ecl = *newecl; + for &newecl in &[QrCodeEcc::Medium, QrCodeEcc::Quartile, QrCodeEcc::High] { // From low to high + if boostecl && datausedbits <= QrCode::get_num_data_codewords(version, newecl) * 8 { + ecl = newecl; } } @@ -252,17 +252,17 @@ impl QrCode { assert_eq!(bb.0.len() % 8, 0, "Assertion error"); // Pad with alternating bytes until data capacity is reached - for padbyte in [0xEC, 0x11].iter().cycle() { + for &padbyte in [0xEC, 0x11].iter().cycle() { if bb.0.len() >= datacapacitybits { break; } - bb.append_bits(*padbyte, 8); + bb.append_bits(padbyte, 8); } // Pack bits into bytes in big endian let mut datacodewords = vec![0u8; bb.0.len() / 8]; - for (i, bit) in bb.0.iter().enumerate() { - datacodewords[i >> 3] |= u8::from(*bit) << (7 - (i & 7)); + for (i, &bit) in bb.0.iter().enumerate() { + datacodewords[i >> 3] |= u8::from(bit) << (7 - (i & 7)); } // Create the QR Code object @@ -806,8 +806,8 @@ impl QrCode { for b in data { // Polynomial division let factor: u8 = b ^ result.remove(0); result.push(0); - for (x, y) in result.iter_mut().zip(divisor.iter()) { - *x ^= QrCode::reed_solomon_multiply(*y, factor); + for (x, &y) in result.iter_mut().zip(divisor.iter()) { + *x ^= QrCode::reed_solomon_multiply(y, factor); } } result @@ -984,8 +984,8 @@ impl QrSegment { /// Any text string can be converted to UTF-8 bytes and encoded as a byte mode segment. pub fn make_bytes(data: &[u8]) -> Self { let mut bb = BitBuffer(Vec::with_capacity(data.len() * 8)); - for b in data { - bb.append_bits(u32::from(*b), 8); + for &b in data { + bb.append_bits(u32::from(b), 8); } QrSegment::new(QrSegmentMode::Byte, data.len(), bb.0) } @@ -998,9 +998,9 @@ impl QrSegment { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 3 + (text.len() + 2) / 3)); let mut accumdata: u32 = 0; let mut accumcount: u8 = 0; - for c in text { - assert!('0' <= *c && *c <= '9', "String contains non-numeric characters"); - accumdata = accumdata * 10 + ((*c as u32) - ('0' as u32)); + for &c in text { + assert!('0' <= c && c <= '9', "String contains non-numeric characters"); + accumdata = accumdata * 10 + ((c as u32) - ('0' as u32)); accumcount += 1; if accumcount == 3 { bb.append_bits(accumdata, 10); @@ -1025,8 +1025,8 @@ impl QrSegment { let mut bb = BitBuffer(Vec::with_capacity(text.len() * 5 + (text.len() + 1) / 2)); let mut accumdata: u32 = 0; let mut accumcount: u32 = 0; - for c in text { - let i = ALPHANUMERIC_CHARSET.iter().position(|x| *x == *c) + for &c in text { + let i = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c) .expect("String contains unencodable characters in alphanumeric mode"); accumdata = accumdata * 45 + (i as u32); accumcount += 1; @@ -1140,7 +1140,7 @@ impl QrSegment { // Tests whether the given string can be encoded as a segment in numeric mode. // A string is encodable iff each character is in the range 0 to 9. fn is_numeric(text: &[char]) -> bool { - text.iter().all(|c| '0' <= *c && *c <= '9') + text.iter().all(|&c| '0' <= c && c <= '9') } } From 6e088f72d39fbf56c5fbdbe9c7401dff40ba42ec Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 13 Sep 2019 04:09:42 +0000 Subject: [PATCH 659/810] Added an assertion to QrCode.getNumRawDataModules() in all language versions. --- c/qrcodegen.c | 1 + cpp/QrCode.cpp | 2 ++ java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 1 + python/qrcodegen.py | 1 + rust/src/lib.rs | 1 + typescript-javascript/qrcodegen.ts | 2 ++ 6 files changed, 8 insertions(+) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 612409a..cf7cd64 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -345,6 +345,7 @@ testable int getNumRawDataModules(int ver) { if (ver >= 7) result -= 36; } + assert(208 <= result && result <= 29648); return result; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 4c8bdfc..a6e2568 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -531,6 +531,8 @@ int QrCode::getNumRawDataModules(int ver) { if (ver >= 7) result -= 36; } + if (!(208 <= result && result <= 29648)) + throw std::logic_error("Assertion error"); return result; } diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index f7d8ab7..68c4f99 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -718,6 +718,7 @@ public final class QrCode { if (ver >= 7) result -= 6 * 3 * 2; // Subtract version information } + assert 208 <= result && result <= 29648; return result; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 6ada1aa..93c0e85 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -550,6 +550,7 @@ class QrCode(object): result -= (25 * numalign - 10) * numalign - 55 if ver >= 7: result -= 36 + assert 208 <= result <= 29648 return result diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1c38e41..258761a 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -753,6 +753,7 @@ impl QrCode { result -= 36; } } + assert!(208 <= result && result <= 29648); result } diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 00e7827..9b75c06 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -619,6 +619,8 @@ namespace qrcodegen { if (ver >= 7) result -= 36; } + if (!(208 <= result && result <= 29648)) + throw "Assertion error"; return result; } From fe992753d2c7d55a16c1b966961bd27e11e622dd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 18 Sep 2019 20:40:23 +0000 Subject: [PATCH 660/810] Tweaked Rust code to convert more integer casts from 'as' to 'from'. --- rust/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 258761a..be7303c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1001,7 +1001,7 @@ impl QrSegment { let mut accumcount: u8 = 0; for &c in text { assert!('0' <= c && c <= '9', "String contains non-numeric characters"); - accumdata = accumdata * 10 + ((c as u32) - ('0' as u32)); + accumdata = accumdata * 10 + (u32::from(c) - u32::from('0')); accumcount += 1; if accumcount == 3 { bb.append_bits(accumdata, 10); @@ -1124,7 +1124,7 @@ impl QrSegment { if seg.numchars >= 1 << ccbits { return None; // The segment's length doesn't fit the field's bit width } - result = result.checked_add(4 + (ccbits as usize) + seg.data.len())?; + result = result.checked_add(4 + usize::from(ccbits) + seg.data.len())?; } Some(result) } @@ -1194,7 +1194,7 @@ impl QrSegmentMode { Byte => [ 8, 16, 16], Kanji => [ 8, 10, 12], Eci => [ 0, 0, 0], - })[((ver.value() + 7) / 17) as usize] + })[usize::from((ver.value() + 7) / 17)] } } From 8dabf86641301b1de0fb91e083fea5a6c529814f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 14 Oct 2019 00:19:03 +0000 Subject: [PATCH 661/810] Renamed some method parameters to completely avoid variable shadowing. --- src/io/nayuki/fastqrcodegen/QrCode.java | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/io/nayuki/fastqrcodegen/QrCode.java b/src/io/nayuki/fastqrcodegen/QrCode.java index b629391..6f43d93 100644 --- a/src/io/nayuki/fastqrcodegen/QrCode.java +++ b/src/io/nayuki/fastqrcodegen/QrCode.java @@ -231,16 +231,16 @@ public final class QrCode { * @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 dataCodewords the bytes representing segments to encode (without ECC) - * @param mask the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice + * @param msk 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 mask) { + public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int msk) { // Check arguments and initialize fields if (ver < MIN_VERSION || ver > MAX_VERSION) throw new IllegalArgumentException("Version value out of range"); - if (mask < -1 || mask > 7) + if (msk < -1 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); version = ver; size = ver * 4 + 17; @@ -253,7 +253,7 @@ public final class QrCode { // Compute ECC, draw modules, do masking byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(tpl.dataOutputBitIndexes, allCodewords); - this.mask = handleConstructorMasking(tpl.masks, mask); + this.mask = handleConstructorMasking(tpl.masks, msk); } @@ -345,9 +345,9 @@ public final class QrCode { // 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 mask) { + private void drawFormatBits(int msk) { // Calculate error correction code and pack bits - int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int data = errorCorrectionLevel.formatBits << 3 | msk; // errCorrLvl is uint2, mask is uint3 int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); @@ -440,35 +440,35 @@ 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[] mask) { - if (mask.length != modules.length) + private void applyMask(int[] msk) { + if (msk.length != modules.length) throw new IllegalArgumentException(); - for (int i = 0; i < mask.length; i++) - modules[i] ^= mask[i]; + for (int i = 0; i < msk.length; i++) + modules[i] ^= msk[i]; } // A messy helper function for the constructor. This QR Code must be in an unmasked state when this // method is called. The 'mask' 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[][] masks, int mask) { - if (mask == -1) { // Automatically choose best mask + private int handleConstructorMasking(int[][] masks, int msk) { + if (msk == -1) { // Automatically choose best mask int minPenalty = Integer.MAX_VALUE; for (int i = 0; i < 8; i++) { applyMask(masks[i]); drawFormatBits(i); int penalty = getPenaltyScore(); if (penalty < minPenalty) { - mask = i; + msk = i; minPenalty = penalty; } applyMask(masks[i]); // Undoes the mask due to XOR } } - assert 0 <= mask && mask <= 7; - applyMask(masks[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 + assert 0 <= msk && msk <= 7; + applyMask(masks[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 } From 67c62461d380352500fc39557fd9f046b7fe1d18 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 14 Oct 2019 00:20:16 +0000 Subject: [PATCH 662/810] Renamed some method parameters to completely avoid variable shadowing, in C++ and Java versions. --- cpp/QrCode.cpp | 26 +++++++-------- cpp/QrCode.hpp | 6 ++-- .../main/java/io/nayuki/qrcodegen/QrCode.java | 32 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index a6e2568..503f39c 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -126,13 +126,13 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, } -QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) : +QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) : // Initialize fields and check arguments version(ver), errorCorrectionLevel(ecl) { if (ver < MIN_VERSION || ver > MAX_VERSION) throw std::domain_error("Version value out of range"); - if (mask < -1 || mask > 7) + if (msk < -1 || msk > 7) throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; size_t sz = static_cast(size); @@ -145,24 +145,24 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int mask) drawCodewords(allCodewords); // Do masking - if (mask == -1) { // Automatically choose best mask + if (msk == -1) { // Automatically choose best mask long minPenalty = LONG_MAX; for (int i = 0; i < 8; i++) { applyMask(i); drawFormatBits(i); long penalty = getPenaltyScore(); if (penalty < minPenalty) { - mask = i; + msk = i; minPenalty = penalty; } applyMask(i); // Undoes the mask due to XOR } } - if (mask < 0 || mask > 7) + if (msk < 0 || msk > 7) throw std::logic_error("Assertion error"); - this->mask = mask; - applyMask(mask); // Apply the final choice of mask - drawFormatBits(mask); // Overwrite old format bits + this->mask = msk; + applyMask(msk); // Apply the final choice of mask + drawFormatBits(msk); // Overwrite old format bits isFunction.clear(); isFunction.shrink_to_fit(); @@ -251,9 +251,9 @@ void QrCode::drawFunctionPatterns() { } -void QrCode::drawFormatBits(int mask) { +void QrCode::drawFormatBits(int msk) { // Calculate error correction code and pack bits - int data = getFormatBits(errorCorrectionLevel) << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int data = getFormatBits(errorCorrectionLevel) << 3 | msk; // errCorrLvl is uint2, msk is uint3 int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -402,14 +402,14 @@ void QrCode::drawCodewords(const vector &data) { } -void QrCode::applyMask(int mask) { - if (mask < 0 || mask > 7) +void QrCode::applyMask(int msk) { + if (msk < 0 || msk > 7) throw std::domain_error("Mask value out of range"); size_t sz = static_cast(size); for (size_t y = 0; y < sz; y++) { for (size_t x = 0; x < sz; x++) { bool invert; - switch (mask) { + switch (msk) { case 0: invert = (x + y) % 2 == 0; break; case 1: invert = y % 2 == 0; break; case 2: invert = x % 3 == 0; break; diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 8ad519f..6babfdf 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -147,7 +147,7 @@ class QrCode final { * This is a low-level API that most users should not use directly. * A mid-level API is the encodeSegments() function. */ - public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int mask); + public: QrCode(int ver, Ecc ecl, const std::vector &dataCodewords, int msk); @@ -201,7 +201,7 @@ class QrCode final { // 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 mask); + private: void drawFormatBits(int msk); // Draws two copies of the version bits (with its own error correction code), @@ -245,7 +245,7 @@ class QrCode final { // 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 mask); + private: void applyMask(int msk); // Calculates and returns the penalty score based on state of this QR Code's current modules. diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 68c4f99..59608e8 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -241,16 +241,16 @@ public final class QrCode { * @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 dataCodewords the bytes representing segments to encode (without ECC) - * @param mask the mask pattern to use, which is either −1 for automatic choice or from 0 to 7 for fixed choice + * @param msk 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 mask) { + public QrCode(int ver, Ecc ecl, byte[] dataCodewords, int msk) { // Check arguments and initialize fields if (ver < MIN_VERSION || ver > MAX_VERSION) throw new IllegalArgumentException("Version value out of range"); - if (mask < -1 || mask > 7) + if (msk < -1 || msk > 7) throw new IllegalArgumentException("Mask value out of range"); version = ver; size = ver * 4 + 17; @@ -263,7 +263,7 @@ public final class QrCode { drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this.mask = handleConstructorMasking(mask); + this.mask = handleConstructorMasking(msk); isFunction = null; } @@ -382,9 +382,9 @@ public final class QrCode { // 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 mask) { + private void drawFormatBits(int msk) { // Calculate error correction code and pack bits - int data = errorCorrectionLevel.formatBits << 3 | mask; // errCorrLvl is uint2, mask is uint3 + int data = errorCorrectionLevel.formatBits << 3 | msk; // errCorrLvl is uint2, mask is uint3 int rem = data; for (int i = 0; i < 10; i++) rem = (rem << 1) ^ ((rem >>> 9) * 0x537); @@ -543,13 +543,13 @@ 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 mask) { - if (mask < 0 || mask > 7) + 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 x = 0; x < size; x++) { boolean invert; - switch (mask) { + switch (msk) { case 0: invert = (x + y) % 2 == 0; break; case 1: invert = y % 2 == 0; break; case 2: invert = x % 3 == 0; break; @@ -569,24 +569,24 @@ 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 mask) { - if (mask == -1) { // Automatically choose best mask + 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) { - mask = i; + msk = i; minPenalty = penalty; } applyMask(i); // Undoes the mask due to XOR } } - 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 + 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 } From 096c70cd4d4bc4bd8628faca5a08d95cae0bfb9a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 11 Jan 2020 18:26:12 +0000 Subject: [PATCH 663/810] Reorganized C++ library code into one implementation source file and one header file (instead of 3+3), changing some spacing and ordering, without changing functionality. --- cpp/BitBuffer.cpp | 41 ------- cpp/BitBuffer.hpp | 52 --------- cpp/Makefile | 2 +- cpp/QrCode.cpp | 206 ++++++++++++++++++++++++++++++++- cpp/QrCode.hpp | 208 ++++++++++++++++++++++++++++++++- cpp/QrCodeGeneratorDemo.cpp | 1 - cpp/QrSegment.cpp | 225 ------------------------------------ cpp/QrSegment.hpp | 216 ---------------------------------- 8 files changed, 413 insertions(+), 538 deletions(-) delete mode 100644 cpp/BitBuffer.cpp delete mode 100644 cpp/BitBuffer.hpp delete mode 100644 cpp/QrSegment.cpp delete mode 100644 cpp/QrSegment.hpp diff --git a/cpp/BitBuffer.cpp b/cpp/BitBuffer.cpp deleted file mode 100644 index e22e9d3..0000000 --- a/cpp/BitBuffer.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include "BitBuffer.hpp" - - -namespace qrcodegen { - -BitBuffer::BitBuffer() - : std::vector() {} - - -void BitBuffer::appendBits(std::uint32_t val, int len) { - if (len < 0 || len > 31 || val >> len != 0) - throw std::domain_error("Value out of range"); - for (int i = len - 1; i >= 0; i--) // Append bit by bit - this->push_back(((val >> i) & 1) != 0); -} - -} diff --git a/cpp/BitBuffer.hpp b/cpp/BitBuffer.hpp deleted file mode 100644 index f30913a..0000000 --- a/cpp/BitBuffer.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include - - -namespace qrcodegen { - -/* - * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. - */ -class BitBuffer final : public std::vector { - - /*---- Constructor ----*/ - - // Creates an empty bit buffer (length 0). - public: BitBuffer(); - - - - /*---- Method ----*/ - - // Appends the given number of low-order bits of the given value - // to this buffer. Requires 0 <= len <= 31 and val < 2^len. - public: void appendBits(std::uint32_t val, int len); - -}; - -} diff --git a/cpp/Makefile b/cpp/Makefile index 1d32943..f83c512 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -52,7 +52,7 @@ CXXFLAGS += -std=c++11 -O LIB = qrcodegen LIBFILE = lib$(LIB).a -LIBOBJ = BitBuffer.o QrCode.o QrSegment.o +LIBOBJ = QrCode.o MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker # Build all binaries diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 503f39c..ad1fa74 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -25,9 +25,10 @@ #include #include #include +#include #include +#include #include -#include "BitBuffer.hpp" #include "QrCode.hpp" using std::int8_t; @@ -38,6 +39,196 @@ using std::vector; namespace qrcodegen { +QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : + modeBits(mode) { + numBitsCharCount[0] = cc0; + numBitsCharCount[1] = cc1; + numBitsCharCount[2] = cc2; +} + + +int QrSegment::Mode::getModeBits() const { + return modeBits; +} + + +int QrSegment::Mode::numCharCountBits(int ver) const { + return numBitsCharCount[(ver + 7) / 17]; +} + + +const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); +const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); +const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); +const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); +const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); + + +QrSegment QrSegment::makeBytes(const vector &data) { + if (data.size() > static_cast(INT_MAX)) + throw std::length_error("Data too long"); + BitBuffer bb; + for (uint8_t b : data) + bb.appendBits(b, 8); + return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); +} + + +QrSegment QrSegment::makeNumeric(const char *digits) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *digits != '\0'; digits++, charCount++) { + char c = *digits; + if (c < '0' || c > '9') + throw std::domain_error("String contains non-numeric characters"); + accumData = accumData * 10 + (c - '0'); + accumCount++; + if (accumCount == 3) { + bb.appendBits(static_cast(accumData), 10); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 or 2 digits remaining + bb.appendBits(static_cast(accumData), accumCount * 3 + 1); + return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); +} + + +QrSegment QrSegment::makeAlphanumeric(const char *text) { + BitBuffer bb; + int accumData = 0; + int accumCount = 0; + int charCount = 0; + for (; *text != '\0'; text++, charCount++) { + const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); + if (temp == nullptr) + throw std::domain_error("String contains unencodable characters in alphanumeric mode"); + accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); + accumCount++; + if (accumCount == 2) { + bb.appendBits(static_cast(accumData), 11); + accumData = 0; + accumCount = 0; + } + } + if (accumCount > 0) // 1 character remaining + bb.appendBits(static_cast(accumData), 6); + return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); +} + + +vector QrSegment::makeSegments(const char *text) { + // Select the most efficient segment encoding automatically + vector result; + if (*text == '\0'); // Leave result empty + else if (isNumeric(text)) + result.push_back(makeNumeric(text)); + else if (isAlphanumeric(text)) + result.push_back(makeAlphanumeric(text)); + else { + vector bytes; + for (; *text != '\0'; text++) + bytes.push_back(static_cast(*text)); + result.push_back(makeBytes(bytes)); + } + return result; +} + + +QrSegment QrSegment::makeEci(long assignVal) { + BitBuffer bb; + if (assignVal < 0) + throw std::domain_error("ECI assignment value out of range"); + else if (assignVal < (1 << 7)) + bb.appendBits(static_cast(assignVal), 8); + else if (assignVal < (1 << 14)) { + bb.appendBits(2, 2); + bb.appendBits(static_cast(assignVal), 14); + } else if (assignVal < 1000000L) { + bb.appendBits(6, 3); + bb.appendBits(static_cast(assignVal), 21); + } else + throw std::domain_error("ECI assignment value out of range"); + return QrSegment(Mode::ECI, 0, std::move(bb)); +} + + +QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : + mode(md), + numChars(numCh), + data(dt) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : + mode(md), + numChars(numCh), + data(std::move(dt)) { + if (numCh < 0) + throw std::domain_error("Invalid value"); +} + + +int QrSegment::getTotalBits(const vector &segs, int version) { + int result = 0; + for (const QrSegment &seg : segs) { + int ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= (1L << ccbits)) + return -1; // The segment's length doesn't fit the field's bit width + if (4 + ccbits > INT_MAX - result) + return -1; // The sum will overflow an int type + result += 4 + ccbits; + if (seg.data.size() > static_cast(INT_MAX - result)) + return -1; // The sum will overflow an int type + result += static_cast(seg.data.size()); + } + return result; +} + + +bool QrSegment::isAlphanumeric(const char *text) { + for (; *text != '\0'; text++) { + if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) + return false; + } + return true; +} + + +bool QrSegment::isNumeric(const char *text) { + for (; *text != '\0'; text++) { + char c = *text; + if (c < '0' || c > '9') + return false; + } + return true; +} + + +QrSegment::Mode QrSegment::getMode() const { + return mode; +} + + +int QrSegment::getNumChars() const { + return numChars; +} + + +const std::vector &QrSegment::getData() const { + return data; +} + + +const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; + + + int QrCode::getFormatBits(Ecc ecl) { switch (ecl) { case Ecc::LOW : return 1; @@ -657,4 +848,17 @@ const int8_t QrCode::NUM_ERROR_CORRECTION_BLOCKS[4][41] = { data_too_long::data_too_long(const std::string &msg) : std::length_error(msg) {} + + +BitBuffer::BitBuffer() + : std::vector() {} + + +void BitBuffer::appendBits(std::uint32_t val, int len) { + if (len < 0 || len > 31 || val >> len != 0) + throw std::domain_error("Value out of range"); + for (int i = len - 1; i >= 0; i--) // Append bit by bit + this->push_back(((val >> i) & 1) != 0); +} + } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 6babfdf..bcc0426 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -28,11 +28,195 @@ #include #include #include -#include "QrSegment.hpp" namespace qrcodegen { +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment::makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +class QrSegment final { + + /*---- Public helper enumeration ----*/ + + /* + * Describes how a segment's data bits are interpreted. Immutable. + */ + public: class Mode final { + + /*-- Constants --*/ + + public: static const Mode NUMERIC; + public: static const Mode ALPHANUMERIC; + public: static const Mode BYTE; + public: static const Mode KANJI; + public: static const Mode ECI; + + + /*-- Fields --*/ + + // The mode indicator bits, which is a uint4 value (range 0 to 15). + private: int modeBits; + + // Number of character count bits for three different version ranges. + private: int numBitsCharCount[3]; + + + /*-- Constructor --*/ + + private: Mode(int mode, int cc0, int cc1, int cc2); + + + /*-- Methods --*/ + + /* + * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). + */ + public: int getModeBits() const; + + /* + * (Package-private) 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]. + */ + public: int numCharCountBits(int ver) const; + + }; + + + + /*---- Static factory functions (mid level) ----*/ + + /* + * Returns a segment representing the given binary data encoded in + * byte mode. All input byte vectors are acceptable. Any text string + * can be converted to UTF-8 bytes and encoded as a byte mode segment. + */ + public: static QrSegment makeBytes(const std::vector &data); + + + /* + * Returns a segment representing the given string of decimal digits encoded in numeric mode. + */ + public: static QrSegment makeNumeric(const char *digits); + + + /* + * Returns a segment representing the given text string encoded in alphanumeric mode. + * The characters allowed are: 0 to 9, A to Z (uppercase only), space, + * dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static QrSegment makeAlphanumeric(const char *text); + + + /* + * Returns a list of zero or more segments to represent the given text string. The result + * may use various segment modes and switch modes to optimize the length of the bit stream. + */ + public: static std::vector makeSegments(const char *text); + + + /* + * Returns a segment representing an Extended Channel Interpretation + * (ECI) designator with the given assignment value. + */ + public: static QrSegment makeEci(long assignVal); + + + /*---- Public static helper functions ----*/ + + /* + * Tests whether the given string can be encoded as a segment in alphanumeric mode. + * A string is encodable iff each character is in the following set: 0 to 9, A to Z + * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + */ + public: static bool isAlphanumeric(const char *text); + + + /* + * Tests whether the given string can be encoded as a segment in numeric mode. + * A string is encodable iff each character is in the range 0 to 9. + */ + public: static bool isNumeric(const char *text); + + + + /*---- Instance fields ----*/ + + /* The mode indicator of this segment. Accessed through getMode(). */ + private: Mode mode; + + /* The length of this segment's unencoded data. Measured in characters for + * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + * Always zero or positive. Not the same as the data's bit length. + * Accessed through getNumChars(). */ + private: int numChars; + + /* The data bits of this segment. Accessed through getData(). */ + private: std::vector data; + + + /*---- Constructors (low level) ----*/ + + /* + * Creates a new QR Code segment with the given attributes and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is copied and stored. + */ + public: QrSegment(Mode md, int numCh, const std::vector &dt); + + + /* + * Creates a new QR Code segment with the given parameters and data. + * The character count (numCh) must agree with the mode and the bit buffer length, + * but the constraint isn't checked. The given bit buffer is moved and stored. + */ + public: QrSegment(Mode md, int numCh, std::vector &&dt); + + + /*---- Methods ----*/ + + /* + * Returns the mode field of this segment. + */ + public: Mode getMode() const; + + + /* + * Returns the character count field of this segment. + */ + public: int getNumChars() const; + + + /* + * Returns the data bits of this segment. + */ + public: const std::vector &getData() const; + + + // (Package-private) Calculates the number of bits needed to encode the given segments at + // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a + // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. + public: static int getTotalBits(const std::vector &segs, int version); + + + /*---- Private constant ----*/ + + /* The set of all legal characters in alphanumeric mode, where + * each character value maps to the index in the string. */ + private: static const char *ALPHANUMERIC_CHARSET; + +}; + + + /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. @@ -347,4 +531,26 @@ class data_too_long : public std::length_error { }; + + +/* + * An appendable sequence of bits (0s and 1s). Mainly used by QrSegment. + */ +class BitBuffer final : public std::vector { + + /*---- Constructor ----*/ + + // Creates an empty bit buffer (length 0). + public: BitBuffer(); + + + + /*---- Method ----*/ + + // Appends the given number of low-order bits of the given value + // to this buffer. Requires 0 <= len <= 31 and val < 2^len. + public: void appendBits(std::uint32_t val, int len); + +}; + } diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 8284cfa..017dcbe 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -30,7 +30,6 @@ #include #include #include -#include "BitBuffer.hpp" #include "QrCode.hpp" using std::uint8_t; diff --git a/cpp/QrSegment.cpp b/cpp/QrSegment.cpp deleted file mode 100644 index 45ea843..0000000 --- a/cpp/QrSegment.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include "QrSegment.hpp" - -using std::uint8_t; -using std::vector; - - -namespace qrcodegen { - -QrSegment::Mode::Mode(int mode, int cc0, int cc1, int cc2) : - modeBits(mode) { - numBitsCharCount[0] = cc0; - numBitsCharCount[1] = cc1; - numBitsCharCount[2] = cc2; -} - - -int QrSegment::Mode::getModeBits() const { - return modeBits; -} - - -int QrSegment::Mode::numCharCountBits(int ver) const { - return numBitsCharCount[(ver + 7) / 17]; -} - - -const QrSegment::Mode QrSegment::Mode::NUMERIC (0x1, 10, 12, 14); -const QrSegment::Mode QrSegment::Mode::ALPHANUMERIC(0x2, 9, 11, 13); -const QrSegment::Mode QrSegment::Mode::BYTE (0x4, 8, 16, 16); -const QrSegment::Mode QrSegment::Mode::KANJI (0x8, 8, 10, 12); -const QrSegment::Mode QrSegment::Mode::ECI (0x7, 0, 0, 0); - - - -QrSegment QrSegment::makeBytes(const vector &data) { - if (data.size() > static_cast(INT_MAX)) - throw std::length_error("Data too long"); - BitBuffer bb; - for (uint8_t b : data) - bb.appendBits(b, 8); - return QrSegment(Mode::BYTE, static_cast(data.size()), std::move(bb)); -} - - -QrSegment QrSegment::makeNumeric(const char *digits) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *digits != '\0'; digits++, charCount++) { - char c = *digits; - if (c < '0' || c > '9') - throw std::domain_error("String contains non-numeric characters"); - accumData = accumData * 10 + (c - '0'); - accumCount++; - if (accumCount == 3) { - bb.appendBits(static_cast(accumData), 10); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 or 2 digits remaining - bb.appendBits(static_cast(accumData), accumCount * 3 + 1); - return QrSegment(Mode::NUMERIC, charCount, std::move(bb)); -} - - -QrSegment QrSegment::makeAlphanumeric(const char *text) { - BitBuffer bb; - int accumData = 0; - int accumCount = 0; - int charCount = 0; - for (; *text != '\0'; text++, charCount++) { - const char *temp = std::strchr(ALPHANUMERIC_CHARSET, *text); - if (temp == nullptr) - throw std::domain_error("String contains unencodable characters in alphanumeric mode"); - accumData = accumData * 45 + static_cast(temp - ALPHANUMERIC_CHARSET); - accumCount++; - if (accumCount == 2) { - bb.appendBits(static_cast(accumData), 11); - accumData = 0; - accumCount = 0; - } - } - if (accumCount > 0) // 1 character remaining - bb.appendBits(static_cast(accumData), 6); - return QrSegment(Mode::ALPHANUMERIC, charCount, std::move(bb)); -} - - -vector QrSegment::makeSegments(const char *text) { - // Select the most efficient segment encoding automatically - vector result; - if (*text == '\0'); // Leave result empty - else if (isNumeric(text)) - result.push_back(makeNumeric(text)); - else if (isAlphanumeric(text)) - result.push_back(makeAlphanumeric(text)); - else { - vector bytes; - for (; *text != '\0'; text++) - bytes.push_back(static_cast(*text)); - result.push_back(makeBytes(bytes)); - } - return result; -} - - -QrSegment QrSegment::makeEci(long assignVal) { - BitBuffer bb; - if (assignVal < 0) - throw std::domain_error("ECI assignment value out of range"); - else if (assignVal < (1 << 7)) - bb.appendBits(static_cast(assignVal), 8); - else if (assignVal < (1 << 14)) { - bb.appendBits(2, 2); - bb.appendBits(static_cast(assignVal), 14); - } else if (assignVal < 1000000L) { - bb.appendBits(6, 3); - bb.appendBits(static_cast(assignVal), 21); - } else - throw std::domain_error("ECI assignment value out of range"); - return QrSegment(Mode::ECI, 0, std::move(bb)); -} - - -QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : - mode(md), - numChars(numCh), - data(dt) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : - mode(md), - numChars(numCh), - data(std::move(dt)) { - if (numCh < 0) - throw std::domain_error("Invalid value"); -} - - -int QrSegment::getTotalBits(const vector &segs, int version) { - int result = 0; - for (const QrSegment &seg : segs) { - int ccbits = seg.mode.numCharCountBits(version); - if (seg.numChars >= (1L << ccbits)) - return -1; // The segment's length doesn't fit the field's bit width - if (4 + ccbits > INT_MAX - result) - return -1; // The sum will overflow an int type - result += 4 + ccbits; - if (seg.data.size() > static_cast(INT_MAX - result)) - return -1; // The sum will overflow an int type - result += static_cast(seg.data.size()); - } - return result; -} - - -bool QrSegment::isAlphanumeric(const char *text) { - for (; *text != '\0'; text++) { - if (std::strchr(ALPHANUMERIC_CHARSET, *text) == nullptr) - return false; - } - return true; -} - - -bool QrSegment::isNumeric(const char *text) { - for (; *text != '\0'; text++) { - char c = *text; - if (c < '0' || c > '9') - return false; - } - return true; -} - - -QrSegment::Mode QrSegment::getMode() const { - return mode; -} - - -int QrSegment::getNumChars() const { - return numChars; -} - - -const std::vector &QrSegment::getData() const { - return data; -} - - -const char *QrSegment::ALPHANUMERIC_CHARSET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"; - -} diff --git a/cpp/QrSegment.hpp b/cpp/QrSegment.hpp deleted file mode 100644 index 663b8bb..0000000 --- a/cpp/QrSegment.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/* - * QR Code generator library (C++) - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#pragma once - -#include -#include -#include "BitBuffer.hpp" - - -namespace qrcodegen { - -/* - * A segment of character/binary/control data in a QR Code symbol. - * Instances of this class are immutable. - * The mid-level way to create a segment is to take the payload data - * and call a static factory function such as QrSegment::makeNumeric(). - * The low-level way to create a segment is to custom-make the bit buffer - * and call the QrSegment() constructor with appropriate values. - * This segment class imposes no length restrictions, but QR Codes have restrictions. - * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. - * Any segment longer than this is meaningless for the purpose of generating QR Codes. - */ -class QrSegment final { - - /*---- Public helper enumeration ----*/ - - /* - * Describes how a segment's data bits are interpreted. Immutable. - */ - public: class Mode final { - - /*-- Constants --*/ - - public: static const Mode NUMERIC; - public: static const Mode ALPHANUMERIC; - public: static const Mode BYTE; - public: static const Mode KANJI; - public: static const Mode ECI; - - - /*-- Fields --*/ - - // The mode indicator bits, which is a uint4 value (range 0 to 15). - private: int modeBits; - - // Number of character count bits for three different version ranges. - private: int numBitsCharCount[3]; - - - /*-- Constructor --*/ - - private: Mode(int mode, int cc0, int cc1, int cc2); - - - /*-- Methods --*/ - - /* - * (Package-private) Returns the mode indicator bits, which is an unsigned 4-bit value (range 0 to 15). - */ - public: int getModeBits() const; - - /* - * (Package-private) 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]. - */ - public: int numCharCountBits(int ver) const; - - }; - - - - /*---- Static factory functions (mid level) ----*/ - - /* - * Returns a segment representing the given binary data encoded in - * byte mode. All input byte vectors are acceptable. Any text string - * can be converted to UTF-8 bytes and encoded as a byte mode segment. - */ - public: static QrSegment makeBytes(const std::vector &data); - - - /* - * Returns a segment representing the given string of decimal digits encoded in numeric mode. - */ - public: static QrSegment makeNumeric(const char *digits); - - - /* - * Returns a segment representing the given text string encoded in alphanumeric mode. - * The characters allowed are: 0 to 9, A to Z (uppercase only), space, - * dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static QrSegment makeAlphanumeric(const char *text); - - - /* - * Returns a list of zero or more segments to represent the given text string. The result - * may use various segment modes and switch modes to optimize the length of the bit stream. - */ - public: static std::vector makeSegments(const char *text); - - - /* - * Returns a segment representing an Extended Channel Interpretation - * (ECI) designator with the given assignment value. - */ - public: static QrSegment makeEci(long assignVal); - - - /*---- Public static helper functions ----*/ - - /* - * Tests whether the given string can be encoded as a segment in alphanumeric mode. - * A string is encodable iff each character is in the following set: 0 to 9, A to Z - * (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - */ - public: static bool isAlphanumeric(const char *text); - - - /* - * Tests whether the given string can be encoded as a segment in numeric mode. - * A string is encodable iff each character is in the range 0 to 9. - */ - public: static bool isNumeric(const char *text); - - - - /*---- Instance fields ----*/ - - /* The mode indicator of this segment. Accessed through getMode(). */ - private: Mode mode; - - /* The length of this segment's unencoded data. Measured in characters for - * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - * Always zero or positive. Not the same as the data's bit length. - * Accessed through getNumChars(). */ - private: int numChars; - - /* The data bits of this segment. Accessed through getData(). */ - private: std::vector data; - - - /*---- Constructors (low level) ----*/ - - /* - * Creates a new QR Code segment with the given attributes and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is copied and stored. - */ - public: QrSegment(Mode md, int numCh, const std::vector &dt); - - - /* - * Creates a new QR Code segment with the given parameters and data. - * The character count (numCh) must agree with the mode and the bit buffer length, - * but the constraint isn't checked. The given bit buffer is moved and stored. - */ - public: QrSegment(Mode md, int numCh, std::vector &&dt); - - - /*---- Methods ----*/ - - /* - * Returns the mode field of this segment. - */ - public: Mode getMode() const; - - - /* - * Returns the character count field of this segment. - */ - public: int getNumChars() const; - - - /* - * Returns the data bits of this segment. - */ - public: const std::vector &getData() const; - - - // (Package-private) Calculates the number of bits needed to encode the given segments at - // the given version. Returns a non-negative number if successful. Otherwise returns -1 if a - // segment has too many characters to fit its length field, or the total bits exceeds INT_MAX. - public: static int getTotalBits(const std::vector &segs, int version); - - - /*---- Private constant ----*/ - - /* The set of all legal characters in alphanumeric mode, where - * each character value maps to the index in the string. */ - private: static const char *ALPHANUMERIC_CHARSET; - -}; - -} From 29119d3f2edba8e1ce62f5d9707b1ef108687726 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 20 Jan 2020 03:06:05 +0000 Subject: [PATCH 664/810] Fixed typo, added blank line. --- rust/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index be7303c..1dd1ad9 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -862,7 +862,7 @@ impl QrCode { } -/*---- Cconstants and tables ----*/ +/*---- Constants and tables ----*/ /// The minimum version number supported in the QR Code Model 2 standard. pub const QrCode_MIN_VERSION: Version = Version( 1); From 6ea933f1596d818bd21e9a6b8d2e851fb8b4bcf1 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 20 Jan 2020 03:22:04 +0000 Subject: [PATCH 665/810] Refactored Rust code to collect some private functions into new private struct. --- rust/src/lib.rs | 83 +++++++++++++++++++++++++++++++------------------ 1 file changed, 52 insertions(+), 31 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 1dd1ad9..56b2eff 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -647,7 +647,7 @@ impl QrCode { for y in 0 .. size { let mut runcolor = false; let mut runx: i32 = 0; - let mut runhistory = [0i32; 7]; + let mut runhistory = FinderPenalty::new(size); let mut padrun = size; // Add white border to initial run for x in 0 .. size { if self.module(x, y) == runcolor { @@ -658,22 +658,22 @@ impl QrCode { result += 1; } } else { - QrCode::finder_penalty_add_history(runx + padrun, &mut runhistory); + runhistory.add_history(runx + padrun); padrun = 0; if !runcolor { - result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3; + result += runhistory.count_patterns() * PENALTY_N3; } runcolor = self.module(x, y); runx = 1; } } - result += self.finder_penalty_terminate_and_count(runcolor, runx + padrun, &mut runhistory) * PENALTY_N3; + result += runhistory.terminate_and_count(runcolor, runx + padrun) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for x in 0 .. size { let mut runcolor = false; let mut runy: i32 = 0; - let mut runhistory = [0i32; 7]; + let mut runhistory = FinderPenalty::new(size); let mut padrun = size; // Add white border to initial run for y in 0 .. size { if self.module(x, y) == runcolor { @@ -684,16 +684,16 @@ impl QrCode { result += 1; } } else { - QrCode::finder_penalty_add_history(runy + padrun, &mut runhistory); + runhistory.add_history(runy + padrun); padrun = 0; if !runcolor { - result += self.finder_penalty_count_patterns(&runhistory) * PENALTY_N3; + result += runhistory.count_patterns() * PENALTY_N3; } runcolor = self.module(x, y); runy = 1; } } - result += self.finder_penalty_terminate_and_count(runcolor, runy + padrun, &mut runhistory) * PENALTY_N3; + result += runhistory.terminate_and_count(runcolor, runy + padrun) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -827,36 +827,57 @@ impl QrCode { z } +} + + +/*---- Helper struct for get_penalty_score() ----*/ + +struct FinderPenalty { + qr_size: i32, + run_history: [i32; 7], +} + + +impl FinderPenalty { - // Can only be called immediately after a white run is added, and - // returns either 0, 1, or 2. A helper function for get_penalty_score(). - fn finder_penalty_count_patterns(&self, runhistory: &[i32;7]) -> i32 { - let n = runhistory[1]; - assert!(n <= self.size * 3); - let core = n > 0 && runhistory[2] == n && runhistory[3] == n * 3 && runhistory[4] == n && runhistory[5] == n; - ( i32::from(core && runhistory[0] >= n * 4 && runhistory[6] >= n) - + i32::from(core && runhistory[6] >= n * 4 && runhistory[0] >= n)) + pub fn new(size: i32) -> Self { + Self { + qr_size: size, + run_history: [0i32; 7], + } } - // Must be called at the end of a line (row or column) of modules. A helper function for get_penalty_score(). - fn finder_penalty_terminate_and_count(&self, currentruncolor: bool, mut currentrunlength: i32, runhistory: &mut [i32;7]) -> i32 { + // Pushes the given value to the front and drops the last value. + pub fn add_history(&mut self, currentrunlength: i32) { + let rh = &mut self.run_history; + for i in (0 .. rh.len()-1).rev() { + rh[i + 1] = rh[i]; + } + rh[0] = currentrunlength; + } + + + // Can only be called immediately after a white run is added, and returns either 0, 1, or 2. + pub fn count_patterns(&self) -> i32 { + let rh = &self.run_history; + let n = rh[1]; + assert!(n <= self.qr_size * 3); + let core = n > 0 && rh[2] == n && rh[3] == n * 3 && rh[4] == n && rh[5] == n; + ( i32::from(core && rh[0] >= n * 4 && rh[6] >= n) + + i32::from(core && rh[6] >= n * 4 && rh[0] >= n)) + } + + + // Must be called at the end of a line (row or column) of modules. + pub fn terminate_and_count(mut self, currentruncolor: bool, mut currentrunlength: i32) -> i32 { if currentruncolor { // Terminate black run - QrCode::finder_penalty_add_history(currentrunlength, runhistory); + self.add_history(currentrunlength); currentrunlength = 0; } - currentrunlength += self.size; // Add white border to final run - QrCode::finder_penalty_add_history(currentrunlength, runhistory); - self.finder_penalty_count_patterns(runhistory) - } - - - // Pushes the given value to the front and drops the last value. A helper function for get_penalty_score(). - fn finder_penalty_add_history(currentrunlength: i32, runhistory: &mut [i32;7]) { - for i in (0 .. runhistory.len()-1).rev() { - runhistory[i + 1] = runhistory[i]; - } - runhistory[0] = currentrunlength; + currentrunlength += self.qr_size; // Add white border to final run + self.add_history(currentrunlength); + self.count_patterns() } } From ec65e320373f9154f7c0c26d13bc15d70012b367 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Jan 2020 00:41:07 +0000 Subject: [PATCH 666/810] Updated copyright year in readme document. --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index c220958..d28e31c 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -175,7 +175,7 @@ Rust language: License ------- -Copyright © 2019 Project Nayuki. (MIT License) +Copyright © 2020 Project Nayuki. (MIT License) [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of From fd8720cd316c0c7a4226ed52e825825de0fe7277 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Jan 2020 00:41:13 +0000 Subject: [PATCH 667/810] Simplified a bit of TypeScript code. --- typescript-javascript/qrcodegen.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 9b75c06..6480f4a 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -572,12 +572,8 @@ namespace qrcodegen { // Balance of black and white modules let black: int = 0; - for (const row of this.modules) { - for (const color of row) { - if (color) - black++; - } - } + for (const row of this.modules) + black = row.reduce((sum, color) => sum + (color ? 1 : 0), black); const total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% const k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; From 1439e8e4a5a966ba3fa38c2771d5c5bc3c46ba31 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Jan 2020 00:53:27 +0000 Subject: [PATCH 668/810] Simplified some Rust code. --- rust/src/lib.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 56b2eff..0d4c018 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -648,7 +648,6 @@ impl QrCode { let mut runcolor = false; let mut runx: i32 = 0; let mut runhistory = FinderPenalty::new(size); - let mut padrun = size; // Add white border to initial run for x in 0 .. size { if self.module(x, y) == runcolor { runx += 1; @@ -658,8 +657,7 @@ impl QrCode { result += 1; } } else { - runhistory.add_history(runx + padrun); - padrun = 0; + runhistory.add_history(runx); if !runcolor { result += runhistory.count_patterns() * PENALTY_N3; } @@ -667,14 +665,13 @@ impl QrCode { runx = 1; } } - result += runhistory.terminate_and_count(runcolor, runx + padrun) * PENALTY_N3; + result += runhistory.terminate_and_count(runcolor, runx) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for x in 0 .. size { let mut runcolor = false; let mut runy: i32 = 0; let mut runhistory = FinderPenalty::new(size); - let mut padrun = size; // Add white border to initial run for y in 0 .. size { if self.module(x, y) == runcolor { runy += 1; @@ -684,8 +681,7 @@ impl QrCode { result += 1; } } else { - runhistory.add_history(runy + padrun); - padrun = 0; + runhistory.add_history(runy); if !runcolor { result += runhistory.count_patterns() * PENALTY_N3; } @@ -693,7 +689,7 @@ impl QrCode { runy = 1; } } - result += runhistory.terminate_and_count(runcolor, runy + padrun) * PENALTY_N3; + result += runhistory.terminate_and_count(runcolor, runy) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -849,7 +845,10 @@ impl FinderPenalty { // Pushes the given value to the front and drops the last value. - pub fn add_history(&mut self, currentrunlength: i32) { + pub fn add_history(&mut self, mut currentrunlength: i32) { + if self.run_history[0] == 0 { + currentrunlength += self.qr_size; // Add white border to initial run + } let rh = &mut self.run_history; for i in (0 .. rh.len()-1).rev() { rh[i + 1] = rh[i]; From fd6917601d0a77b6b6df81599118212d8cdb9a27 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 27 Jan 2020 01:01:37 +0000 Subject: [PATCH 669/810] Fixed path in batch tester, due to commit f759146df37f. --- python/qrcodegen-batch-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index f0d3663..ef56b76 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -33,7 +33,7 @@ CHILD_PROGRAMS = [ ["python2", "-B", "../python/qrcodegen-worker.py"], # Python 2 program ["python3", "-B", "../python/qrcodegen-worker.py"], # Python 3 program ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program - ["node", "../typescript/qrcodegen-worker.js"], # TypeScript program + ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program ["../c/qrcodegen-worker"], # C program ["../cpp/QrCodeGeneratorWorker"], # C++ program ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program From 2da5091674ac623523d226b41dd82caade69ca14 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 29 Jan 2020 17:07:07 +0000 Subject: [PATCH 670/810] Tweaked Rust code to add useful auto-derived traits to almost all structs. --- rust/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 0d4c018..6a90c81 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -103,7 +103,7 @@ /// appropriate version number, and call the `QrCode::encode_codewords()` constructor. /// /// (Note that all ways require supplying the desired error correction level.) -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct QrCode { // Scalar parameters: @@ -921,7 +921,7 @@ static NUM_ERROR_CORRECTION_BLOCKS: [[i8; 41]; 4] = [ /*---- QrCodeEcc functionality ----*/ /// The error correction level in a QR Code symbol. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] pub enum QrCodeEcc { /// The QR Code can tolerate about 7% erroneous codewords. Low , @@ -977,7 +977,7 @@ impl QrCodeEcc { /// This segment struct imposes no length restrictions, but QR Codes have restrictions. /// Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. /// Any segment longer than this is meaningless for the purpose of generating QR Codes. -#[derive(Clone)] +#[derive(Clone, PartialEq, Eq)] pub struct QrSegment { // The mode indicator of this segment. Accessed through mode(). @@ -1178,7 +1178,7 @@ static ALPHANUMERIC_CHARSET: [char; 45] = ['0','1','2','3','4','5','6','7','8',' /*---- QrSegmentMode functionality ----*/ /// Describes how a segment's data bits are interpreted. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub enum QrSegmentMode { Numeric, Alphanumeric, @@ -1272,7 +1272,7 @@ impl std::fmt::Display for DataTooLong { /// A number between 1 and 40 (inclusive). -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Version(u8); impl Version { @@ -1292,7 +1292,7 @@ impl Version { /// A number between 0 and 7 (inclusive). -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Mask(u8); impl Mask { From ae22a368bb30651449466161b404582816caa7d8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 29 Jan 2020 17:23:44 +0000 Subject: [PATCH 671/810] De-optimized a bit of Java code to tighten scope at the expense of allocating more temporary memory. --- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 59608e8..c0a8871 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -596,11 +596,10 @@ public final class QrCode { int result = 0; // Adjacent modules in row having same color, and finder-like patterns - int[] runHistory = new int[7]; for (int y = 0; y < size; y++) { boolean runColor = false; int runX = 0; - Arrays.fill(runHistory, 0); + int[] runHistory = new int[7]; int padRun = size; // Add white border to initial run for (int x = 0; x < size; x++) { if (modules[y][x] == runColor) { @@ -624,7 +623,7 @@ public final class QrCode { for (int x = 0; x < size; x++) { boolean runColor = false; int runY = 0; - Arrays.fill(runHistory, 0); + int[] runHistory = new int[7]; int padRun = size; // Add white border to initial run for (int y = 0; y < size; y++) { if (modules[y][x] == runColor) { From 13a25580a3e2a29b2b6653802d58d39056f3eaf2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 29 Jan 2020 17:52:21 +0000 Subject: [PATCH 672/810] Simplified QrCode.getPenalty() in all language versions except Rust, related to commit 1439e8e4a5a9. --- c/qrcodegen.c | 22 +++++++++---------- cpp/QrCode.cpp | 16 ++++++-------- cpp/QrCode.hpp | 2 +- .../main/java/io/nayuki/qrcodegen/QrCode.java | 16 ++++++-------- python/qrcodegen.py | 22 ++++++++++--------- typescript-javascript/qrcodegen.ts | 20 ++++++++--------- 6 files changed, 46 insertions(+), 52 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index cf7cd64..31219a7 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -74,7 +74,7 @@ static void applyMask(const uint8_t functionModules[], uint8_t qrcode[], enum qr static long getPenaltyScore(const uint8_t qrcode[]); static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize); static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize); -static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7]); +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); testable bool getModule(const uint8_t qrcode[], int x, int y); testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); @@ -642,7 +642,6 @@ static long getPenaltyScore(const uint8_t qrcode[]) { bool runColor = false; int runX = 0; int runHistory[7] = {0}; - int padRun = qrsize; // Add white border to initial run for (int x = 0; x < qrsize; x++) { if (getModule(qrcode, x, y) == runColor) { runX++; @@ -651,22 +650,20 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runX > 5) result++; } else { - finderPenaltyAddHistory(runX + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runX, runHistory, qrsize); if (!runColor) result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; runColor = getModule(qrcode, x, y); runX = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory, qrsize) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory, qrsize) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < qrsize; x++) { bool runColor = false; int runY = 0; int runHistory[7] = {0}; - int padRun = qrsize; // Add white border to initial run for (int y = 0; y < qrsize; y++) { if (getModule(qrcode, x, y) == runColor) { runY++; @@ -675,15 +672,14 @@ static long getPenaltyScore(const uint8_t qrcode[]) { else if (runY > 5) result++; } else { - finderPenaltyAddHistory(runY + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runY, runHistory, qrsize); if (!runColor) result += finderPenaltyCountPatterns(runHistory, qrsize) * PENALTY_N3; runColor = getModule(qrcode, x, y); runY = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory, qrsize) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory, qrsize) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -729,17 +725,19 @@ static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { if (currentRunColor) { // Terminate black run - finderPenaltyAddHistory(currentRunLength, runHistory); + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); currentRunLength = 0; } currentRunLength += qrsize; // Add white border to final run - finderPenaltyAddHistory(currentRunLength, runHistory); + finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); return finderPenaltyCountPatterns(runHistory, qrsize); } // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). -static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7]) { +static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { + if (runHistory[0] == 0) + currentRunLength += qrsize; // Add white border to initial run memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); runHistory[0] = currentRunLength; } diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index ad1fa74..b9de862 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -625,7 +625,6 @@ long QrCode::getPenaltyScore() const { bool runColor = false; int runX = 0; std::array runHistory = {}; - int padRun = size; // Add white border to initial run for (int x = 0; x < size; x++) { if (module(x, y) == runColor) { runX++; @@ -634,22 +633,20 @@ long QrCode::getPenaltyScore() const { else if (runX > 5) result++; } else { - finderPenaltyAddHistory(runX + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runX, runHistory); if (!runColor) result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = module(x, y); runX = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { bool runColor = false; int runY = 0; std::array runHistory = {}; - int padRun = size; // Add white border to initial run for (int y = 0; y < size; y++) { if (module(x, y) == runColor) { runY++; @@ -658,15 +655,14 @@ long QrCode::getPenaltyScore() const { else if (runY > 5) result++; } else { - finderPenaltyAddHistory(runY + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runY, runHistory); if (!runColor) result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = module(x, y); runY = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -807,7 +803,9 @@ int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunL } -void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) { +void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { + if (runHistory.at(0) == 0) + currentRunLength += size; // Add white border to initial run std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); runHistory.at(0) = currentRunLength; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index bcc0426..7341e41 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -482,7 +482,7 @@ class QrCode final { // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - private: static void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory); + private: void finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const; // Returns true iff the i'th bit of x is set to 1. diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index c0a8871..ea0196c 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -600,7 +600,6 @@ public final class QrCode { boolean runColor = false; int runX = 0; int[] runHistory = new int[7]; - int padRun = size; // Add white border to initial run for (int x = 0; x < size; x++) { if (modules[y][x] == runColor) { runX++; @@ -609,22 +608,20 @@ public final class QrCode { else if (runX > 5) result++; } else { - finderPenaltyAddHistory(runX + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runX, runHistory); if (!runColor) result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = modules[y][x]; runX = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runX, runHistory) * PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (int x = 0; x < size; x++) { boolean runColor = false; int runY = 0; int[] runHistory = new int[7]; - int padRun = size; // Add white border to initial run for (int y = 0; y < size; y++) { if (modules[y][x] == runColor) { runY++; @@ -633,15 +630,14 @@ public final class QrCode { else if (runY > 5) result++; } else { - finderPenaltyAddHistory(runY + padRun, runHistory); - padRun = 0; + finderPenaltyAddHistory(runY, runHistory); if (!runColor) result += finderPenaltyCountPatterns(runHistory) * PENALTY_N3; runColor = modules[y][x]; runY = 1; } } - result += finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * PENALTY_N3; + result += finderPenaltyTerminateAndCount(runColor, runY, runHistory) * PENALTY_N3; } // 2*2 blocks of modules having same color @@ -814,7 +810,9 @@ public final class QrCode { // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - private static void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { + private void finderPenaltyAddHistory(int currentRunLength, int[] runHistory) { + if (runHistory[0] == 0) + currentRunLength += size; // Add white border to initial run System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); runHistory[0] = currentRunLength; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 93c0e85..260ace8 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -467,7 +467,6 @@ class QrCode(object): runcolor = False runx = 0 runhistory = collections.deque([0] * 7, 7) - padrun = size # Add white border to initial run for x in range(size): if modules[y][x] == runcolor: runx += 1 @@ -476,19 +475,17 @@ class QrCode(object): elif runx > 5: result += 1 else: - runhistory.appendleft(runx + padrun) - padrun = 0 + self._finder_penalty_add_history(runx, runhistory) if not runcolor: result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3 runcolor = modules[y][x] runx = 1 - result += self._finder_penalty_terminate_and_count(runcolor, runx + padrun, runhistory) * QrCode._PENALTY_N3 + result += self._finder_penalty_terminate_and_count(runcolor, runx, runhistory) * QrCode._PENALTY_N3 # Adjacent modules in column having same color, and finder-like patterns for x in range(size): runcolor = False runy = 0 runhistory = collections.deque([0] * 7, 7) - padrun = size # Add white border to initial run for y in range(size): if modules[y][x] == runcolor: runy += 1 @@ -497,13 +494,12 @@ class QrCode(object): elif runy > 5: result += 1 else: - runhistory.appendleft(runy + padrun) - padrun = 0 + self._finder_penalty_add_history(runy, runhistory) if not runcolor: result += self._finder_penalty_count_patterns(runhistory) * QrCode._PENALTY_N3 runcolor = modules[y][x] runy = 1 - result += self._finder_penalty_terminate_and_count(runcolor, runy + padrun, runhistory) * QrCode._PENALTY_N3 + result += self._finder_penalty_terminate_and_count(runcolor, runy, runhistory) * QrCode._PENALTY_N3 # 2*2 blocks of modules having same color for y in range(size - 1): @@ -628,13 +624,19 @@ class QrCode(object): def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory): """Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score().""" if currentruncolor: # Terminate black run - runhistory.appendleft(currentrunlength) + self._finder_penalty_add_history(currentrunlength, runhistory) currentrunlength = 0 currentrunlength += self._size # Add white border to final run - runhistory.appendleft(currentrunlength) + self._finder_penalty_add_history(currentrunlength, runhistory) return self._finder_penalty_count_patterns(runhistory) + def _finder_penalty_add_history(self, currentrunlength, runhistory): + if runhistory[0] == 0: + currentrunlength += self._size # Add white border to initial run + runhistory.appendleft(currentrunlength) + + # ---- Constants and tables ---- MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 6480f4a..88663ee 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -515,7 +515,6 @@ namespace qrcodegen { let runColor = false; let runX = 0; let runHistory = [0,0,0,0,0,0,0]; - let padRun = this.size; for (let x = 0; x < this.size; x++) { if (this.modules[y][x] == runColor) { runX++; @@ -524,22 +523,20 @@ namespace qrcodegen { else if (runX > 5) result++; } else { - QrCode.finderPenaltyAddHistory(runX + padRun, runHistory); - padRun = 0; + this.finderPenaltyAddHistory(runX, runHistory); if (!runColor) result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = this.modules[y][x]; runX = 1; } } - result += this.finderPenaltyTerminateAndCount(runColor, runX + padRun, runHistory) * QrCode.PENALTY_N3; + result += this.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * QrCode.PENALTY_N3; } // Adjacent modules in column having same color, and finder-like patterns for (let x = 0; x < this.size; x++) { let runColor = false; let runY = 0; let runHistory = [0,0,0,0,0,0,0]; - let padRun = this.size; for (let y = 0; y < this.size; y++) { if (this.modules[y][x] == runColor) { runY++; @@ -548,15 +545,14 @@ namespace qrcodegen { else if (runY > 5) result++; } else { - QrCode.finderPenaltyAddHistory(runY + padRun, runHistory); - padRun = 0; + this.finderPenaltyAddHistory(runY, runHistory); if (!runColor) result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; runColor = this.modules[y][x]; runY = 1; } } - result += this.finderPenaltyTerminateAndCount(runColor, runY + padRun, runHistory) * QrCode.PENALTY_N3; + result += this.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * QrCode.PENALTY_N3; } // 2*2 blocks of modules having same color @@ -705,17 +701,19 @@ namespace qrcodegen { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). private finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array): int { if (currentRunColor) { // Terminate black run - QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + this.finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } currentRunLength += this.size; // Add white border to final run - QrCode.finderPenaltyAddHistory(currentRunLength, runHistory); + this.finderPenaltyAddHistory(currentRunLength, runHistory); return this.finderPenaltyCountPatterns(runHistory); } // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). - private static finderPenaltyAddHistory(currentRunLength: int, runHistory: Array): void { + private finderPenaltyAddHistory(currentRunLength: int, runHistory: Array): void { + if (runHistory[0] == 0) + currentRunLength += this.size; // Add white border to initial run runHistory.pop(); runHistory.unshift(currentRunLength); } From 5217af7cf69c02f77c88bf489e1304676035bf2d Mon Sep 17 00:00:00 2001 From: Thomas Klausner Date: Sat, 8 Feb 2020 18:48:32 +0100 Subject: [PATCH 673/810] Honor LDFLAGS when building executables. --- c/Makefile | 4 ++-- cpp/Makefile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/c/Makefile b/c/Makefile index fd0c367..314d175 100644 --- a/c/Makefile +++ b/c/Makefile @@ -65,11 +65,11 @@ clean: # Executable files %: %.o $(LIBFILE) - $(CC) $(CFLAGS) -o $@ $< -L . -l $(LIB) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB) # Special executable qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c) - $(CC) $(CFLAGS) -DQRCODEGEN_TEST -o $@ $^ + $(CC) $(CFLAGS) $(LDFLAGS) -DQRCODEGEN_TEST -o $@ $^ # The library $(LIBFILE): $(LIBOBJ) diff --git a/cpp/Makefile b/cpp/Makefile index f83c512..374bfe9 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -65,7 +65,7 @@ clean: # Executable files %: %.o $(LIBFILE) - $(CXX) $(CXXFLAGS) -o $@ $< -L . -l $(LIB) + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB) # The library $(LIBFILE): $(LIBOBJ) From fd849b188cf78c78b9ec36876add6bf47a82b8e6 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 5 Mar 2020 17:46:11 +0000 Subject: [PATCH 674/810] Updated package version numbers. --- java/pom.xml | 2 +- python/setup.py | 2 +- rust/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/java/pom.xml b/java/pom.xml index d36e826..b6ecffb 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -4,7 +4,7 @@ io.nayuki qrcodegen - 1.5.0 + 1.6.0 jar UTF-8 diff --git a/python/setup.py b/python/setup.py index 5bffaaf..23ca70e 100644 --- a/python/setup.py +++ b/python/setup.py @@ -27,7 +27,7 @@ import setuptools setuptools.setup( name = "qrcodegen", description = "High quality QR Code generator library for Python 2 and 3", - version = "1.5.0", + version = "1.6.0", platforms = "OS Independent", license = "MIT License", diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 001b52d..251cbbb 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qrcodegen" -version = "1.5.0" +version = "1.6.0" authors = ["Project Nayuki"] description = "High-quality QR Code generator library" homepage = "https://www.nayuki.io/page/qr-code-generator-library" From 71c75cfeb0f06788ebc43a39b704c39fcf5eba7c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 5 Mar 2020 19:00:48 +0000 Subject: [PATCH 675/810] Tweaked Java Maven POM configuration. --- java/pom.xml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/java/pom.xml b/java/pom.xml index b6ecffb..0645a73 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -14,6 +14,15 @@ + + org.apache.maven.plugins + maven-release-plugin + + + java/pom.xml + + + org.apache.maven.plugins maven-source-plugin @@ -30,7 +39,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 3.1.1 attach-javadocs From f93ce5217e9633b7475774fee4c6c048e98b56fd Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 24 Apr 2020 17:40:17 +0000 Subject: [PATCH 676/810] Simplified code by removing support for Python 2, leaving only support for Python 3. --- python/qrcodegen-batch-test.py | 7 ++----- python/qrcodegen-demo.py | 27 +++++++++++++-------------- python/qrcodegen-worker.py | 5 ++--- python/qrcodegen.py | 19 ++++++++----------- python/setup.py | 5 ++--- 5 files changed, 27 insertions(+), 36 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index ef56b76..2d07208 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -1,5 +1,5 @@ # -# QR Code generator batch test (Python 3) +# QR Code generator batch test (Python) # # Runs various versions of the QR Code generator test worker as subprocesses, # feeds each one the same random input, and compares their output for equality. @@ -25,13 +25,10 @@ # import itertools, random, subprocess, sys, time -if sys.version_info.major < 3: - raise RuntimeError("Requires Python 3+") CHILD_PROGRAMS = [ - ["python2", "-B", "../python/qrcodegen-worker.py"], # Python 2 program - ["python3", "-B", "../python/qrcodegen-worker.py"], # Python 3 program + ["python3", "-B", "../python/qrcodegen-worker.py"], # Python program ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program ["../c/qrcodegen-worker"], # C program diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index e809a6f..f6affed 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -1,5 +1,5 @@ # -# QR Code generator demo (Python 2, 3) +# QR Code generator demo (Python) # # Run this command-line program with no arguments. The program computes a bunch of demonstration # QR Codes and prints them to the console. Also, the SVG code for one QR Code is printed as a sample. @@ -24,7 +24,6 @@ # Software. # -from __future__ import print_function from qrcodegen import QrCode, QrSegment @@ -41,7 +40,7 @@ def main(): def do_basic_demo(): """Creates a single QR Code, then prints it to the console.""" - text = u"Hello, world!" # User-supplied Unicode text + text = "Hello, world!" # User-supplied Unicode text errcorlvl = QrCode.Ecc.LOW # Error correction level # Make and print the QR Code symbol @@ -62,7 +61,7 @@ def do_variety_demo(): print_qr(qr) # Unicode text as UTF-8 - qr = QrCode.encode_text(u"\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE) + qr = QrCode.encode_text("\u3053\u3093\u306B\u3061\u0077\u0061\u3001\u4E16\u754C\uFF01\u0020\u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE) print_qr(qr) # Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) @@ -93,9 +92,9 @@ def do_segment_demo(): print_qr(qr) # Illustration "golden" - golden0 = u"Golden ratio \u03C6 = 1." - golden1 = u"6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" - golden2 = u"......" + golden0 = "Golden ratio \u03C6 = 1." + golden1 = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374" + golden2 = "......" qr = QrCode.encode_text(golden0 + golden1 + golden2, QrCode.Ecc.LOW) print_qr(qr) @@ -107,7 +106,7 @@ def do_segment_demo(): print_qr(qr) # Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters - madoka = u"\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F" + madoka = "\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F" qr = QrCode.encode_text(madoka, QrCode.Ecc.LOW) print_qr(qr) @@ -157,11 +156,11 @@ def do_mask_demo(): # Chinese text as UTF-8 segs = QrSegment.make_segments( - u"\u7DAD\u57FA\u767E\u79D1\uFF08\u0057\u0069\u006B\u0069\u0070\u0065\u0064\u0069\u0061\uFF0C" - "\u8046\u807D\u0069\u002F\u02CC\u0077\u026A\u006B\u1D7B\u02C8\u0070\u0069\u02D0\u0064\u0069" - "\u002E\u0259\u002F\uFF09\u662F\u4E00\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8" - "\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08" - "\u756B") + "\u7DAD\u57FA\u767E\u79D1\uFF08\u0057\u0069\u006B\u0069\u0070\u0065\u0064\u0069\u0061\uFF0C" + "\u8046\u807D\u0069\u002F\u02CC\u0077\u026A\u006B\u1D7B\u02C8\u0070\u0069\u02D0\u0064\u0069" + "\u002E\u0259\u002F\uFF09\u662F\u4E00\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8" + "\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08" + "\u756B") print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=0)) # Force mask 0 print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=1)) # Force mask 1 print_qr(QrCode.encode_segments(segs, QrCode.Ecc.MEDIUM, mask=5)) # Force mask 5 @@ -176,7 +175,7 @@ def print_qr(qrcode): border = 4 for y in range(-border, qrcode.get_size() + border): for x in range(-border, qrcode.get_size() + border): - print(u"\u2588 "[1 if qrcode.get_module(x,y) else 0] * 2, end="") + print("\u2588 "[1 if qrcode.get_module(x,y) else 0] * 2, end="") print() print() diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py index b159bb6..6d3be75 100644 --- a/python/qrcodegen-worker.py +++ b/python/qrcodegen-worker.py @@ -1,5 +1,5 @@ # -# QR Code generator test worker (Python 2, 3) +# QR Code generator test worker (Python) # # This program reads data and encoding parameters from standard input and writes # QR Code bitmaps to standard output. The I/O format is one integer per line. @@ -28,11 +28,10 @@ import sys import qrcodegen -py3 = sys.version_info.major >= 3 def read_int(): - return int((input if py3 else raw_input)()) + return int(input()) def main(): diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 260ace8..a3134f3 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -1,5 +1,5 @@ # -# QR Code generator library (Python 2, 3) +# QR Code generator library (Python) # # Copyright (c) Project Nayuki. (MIT License) # https://www.nayuki.io/page/qr-code-generator-library @@ -21,7 +21,7 @@ # Software. # -import collections, itertools, re, sys +import collections, itertools, re """ @@ -60,7 +60,7 @@ This module "qrcodegen", public members: # ---- QR Code symbol class ---- -class QrCode(object): +class QrCode: """A QR Code symbol, which is a type of two-dimension barcode. Invented by Denso Wave and described in the ISO/IEC 18004 standard. Instances of this class represent an immutable square grid of black and white cells. @@ -678,7 +678,7 @@ class QrCode(object): # ---- Public helper enumeration ---- - class Ecc(object): + class Ecc: """The error correction level in a QR Code symbol. Immutable.""" # Private constructor def __init__(self, i, fb): @@ -695,7 +695,7 @@ class QrCode(object): # ---- Data segment class ---- -class QrSegment(object): +class QrSegment: """A segment of character/binary/control data in a QR Code symbol. Instances of this class are immutable. The mid-level way to create a segment is to take the payload data @@ -713,11 +713,8 @@ class QrSegment(object): """Returns a segment representing the given binary data encoded in byte mode. All input byte lists are acceptable. Any text string can be converted to UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment.""" - py3 = sys.version_info.major >= 3 - if (py3 and isinstance(data, str)) or (not py3 and isinstance(data, unicode)): + if isinstance(data, str): raise TypeError("Byte string/list expected") - if not py3 and isinstance(data, str): - data = bytearray(data) bb = _BitBuffer() for b in data: bb.append_bits(b, 8) @@ -759,7 +756,7 @@ class QrSegment(object): def make_segments(text): """Returns a new mutable list of zero or more segments to represent the given Unicode text string. The result may use various segment modes and switch modes to optimize the length of the bit stream.""" - if not (isinstance(text, str) or (sys.version_info.major < 3 and isinstance(text, unicode))): + if not isinstance(text, str): raise TypeError("Text string expected") # Select the most efficient segment encoding automatically @@ -867,7 +864,7 @@ class QrSegment(object): # ---- Public helper enumeration ---- - class Mode(object): + class Mode: """Describes how a segment's data bits are interpreted. Immutable.""" # Private constructor diff --git a/python/setup.py b/python/setup.py index 23ca70e..cd4da26 100644 --- a/python/setup.py +++ b/python/setup.py @@ -1,5 +1,5 @@ # -# QR Code generator Distutils script (Python 2, 3) +# QR Code generator Distutils script (Python) # # Copyright (c) Project Nayuki. (MIT License) # https://www.nayuki.io/page/qr-code-generator-library @@ -26,7 +26,7 @@ import setuptools setuptools.setup( name = "qrcodegen", - description = "High quality QR Code generator library for Python 2 and 3", + description = "High quality QR Code generator library for Python", version = "1.6.0", platforms = "OS Independent", license = "MIT License", @@ -42,7 +42,6 @@ setuptools.setup( "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", - "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: Python Modules", From 979de6d335500451e3f5491cad12b59b5adfacea Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 24 Apr 2020 17:57:23 +0000 Subject: [PATCH 677/810] Simplified a bit of Python code. --- python/qrcodegen-batch-test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index 2d07208..5f2370d 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -70,10 +70,10 @@ def do_trial(): mode = random.randrange(4) if mode == 0: # Numeric length = round((2 * 7089) ** random.random()) - data = [random.randrange(48, 58) for _ in range(length)] + data = random.choices(b"0123456789", k=length) elif mode == 1: # Alphanumeric length = round((2 * 4296) ** random.random()) - data = [ord(random.choice("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")) for _ in range(length)] + data = random.choices(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", k=length) elif mode == 2: # ASCII length = round((2 * 2953) ** random.random()) data = [random.randrange(128) for _ in range(length)] From 49f9ea0890f5d8b208909c6aa670f6241824a986 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 24 Apr 2020 18:06:22 +0000 Subject: [PATCH 678/810] Tweaked Python code to allow static type checking, without changing behavior. --- python/qrcodegen-batch-test.py | 16 ++++++++++++---- python/qrcodegen.py | 33 +++++++++++++++++++++++---------- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index 5f2370d..720a26d 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -25,6 +25,7 @@ # import itertools, random, subprocess, sys, time +from typing import Optional, TypeVar CHILD_PROGRAMS = [ @@ -56,7 +57,7 @@ def main(): for proc in subprocs: if proc.poll() is None: print(-1, file=proc.stdin) - proc.stdin.flush() + not_none(proc.stdin).flush() sys.exit("Error: One or more workers failed to start") # Do tests @@ -120,15 +121,22 @@ def write_all(val): def flush_all(): for proc in subprocs: - proc.stdin.flush() + not_none(proc.stdin).flush() def read_verify(): - val = subprocs[0].stdout.readline().rstrip("\r\n") + val = not_none(subprocs[0].stdout).readline().rstrip("\r\n") for proc in subprocs[1 : ]: - if proc.stdout.readline().rstrip("\r\n") != val: + if not_none(proc.stdout).readline().rstrip("\r\n") != val: raise ValueError("Mismatch") return int(val) +T = TypeVar("T") +def not_none(obj: Optional[T]) -> T: + if obj is None: + raise TypeError() + return obj + + if __name__ == "__main__": main() diff --git a/python/qrcodegen.py b/python/qrcodegen.py index a3134f3..ce3118c 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -650,19 +650,19 @@ class QrCode: _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 - (None, 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 - (None, 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 - (None, 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 - (None, 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 + # 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 _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 - (None, 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 - (None, 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 - (None, 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 - (None, 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 + # 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 _MASK_PATTERNS = ( (lambda x, y: (x + y) % 2 ), @@ -684,6 +684,12 @@ class QrCode: def __init__(self, i, fb): self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) + + # Placeholders + LOW : QrCode.Ecc + MEDIUM : QrCode.Ecc + QUARTILE: QrCode.Ecc + HIGH : QrCode.Ecc # Public constants. Create them outside the class. Ecc.LOW = Ecc(0, 1) # The QR Code can tolerate about 7% erroneous codewords @@ -882,6 +888,13 @@ class QrSegment: """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].""" return self._charcounts[(ver + 7) // 17] + + # Placeholders + NUMERIC : QrSegment.Mode + ALPHANUMERIC: QrSegment.Mode + BYTE : QrSegment.Mode + KANJI : QrSegment.Mode + ECI : QrSegment.Mode # Public constants. Create them outside the class. Mode.NUMERIC = Mode(0x1, (10, 12, 14)) From 55dd3c881ea10d5b4c73be7e1e3603d2ecf77dbc Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 24 Apr 2020 18:07:59 +0000 Subject: [PATCH 679/810] Added type annotations to all Python functions and methods, and almost all global variables. --- python/qrcodegen-batch-test.py | 16 +++--- python/qrcodegen-demo.py | 12 ++--- python/qrcodegen-worker.py | 4 +- python/qrcodegen.py | 91 +++++++++++++++++----------------- 4 files changed, 62 insertions(+), 61 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index 720a26d..b4279e0 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -25,10 +25,10 @@ # import itertools, random, subprocess, sys, time -from typing import Optional, TypeVar +from typing import List, Optional, TypeVar -CHILD_PROGRAMS = [ +CHILD_PROGRAMS: List[List[str]] = [ ["python3", "-B", "../python/qrcodegen-worker.py"], # Python program ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program @@ -38,9 +38,9 @@ CHILD_PROGRAMS = [ ] -subprocs = [] +subprocs: List[subprocess.Popen] = [] -def main(): +def main() -> None: # Launch workers global subprocs try: @@ -67,7 +67,7 @@ def main(): print() -def do_trial(): +def do_trial() -> None: mode = random.randrange(4) if mode == 0: # Numeric length = round((2 * 7089) ** random.random()) @@ -115,15 +115,15 @@ def do_trial(): read_verify() -def write_all(val): +def write_all(val: int) -> None: for proc in subprocs: print(val, file=proc.stdin) -def flush_all(): +def flush_all() -> None: for proc in subprocs: not_none(proc.stdin).flush() -def read_verify(): +def read_verify() -> int: val = not_none(subprocs[0].stdout).readline().rstrip("\r\n") for proc in subprocs[1 : ]: if not_none(proc.stdout).readline().rstrip("\r\n") != val: diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index f6affed..159dd56 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -27,7 +27,7 @@ from qrcodegen import QrCode, QrSegment -def main(): +def main() -> None: """The main application program.""" do_basic_demo() do_variety_demo() @@ -38,7 +38,7 @@ def main(): # ---- Demo suite ---- -def do_basic_demo(): +def do_basic_demo() -> None: """Creates a single QR Code, then prints it to the console.""" text = "Hello, world!" # User-supplied Unicode text errcorlvl = QrCode.Ecc.LOW # Error correction level @@ -49,7 +49,7 @@ def do_basic_demo(): print(qr.to_svg_str(4)) -def do_variety_demo(): +def do_variety_demo() -> None: """Creates a variety of QR Codes that exercise different features of the library, and prints each one to the console.""" # Numeric mode encoding (3.33 bits per digit) @@ -76,7 +76,7 @@ def do_variety_demo(): print_qr(qr) -def do_segment_demo(): +def do_segment_demo() -> None: """Creates QR Codes with manually specified segments for better compactness.""" # Illustration "silver" @@ -146,7 +146,7 @@ def do_segment_demo(): print_qr(qr) -def do_mask_demo(): +def do_mask_demo() -> None: """Creates QR Codes with the same size and contents but different mask patterns.""" # Project Nayuki URL @@ -170,7 +170,7 @@ def do_mask_demo(): # ---- Utilities ---- -def print_qr(qrcode): +def print_qr(qrcode: QrCode) -> None: """Prints the given QrCode object to the console.""" border = 4 for y in range(-border, qrcode.get_size() + border): diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py index 6d3be75..a7b0ce6 100644 --- a/python/qrcodegen-worker.py +++ b/python/qrcodegen-worker.py @@ -30,11 +30,11 @@ import sys import qrcodegen -def read_int(): +def read_int() -> int: return int(input()) -def main(): +def main() -> None: while True: # Read data or exit diff --git a/python/qrcodegen.py b/python/qrcodegen.py index ce3118c..744d9ae 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -22,6 +22,7 @@ # import collections, itertools, re +from typing import List, Optional, Tuple """ @@ -79,7 +80,7 @@ class QrCode: # ---- Static factory functions (high level) ---- @staticmethod - def encode_text(text, ecl): + def encode_text(text: str, ecl: QrCode.Ecc) -> QrCode: """Returns a QR Code representing the given Unicode text string at the given error correction level. As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible @@ -90,7 +91,7 @@ class QrCode: @staticmethod - def encode_binary(data, ecl): + def encode_binary(data: bytes, ecl: QrCode.Ecc) -> QrCode: """Returns a QR Code representing the given binary data at the given error correction level. This function always encodes using the binary segment mode, not any text mode. The maximum number of bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. @@ -103,7 +104,7 @@ class QrCode: # ---- Static factory functions (mid level) ---- @staticmethod - def encode_segments(segs, ecl, minversion=1, maxversion=40, mask=-1, boostecl=True): + def encode_segments(segs: List[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode: """Returns a QR Code representing the given segments with the given encoding parameters. The smallest possible QR Code version within the given range is automatically chosen for the output. Iff boostecl is true, then the ECC level of the result @@ -168,7 +169,7 @@ class QrCode: # ---- Constructor (low level) ---- - def __init__(self, version, errcorlvl, datacodewords, mask): + def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: List[int], mask: int) -> None: """Creates a new QR Code with the given version number, error correction level, data codeword bytes, and mask number. This is a low-level API that most users should not use directly. @@ -230,23 +231,23 @@ class QrCode: # ---- Accessor methods ---- - def get_version(self): + def get_version(self) -> int: """Returns this QR Code's version number, in the range [1, 40].""" return self._version - def get_size(self): + def get_size(self) -> int: """Returns this QR Code's size, in the range [21, 177].""" return self._size - def get_error_correction_level(self): + def get_error_correction_level(self) -> QrCode.Ecc: """Returns this QR Code's error correction level.""" return self._errcorlvl - def get_mask(self): + def get_mask(self) -> int: """Returns this QR Code's mask, in the range [0, 7].""" return self._mask - def get_module(self, x, y): + def get_module(self, x, y) -> bool: """Returns the color of the module (pixel) at the given coordinates, which is False for white or True for black. The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then False (white) is returned.""" @@ -255,7 +256,7 @@ class QrCode: # ---- Public instance methods ---- - def to_svg_str(self, border): + def to_svg_str(self, border: int) -> str: """Returns a string of SVG code for an image depicting this QR Code, with the given number of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" if border < 0: @@ -276,7 +277,7 @@ class QrCode: # ---- Private helper methods for constructor: Drawing function modules ---- - def _draw_function_patterns(self): + def _draw_function_patterns(self) -> None: """Reads this object's version field, and draws and marks all function modules.""" # Draw horizontal and vertical timing patterns for i in range(self._size): @@ -302,7 +303,7 @@ class QrCode: self._draw_version() - def _draw_format_bits(self, mask): + def _draw_format_bits(self, mask) -> None: """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.""" # Calculate error correction code and pack bits @@ -330,7 +331,7 @@ class QrCode: self._set_function_module(8, self._size - 8, True) # Always black - def _draw_version(self): + def _draw_version(self) -> None: """Draws two copies of the version bits (with its own error correction code), based on this object's version field, iff 7 <= version <= 40.""" if self._version < 7: @@ -352,7 +353,7 @@ class QrCode: self._set_function_module(b, a, bit) - def _draw_finder_pattern(self, x, y): + def _draw_finder_pattern(self, x, y) -> None: """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). Modules can be out of bounds.""" for dy in range(-4, 5): @@ -363,7 +364,7 @@ class QrCode: self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4)) - def _draw_alignment_pattern(self, x, y): + def _draw_alignment_pattern(self, x, y) -> None: """Draws a 5*5 alignment pattern, with the center module at (x, y). All modules must be in bounds.""" for dy in range(-2, 3): @@ -371,7 +372,7 @@ class QrCode: self._set_function_module(x + dx, y + dy, max(abs(dx), abs(dy)) != 1) - def _set_function_module(self, x, y, isblack): + def _set_function_module(self, x: int, y: int, isblack: bool) -> None: """Sets the color of a module and marks it as a function module. Only used by the constructor. Coordinates must be in bounds.""" assert type(isblack) is bool @@ -381,7 +382,7 @@ class QrCode: # ---- Private helper methods for constructor: Codewords and masking ---- - def _add_ecc_and_interleave(self, data): + def _add_ecc_and_interleave(self, data: List[int]) -> List[int]: """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.""" version = self._version @@ -418,7 +419,7 @@ class QrCode: return result - def _draw_codewords(self, data): + def _draw_codewords(self, data: List[int]) -> None: """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.""" assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 @@ -441,7 +442,7 @@ class QrCode: assert i == len(data) * 8 - def _apply_mask(self, mask): + def _apply_mask(self, mask: int) -> None: """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 @@ -455,7 +456,7 @@ class QrCode: self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x]) - def _get_penalty_score(self): + def _get_penalty_score(self) -> int: """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.""" result = 0 @@ -518,7 +519,7 @@ class QrCode: # ---- Private helper functions ---- - def _get_alignment_pattern_positions(self): + def _get_alignment_pattern_positions(self) -> List[int]: """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 integers.""" @@ -534,7 +535,7 @@ class QrCode: @staticmethod - def _get_num_raw_data_modules(ver): + def _get_num_raw_data_modules(ver) -> int: """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.""" @@ -551,7 +552,7 @@ class QrCode: @staticmethod - def _get_num_data_codewords(ver, ecl): + def _get_num_data_codewords(ver, ecl: QrCode.Ecc) -> int: """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.""" @@ -561,7 +562,7 @@ class QrCode: @staticmethod - def _reed_solomon_compute_divisor(degree): + def _reed_solomon_compute_divisor(degree: int) -> List[int]: """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.""" if not (1 <= degree <= 255): @@ -585,7 +586,7 @@ class QrCode: @staticmethod - def _reed_solomon_compute_remainder(data, divisor): + def _reed_solomon_compute_remainder(data: List[int], divisor: List[int]) -> List[int]: """Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.""" result = [0] * len(divisor) for b in data: # Polynomial division @@ -597,7 +598,7 @@ class QrCode: @staticmethod - def _reed_solomon_multiply(x, y): + def _reed_solomon_multiply(x: int, y: int) -> int: """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.""" if x >> 8 != 0 or y >> 8 != 0: @@ -611,7 +612,7 @@ class QrCode: return z - def _finder_penalty_count_patterns(self, runhistory): + def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int: """Can only be called immediately after a white run is added, and returns either 0, 1, or 2. A helper function for _get_penalty_score().""" n = runhistory[1] @@ -621,7 +622,7 @@ class QrCode: + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0) - def _finder_penalty_terminate_and_count(self, currentruncolor, currentrunlength, runhistory): + def _finder_penalty_terminate_and_count(self, currentruncolor: bool, currentrunlength: int, runhistory: collections.deque) -> int: """Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score().""" if currentruncolor: # Terminate black run self._finder_penalty_add_history(currentrunlength, runhistory) @@ -631,7 +632,7 @@ class QrCode: return self._finder_penalty_count_patterns(runhistory) - def _finder_penalty_add_history(self, currentrunlength, runhistory): + def _finder_penalty_add_history(self, currentrunlength: int, runhistory: collections.deque) -> None: if runhistory[0] == 0: currentrunlength += self._size # Add white border to initial run runhistory.appendleft(currentrunlength) @@ -681,7 +682,7 @@ class QrCode: class Ecc: """The error correction level in a QR Code symbol. Immutable.""" # Private constructor - def __init__(self, i, fb): + def __init__(self, i: int, fb: int) -> None: self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) @@ -715,7 +716,7 @@ class QrSegment: # ---- Static factory functions (mid level) ---- @staticmethod - def make_bytes(data): + def make_bytes(data) -> QrSegment: """Returns a segment representing the given binary data encoded in byte mode. All input byte lists are acceptable. Any text string can be converted to UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment.""" @@ -728,7 +729,7 @@ class QrSegment: @staticmethod - def make_numeric(digits): + def make_numeric(digits: str) -> QrSegment: """Returns a segment representing the given string of decimal digits encoded in numeric mode.""" if QrSegment.NUMERIC_REGEX.match(digits) is None: raise ValueError("String contains non-numeric characters") @@ -742,7 +743,7 @@ class QrSegment: @staticmethod - def make_alphanumeric(text): + def make_alphanumeric(text: str) -> QrSegment: """Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.""" @@ -759,7 +760,7 @@ class QrSegment: @staticmethod - def make_segments(text): + def make_segments(text: str) -> List[QrSegment]: """Returns a new mutable list of zero or more segments to represent the given Unicode text string. The result may use various segment modes and switch modes to optimize the length of the bit stream.""" if not isinstance(text, str): @@ -777,7 +778,7 @@ class QrSegment: @staticmethod - def make_eci(assignval): + def make_eci(assignval: int) -> QrSegment: """Returns a segment representing an Extended Channel Interpretation (ECI) designator with the given assignment value.""" bb = _BitBuffer() @@ -798,7 +799,7 @@ class QrSegment: # ---- Constructor (low level) ---- - def __init__(self, mode, numch, bitdata): + def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: List[int]) -> None: """Creates a new QR Code segment with the given attributes and data. The character count (numch) must agree with the mode and the bit buffer length, but the constraint isn't checked. The given bit buffer is cloned and stored.""" @@ -822,22 +823,22 @@ class QrSegment: # ---- Accessor methods ---- - def get_mode(self): + def get_mode(self) -> QrSegment.Mode: """Returns the mode field of this segment.""" return self._mode - def get_num_chars(self): + def get_num_chars(self) -> int: """Returns the character count field of this segment.""" return self._numchars - def get_data(self): + def get_data(self) -> List[int]: """Returns a new copy of the data bits of this segment.""" return list(self._bitdata) # Make defensive copy # Package-private function @staticmethod - def get_total_bits(segs, version): + def get_total_bits(segs, version: int) -> Optional[int]: """Calculates the number of bits needed to encode the given segments at the given version. Returns a non-negative number if successful. Otherwise returns None if a segment has too many characters to fit its length field.""" @@ -874,17 +875,17 @@ class QrSegment: """Describes how a segment's data bits are interpreted. Immutable.""" # Private constructor - def __init__(self, modebits, charcounts): + def __init__(self, modebits: int, charcounts: Tuple[int,int,int]): self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) self._charcounts = charcounts # Number of character count bits for three different version ranges # Package-private method - def get_mode_bits(self): + def get_mode_bits(self) -> int: """Returns an unsigned 4-bit integer value (range 0 to 15) representing the mode indicator bits for this mode object.""" return self._modebits # Package-private method - def num_char_count_bits(self, ver): + def num_char_count_bits(self, ver: int) -> int: """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].""" return self._charcounts[(ver + 7) // 17] @@ -910,7 +911,7 @@ class QrSegment: class _BitBuffer(list): """An appendable sequence of bits (0s and 1s). Mainly used by QrSegment.""" - def append_bits(self, val, n): + def append_bits(self, val: int, n: int) -> None: """Appends the given number of low-order bits of the given value to this buffer. Requires n >= 0 and 0 <= val < 2^n.""" if n < 0 or val >> n != 0: @@ -918,7 +919,7 @@ class _BitBuffer(list): self.extend(((val >> i) & 1) for i in reversed(range(n))) -def _get_bit(x, i): +def _get_bit(x: int, i: int) -> bool: """Returns true iff the i'th bit of x is set to 1.""" return (x >> i) & 1 != 0 From 9ed47110a564f00d7cfd1baf4bbd4215544981c4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Fri, 8 May 2020 16:02:06 +0000 Subject: [PATCH 680/810] Simplified regular expressions for Python 3. --- python/qrcodegen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 744d9ae..884ee32 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -856,14 +856,14 @@ class QrSegment: # (Public) Describes precisely all strings that are encodable in numeric mode. # To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None # A string is encodable iff each character is in the range 0 to 9. - NUMERIC_REGEX = re.compile(r"[0-9]*\Z") + NUMERIC_REGEX = re.compile(r"[0-9]*") # (Public) Describes precisely all strings that are encodable in alphanumeric mode. # To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None # A string is encodable iff each character is in the following set: 0 to 9, A to Z # (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*\Z") + ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*") # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. _ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} From 6951a7e49eb956dab9894ed4177c2638070aaea2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 9 Jun 2020 15:04:21 +0000 Subject: [PATCH 681/810] Fixed regex usage in Python code (due to commit 9ed47110a564). --- python/qrcodegen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 884ee32..d5d3432 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -731,7 +731,7 @@ class QrSegment: @staticmethod def make_numeric(digits: str) -> QrSegment: """Returns a segment representing the given string of decimal digits encoded in numeric mode.""" - if QrSegment.NUMERIC_REGEX.match(digits) is None: + if QrSegment.NUMERIC_REGEX.fullmatch(digits) is None: raise ValueError("String contains non-numeric characters") bb = _BitBuffer() i = 0 @@ -747,7 +747,7 @@ class QrSegment: """Returns a segment representing the given text string encoded in alphanumeric mode. The characters allowed are: 0 to 9, A to Z (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon.""" - if QrSegment.ALPHANUMERIC_REGEX.match(text) is None: + if QrSegment.ALPHANUMERIC_REGEX.fullmatch(text) is None: raise ValueError("String contains unencodable characters in alphanumeric mode") bb = _BitBuffer() for i in range(0, len(text) - 1, 2): # Process groups of 2 @@ -769,9 +769,9 @@ class QrSegment: # Select the most efficient segment encoding automatically if text == "": return [] - elif QrSegment.NUMERIC_REGEX.match(text) is not None: + elif QrSegment.NUMERIC_REGEX.fullmatch(text) is not None: return [QrSegment.make_numeric(text)] - elif QrSegment.ALPHANUMERIC_REGEX.match(text) is not None: + elif QrSegment.ALPHANUMERIC_REGEX.fullmatch(text) is not None: return [QrSegment.make_alphanumeric(text)] else: return [QrSegment.make_bytes(text.encode("UTF-8"))] From c3a72b7333449d488c9bcf2c4f2417f7d8bf81a7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 9 Jun 2020 15:05:02 +0000 Subject: [PATCH 682/810] Fixed handling of type annotations in Python code (due to commit 55dd3c881ea1). --- python/qrcodegen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d5d3432..967d5ae 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -21,6 +21,7 @@ # Software. # +from __future__ import annotations import collections, itertools, re from typing import List, Optional, Tuple From 08ac806145aa6ab5d3e9df61d023092bb6cfe761 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 9 Jun 2020 17:52:12 +0000 Subject: [PATCH 683/810] Updated Python setup files due to commit f93ce5217e96. --- python/setup.cfg | 2 -- python/setup.py | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 python/setup.cfg diff --git a/python/setup.cfg b/python/setup.cfg deleted file mode 100644 index 2a9acf1..0000000 --- a/python/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal = 1 diff --git a/python/setup.py b/python/setup.py index cd4da26..50272bd 100644 --- a/python/setup.py +++ b/python/setup.py @@ -29,6 +29,7 @@ setuptools.setup( description = "High quality QR Code generator library for Python", version = "1.6.0", platforms = "OS Independent", + python_requires = '>=3', license = "MIT License", author = "Project Nayuki", From 4fae48a99a00b522508ad86bab46b5e6c8b6f33e Mon Sep 17 00:00:00 2001 From: Igor Guerrero Date: Thu, 25 Jun 2020 23:29:27 -0400 Subject: [PATCH 684/810] Update README to add syntax highlighting to each example --- Readme.markdown | 226 +++++++++++++++++++++++++----------------------- 1 file changed, 119 insertions(+), 107 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index d28e31c..b213814 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -43,133 +43,145 @@ Examples Java language: - import java.awt.image.BufferedImage; - import java.io.File; - import java.util.List; - import javax.imageio.ImageIO; - import io.nayuki.qrcodegen.*; - - // Simple operation - QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); - BufferedImage img = qr0.toImage(4, 10); - ImageIO.write(img, "png", new File("qr-code.png")); - - // Manual operation - List segs = QrSegment.makeSegments("3141592653589793238462643383"); - QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); - for (int y = 0; y < qr1.size; y++) { - for (int x = 0; x < qr1.size; x++) { - (... paint qr1.getModule(x, y) ...) - } +```java +import java.awt.image.BufferedImage; +import java.io.File; +import java.util.List; +import javax.imageio.ImageIO; +import io.nayuki.qrcodegen.*; + +// Simple operation +QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); +BufferedImage img = qr0.toImage(4, 10); +ImageIO.write(img, "png", new File("qr-code.png")); + +// Manual operation +List segs = QrSegment.makeSegments("3141592653589793238462643383"); +QrCode qr1 = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, 5, 5, 2, false); +for (int y = 0; y < qr1.size; y++) { + for (int x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` TypeScript/JavaScript languages: - // Name abbreviated for the sake of these examples here - var QRC = qrcodegen.QrCode; - - // Simple operation - var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); - var svg = qr0.toSvgString(4); - - // Manual operation - var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); - var qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); - for (var y = 0; y < qr1.size; y++) { - for (var x = 0; x < qr1.size; x++) { - (... paint qr1.getModule(x, y) ...) - } +```javascript +// Name abbreviated for the sake of these examples here +var QRC = qrcodegen.QrCode; + +// Simple operation +var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); +var svg = qr0.toSvgString(4); + +// Manual operation +var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); +var qr1 = QRC.encodeSegments(segs, QRC.Ecc.HIGH, 5, 5, 2, false); +for (var y = 0; y < qr1.size; y++) { + for (var x = 0; x < qr1.size; x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` Python language: - from qrcodegen import * - - # Simple operation - qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) - svg = qr0.to_svg_str(4) - - # Manual operation - segs = QrSegment.make_segments("3141592653589793238462643383") - qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) - for y in range(qr1.get_size()): - for x in range(qr1.get_size()): - (... paint qr1.get_module(x, y) ...) +```python +from qrcodegen import * + +# Simple operation +qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) +svg = qr0.to_svg_str(4) + +# Manual operation +segs = QrSegment.make_segments("3141592653589793238462643383") +qr1 = QrCode.encode_segments(segs, QrCode.Ecc.HIGH, 5, 5, 2, False) +for y in range(qr1.get_size()): + for x in range(qr1.get_size()): + (... paint qr1.get_module(x, y) ...) +``` C++ language: - #include - #include - #include "QrCode.hpp" - using namespace qrcodegen; - - // Simple operation - QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); - std::string svg = qr0.toSvgString(4); - - // Manual operation - std::vector segs = - QrSegment::makeSegments("3141592653589793238462643383"); - QrCode qr1 = QrCode::encodeSegments( - segs, QrCode::Ecc::HIGH, 5, 5, 2, false); - for (int y = 0; y < qr1.getSize(); y++) { - for (int x = 0; x < qr1.getSize(); x++) { - (... paint qr1.getModule(x, y) ...) - } +```cpp +#include +#include +#include "QrCode.hpp" +using namespace qrcodegen; + +// Simple operation +QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); +std::string svg = qr0.toSvgString(4); + +// Manual operation +std::vector segs = + QrSegment::makeSegments("3141592653589793238462643383"); +QrCode qr1 = QrCode::encodeSegments( + segs, QrCode::Ecc::HIGH, 5, 5, 2, false); +for (int y = 0; y < qr1.getSize(); y++) { + for (int x = 0; x < qr1.getSize(); x++) { + (... paint qr1.getModule(x, y) ...) } +} +``` C language: - #include - #include - #include "qrcodegen.h" - - // Text data - uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; - uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; - bool ok = qrcodegen_encodeText("Hello, world!", - tempBuffer, qr0, qrcodegen_Ecc_MEDIUM, - qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, - qrcodegen_Mask_AUTO, true); - if (!ok) - return; - - int size = qrcodegen_getSize(qr0); - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) { - (... paint qrcodegen_getModule(qr0, x, y) ...) - } +```c +#include +#include +#include "qrcodegen.h" + +// Text data +uint8_t qr0[qrcodegen_BUFFER_LEN_MAX]; +uint8_t tempBuffer[qrcodegen_BUFFER_LEN_MAX]; +bool ok = qrcodegen_encodeText("Hello, world!", + tempBuffer, qr0, qrcodegen_Ecc_MEDIUM, + qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, + qrcodegen_Mask_AUTO, true); +if (!ok) + return; + +int size = qrcodegen_getSize(qr0); +for (int y = 0; y < size; y++) { + for (int x = 0; x < size; x++) { + (... paint qrcodegen_getModule(qr0, x, y) ...) } - - // Binary data - uint8_t dataAndTemp[qrcodegen_BUFFER_LEN_FOR_VERSION(7)] - = {0xE3, 0x81, 0x82}; - uint8_t qr1[qrcodegen_BUFFER_LEN_FOR_VERSION(7)]; - ok = qrcodegen_encodeBinary(dataAndTemp, 3, qr1, - qrcodegen_Ecc_HIGH, 2, 7, qrcodegen_Mask_4, false); +} + +// Binary data +uint8_t dataAndTemp[qrcodegen_BUFFER_LEN_FOR_VERSION(7)] + = {0xE3, 0x81, 0x82}; +uint8_t qr1[qrcodegen_BUFFER_LEN_FOR_VERSION(7)]; +ok = qrcodegen_encodeBinary(dataAndTemp, 3, qr1, + qrcodegen_Ecc_HIGH, 2, 7, qrcodegen_Mask_4, false); +``` Rust language: - extern crate qrcodegen; - use qrcodegen::QrCode; - use qrcodegen::QrCodeEcc; - use qrcodegen::QrSegment; - - // Simple operation - let qr = QrCode::encode_text("Hello, world!", - QrCodeEcc::Medium).unwrap(); - let svg = qr.to_svg_string(4); - - // Manual operation - let chrs: Vec = "3141592653589793238462643383".chars().collect(); - let segs = QrSegment::make_segments(&chrs); - let qr = QrCode::encode_segments_advanced( - &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); - for y in 0 .. qr.size() { - for x in 0 .. qr.size() { - (... paint qr.get_module(x, y) ...) - } +```rust +extern crate qrcodegen; +use qrcodegen::QrCode; +use qrcodegen::QrCodeEcc; +use qrcodegen::QrSegment; + +// Simple operation +let qr = QrCode::encode_text("Hello, world!", + QrCodeEcc::Medium).unwrap(); +let svg = qr.to_svg_string(4); + +// Manual operation +let chrs: Vec = "3141592653589793238462643383".chars().collect(); +let segs = QrSegment::make_segments(&chrs); +let qr = QrCode::encode_segments_advanced( + &segs, QrCodeEcc::High, 5, 5, Some(Mask::new(2)), false).unwrap(); +for y in 0 .. qr.size() { + for x in 0 .. qr.size() { + (... paint qr.get_module(x, y) ...) } +} +``` License From 0df0908723203276091061dd2803b7fd6dcf8a6e Mon Sep 17 00:00:00 2001 From: Neil Haran Date: Sat, 18 Jul 2020 13:06:14 +0100 Subject: [PATCH 685/810] In C++ version, use const-reference in QrSegment ctor rather than deep copy. --- cpp/QrCode.cpp | 4 ++-- cpp/QrCode.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index b9de862..fa5b8fd 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -156,7 +156,7 @@ QrSegment QrSegment::makeEci(long assignVal) { } -QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : +QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : mode(md), numChars(numCh), data(dt) { @@ -165,7 +165,7 @@ QrSegment::QrSegment(Mode md, int numCh, const std::vector &dt) : } -QrSegment::QrSegment(Mode md, int numCh, std::vector &&dt) : +QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : mode(md), numChars(numCh), data(std::move(dt)) { diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 7341e41..06d661a 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -170,7 +170,7 @@ class QrSegment final { * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The given bit buffer is copied and stored. */ - public: QrSegment(Mode md, int numCh, const std::vector &dt); + public: QrSegment(const Mode &md, int numCh, const std::vector &dt); /* @@ -178,7 +178,7 @@ class QrSegment final { * The character count (numCh) must agree with the mode and the bit buffer length, * but the constraint isn't checked. The given bit buffer is moved and stored. */ - public: QrSegment(Mode md, int numCh, std::vector &&dt); + public: QrSegment(const Mode &md, int numCh, std::vector &&dt); /*---- Methods ----*/ From 98963e5cbaa8a643c484e9d3c18b9b0b86c6ef24 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 01:27:54 +0000 Subject: [PATCH 686/810] Tweaked Rust code to narrow the bit width of QrCodeEcc.format_bits(). --- rust/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 6a90c81..66d354c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -438,7 +438,7 @@ impl QrCode { // Calculate error correction code and pack bits let bits: u32 = { // errcorrlvl is uint2, mask is uint3 - let data: u32 = self.errorcorrectionlevel.format_bits() << 3 | u32::from(mask.value()); + let data: u32 = u32::from(self.errorcorrectionlevel.format_bits() << 3 | mask.value()); let mut rem: u32 = data; for _ in 0 .. 10 { rem = (rem << 1) ^ ((rem >> 9) * 0x537); @@ -949,7 +949,7 @@ impl QrCodeEcc { // Returns an unsigned 2-bit integer (in the range 0 to 3). - fn format_bits(self) -> u32 { + fn format_bits(self) -> u8 { use QrCodeEcc::*; match self { Low => 1, From 705ce44efd233cfdf23fa983d88d95ebb90b13b4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 01:34:20 +0000 Subject: [PATCH 687/810] Fixed arithmetic overflow in Rust code on platforms where usize is 16 bits wide. --- rust/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 66d354c..b247258 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1141,8 +1141,11 @@ impl QrSegment { let mut result: usize = 0; for seg in segs { let ccbits = seg.mode.num_char_count_bits(version); - if seg.numchars >= 1 << ccbits { - return None; // The segment's length doesn't fit the field's bit width + // ccbits can be as large as 16, but usize can be as small as 16 + if let Some(limit) = 1usize.checked_shl(u32::from(ccbits)) { + if seg.numchars >= limit { + return None; // The segment's length doesn't fit the field's bit width + } } result = result.checked_add(4 + usize::from(ccbits) + seg.data.len())?; } From 43020cbd67847b7c2726cb8fc8dcc28aa0e47b68 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 01:38:21 +0000 Subject: [PATCH 688/810] Fixed Rust code to avoid arithmetic overflow when a segment's bit length is very near usize::MAX. --- rust/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b247258..88340a1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1147,7 +1147,8 @@ impl QrSegment { return None; // The segment's length doesn't fit the field's bit width } } - result = result.checked_add(4 + usize::from(ccbits) + seg.data.len())?; + result = result.checked_add(4 + usize::from(ccbits))?; + result = result.checked_add(seg.data.len())?; } Some(result) } From d00cbd35857b26d987f40417b8716f69c0cd7f36 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 01:58:56 +0000 Subject: [PATCH 689/810] Added static types to some variables in Rust code. --- rust/src/lib.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 88340a1..17da2fc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -207,7 +207,7 @@ impl QrCode { assert!(minversion.value() <= maxversion.value(), "Invalid value"); // Find the minimal version number to use - let mut version = minversion; + let mut version: Version = minversion; let datausedbits: usize = loop { // Number of data bits available let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; @@ -245,9 +245,9 @@ impl QrCode { // Add terminator and pad up to a byte if applicable let datacapacitybits: usize = QrCode::get_num_data_codewords(version, ecl) * 8; assert!(bb.0.len() <= datacapacitybits); - let numzerobits = std::cmp::min(4, datacapacitybits - bb.0.len()); + let numzerobits: usize = std::cmp::min(4, datacapacitybits - bb.0.len()); bb.append_bits(0, numzerobits as u8); - let numzerobits = bb.0.len().wrapping_neg() & 7; + let numzerobits: usize = bb.0.len().wrapping_neg() & 7; bb.append_bits(0, numzerobits as u8); assert_eq!(bb.0.len() % 8, 0, "Assertion error"); @@ -539,8 +539,8 @@ impl QrCode { // 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. fn add_ecc_and_interleave(&self, data: &[u8]) -> Vec { - let ver = self.version; - let ecl = self.errorcorrectionlevel; + let ver: Version = self.version; + let ecl: QrCodeEcc = self.errorcorrectionlevel; assert_eq!(data.len(), QrCode::get_num_data_codewords(ver, ecl), "Illegal argument"); // Calculate parameter numbers @@ -720,7 +720,7 @@ impl QrCode { // 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. fn get_alignment_pattern_positions(&self) -> Vec { - let ver = self.version.value(); + let ver: u8 = self.version.value(); if ver == 1 { vec![] } else { @@ -1047,7 +1047,7 @@ impl QrSegment { let mut accumdata: u32 = 0; let mut accumcount: u32 = 0; for &c in text { - let i = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c) + let i: usize = ALPHANUMERIC_CHARSET.iter().position(|&x| x == c) .expect("String contains unencodable characters in alphanumeric mode"); accumdata = accumdata * 45 + (i as u32); accumcount += 1; @@ -1140,7 +1140,7 @@ impl QrSegment { fn get_total_bits(segs: &[Self], version: Version) -> Option { let mut result: usize = 0; for seg in segs { - let ccbits = seg.mode.num_char_count_bits(version); + let ccbits: u8 = seg.mode.num_char_count_bits(version); // ccbits can be as large as 16, but usize can be as small as 16 if let Some(limit) = 1usize.checked_shl(u32::from(ccbits)) { if seg.numchars >= limit { From bafd258293031bcff605932a39eed0bbe8041734 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 02:02:06 +0000 Subject: [PATCH 690/810] Clarified a few pieces of Rust code. --- rust/examples/qrcodegen-demo.rs | 4 ++-- rust/src/lib.rs | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs index 80b2b7a..67665f8 100644 --- a/rust/examples/qrcodegen-demo.rs +++ b/rust/examples/qrcodegen-demo.rs @@ -128,8 +128,8 @@ fn do_segment_demo() { 0x0000, 0x0208, 0x01FF, 0x0008, ]; let mut bb = qrcodegen::BitBuffer(Vec::new()); - for c in &kanjichars { - bb.append_bits(*c, 13); + for &c in &kanjichars { + bb.append_bits(c, 13); } let segs = vec![ QrSegment::new(qrcodegen::QrSegmentMode::Kanji, kanjichars.len(), bb.0), diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 17da2fc..d342e32 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -296,7 +296,7 @@ impl QrCode { // Do masking if mask.is_none() { // Automatically choose best mask - let mut minpenalty: i32 = std::i32::MAX; + let mut minpenalty = std::i32::MAX; for i in 0u8 .. 8 { let newmask = Mask::new(i); result.apply_mask(newmask); @@ -617,10 +617,9 @@ impl QrCode { // 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. fn apply_mask(&mut self, mask: Mask) { - let mask: u8 = mask.value(); for y in 0 .. self.size { for x in 0 .. self.size { - let invert: bool = match mask { + let invert: bool = match mask.value() { 0 => (x + y) % 2 == 0, 1 => y % 2 == 0, 2 => x % 3 == 0, From 8cbd1f506ac59406b18d13a199187c2756f2e06f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 14 Oct 2020 02:09:27 +0000 Subject: [PATCH 691/810] Changed Rust API to move the version min/max values into associated constants for its type. --- rust/examples/qrcodegen-demo.rs | 15 +++++++-------- rust/examples/qrcodegen-worker.rs | 4 ++-- rust/src/lib.rs | 21 ++++++++++----------- 3 files changed, 19 insertions(+), 21 deletions(-) diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs index 67665f8..91fee9d 100644 --- a/rust/examples/qrcodegen-demo.rs +++ b/rust/examples/qrcodegen-demo.rs @@ -29,8 +29,7 @@ use qrcodegen::Mask; use qrcodegen::QrCode; use qrcodegen::QrCodeEcc; use qrcodegen::QrSegment; -use qrcodegen::QrCode_MAX_VERSION; -use qrcodegen::QrCode_MIN_VERSION; +use qrcodegen::Version; // The main application program. @@ -143,20 +142,20 @@ fn do_segment_demo() { fn do_mask_demo() { // Project Nayuki URL let segs = QrSegment::make_segments(&to_chars("https://www.nayuki.io/")); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true).unwrap(); // Automatic mask + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, None, true).unwrap(); // Automatic mask print_qr(&qr); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(3)), true).unwrap(); // Force mask 3 + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::High, Version::MIN, Version::MAX, Some(Mask::new(3)), true).unwrap(); // Force mask 3 print_qr(&qr); // Chinese text as UTF-8 let segs = QrSegment::make_segments(&to_chars("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫")); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(0)), true).unwrap(); // Force mask 0 + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(0)), true).unwrap(); // Force mask 0 print_qr(&qr); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(1)), true).unwrap(); // Force mask 1 + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(1)), true).unwrap(); // Force mask 1 print_qr(&qr); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(5)), true).unwrap(); // Force mask 5 + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(5)), true).unwrap(); // Force mask 5 print_qr(&qr); - let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, QrCode_MIN_VERSION, QrCode_MAX_VERSION, Some(Mask::new(7)), true).unwrap(); // Force mask 7 + let qr = QrCode::encode_segments_advanced(&segs, QrCodeEcc::Medium, Version::MIN, Version::MAX, Some(Mask::new(7)), true).unwrap(); // Force mask 7 print_qr(&qr); } diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs index da9a0a3..7238392 100644 --- a/rust/examples/qrcodegen-worker.rs +++ b/rust/examples/qrcodegen-worker.rs @@ -59,9 +59,9 @@ fn main() { let mask = read_int(); let boostecl = read_int(); assert!(0 <= errcorlvl && errcorlvl <= 3); - assert!(i16::from(qrcodegen::QrCode_MIN_VERSION.value()) <= minversion + assert!(i16::from(Version::MIN.value()) <= minversion && minversion <= maxversion - && maxversion <= i16::from(qrcodegen::QrCode_MAX_VERSION.value())); + && maxversion <= i16::from(Version::MAX.value())); assert!(-1 <= mask && mask <= 7); assert!(boostecl >> 1 == 0); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d342e32..a2c4d76 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -184,7 +184,7 @@ impl QrCode { /// Returns a wrapped `QrCode` if successful, or `Err` if the /// data is too long to fit in any version at the given ECC level. pub fn encode_segments(segs: &[QrSegment], ecl: QrCodeEcc) -> Result { - QrCode::encode_segments_advanced(segs, ecl, QrCode_MIN_VERSION, QrCode_MAX_VERSION, None, true) + QrCode::encode_segments_advanced(segs, ecl, Version::MIN, Version::MAX, None, true) } @@ -883,13 +883,6 @@ impl FinderPenalty { /*---- Constants and tables ----*/ -/// The minimum version number supported in the QR Code Model 2 standard. -pub const QrCode_MIN_VERSION: Version = Version( 1); - -/// The maximum version number supported in the QR Code Model 2 standard. -pub const QrCode_MAX_VERSION: Version = Version(40); - - // For use in get_penalty_score(), when evaluating which mask is best. const PENALTY_N1: i32 = 3; const PENALTY_N2: i32 = 3; @@ -1252,8 +1245,8 @@ impl BitBuffer { /// /// - Decrease the error correction level if it was greater than `QrCodeEcc::Low`. /// - If the `encode_segments_advanced()` function was called, then increase the maxversion -/// argument if it was less than `QrCode_MAX_VERSION`. (This advice does not apply to the -/// other factory functions because they search all versions up to `QrCode_MAX_VERSION`.) +/// argument if it was less than `Version::MAX`. (This advice does not apply to the +/// other factory functions because they search all versions up to `Version::MAX`.) /// - Split the text data into better or optimal segments in order to reduce the number of bits required. /// - Change the text or binary data to be shorter. /// - Change the text to fit the character set of a particular segment mode (e.g. alphanumeric). @@ -1279,11 +1272,17 @@ impl std::fmt::Display for DataTooLong { pub struct Version(u8); impl Version { + /// The minimum version number supported in the QR Code Model 2 standard. + pub const MIN: Version = Version( 1); + + /// The maximum version number supported in the QR Code Model 2 standard. + pub const MAX: Version = Version(40); + /// Creates a version object from the given number. /// /// Panics if the number is outside the range [1, 40]. pub fn new(ver: u8) -> Self { - assert!(QrCode_MIN_VERSION.value() <= ver && ver <= QrCode_MAX_VERSION.value(), "Version number out of range"); + assert!(Version::MIN.value() <= ver && ver <= Version::MAX.value(), "Version number out of range"); Self(ver) } From f9d1172e2983bf4cf72e6b0db7b12c0172fd3606 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 25 Jan 2021 23:11:54 +0000 Subject: [PATCH 692/810] Fixed the names of some methods in comments. --- python/qrcodegen.py | 4 ++-- rust/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 967d5ae..66f84b8 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -446,7 +446,7 @@ class QrCode: def _apply_mask(self, mask: int) -> None: """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 + before masking. Due to the arithmetic of XOR, calling _apply_mask() 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.""" if not (0 <= mask <= 7): @@ -644,7 +644,7 @@ class QrCode: MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard MAX_VERSION = 40 # The maximum version number supported in the QR Code Model 2 standard - # For use in getPenaltyScore(), when evaluating which mask is best. + # For use in _get_penalty_score(), when evaluating which mask is best. _PENALTY_N1 = 3 _PENALTY_N2 = 3 _PENALTY_N3 = 40 diff --git a/rust/src/lib.rs b/rust/src/lib.rs index a2c4d76..64f563c 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -613,7 +613,7 @@ impl QrCode { // 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 + // before masking. Due to the arithmetic of XOR, calling apply_mask() 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. fn apply_mask(&mut self, mask: Mask) { From a807ee27db5e1724aa47ea70c83d738c0a0f0b42 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 25 Jan 2021 23:15:49 +0000 Subject: [PATCH 693/810] Added type annotations to instance fields in Python code. --- python/qrcodegen.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 66f84b8..f311f1a 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -168,6 +168,16 @@ class QrCode: return QrCode(version, ecl, datacodewords, mask) + # ---- Private fields ---- + + _version: int + _size: int + _errcorlvl: QrCode.Ecc + _mask: int + _modules: List[List[bool]] + _isfunction: List[List[bool]] + + # ---- Constructor (low level) ---- def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: List[int], mask: int) -> None: @@ -681,6 +691,9 @@ class QrCode: # ---- Public helper enumeration ---- class Ecc: + ordinal: int + formatbits: int + """The error correction level in a QR Code symbol. Immutable.""" # Private constructor def __init__(self, i: int, fb: int) -> None: @@ -798,6 +811,13 @@ class QrSegment: return QrSegment(QrSegment.Mode.ECI, 0, bb) + # ---- Private fields ---- + + _mode: QrSegment.Mode + _numchars: int + _bitdata: List[int] + + # ---- Constructor (low level) ---- def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: List[int]) -> None: @@ -875,6 +895,9 @@ class QrSegment: class Mode: """Describes how a segment's data bits are interpreted. Immutable.""" + _modebits: int + _charcounts: Tuple[int,int,int] + # Private constructor def __init__(self, modebits: int, charcounts: Tuple[int,int,int]): self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) From 8518684c0f33d004fa93971be2c6a8eca3167d1e Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Mon, 25 Jan 2021 23:23:00 +0000 Subject: [PATCH 694/810] Moved comments in Python code from field assignments to field declarations. --- python/qrcodegen.py | 63 +++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index f311f1a..d4ac1eb 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -170,11 +170,27 @@ class QrCode: # ---- Private fields ---- + # The version number of this QR Code, which is between 1 and 40 (inclusive). + # This determines the size of this barcode. _version: int + + # The width and height of this QR Code, measured in modules, between + # 21 and 177 (inclusive). This is equal to version * 4 + 17. _size: int + + # The error correction level used in this QR Code. _errcorlvl: QrCode.Ecc + + # 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. _mask: int + + # The modules of this QR Code (False = white, True = black). + # Immutable after constructor finishes. Accessed through get_module(). _modules: List[List[bool]] + + # Indicates function modules that are not subjected to masking. Discarded when constructor finishes. _isfunction: List[List[bool]] @@ -194,22 +210,12 @@ class QrCode: if not isinstance(errcorlvl, QrCode.Ecc): raise TypeError("QrCode.Ecc expected") - # The version number of this QR Code, which is between 1 and 40 (inclusive). - # This determines the size of this barcode. self._version = version - - # The width and height of this QR Code, measured in modules, between - # 21 and 177 (inclusive). This is equal to version * 4 + 17. self._size = version * 4 + 17 - - # The error correction level used in this QR Code. self._errcorlvl = errcorlvl # Initialize both grids to be size*size arrays of Boolean false - # The modules of this QR Code (False = white, True = black). - # Immutable after constructor finishes. Accessed through get_module(). self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white - # Indicates function modules that are not subjected to masking. Discarded when constructor finishes self._isfunction = [[False] * self._size for _ in range(self._size)] # Compute ECC, draw modules @@ -231,10 +237,6 @@ class QrCode: assert 0 <= mask <= 7 self._apply_mask(mask) # Apply the final choice of mask self._draw_format_bits(mask) # Overwrite old format bits - - # 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. self._mask = mask del self._isfunction @@ -691,14 +693,14 @@ class QrCode: # ---- Public helper enumeration ---- class Ecc: - ordinal: int - formatbits: int + ordinal: int # (Public) In the range 0 to 3 (unsigned 2-bit integer) + formatbits: int # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) """The error correction level in a QR Code symbol. Immutable.""" # Private constructor def __init__(self, i: int, fb: int) -> None: - self.ordinal = i # (Public) In the range 0 to 3 (unsigned 2-bit integer) - self.formatbits = fb # (Package-private) In the range 0 to 3 (unsigned 2-bit integer) + self.ordinal = i + self.formatbits = fb # Placeholders LOW : QrCode.Ecc @@ -813,8 +815,16 @@ class QrSegment: # ---- Private fields ---- + # The mode indicator of this segment. Accessed through get_mode(). _mode: QrSegment.Mode + + # The length of this segment's unencoded data. Measured in characters for + # numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. + # Always zero or positive. Not the same as the data's bit length. + # Accessed through get_num_chars(). _numchars: int + + # The data bits of this segment. Accessed through get_data(). _bitdata: List[int] @@ -828,17 +838,8 @@ class QrSegment: raise TypeError("QrSegment.Mode expected") if numch < 0: raise ValueError() - - # The mode indicator of this segment. Accessed through get_mode(). self._mode = mode - - # The length of this segment's unencoded data. Measured in characters for - # numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. - # Always zero or positive. Not the same as the data's bit length. - # Accessed through get_num_chars(). self._numchars = numch - - # The data bits of this segment. Accessed through get_data(). self._bitdata = list(bitdata) # Make defensive copy @@ -895,13 +896,13 @@ class QrSegment: class Mode: """Describes how a segment's data bits are interpreted. Immutable.""" - _modebits: int - _charcounts: Tuple[int,int,int] + _modebits: int # The mode indicator bits, which is a uint4 value (range 0 to 15) + _charcounts: Tuple[int,int,int] # Number of character count bits for three different version ranges # Private constructor def __init__(self, modebits: int, charcounts: Tuple[int,int,int]): - self._modebits = modebits # The mode indicator bits, which is a uint4 value (range 0 to 15) - self._charcounts = charcounts # Number of character count bits for three different version ranges + self._modebits = modebits + self._charcounts = charcounts # Package-private method def get_mode_bits(self) -> int: From a999dca15f3a1184402bf57b9ce15047b80dbf1b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Thu, 1 Jul 2021 04:35:23 +0000 Subject: [PATCH 695/810] Simplified an expression because C++11 natively supports for-each over a braced list, without needing to construct a typed object. --- cpp/QrCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index b9de862..79890a5 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -279,7 +279,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, throw std::logic_error("Assertion error"); // Increase the error correction level while the data still fits in the current version number - for (Ecc newEcl : vector{Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high + for (Ecc newEcl : {Ecc::MEDIUM, Ecc::QUARTILE, Ecc::HIGH}) { // From low to high if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8) ecl = newEcl; } From 68b2b7782b39a5557bc976b57742256b6f1195e3 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sat, 24 Jul 2021 21:54:42 +0000 Subject: [PATCH 696/810] Simplified Java and C++ code to remove unnecessary `this`, also improving consistency with other field assignments, enabled by a local variable renaming in commit 67c62461d380. --- cpp/QrCode.cpp | 2 +- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 79890a5..60cfd67 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -351,7 +351,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) } if (msk < 0 || msk > 7) throw std::logic_error("Assertion error"); - this->mask = msk; + mask = msk; applyMask(msk); // Apply the final choice of mask drawFormatBits(msk); // Overwrite old format bits diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index ea0196c..1b4c7d0 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -263,7 +263,7 @@ public final class QrCode { drawFunctionPatterns(); byte[] allCodewords = addEccAndInterleave(dataCodewords); drawCodewords(allCodewords); - this.mask = handleConstructorMasking(msk); + mask = handleConstructorMasking(msk); isFunction = null; } From 772a311c564f8f2c4371f2693dc0bf67974e9ffb Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Sun, 25 Jul 2021 03:30:37 +0000 Subject: [PATCH 697/810] Tweaked a bit of C++ code to use strict bounds checking for consistency. --- cpp/QrCode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 60cfd67..90a3989 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -310,7 +310,7 @@ QrCode QrCode::encodeSegments(const vector &segs, Ecc ecl, // Pack bits into bytes in big endian vector dataCodewords(bb.size() / 8); for (size_t i = 0; i < bb.size(); i++) - dataCodewords[i >> 3] |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); + dataCodewords.at(i >> 3) |= (bb.at(i) ? 1 : 0) << (7 - (i & 7)); // Create the QR Code object return QrCode(version, ecl, dataCodewords, mask); From d11eb098cb1a6e84bd3734e849842cc1fac9bf0a Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:18:32 +0000 Subject: [PATCH 698/810] Added more type annotations to class-level members in Python library code, continuing the work of commit 55dd3c881ea1. --- python/qrcodegen.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index d4ac1eb..529cb3b 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -23,7 +23,8 @@ from __future__ import annotations import collections, itertools, re -from typing import List, Optional, Tuple +from collections.abc import Sequence +from typing import Callable, Dict, List, Optional, Tuple """ @@ -260,7 +261,7 @@ class QrCode: """Returns this QR Code's mask, in the range [0, 7].""" return self._mask - def get_module(self, x, y) -> bool: + def get_module(self, x: int, y: int) -> bool: """Returns the color of the module (pixel) at the given coordinates, which is False for white or True for black. The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then False (white) is returned.""" @@ -316,7 +317,7 @@ class QrCode: self._draw_version() - def _draw_format_bits(self, mask) -> None: + def _draw_format_bits(self, mask: int) -> None: """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.""" # Calculate error correction code and pack bits @@ -366,7 +367,7 @@ class QrCode: self._set_function_module(b, a, bit) - def _draw_finder_pattern(self, x, y) -> None: + def _draw_finder_pattern(self, x: int, y: int) -> None: """Draws a 9*9 finder pattern including the border separator, with the center module at (x, y). Modules can be out of bounds.""" for dy in range(-4, 5): @@ -377,7 +378,7 @@ class QrCode: self._set_function_module(xx, yy, max(abs(dx), abs(dy)) not in (2, 4)) - def _draw_alignment_pattern(self, x, y) -> None: + def _draw_alignment_pattern(self, x: int, y: int) -> None: """Draws a 5*5 alignment pattern, with the center module at (x, y). All modules must be in bounds.""" for dy in range(-2, 3): @@ -548,7 +549,7 @@ class QrCode: @staticmethod - def _get_num_raw_data_modules(ver) -> int: + def _get_num_raw_data_modules(ver: int) -> int: """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.""" @@ -565,7 +566,7 @@ class QrCode: @staticmethod - def _get_num_data_codewords(ver, ecl: QrCode.Ecc) -> int: + def _get_num_data_codewords(ver: int, ecl: QrCode.Ecc) -> int: """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.""" @@ -653,16 +654,16 @@ class QrCode: # ---- Constants and tables ---- - MIN_VERSION = 1 # The minimum version number supported in the QR Code Model 2 standard - MAX_VERSION = 40 # The maximum version number supported in the QR Code Model 2 standard + MIN_VERSION: int = 1 # The minimum version number supported in the QR Code Model 2 standard + MAX_VERSION: int = 40 # The maximum version number supported in the QR Code Model 2 standard # For use in _get_penalty_score(), when evaluating which mask is best. - _PENALTY_N1 = 3 - _PENALTY_N2 = 3 - _PENALTY_N3 = 40 - _PENALTY_N4 = 10 + _PENALTY_N1: int = 3 + _PENALTY_N2: int = 3 + _PENALTY_N3: int = 40 + _PENALTY_N4: int = 10 - _ECC_CODEWORDS_PER_BLOCK = ( + _ECC_CODEWORDS_PER_BLOCK: Sequence[Sequence[int]] = ( # 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 @@ -670,7 +671,7 @@ class QrCode: (-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 - _NUM_ERROR_CORRECTION_BLOCKS = ( + _NUM_ERROR_CORRECTION_BLOCKS: Sequence[Sequence[int]] = ( # 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 @@ -678,7 +679,7 @@ class QrCode: (-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 - _MASK_PATTERNS = ( + _MASK_PATTERNS: Sequence[Callable[[int,int],int]] = ( (lambda x, y: (x + y) % 2 ), (lambda x, y: y % 2 ), (lambda x, y: x % 3 ), @@ -878,17 +879,16 @@ class QrSegment: # (Public) Describes precisely all strings that are encodable in numeric mode. # To test whether a string s is encodable: ok = NUMERIC_REGEX.fullmatch(s) is not None # A string is encodable iff each character is in the range 0 to 9. - NUMERIC_REGEX = re.compile(r"[0-9]*") + NUMERIC_REGEX: re.Pattern = re.compile(r"[0-9]*") # (Public) Describes precisely all strings that are encodable in alphanumeric mode. # To test whether a string s is encodable: ok = ALPHANUMERIC_REGEX.fullmatch(s) is not None # A string is encodable iff each character is in the following set: 0 to 9, A to Z # (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. - - ALPHANUMERIC_REGEX = re.compile(r"[A-Z0-9 $%*+./:-]*") + ALPHANUMERIC_REGEX: re.Pattern = re.compile(r"[A-Z0-9 $%*+./:-]*") # (Private) Dictionary of "0"->0, "A"->10, "$"->37, etc. - _ALPHANUMERIC_ENCODING_TABLE = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} + _ALPHANUMERIC_ENCODING_TABLE: Dict[str,int] = {ch: i for (i, ch) in enumerate("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")} # ---- Public helper enumeration ---- From e5d21aee09d1517c59e876671719c77b279c3aaf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:27:50 +0000 Subject: [PATCH 699/810] Updated Python code so that public functions accept bytes or sequences of integers, removed some run-time type checks. --- python/qrcodegen.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 529cb3b..3106b8e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -24,7 +24,7 @@ from __future__ import annotations import collections, itertools, re from collections.abc import Sequence -from typing import Callable, Dict, List, Optional, Tuple +from typing import Callable, Dict, List, Optional, Tuple, Union """ @@ -93,13 +93,11 @@ class QrCode: @staticmethod - def encode_binary(data: bytes, ecl: QrCode.Ecc) -> QrCode: + def encode_binary(data: Union[bytes,Sequence[int]], ecl: QrCode.Ecc) -> QrCode: """Returns a QR Code representing the given binary data at the given error correction level. This function always encodes using the binary segment mode, not any text mode. The maximum number of bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" - if not isinstance(data, (bytes, bytearray)): - raise TypeError("Byte string/list expected") return QrCode.encode_segments([QrSegment.make_bytes(data)], ecl) @@ -197,7 +195,7 @@ class QrCode: # ---- Constructor (low level) ---- - def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: List[int], mask: int) -> None: + def __init__(self, version: int, errcorlvl: QrCode.Ecc, datacodewords: Union[bytes,Sequence[int]], mask: int) -> None: """Creates a new QR Code with the given version number, error correction level, data codeword bytes, and mask number. This is a low-level API that most users should not use directly. @@ -221,7 +219,7 @@ class QrCode: # Compute ECC, draw modules self._draw_function_patterns() - allcodewords = self._add_ecc_and_interleave(datacodewords) + allcodewords = self._add_ecc_and_interleave(list(datacodewords)) self._draw_codewords(allcodewords) # Do masking @@ -733,12 +731,10 @@ class QrSegment: # ---- Static factory functions (mid level) ---- @staticmethod - def make_bytes(data) -> QrSegment: + def make_bytes(data: Union[bytes,Sequence[int]]) -> QrSegment: """Returns a segment representing the given binary data encoded in byte mode. All input byte lists are acceptable. Any text string can be converted to UTF-8 bytes (s.encode("UTF-8")) and encoded as a byte mode segment.""" - if isinstance(data, str): - raise TypeError("Byte string/list expected") bb = _BitBuffer() for b in data: bb.append_bits(b, 8) From 83300fd61925771e1aa880089039d1a28b566339 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:33:09 +0000 Subject: [PATCH 700/810] Updated private Python functions to operate on bytes instead of lists of integers, thus conveying the constrained value range more clearly. --- python/qrcodegen.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 3106b8e..652eeae 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -159,7 +159,7 @@ class QrCode: bb.append_bits(padbyte, 8) # Pack bits into bytes in big endian - datacodewords = [0] * (len(bb) // 8) + datacodewords = bytearray([0] * (len(bb) // 8)) for (i, bit) in enumerate(bb): datacodewords[i >> 3] |= bit << (7 - (i & 7)) @@ -219,7 +219,7 @@ class QrCode: # Compute ECC, draw modules self._draw_function_patterns() - allcodewords = self._add_ecc_and_interleave(list(datacodewords)) + allcodewords = self._add_ecc_and_interleave(bytearray(datacodewords)) self._draw_codewords(allcodewords) # Do masking @@ -394,7 +394,7 @@ class QrCode: # ---- Private helper methods for constructor: Codewords and masking ---- - def _add_ecc_and_interleave(self, data: List[int]) -> List[int]: + def _add_ecc_and_interleave(self, data: bytearray) -> bytes: """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.""" version = self._version @@ -421,7 +421,7 @@ class QrCode: assert k == len(data) # Interleave (not concatenate) the bytes from every block into a single sequence - result = [] + result = bytearray() for i in range(len(blocks[0])): for (j, blk) in enumerate(blocks): # Skip the padding byte in short blocks @@ -431,7 +431,7 @@ class QrCode: return result - def _draw_codewords(self, data: List[int]) -> None: + def _draw_codewords(self, data: bytes) -> None: """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.""" assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 @@ -574,14 +574,14 @@ class QrCode: @staticmethod - def _reed_solomon_compute_divisor(degree: int) -> List[int]: + def _reed_solomon_compute_divisor(degree: int) -> bytes: """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.""" if not (1 <= degree <= 255): raise ValueError("Degree out of range") # Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. # For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the uint8 array [255, 8, 93]. - result = [0] * (degree - 1) + [1] # Start off with the monomial x^0 + result = bytearray([0] * (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. @@ -598,9 +598,9 @@ class QrCode: @staticmethod - def _reed_solomon_compute_remainder(data: List[int], divisor: List[int]) -> List[int]: + def _reed_solomon_compute_remainder(data: bytes, divisor: bytes) -> bytes: """Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.""" - result = [0] * len(divisor) + result = bytearray([0] * len(divisor)) for b in data: # Polynomial division factor = b ^ result.pop(0) result.append(0) From 3c3aec6b9c2fdd76b4d5cedade960ca0b55c19d0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:34:31 +0000 Subject: [PATCH 701/810] Relaxed some Python function parameters from List[int] to Sequence[int]. --- python/qrcodegen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 652eeae..c51478e 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -104,7 +104,7 @@ class QrCode: # ---- Static factory functions (mid level) ---- @staticmethod - def encode_segments(segs: List[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode: + def encode_segments(segs: Sequence[QrSegment], ecl: QrCode.Ecc, minversion: int = 1, maxversion: int = 40, mask: int = -1, boostecl: bool = True) -> QrCode: """Returns a QR Code representing the given segments with the given encoding parameters. The smallest possible QR Code version within the given range is automatically chosen for the output. Iff boostecl is true, then the ECC level of the result @@ -827,7 +827,7 @@ class QrSegment: # ---- Constructor (low level) ---- - def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: List[int]) -> None: + def __init__(self, mode: QrSegment.Mode, numch: int, bitdata: Sequence[int]) -> None: """Creates a new QR Code segment with the given attributes and data. The character count (numch) must agree with the mode and the bit buffer length, but the constraint isn't checked. The given bit buffer is cloned and stored.""" From 0ff7b57a81bc4d518ad0df74b4b5c5d83aeab293 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:48:26 +0000 Subject: [PATCH 702/810] Added type annotations to nearly all local variables in Python library code. --- python/qrcodegen.py | 114 ++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index c51478e..83d67cc 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -88,7 +88,7 @@ class QrCode: Unicode code points (not UTF-16 code units) if the low error correction level is used. The smallest possible QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version.""" - segs = QrSegment.make_segments(text) + segs: List[QrSegment] = QrSegment.make_segments(text) return QrCode.encode_segments(segs, ecl) @@ -120,12 +120,12 @@ class QrCode: # Find the minimal version number to use for version in range(minversion, maxversion + 1): - datacapacitybits = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available - datausedbits = QrSegment.get_total_bits(segs, version) + datacapacitybits: int = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available + datausedbits: Optional[int] = QrSegment.get_total_bits(segs, version) if datausedbits is not None and 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 - msg = "Segment too long" + msg: str = "Segment too long" if datausedbits is not None: msg = "Data length = {} bits, Max capacity = {} bits".format(datausedbits, datacapacitybits) raise DataTooLongError(msg) @@ -219,12 +219,12 @@ class QrCode: # Compute ECC, draw modules self._draw_function_patterns() - allcodewords = self._add_ecc_and_interleave(bytearray(datacodewords)) + allcodewords: bytes = self._add_ecc_and_interleave(bytearray(datacodewords)) self._draw_codewords(allcodewords) # Do masking if mask == -1: # Automatically choose best mask - minpenalty = 1 << 32 + minpenalty: int = 1 << 32 for i in range(8): self._apply_mask(i) self._draw_format_bits(i) @@ -273,7 +273,7 @@ class QrCode: of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" if border < 0: raise ValueError("Border must be non-negative") - parts = [] + parts: List[str] = [] for y in range(self._size): for x in range(self._size): if self.get_module(x, y): @@ -302,9 +302,9 @@ class QrCode: self._draw_finder_pattern(3, self._size - 4) # Draw numerous alignment patterns - alignpatpos = self._get_alignment_pattern_positions() - numalign = len(alignpatpos) - skips = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) + alignpatpos: List[int] = self._get_alignment_pattern_positions() + numalign: int = len(alignpatpos) + skips: Sequence[Tuple[int,int]] = ((0, 0), (0, numalign - 1), (numalign - 1, 0)) for i in range(numalign): for j in range(numalign): if (i, j) not in skips: # Don't draw on the three finder corners @@ -319,11 +319,11 @@ class QrCode: """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.""" # Calculate error correction code and pack bits - data = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3 - rem = data + data: int = self._errcorlvl.formatbits << 3 | mask # errCorrLvl is uint2, mask is uint3 + rem: int = data for _ in range(10): rem = (rem << 1) ^ ((rem >> 9) * 0x537) - bits = (data << 10 | rem) ^ 0x5412 # uint15 + bits: int = (data << 10 | rem) ^ 0x5412 # uint15 assert bits >> 15 == 0 # Draw first copy @@ -350,17 +350,17 @@ class QrCode: return # Calculate error correction code and pack bits - rem = self._version # version is uint6, in the range [7, 40] + rem: int = self._version # version is uint6, in the range [7, 40] for _ in range(12): rem = (rem << 1) ^ ((rem >> 11) * 0x1F25) - bits = self._version << 12 | rem # uint18 + bits: int = self._version << 12 | rem # uint18 assert bits >> 18 == 0 # Draw two copies for i in range(18): - bit = _get_bit(bits, i) - a = self._size - 11 + i % 3 - b = i // 3 + bit: bool = _get_bit(bits, i) + a: int = self._size - 11 + i % 3 + b: int = i // 3 self._set_function_module(a, b, bit) self._set_function_module(b, a, bit) @@ -397,24 +397,24 @@ class QrCode: def _add_ecc_and_interleave(self, data: bytearray) -> bytes: """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.""" - version = self._version + version: int = self._version assert len(data) == QrCode._get_num_data_codewords(version, self._errcorlvl) # Calculate parameter numbers - numblocks = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] - blockecclen = QrCode._ECC_CODEWORDS_PER_BLOCK [self._errcorlvl.ordinal][version] - rawcodewords = QrCode._get_num_raw_data_modules(version) // 8 - numshortblocks = numblocks - rawcodewords % numblocks - shortblocklen = rawcodewords // numblocks + numblocks: int = QrCode._NUM_ERROR_CORRECTION_BLOCKS[self._errcorlvl.ordinal][version] + blockecclen: int = QrCode._ECC_CODEWORDS_PER_BLOCK [self._errcorlvl.ordinal][version] + rawcodewords: int = QrCode._get_num_raw_data_modules(version) // 8 + numshortblocks: int = numblocks - rawcodewords % numblocks + shortblocklen: int = rawcodewords // numblocks # Split data into blocks and append ECC to each block - blocks = [] - rsdiv = QrCode._reed_solomon_compute_divisor(blockecclen) - k = 0 + blocks: List[bytes] = [] + rsdiv: bytes = QrCode._reed_solomon_compute_divisor(blockecclen) + k: int = 0 for i in range(numblocks): - dat = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] + dat: bytearray = data[k : k + shortblocklen - blockecclen + (0 if i < numshortblocks else 1)] k += len(dat) - ecc = QrCode._reed_solomon_compute_remainder(dat, rsdiv) + ecc: bytes = QrCode._reed_solomon_compute_remainder(dat, rsdiv) if i < numshortblocks: dat.append(0) blocks.append(dat + ecc) @@ -436,16 +436,16 @@ class QrCode: data area of this QR Code. Function modules need to be marked off before this is called.""" assert len(data) == QrCode._get_num_raw_data_modules(self._version) // 8 - i = 0 # Bit index into the data + i: int = 0 # Bit index into the data # Do the funny zigzag scan for right in range(self._size - 1, 0, -2): # Index of right column in each column pair if right <= 6: right -= 1 for vert in range(self._size): # Vertical counter for j in range(2): - x = right - j # Actual x coordinate - upward = (right + 1) & 2 == 0 - y = (self._size - 1 - vert) if upward else vert # Actual y coordinate + x: int = right - j # Actual x coordinate + upward: bool = (right + 1) & 2 == 0 + y: int = (self._size - 1 - vert) if upward else vert # Actual y coordinate if not self._isfunction[y][x] and i < len(data) * 8: self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7)) i += 1 @@ -462,7 +462,7 @@ class QrCode: QR Code needs exactly one (not zero, two, etc.) mask applied.""" if not (0 <= mask <= 7): raise ValueError("Mask value out of range") - masker = QrCode._MASK_PATTERNS[mask] + masker: Callable[[int,int],int] = QrCode._MASK_PATTERNS[mask] for y in range(self._size): for x in range(self._size): self._modules[y][x] ^= (masker(x, y) == 0) and (not self._isfunction[y][x]) @@ -471,14 +471,14 @@ class QrCode: def _get_penalty_score(self) -> int: """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.""" - result = 0 - size = self._size - modules = self._modules + result: int = 0 + size: int = self._size + modules: List[List[bool]] = self._modules # Adjacent modules in row having same color, and finder-like patterns for y in range(size): - runcolor = False - runx = 0 + runcolor: bool = False + runx: int = 0 runhistory = collections.deque([0] * 7, 7) for x in range(size): if modules[y][x] == runcolor: @@ -521,10 +521,10 @@ class QrCode: result += QrCode._PENALTY_N2 # Balance of black and white modules - black = sum((1 if cell else 0) for row in modules for cell in row) - total = size**2 # Note that size is odd, so black/total != 1/2 + black: int = sum((1 if cell else 0) for row in modules for cell in row) + total: int = size**2 # Note that size is odd, so black/total != 1/2 # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - k = (abs(black * 20 - total * 10) + total - 1) // total - 1 + k: int = (abs(black * 20 - total * 10) + total - 1) // total - 1 result += k * QrCode._PENALTY_N4 return result @@ -535,14 +535,14 @@ class QrCode: """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 integers.""" - ver = self._version + ver: int = self._version if ver == 1: return [] else: - numalign = ver // 7 + 2 - step = 26 if (ver == 32) else \ + numalign: int = ver // 7 + 2 + step: int = 26 if (ver == 32) else \ (ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2 - result = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6] + result: List[int] = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6] return list(reversed(result)) @@ -553,9 +553,9 @@ class QrCode: The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table.""" if not (QrCode.MIN_VERSION <= ver <= QrCode.MAX_VERSION): raise ValueError("Version number out of range") - result = (16 * ver + 128) * ver + 64 + result: int = (16 * ver + 128) * ver + 64 if ver >= 2: - numalign = ver // 7 + 2 + numalign: int = ver // 7 + 2 result -= (25 * numalign - 10) * numalign - 55 if ver >= 7: result -= 36 @@ -586,7 +586,7 @@ class QrCode: # 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). - root = 1 + root: int = 1 for _ in range(degree): # Unused variable i # Multiply the current product by (x - r^i) for j in range(degree): @@ -602,7 +602,7 @@ class QrCode: """Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials.""" result = bytearray([0] * len(divisor)) for b in data: # Polynomial division - factor = b ^ result.pop(0) + factor: int = b ^ result.pop(0) result.append(0) for (i, coef) in enumerate(divisor): result[i] ^= QrCode._reed_solomon_multiply(coef, factor) @@ -616,7 +616,7 @@ class QrCode: if x >> 8 != 0 or y >> 8 != 0: raise ValueError("Byte out of range") # Russian peasant multiplication - z = 0 + z: int = 0 for i in reversed(range(8)): z = (z << 1) ^ ((z >> 7) * 0x11D) z ^= ((y >> i) & 1) * x @@ -627,9 +627,9 @@ class QrCode: def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int: """Can only be called immediately after a white run is added, and returns either 0, 1, or 2. A helper function for _get_penalty_score().""" - n = runhistory[1] + n: int = runhistory[1] assert n <= self._size * 3 - core = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3 + core: bool = n > 0 and (runhistory[2] == runhistory[4] == runhistory[5] == n) and runhistory[3] == n * 3 return (1 if (core and runhistory[0] >= n * 4 and runhistory[6] >= n) else 0) \ + (1 if (core and runhistory[6] >= n * 4 and runhistory[0] >= n) else 0) @@ -747,9 +747,9 @@ class QrSegment: if QrSegment.NUMERIC_REGEX.fullmatch(digits) is None: raise ValueError("String contains non-numeric characters") bb = _BitBuffer() - i = 0 + i: int = 0 while i < len(digits): # Consume up to 3 digits per iteration - n = min(len(digits) - i, 3) + n: int = min(len(digits) - i, 3) bb.append_bits(int(digits[i : i + n]), n * 3 + 1) i += n return QrSegment(QrSegment.Mode.NUMERIC, len(digits), bb) @@ -764,7 +764,7 @@ class QrSegment: raise ValueError("String contains unencodable characters in alphanumeric mode") bb = _BitBuffer() for i in range(0, len(text) - 1, 2): # Process groups of 2 - temp = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 + temp: int = QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i]] * 45 temp += QrSegment._ALPHANUMERIC_ENCODING_TABLE[text[i + 1]] bb.append_bits(temp, 11) if len(text) % 2 > 0: # 1 character remaining @@ -863,7 +863,7 @@ class QrSegment: returns None if a segment has too many characters to fit its length field.""" result = 0 for seg in segs: - ccbits = seg.get_mode().num_char_count_bits(version) + ccbits: int = seg.get_mode().num_char_count_bits(version) if seg.get_num_chars() >= (1 << ccbits): return None # The segment's length doesn't fit the field's bit width result += 4 + ccbits + len(seg._bitdata) From 1ca1d43f1ccd8817e7f632ef3c5ee32caa331b3b Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:49:42 +0000 Subject: [PATCH 703/810] Deleted Python module summary comment because static type hints make it redundant. --- python/qrcodegen.py | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 83d67cc..535639f 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -27,40 +27,6 @@ from collections.abc import Sequence from typing import Callable, Dict, List, Optional, Tuple, Union -""" -This module "qrcodegen", public members: -- Class QrCode: - - Function encode_text(str text, QrCode.Ecc ecl) -> QrCode - - Function encode_binary(bytes data, QrCode.Ecc ecl) -> QrCode - - Function encode_segments(list segs, QrCode.Ecc ecl, - int minversion=1, int maxversion=40, mask=-1, boostecl=true) -> QrCode - - Constants int MIN_VERSION, MAX_VERSION - - Constructor QrCode(int version, QrCode.Ecc ecl, bytes datacodewords, int mask) - - Method get_version() -> int - - Method get_size() -> int - - Method get_error_correction_level() -> QrCode.Ecc - - Method get_mask() -> int - - Method get_module(int x, int y) -> bool - - Method to_svg_str(int border) -> str - - Enum Ecc: - - Constants LOW, MEDIUM, QUARTILE, HIGH - - Field int ordinal -- Class QrSegment: - - Function make_bytes(bytes data) -> QrSegment - - Function make_numeric(str digits) -> QrSegment - - Function make_alphanumeric(str text) -> QrSegment - - Function make_segments(str text) -> list - - Function make_eci(int assignval) -> QrSegment - - Constructor QrSegment(QrSegment.Mode mode, int numch, list bitdata) - - Method get_mode() -> QrSegment.Mode - - Method get_num_chars() -> int - - Method get_data() -> list - - Constants regex NUMERIC_REGEX, ALPHANUMERIC_REGEX - - Enum Mode: - - Constants NUMERIC, ALPHANUMERIC, BYTE, KANJI, ECI -""" - - # ---- QR Code symbol class ---- class QrCode: From 27dd722961b2f82aea1cb28d79769be0ac5fa80c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 22:54:44 +0000 Subject: [PATCH 704/810] Parenthesized most clauses of `and`/`or` expressions in Python code for clarity. --- python/qrcodegen.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 535639f..c0924c9 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -88,7 +88,7 @@ class QrCode: for version in range(minversion, maxversion + 1): datacapacitybits: int = QrCode._get_num_data_codewords(version, ecl) * 8 # Number of data bits available datausedbits: Optional[int] = QrSegment.get_total_bits(segs, version) - if datausedbits is not None and datausedbits <= datacapacitybits: + if (datausedbits is not None) and (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 msg: str = "Segment too long" @@ -100,7 +100,7 @@ class QrCode: # Increase the error correction level while the data still fits in the current version number for newecl in (QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH): # From low to high - if boostecl and datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8: + if boostecl and (datausedbits <= QrCode._get_num_data_codewords(version, newecl) * 8): ecl = newecl # Concatenate all segments to create the data bit string @@ -391,7 +391,7 @@ class QrCode: for i in range(len(blocks[0])): for (j, blk) in enumerate(blocks): # Skip the padding byte in short blocks - if i != shortblocklen - blockecclen or j >= numshortblocks: + if (i != shortblocklen - blockecclen) or (j >= numshortblocks): result.append(blk[i]) assert len(result) == rawcodewords return result @@ -412,7 +412,7 @@ class QrCode: x: int = right - j # Actual x coordinate upward: bool = (right + 1) & 2 == 0 y: int = (self._size - 1 - vert) if upward else vert # Actual y coordinate - if not self._isfunction[y][x] and i < len(data) * 8: + if (not self._isfunction[y][x]) and (i < len(data) * 8): self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7)) i += 1 # If this QR Code has any remainder bits (0 to 7), they were assigned as @@ -579,7 +579,7 @@ class QrCode: def _reed_solomon_multiply(x: int, y: int) -> int: """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.""" - if x >> 8 != 0 or y >> 8 != 0: + if (x >> 8 != 0) or (y >> 8 != 0): raise ValueError("Byte out of range") # Russian peasant multiplication z: int = 0 @@ -901,7 +901,7 @@ class _BitBuffer(list): def append_bits(self, val: int, n: int) -> None: """Appends the given number of low-order bits of the given value to this buffer. Requires n >= 0 and 0 <= val < 2^n.""" - if n < 0 or val >> n != 0: + if (n < 0) or (val >> n != 0): raise ValueError("Value out of range") self.extend(((val >> i) & 1) for i in reversed(range(n))) From aa32fe12359ad660730a888cca4af851edd975ec Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 23:00:52 +0000 Subject: [PATCH 705/810] Added more type annotations to Python tester programs. --- python/qrcodegen-batch-test.py | 18 ++++++++++-------- python/qrcodegen-worker.py | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index b4279e0..babd9d8 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -68,7 +68,9 @@ def main() -> None: def do_trial() -> None: - mode = random.randrange(4) + mode: int = random.randrange(4) + length: int + data: List[int] if mode == 0: # Numeric length = round((2 * 7089) ** random.random()) data = random.choices(b"0123456789", k=length) @@ -88,12 +90,12 @@ def do_trial() -> None: for b in data: write_all(b) - errcorlvl = random.randrange(4) - minversion = random.randint(1, 40) - maxversion = random.randint(1, 40) + errcorlvl: int = random.randrange(4) + minversion: int = random.randint(1, 40) + maxversion: int = random.randint(1, 40) if minversion > maxversion: minversion, maxversion = maxversion, minversion - mask = -1 + mask: int = -1 if random.random() < 0.5: mask = random.randrange(8) boostecl = int(random.random() < 0.2) @@ -106,11 +108,11 @@ def do_trial() -> None: write_all(boostecl) flush_all() - version = read_verify() + version: int = read_verify() print(" version={}".format(version), end="") if version == -1: return - size = version * 4 + 17 + size: int = version * 4 + 17 for _ in range(size**2): read_verify() @@ -124,7 +126,7 @@ def flush_all() -> None: not_none(proc.stdin).flush() def read_verify() -> int: - val = not_none(subprocs[0].stdout).readline().rstrip("\r\n") + val: str = not_none(subprocs[0].stdout).readline().rstrip("\r\n") for proc in subprocs[1 : ]: if not_none(proc.stdout).readline().rstrip("\r\n") != val: raise ValueError("Mismatch") diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py index a7b0ce6..7ea4a65 100644 --- a/python/qrcodegen-worker.py +++ b/python/qrcodegen-worker.py @@ -27,6 +27,7 @@ # import sys +from typing import List, Sequence import qrcodegen @@ -38,26 +39,26 @@ def main() -> None: while True: # Read data or exit - length = read_int() + length: int = read_int() if length == -1: break data = bytearray(read_int() for _ in range(length)) # Read encoding parameters - errcorlvl = read_int() - minversion = read_int() - maxversion = read_int() - mask = read_int() - boostecl = read_int() + errcorlvl : int = read_int() + minversion: int = read_int() + maxversion: int = read_int() + mask : int = read_int() + boostecl : int = read_int() # Make segments for encoding if all((b < 128) for b in data): # Is ASCII - segs = qrcodegen.QrSegment.make_segments(data.decode("ASCII")) + segs: List[qrcodegen.QrSegment] = qrcodegen.QrSegment.make_segments(data.decode("ASCII")) else: segs = [qrcodegen.QrSegment.make_bytes(data)] try: # Try to make QR Code symbol - qr = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) + qr: qrcodegen.QrCode = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) # Print grid of modules print(qr.get_version()) for y in range(qr.get_size()): @@ -69,7 +70,7 @@ def main() -> None: sys.stdout.flush() -ECC_LEVELS = ( +ECC_LEVELS: Sequence[qrcodegen.QrCode.Ecc] = ( qrcodegen.QrCode.Ecc.LOW, qrcodegen.QrCode.Ecc.MEDIUM, qrcodegen.QrCode.Ecc.QUARTILE, From 2c76b0a17047754218305c7d5516fc58ccb56698 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 23:02:54 +0000 Subject: [PATCH 706/810] Updated copyright year in readme document. --- Readme.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.markdown b/Readme.markdown index d28e31c..7e8b15f 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -175,7 +175,7 @@ Rust language: License ------- -Copyright © 2020 Project Nayuki. (MIT License) +Copyright © 2021 Project Nayuki. (MIT License) [https://www.nayuki.io/page/qr-code-generator-library](https://www.nayuki.io/page/qr-code-generator-library) Permission is hereby granted, free of charge, to any person obtaining a copy of From 2aed7af53ef097a11fe308516bfbb2da5e6e85a4 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 23:19:30 +0000 Subject: [PATCH 707/810] Updated field and method in C++ code in order to store `Mode` objects by pointer instead of value. --- cpp/QrCode.cpp | 10 +++++----- cpp/QrCode.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index fa5b8fd..ba30176 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -157,7 +157,7 @@ QrSegment QrSegment::makeEci(long assignVal) { QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : - mode(md), + mode(&md), numChars(numCh), data(dt) { if (numCh < 0) @@ -166,7 +166,7 @@ QrSegment::QrSegment(const Mode &md, int numCh, const std::vector &dt) : QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : - mode(md), + mode(&md), numChars(numCh), data(std::move(dt)) { if (numCh < 0) @@ -177,7 +177,7 @@ QrSegment::QrSegment(const Mode &md, int numCh, std::vector &&dt) : int QrSegment::getTotalBits(const vector &segs, int version) { int result = 0; for (const QrSegment &seg : segs) { - int ccbits = seg.mode.numCharCountBits(version); + int ccbits = seg.mode->numCharCountBits(version); if (seg.numChars >= (1L << ccbits)) return -1; // The segment's length doesn't fit the field's bit width if (4 + ccbits > INT_MAX - result) @@ -210,8 +210,8 @@ bool QrSegment::isNumeric(const char *text) { } -QrSegment::Mode QrSegment::getMode() const { - return mode; +const QrSegment::Mode &QrSegment::getMode() const { + return *mode; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 06d661a..9e7f7fc 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -151,7 +151,7 @@ class QrSegment final { /*---- Instance fields ----*/ /* The mode indicator of this segment. Accessed through getMode(). */ - private: Mode mode; + private: const Mode *mode; /* The length of this segment's unencoded data. Measured in characters for * numeric/alphanumeric/kanji mode, bytes for byte mode, and 0 for ECI mode. @@ -186,7 +186,7 @@ class QrSegment final { /* * Returns the mode field of this segment. */ - public: Mode getMode() const; + public: const Mode &getMode() const; /* From 99e7d59383a862f6f97cec9f8c906717b88db416 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 23:34:00 +0000 Subject: [PATCH 708/810] Changed the word "white" to "light" in {comments, local variables, a private C function} in order to match the vocabulary in the QR Code specification document. --- c/qrcodegen-test.c | 18 +++++++------- c/qrcodegen.c | 24 +++++++++---------- c/qrcodegen.h | 6 ++--- cpp/QrCode.cpp | 10 ++++---- cpp/QrCode.hpp | 10 ++++---- .../main/java/io/nayuki/qrcodegen/QrCode.java | 24 +++++++++---------- python/qrcodegen.py | 20 ++++++++-------- rust/src/lib.rs | 20 ++++++++-------- typescript-javascript/qrcodegen.ts | 22 ++++++++--------- 9 files changed, 77 insertions(+), 77 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 37612d7..76d1d0f 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -374,7 +374,7 @@ static void testInitializeFunctionModulesEtc(void) { else assert(size == ver * 4 + 17); - bool hasWhite = false; + bool hasLight = false; bool hasBlack = false; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { @@ -382,10 +382,10 @@ static void testInitializeFunctionModulesEtc(void) { if (color) hasBlack = true; else - hasWhite = true; + hasLight = true; } } - assert(hasWhite && hasBlack); + assert(hasLight && hasBlack); free(qrcode); numTestCases++; } @@ -424,11 +424,11 @@ static void testGetSetModule(void) { initializeFunctionModules(23, qrcode); int size = qrcodegen_getSize(qrcode); - for (int y = 0; y < size; y++) { // Clear all to white + for (int y = 0; y < size; y++) { // Clear all to light for (int x = 0; x < size; x++) setModule(qrcode, x, y, false); } - for (int y = 0; y < size; y++) { // Check all white + for (int y = 0; y < size; y++) { // Check all light for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == false); } @@ -441,7 +441,7 @@ static void testGetSetModule(void) { assert(qrcodegen_getModule(qrcode, x, y) == true); } - // Set some out of bounds modules to white + // Set some out of bounds modules to light setModuleBounded(qrcode, -1, -1, false); setModuleBounded(qrcode, -1, 0, false); setModuleBounded(qrcode, 0, -1, false); @@ -453,13 +453,13 @@ static void testGetSetModule(void) { assert(qrcodegen_getModule(qrcode, x, y) == true); } - // Set some modules to white + // Set some modules to light setModule(qrcode, 3, 8, false); setModule(qrcode, 61, 49, false); for (int y = 0; y < size; y++) { // Check most black for (int x = 0; x < size; x++) { - bool white = (x == 3 && y == 8) || (x == 61 && y == 49); - assert(qrcodegen_getModule(qrcode, x, y) != white); + bool light = (x == 3 && y == 8) || (x == 61 && y == 49); + assert(qrcodegen_getModule(qrcode, x, y) != light); } } numTestCases++; diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 31219a7..7166028 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -64,7 +64,7 @@ testable void reedSolomonComputeRemainder(const uint8_t data[], int dataLen, testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); testable void initializeFunctionModules(int version, uint8_t qrcode[]); -static void drawWhiteFunctionModules(uint8_t qrcode[], int version); +static void drawLightFunctionModules(uint8_t qrcode[], int version); static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]); testable int getAlignmentPatternPositions(int version, uint8_t result[7]); static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]); @@ -259,7 +259,7 @@ bool qrcodegen_encodeSegmentsAdvanced(const struct qrcodegen_Segment segs[], siz addEccAndInterleave(qrcode, version, ecl, tempBuffer); initializeFunctionModules(version, qrcode); drawCodewords(tempBuffer, getNumRawDataModules(version) / 8, qrcode); - drawWhiteFunctionModules(qrcode, version); + drawLightFunctionModules(qrcode, version); initializeFunctionModules(version, tempBuffer); // Handle masking @@ -413,7 +413,7 @@ testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { /*---- Drawing function modules ----*/ -// Clears the given QR Code grid with white modules for the given +// Clears the given QR Code grid with light modules for the given // version's size, then marks every function module as black. testable void initializeFunctionModules(int version, uint8_t qrcode[]) { // Initialize QR Code @@ -449,10 +449,10 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) { } -// Draws white function modules and possibly some black modules onto the given QR Code, without changing +// Draws light function modules and possibly some black modules onto the given QR Code, without changing // non-function modules. This does not draw the format bits. This requires all function modules to be previously // marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. -static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { +static void drawLightFunctionModules(uint8_t qrcode[], int version) { // Draw horizontal and vertical timing patterns int qrsize = qrcodegen_getSize(qrcode); for (int i = 7; i < qrsize - 7; i += 2) { @@ -512,7 +512,7 @@ static void drawWhiteFunctionModules(uint8_t qrcode[], int version) { // Draws two copies of the format bits (with its own error correction code) based // on the given mask and error correction level. This always draws all modules of -// the format bits, unlike drawWhiteFunctionModules() which might skip black modules. +// the format bits, unlike drawLightFunctionModules() which might skip black modules. static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { // Calculate error correction code and pack bits assert(0 <= (int)mask && (int)mask <= 7); @@ -573,7 +573,7 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco /*---- Drawing data modules and masking ----*/ // Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of -// the QR Code to be black at function modules and white at codeword modules (including unused remainder bits). +// the QR Code to be black at function modules and light at codeword modules (including unused remainder bits). static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { int qrsize = qrcodegen_getSize(qrcode); int i = 0; // Bit index into the data @@ -592,7 +592,7 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -693,7 +693,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } } - // Balance of black and white modules + // Balance of black and light modules int black = 0; for (int y = 0; y < qrsize; y++) { for (int x = 0; x < qrsize; x++) { @@ -709,7 +709,7 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } -// Can only be called immediately after a white run is added, and +// Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { int n = runHistory[1]; @@ -728,7 +728,7 @@ static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLe finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); currentRunLength = 0; } - currentRunLength += qrsize; // Add white border to final run + currentRunLength += qrsize; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); return finderPenaltyCountPatterns(runHistory, qrsize); } @@ -737,7 +737,7 @@ static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLe // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize) { if (runHistory[0] == 0) - currentRunLength += qrsize; // Add white border to initial run + currentRunLength += qrsize; // Add light border to initial run memmove(&runHistory[1], &runHistory[0], 6 * sizeof(runHistory[0])); runHistory[0] = currentRunLength; } diff --git a/c/qrcodegen.h b/c/qrcodegen.h index a99b94d..fd0ae9c 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -36,7 +36,7 @@ extern "C" { /* * This library creates QR Code symbols, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * A QR Code structure is an immutable square grid of black and white cells. + * A QR Code structure is an immutable square grid of black and light cells. * The library provides functions to create a QR Code from text or binary data. * The library covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -300,8 +300,8 @@ int qrcodegen_getSize(const uint8_t qrcode[]); /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. + * for light or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index b1673e2..88186e1 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -327,7 +327,7 @@ QrCode::QrCode(int ver, Ecc ecl, const vector &dataCodewords, int msk) throw std::domain_error("Mask value out of range"); size = ver * 4 + 17; size_t sz = static_cast(size); - modules = vector >(sz, vector(sz)); // Initially all white + modules = vector >(sz, vector(sz)); // Initially all light isFunction = vector >(sz, vector(sz)); // Compute ECC, draw modules @@ -584,7 +584,7 @@ void QrCode::drawCodewords(const vector &data) { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -676,7 +676,7 @@ long QrCode::getPenaltyScore() const { } } - // Balance of black and white modules + // Balance of black and light modules int black = 0; for (const vector &row : modules) { for (bool color : row) { @@ -797,7 +797,7 @@ int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunL finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } - currentRunLength += size; // Add white border to final run + currentRunLength += size; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory); return finderPenaltyCountPatterns(runHistory); } @@ -805,7 +805,7 @@ int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunL void QrCode::finderPenaltyAddHistory(int currentRunLength, std::array &runHistory) const { if (runHistory.at(0) == 0) - currentRunLength += size; // Add white border to initial run + currentRunLength += size; // Add light border to initial run std::copy_backward(runHistory.cbegin(), runHistory.cend() - 1, runHistory.end()); runHistory.at(0) = currentRunLength; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 9e7f7fc..55a1ee8 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -220,7 +220,7 @@ class QrSegment final { /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and white cells. + * Instances of this class represent an immutable square grid of black and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -314,7 +314,7 @@ class QrCode final { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = black). // Immutable after constructor finishes. Accessed through getModule(). private: std::vector > modules; @@ -363,8 +363,8 @@ class QrCode final { /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for white or true for black. The top left corner has the coordinates (x=0, y=0). - * If the given coordinates are out of bounds, then false (white) is returned. + * for light or true for black. The top left corner has the coordinates (x=0, y=0). + * If the given coordinates are out of bounds, then false (light) is returned. */ public: bool getModule(int x, int y) const; @@ -472,7 +472,7 @@ class QrCode final { private: static std::uint8_t reedSolomonMultiply(std::uint8_t x, std::uint8_t y); - // Can only be called immediately after a white run is added, and + // Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private: int finderPenaltyCountPatterns(const std::array &runHistory) const; diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 1b4c7d0..1a74fcb 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -32,7 +32,7 @@ import java.util.Objects; /** * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - *

      Instances of this class represent an immutable square grid of black and white cells. + *

      Instances of this class represent an immutable square grid of black and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

      @@ -222,7 +222,7 @@ public final class QrCode { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = black). // Immutable after constructor finishes. Accessed through getModule(). private boolean[][] modules; @@ -256,7 +256,7 @@ public final class QrCode { size = ver * 4 + 17; errorCorrectionLevel = Objects.requireNonNull(ecl); Objects.requireNonNull(dataCodewords); - modules = new boolean[size][size]; // Initially all white + modules = new boolean[size][size]; // Initially all light isFunction = new boolean[size][size]; // Compute ECC, draw modules, do masking @@ -273,12 +273,12 @@ public final class QrCode { /** * 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). - * If the specified coordinates are out of bounds, then {@code false} (white) is returned. + * for light or {@code true} for black. The top left corner has the coordinates (x=0, y=0). + * If the specified coordinates are out of bounds, then {@code false} (light) is returned. * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge * @return {@code true} if the coordinates are in bounds and the module - * at that location is black, or {@code false} (white) otherwise + * at that location is black, or {@code false} (light) otherwise */ public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; @@ -287,7 +287,7 @@ public final class QrCode { /** * 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 + *

      For example, toImage(scale=10, border=4) means to pad the QR Code with 4 light * border modules on all four sides, and use 10×10 pixels to represent each module. * The resulting image only contains the hex colors 000000 and FFFFFF. * @param scale the side length (measured in pixels, must be positive) of each module @@ -530,7 +530,7 @@ public final class QrCode { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -651,7 +651,7 @@ public final class QrCode { } } - // Balance of black and white modules + // Balance of black and light modules int black = 0; for (boolean[] row : modules) { for (boolean color : row) { @@ -786,7 +786,7 @@ public final class QrCode { } - // Can only be called immediately after a white run is added, and + // Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private int finderPenaltyCountPatterns(int[] runHistory) { int n = runHistory[1]; @@ -803,7 +803,7 @@ public final class QrCode { finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } - currentRunLength += size; // Add white border to final run + currentRunLength += size; // Add light border to final run finderPenaltyAddHistory(currentRunLength, runHistory); return finderPenaltyCountPatterns(runHistory); } @@ -812,7 +812,7 @@ public final class QrCode { // 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) - currentRunLength += size; // Add white border to initial run + currentRunLength += size; // Add light border to initial run System.arraycopy(runHistory, 0, runHistory, 1, runHistory.length - 1); runHistory[0] = currentRunLength; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index c0924c9..115d31d 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -32,7 +32,7 @@ from typing import Callable, Dict, List, Optional, Tuple, Union class QrCode: """A QR Code symbol, which is a type of two-dimension barcode. Invented by Denso Wave and described in the ISO/IEC 18004 standard. - Instances of this class represent an immutable square grid of black and white cells. + Instances of this class represent an immutable square grid of black and light cells. The class provides static factory functions to create a QR Code from text or binary data. The class covers the QR Code Model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -151,7 +151,7 @@ class QrCode: # the resulting object still has a mask value between 0 and 7. _mask: int - # The modules of this QR Code (False = white, True = black). + # The modules of this QR Code (False = light, True = black). # Immutable after constructor finishes. Accessed through get_module(). _modules: List[List[bool]] @@ -180,7 +180,7 @@ class QrCode: self._errcorlvl = errcorlvl # Initialize both grids to be size*size arrays of Boolean false - self._modules = [[False] * self._size for _ in range(self._size)] # Initially all white + self._modules = [[False] * self._size for _ in range(self._size)] # Initially all light self._isfunction = [[False] * self._size for _ in range(self._size)] # Compute ECC, draw modules @@ -227,8 +227,8 @@ class QrCode: def get_module(self, x: int, y: int) -> bool: """Returns the color of the module (pixel) at the given coordinates, which is False - for white or True for black. The top left corner has the coordinates (x=0, y=0). - If the given coordinates are out of bounds, then False (white) is returned.""" + for light or True for black. The top left corner has the coordinates (x=0, y=0). + If the given coordinates are out of bounds, then False (light) is returned.""" return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x] @@ -416,7 +416,7 @@ class QrCode: self._modules[y][x] = _get_bit(data[i >> 3], 7 - (i & 7)) i += 1 # If this QR Code has any remainder bits (0 to 7), they were assigned as - # 0/false/white by the constructor and are left unchanged by this method + # 0/false/light by the constructor and are left unchanged by this method assert i == len(data) * 8 @@ -486,7 +486,7 @@ class QrCode: if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: result += QrCode._PENALTY_N2 - # Balance of black and white modules + # Balance of black and light modules black: int = sum((1 if cell else 0) for row in modules for cell in row) total: int = size**2 # Note that size is odd, so black/total != 1/2 # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% @@ -591,7 +591,7 @@ class QrCode: def _finder_penalty_count_patterns(self, runhistory: collections.deque) -> int: - """Can only be called immediately after a white run is added, and + """Can only be called immediately after a light run is added, and returns either 0, 1, or 2. A helper function for _get_penalty_score().""" n: int = runhistory[1] assert n <= self._size * 3 @@ -605,14 +605,14 @@ class QrCode: if currentruncolor: # Terminate black run self._finder_penalty_add_history(currentrunlength, runhistory) currentrunlength = 0 - currentrunlength += self._size # Add white border to final run + currentrunlength += self._size # Add light border to final run self._finder_penalty_add_history(currentrunlength, runhistory) return self._finder_penalty_count_patterns(runhistory) def _finder_penalty_add_history(self, currentrunlength: int, runhistory: collections.deque) -> None: if runhistory[0] == 0: - currentrunlength += self._size # Add white border to initial run + currentrunlength += self._size # Add light border to initial run runhistory.appendleft(currentrunlength) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 64f563c..dbfb855 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -88,7 +88,7 @@ /// /// Invented by Denso Wave and described in the ISO/IEC 18004 standard. /// -/// Instances of this struct represent an immutable square grid of black and white cells. +/// Instances of this struct represent an immutable square grid of black and light cells. /// The impl provides static factory functions to create a QR Code from text or binary data. /// The struct and impl cover the QR Code Model 2 specification, supporting all versions /// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -126,7 +126,7 @@ pub struct QrCode { // Grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = black). // Immutable after constructor finishes. Accessed through get_module(). modules: Vec, @@ -285,7 +285,7 @@ impl QrCode { size: size as i32, mask: Mask::new(0), // Dummy value errorcorrectionlevel: ecl, - modules : vec![false; size * size], // Initially all white + modules : vec![false; size * size], // Initially all light isfunction: vec![false; size * size], }; @@ -347,10 +347,10 @@ impl QrCode { /// Returns the color of the module (pixel) at the given coordinates, - /// which is `false` for white or `true` for black. + /// which is `false` for light or `true` for black. /// /// The top left corner has the coordinates (x=0, y=0). If the given - /// coordinates are out of bounds, then `false` (white) is returned. + /// coordinates are out of bounds, then `false` (light) is returned. pub fn get_module(&self, x: i32, y: i32) -> bool { 0 <= x && x < self.size && 0 <= y && y < self.size && self.module(x, y) } @@ -602,7 +602,7 @@ impl QrCode { i += 1; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } right -= 2; @@ -703,7 +703,7 @@ impl QrCode { } } - // Balance of black and white modules + // Balance of black and light modules let black: i32 = self.modules.iter().copied().map(i32::from).sum(); let total: i32 = size * size; // Note that size is odd, so black/total != 1/2 // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% @@ -846,7 +846,7 @@ impl FinderPenalty { // Pushes the given value to the front and drops the last value. pub fn add_history(&mut self, mut currentrunlength: i32) { if self.run_history[0] == 0 { - currentrunlength += self.qr_size; // Add white border to initial run + currentrunlength += self.qr_size; // Add light border to initial run } let rh = &mut self.run_history; for i in (0 .. rh.len()-1).rev() { @@ -856,7 +856,7 @@ impl FinderPenalty { } - // Can only be called immediately after a white run is added, and returns either 0, 1, or 2. + // Can only be called immediately after a light run is added, and returns either 0, 1, or 2. pub fn count_patterns(&self) -> i32 { let rh = &self.run_history; let n = rh[1]; @@ -873,7 +873,7 @@ impl FinderPenalty { self.add_history(currentrunlength); currentrunlength = 0; } - currentrunlength += self.qr_size; // Add white border to final run + currentrunlength += self.qr_size; // Add light border to final run self.add_history(currentrunlength); self.count_patterns() } diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 88663ee..77f07bc 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -36,7 +36,7 @@ namespace qrcodegen { /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and white cells. + * Instances of this class represent an immutable square grid of black and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -155,7 +155,7 @@ namespace qrcodegen { // 21 and 177 (inclusive). This is equal to version * 4 + 17. public readonly size: int; - // The modules of this QR Code (false = white, true = black). + // The modules of this QR Code (false = light, true = black). // Immutable after constructor finishes. Accessed through getModule(). private readonly modules : Array> = []; @@ -196,7 +196,7 @@ namespace qrcodegen { for (let i = 0; i < this.size; i++) row.push(false); for (let i = 0; i < this.size; i++) { - this.modules .push(row.slice()); // Initially all white + this.modules .push(row.slice()); // Initially all light this.isFunction.push(row.slice()); } @@ -232,8 +232,8 @@ namespace qrcodegen { /*-- Accessor methods --*/ // Returns the color of the module (pixel) at the given coordinates, which is false - // for white or true for black. The top left corner has the coordinates (x=0, y=0). - // If the given coordinates are out of bounds, then false (white) is returned. + // for light or true for black. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then false (light) is returned. public getModule(x: int, y: int): boolean { return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x]; } @@ -243,7 +243,7 @@ namespace qrcodegen { // Draws this QR Code, with the given module scale and border modules, onto the given HTML // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. - // The drawn image is be purely black and white, and fully opaque. + // The drawn image is be purely black and light, and fully opaque. // The scale must be a positive integer and the border must be a non-negative integer. public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) @@ -467,7 +467,7 @@ namespace qrcodegen { i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as - // 0/false/white by the constructor and are left unchanged by this method + // 0/false/light by the constructor and are left unchanged by this method } } } @@ -566,7 +566,7 @@ namespace qrcodegen { } } - // Balance of black and white modules + // Balance of black and light modules let black: int = 0; for (const row of this.modules) black = row.reduce((sum, color) => sum + (color ? 1 : 0), black); @@ -686,7 +686,7 @@ namespace qrcodegen { } - // Can only be called immediately after a white run is added, and + // Can only be called immediately after a light run is added, and // returns either 0, 1, or 2. A helper function for getPenaltyScore(). private finderPenaltyCountPatterns(runHistory: Array): int { const n: int = runHistory[1]; @@ -704,7 +704,7 @@ namespace qrcodegen { this.finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } - currentRunLength += this.size; // Add white border to final run + currentRunLength += this.size; // Add light border to final run this.finderPenaltyAddHistory(currentRunLength, runHistory); return this.finderPenaltyCountPatterns(runHistory); } @@ -713,7 +713,7 @@ namespace qrcodegen { // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). private finderPenaltyAddHistory(currentRunLength: int, runHistory: Array): void { if (runHistory[0] == 0) - currentRunLength += this.size; // Add white border to initial run + currentRunLength += this.size; // Add light border to initial run runHistory.pop(); runHistory.unshift(currentRunLength); } From 1fd0216dfebfb36d704752053d5a10ec5af65b24 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Tue, 27 Jul 2021 23:35:06 +0000 Subject: [PATCH 709/810] Changed the word "black" to "dark" in {comments, local variables,} in order to match the vocabulary in the QR Code specification document. --- c/qrcodegen-test.c | 18 ++++---- c/qrcodegen.c | 46 +++++++++---------- c/qrcodegen.h | 4 +- cpp/QrCode.cpp | 20 ++++---- cpp/QrCode.hpp | 8 ++-- .../main/java/io/nayuki/qrcodegen/QrCode.java | 30 ++++++------ python/qrcodegen.py | 26 +++++------ rust/src/lib.rs | 24 +++++----- typescript-javascript/qrcodegen.ts | 28 +++++------ 9 files changed, 102 insertions(+), 102 deletions(-) diff --git a/c/qrcodegen-test.c b/c/qrcodegen-test.c index 76d1d0f..fd57380 100644 --- a/c/qrcodegen-test.c +++ b/c/qrcodegen-test.c @@ -55,8 +55,8 @@ uint8_t reedSolomonMultiply(uint8_t x, uint8_t y); void initializeFunctionModules(int version, uint8_t qrcode[]); int getAlignmentPatternPositions(int version, uint8_t result[7]); bool getModule(const uint8_t qrcode[], int x, int y); -void setModule(uint8_t qrcode[], int x, int y, bool isBlack); -void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +void setModule(uint8_t qrcode[], int x, int y, bool isDark); +void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); int getTotalBits(const struct qrcodegen_Segment segs[], size_t len, int version); @@ -375,17 +375,17 @@ static void testInitializeFunctionModulesEtc(void) { assert(size == ver * 4 + 17); bool hasLight = false; - bool hasBlack = false; + bool hasDark = false; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { bool color = qrcodegen_getModule(qrcode, x, y); if (color) - hasBlack = true; + hasDark = true; else hasLight = true; } } - assert(hasLight && hasBlack); + assert(hasLight && hasDark); free(qrcode); numTestCases++; } @@ -432,11 +432,11 @@ static void testGetSetModule(void) { for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == false); } - for (int y = 0; y < size; y++) { // Set all to black + for (int y = 0; y < size; y++) { // Set all to dark for (int x = 0; x < size; x++) setModule(qrcode, x, y, true); } - for (int y = 0; y < size; y++) { // Check all black + for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } @@ -448,7 +448,7 @@ static void testGetSetModule(void) { setModuleBounded(qrcode, size, 5, false); setModuleBounded(qrcode, 72, size, false); setModuleBounded(qrcode, size, size, false); - for (int y = 0; y < size; y++) { // Check all black + for (int y = 0; y < size; y++) { // Check all dark for (int x = 0; x < size; x++) assert(qrcodegen_getModule(qrcode, x, y) == true); } @@ -456,7 +456,7 @@ static void testGetSetModule(void) { // Set some modules to light setModule(qrcode, 3, 8, false); setModule(qrcode, 61, 49, false); - for (int y = 0; y < size; y++) { // Check most black + for (int y = 0; y < size; y++) { // Check most dark for (int x = 0; x < size; x++) { bool light = (x == 3 && y == 8) || (x == 61 && y == 49); assert(qrcodegen_getModule(qrcode, x, y) != light); diff --git a/c/qrcodegen.c b/c/qrcodegen.c index 7166028..e67a7d3 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -77,8 +77,8 @@ static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLe static void finderPenaltyAddHistory(int currentRunLength, int runHistory[7], int qrsize); testable bool getModule(const uint8_t qrcode[], int x, int y); -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack); -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack); +testable void setModule(uint8_t qrcode[], int x, int y, bool isDark); +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark); static bool getBit(int x, int i); testable int calcSegmentBitLength(enum qrcodegen_Mode mode, size_t numChars); @@ -414,7 +414,7 @@ testable uint8_t reedSolomonMultiply(uint8_t x, uint8_t y) { /*---- Drawing function modules ----*/ // Clears the given QR Code grid with light modules for the given -// version's size, then marks every function module as black. +// version's size, then marks every function module as dark. testable void initializeFunctionModules(int version, uint8_t qrcode[]) { // Initialize QR Code int qrsize = version * 4 + 17; @@ -449,9 +449,9 @@ testable void initializeFunctionModules(int version, uint8_t qrcode[]) { } -// Draws light function modules and possibly some black modules onto the given QR Code, without changing +// Draws light function modules and possibly some dark modules onto the given QR Code, without changing // non-function modules. This does not draw the format bits. This requires all function modules to be previously -// marked black (namely by initializeFunctionModules()), because this may skip redrawing black function modules. +// marked dark (namely by initializeFunctionModules()), because this may skip redrawing dark function modules. static void drawLightFunctionModules(uint8_t qrcode[], int version) { // Draw horizontal and vertical timing patterns int qrsize = qrcodegen_getSize(qrcode); @@ -512,7 +512,7 @@ static void drawLightFunctionModules(uint8_t qrcode[], int version) { // Draws two copies of the format bits (with its own error correction code) based // on the given mask and error correction level. This always draws all modules of -// the format bits, unlike drawLightFunctionModules() which might skip black modules. +// the format bits, unlike drawLightFunctionModules() which might skip dark modules. static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uint8_t qrcode[]) { // Calculate error correction code and pack bits assert(0 <= (int)mask && (int)mask <= 7); @@ -539,7 +539,7 @@ static void drawFormatBits(enum qrcodegen_Ecc ecl, enum qrcodegen_Mask mask, uin setModule(qrcode, qrsize - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setModule(qrcode, 8, qrsize - 15 + i, getBit(bits, i)); - setModule(qrcode, 8, qrsize - 8, true); // Always black + setModule(qrcode, 8, qrsize - 8, true); // Always dark } @@ -560,7 +560,7 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { } -// Sets every pixel in the range [left : left + width] * [top : top + height] to black. +// Sets every pixel in the range [left : left + width] * [top : top + height] to dark. static void fillRectangle(int left, int top, int width, int height, uint8_t qrcode[]) { for (int dy = 0; dy < height; dy++) { for (int dx = 0; dx < width; dx++) @@ -573,7 +573,7 @@ static void fillRectangle(int left, int top, int width, int height, uint8_t qrco /*---- Drawing data modules and masking ----*/ // Draws the raw codewords (including data and ECC) onto the given QR Code. This requires the initial state of -// the QR Code to be black at function modules and light at codeword modules (including unused remainder bits). +// the QR Code to be dark at function modules and light at codeword modules (including unused remainder bits). static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { int qrsize = qrcodegen_getSize(qrcode); int i = 0; // Bit index into the data @@ -587,8 +587,8 @@ static void drawCodewords(const uint8_t data[], int dataLen, uint8_t qrcode[]) { bool upward = ((right + 1) & 2) == 0; int y = upward ? qrsize - 1 - vert : vert; // Actual y coordinate if (!getModule(qrcode, x, y) && i < dataLen * 8) { - bool black = getBit(data[i >> 3], 7 - (i & 7)); - setModule(qrcode, x, y, black); + bool dark = getBit(data[i >> 3], 7 - (i & 7)); + setModule(qrcode, x, y, dark); i++; } // If this QR Code has any remainder bits (0 to 7), they were assigned as @@ -693,17 +693,17 @@ static long getPenaltyScore(const uint8_t qrcode[]) { } } - // Balance of black and light modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (int y = 0; y < qrsize; y++) { for (int x = 0; x < qrsize; x++) { if (getModule(qrcode, x, y)) - black++; + dark++; } } - int total = qrsize * qrsize; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = (int)((labs(black * 20L - total * 10L) + total - 1) / total) - 1; + int total = qrsize * qrsize; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (int)((labs(dark * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; return result; } @@ -715,7 +715,7 @@ static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { int n = runHistory[1]; assert(n <= qrsize * 3); bool core = n > 0 && runHistory[2] == n && runHistory[3] == n * 3 && runHistory[4] == n && runHistory[5] == n; - // The maximum QR Code size is 177, hence the black run length n <= 177. + // The maximum QR Code size is 177, hence the dark run length n <= 177. // Arithmetic is promoted to int, so n*4 will not overflow. return (core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0); @@ -724,7 +724,7 @@ static int finderPenaltyCountPatterns(const int runHistory[7], int qrsize) { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). static int finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, int runHistory[7], int qrsize) { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory, qrsize); currentRunLength = 0; } @@ -774,13 +774,13 @@ testable bool getModule(const uint8_t qrcode[], int x, int y) { // Sets the module at the given coordinates, which must be in bounds. -testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { +testable void setModule(uint8_t qrcode[], int x, int y, bool isDark) { int qrsize = qrcode[0]; assert(21 <= qrsize && qrsize <= 177 && 0 <= x && x < qrsize && 0 <= y && y < qrsize); int index = y * qrsize + x; int bitIndex = index & 7; int byteIndex = (index >> 3) + 1; - if (isBlack) + if (isDark) qrcode[byteIndex] |= 1 << bitIndex; else qrcode[byteIndex] &= (1 << bitIndex) ^ 0xFF; @@ -788,10 +788,10 @@ testable void setModule(uint8_t qrcode[], int x, int y, bool isBlack) { // Sets the module at the given coordinates, doing nothing if out of bounds. -testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isBlack) { +testable void setModuleBounded(uint8_t qrcode[], int x, int y, bool isDark) { int qrsize = qrcode[0]; if (0 <= x && x < qrsize && 0 <= y && y < qrsize) - setModule(qrcode, x, y, isBlack); + setModule(qrcode, x, y, isDark); } diff --git a/c/qrcodegen.h b/c/qrcodegen.h index fd0ae9c..94aaef9 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -36,7 +36,7 @@ extern "C" { /* * This library creates QR Code symbols, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * A QR Code structure is an immutable square grid of black and light cells. + * A QR Code structure is an immutable square grid of dark and light cells. * The library provides functions to create a QR Code from text or binary data. * The library covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -300,7 +300,7 @@ int qrcodegen_getSize(const uint8_t qrcode[]); /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for light or true for black. The top left corner has the coordinates (x=0, y=0). + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). * If the given coordinates are out of bounds, then false (light) is returned. */ bool qrcodegen_getModule(const uint8_t qrcode[], int x, int y); diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 88186e1..538204e 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -466,7 +466,7 @@ void QrCode::drawFormatBits(int msk) { setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always black + setFunctionModule(8, size - 8, true); // Always dark } @@ -513,10 +513,10 @@ void QrCode::drawAlignmentPattern(int x, int y) { } -void QrCode::setFunctionModule(int x, int y, bool isBlack) { +void QrCode::setFunctionModule(int x, int y, bool isDark) { size_t ux = static_cast(x); size_t uy = static_cast(y); - modules .at(uy).at(ux) = isBlack; + modules .at(uy).at(ux) = isDark; isFunction.at(uy).at(ux) = true; } @@ -676,17 +676,17 @@ long QrCode::getPenaltyScore() const { } } - // Balance of black and light modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (const vector &row : modules) { for (bool color : row) { if (color) - black++; + dark++; } } - int total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = static_cast((std::abs(black * 20L - total * 10L) + total - 1) / total) - 1; + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = static_cast((std::abs(dark * 20L - total * 10L) + total - 1) / total) - 1; result += k * PENALTY_N4; return result; } @@ -793,7 +793,7 @@ int QrCode::finderPenaltyCountPatterns(const std::array &runHistory) cons int QrCode::finderPenaltyTerminateAndCount(bool currentRunColor, int currentRunLength, std::array &runHistory) const { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 55a1ee8..2a1e987 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -220,7 +220,7 @@ class QrSegment final { /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and light cells. + * Instances of this class represent an immutable square grid of dark and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -314,7 +314,7 @@ class QrCode final { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = light, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through getModule(). private: std::vector > modules; @@ -363,7 +363,7 @@ class QrCode final { /* * Returns the color of the module (pixel) at the given coordinates, which is false - * for light or true for black. The top left corner has the coordinates (x=0, y=0). + * for light or true for dark. The top left corner has the coordinates (x=0, y=0). * If the given coordinates are out of bounds, then false (light) is returned. */ public: bool getModule(int x, int y) const; @@ -405,7 +405,7 @@ class QrCode final { // 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, bool isBlack); + private: void setFunctionModule(int x, int y, bool isDark); // Returns the color of the module at the given coordinates, which must be in range. diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 1a74fcb..3bd3f4b 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -32,7 +32,7 @@ import java.util.Objects; /** * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - *

      Instances of this class represent an immutable square grid of black and light cells. + *

      Instances of this class represent an immutable square grid of dark and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes.

      @@ -222,7 +222,7 @@ public final class QrCode { // Private grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = light, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through getModule(). private boolean[][] modules; @@ -273,12 +273,12 @@ public final class QrCode { /** * Returns the color of the module (pixel) at the specified coordinates, which is {@code false} - * for light or {@code true} for black. The top left corner has the coordinates (x=0, y=0). + * for light or {@code true} for dark. The top left corner has the coordinates (x=0, y=0). * If the specified coordinates are out of bounds, then {@code false} (light) is returned. * @param x the x coordinate, where 0 is the left edge and size−1 is the right edge * @param y the y coordinate, where 0 is the top edge and size−1 is the bottom edge * @return {@code true} if the coordinates are in bounds and the module - * at that location is black, or {@code false} (light) otherwise + * at that location is dark, or {@code false} (light) otherwise */ public boolean getModule(int x, int y) { return 0 <= x && x < size && 0 <= y && y < size && modules[y][x]; @@ -405,7 +405,7 @@ public final class QrCode { setFunctionModule(size - 1 - i, 8, getBit(bits, i)); for (int i = 8; i < 15; i++) setFunctionModule(8, size - 15 + i, getBit(bits, i)); - setFunctionModule(8, size - 8, true); // Always black + setFunctionModule(8, size - 8, true); // Always dark } @@ -459,8 +459,8 @@ public final class QrCode { // 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; + private void setFunctionModule(int x, int y, boolean isDark) { + modules[y][x] = isDark; isFunction[y][x] = true; } @@ -651,17 +651,17 @@ public final class QrCode { } } - // Balance of black and light modules - int black = 0; + // Balance of dark and light modules + int dark = 0; for (boolean[] row : modules) { for (boolean color : row) { if (color) - black++; + dark++; } } - int total = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - int k = (Math.abs(black * 20 - total * 10) + total - 1) / total - 1; + int total = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + int k = (Math.abs(dark * 20 - total * 10) + total - 1) / total - 1; result += k * PENALTY_N4; return result; } @@ -702,7 +702,7 @@ public final class QrCode { 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 - result -= 15 * 2 + 1; // Subtract the format information and black module + result -= 15 * 2 + 1; // Subtract the format information and dark module result -= (size - 16) * 2; // Subtract the timing patterns (excluding finders) // The five lines above are equivalent to: int result = (16 * ver + 128) * ver + 64; if (ver >= 2) { @@ -799,7 +799,7 @@ public final class QrCode { // 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 + if (currentRunColor) { // Terminate dark run finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 115d31d..7cbea57 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -32,7 +32,7 @@ from typing import Callable, Dict, List, Optional, Tuple, Union class QrCode: """A QR Code symbol, which is a type of two-dimension barcode. Invented by Denso Wave and described in the ISO/IEC 18004 standard. - Instances of this class represent an immutable square grid of black and light cells. + Instances of this class represent an immutable square grid of dark and light cells. The class provides static factory functions to create a QR Code from text or binary data. The class covers the QR Code Model 2 specification, supporting all versions (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -151,7 +151,7 @@ class QrCode: # the resulting object still has a mask value between 0 and 7. _mask: int - # The modules of this QR Code (False = light, True = black). + # The modules of this QR Code (False = light, True = dark). # Immutable after constructor finishes. Accessed through get_module(). _modules: List[List[bool]] @@ -227,7 +227,7 @@ class QrCode: def get_module(self, x: int, y: int) -> bool: """Returns the color of the module (pixel) at the given coordinates, which is False - for light or True for black. The top left corner has the coordinates (x=0, y=0). + for light or True for dark. The top left corner has the coordinates (x=0, y=0). If the given coordinates are out of bounds, then False (light) is returned.""" return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x] @@ -306,7 +306,7 @@ class QrCode: self._set_function_module(self._size - 1 - i, 8, _get_bit(bits, i)) for i in range(8, 15): self._set_function_module(8, self._size - 15 + i, _get_bit(bits, i)) - self._set_function_module(8, self._size - 8, True) # Always black + self._set_function_module(8, self._size - 8, True) # Always dark def _draw_version(self) -> None: @@ -350,11 +350,11 @@ class QrCode: self._set_function_module(x + dx, y + dy, max(abs(dx), abs(dy)) != 1) - def _set_function_module(self, x: int, y: int, isblack: bool) -> None: + def _set_function_module(self, x: int, y: int, isdark: bool) -> None: """Sets the color of a module and marks it as a function module. Only used by the constructor. Coordinates must be in bounds.""" - assert type(isblack) is bool - self._modules[y][x] = isblack + assert type(isdark) is bool + self._modules[y][x] = isdark self._isfunction[y][x] = True @@ -486,11 +486,11 @@ class QrCode: if modules[y][x] == modules[y][x + 1] == modules[y + 1][x] == modules[y + 1][x + 1]: result += QrCode._PENALTY_N2 - # Balance of black and light modules - black: int = sum((1 if cell else 0) for row in modules for cell in row) - total: int = size**2 # Note that size is odd, so black/total != 1/2 - # Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - k: int = (abs(black * 20 - total * 10) + total - 1) // total - 1 + # Balance of dark and light modules + dark: int = sum((1 if cell else 0) for row in modules for cell in row) + total: int = size**2 # Note that size is odd, so dark/total != 1/2 + # Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + k: int = (abs(dark * 20 - total * 10) + total - 1) // total - 1 result += k * QrCode._PENALTY_N4 return result @@ -602,7 +602,7 @@ class QrCode: def _finder_penalty_terminate_and_count(self, currentruncolor: bool, currentrunlength: int, runhistory: collections.deque) -> int: """Must be called at the end of a line (row or column) of modules. A helper function for _get_penalty_score().""" - if currentruncolor: # Terminate black run + if currentruncolor: # Terminate dark run self._finder_penalty_add_history(currentrunlength, runhistory) currentrunlength = 0 currentrunlength += self._size # Add light border to final run diff --git a/rust/src/lib.rs b/rust/src/lib.rs index dbfb855..2999e29 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -88,7 +88,7 @@ /// /// Invented by Denso Wave and described in the ISO/IEC 18004 standard. /// -/// Instances of this struct represent an immutable square grid of black and light cells. +/// Instances of this struct represent an immutable square grid of dark and light cells. /// The impl provides static factory functions to create a QR Code from text or binary data. /// The struct and impl cover the QR Code Model 2 specification, supporting all versions /// (sizes) from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -126,7 +126,7 @@ pub struct QrCode { // Grids of modules/pixels, with dimensions of size*size: - // The modules of this QR Code (false = light, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through get_module(). modules: Vec, @@ -347,7 +347,7 @@ impl QrCode { /// Returns the color of the module (pixel) at the given coordinates, - /// which is `false` for light or `true` for black. + /// which is `false` for light or `true` for dark. /// /// The top left corner has the coordinates (x=0, y=0). If the given /// coordinates are out of bounds, then `false` (light) is returned. @@ -466,7 +466,7 @@ impl QrCode { for i in 8 .. 15 { self.set_function_module(8, size - 15 + i, get_bit(bits, i)); } - self.set_function_module(8, size - 8, true); // Always black + self.set_function_module(8, size - 8, true); // Always dark } @@ -528,8 +528,8 @@ impl QrCode { // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. - fn set_function_module(&mut self, x: i32, y: i32, isblack: bool) { - *self.module_mut(x, y) = isblack; + fn set_function_module(&mut self, x: i32, y: i32, isdark: bool) { + *self.module_mut(x, y) = isdark; self.isfunction[(y * self.size + x) as usize] = true; } @@ -703,11 +703,11 @@ impl QrCode { } } - // Balance of black and light modules - let black: i32 = self.modules.iter().copied().map(i32::from).sum(); - let total: i32 = size * size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - let k: i32 = ((black * 20 - total * 10).abs() + total - 1) / total - 1; + // Balance of dark and light modules + let dark: i32 = self.modules.iter().copied().map(i32::from).sum(); + let total: i32 = size * size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + let k: i32 = ((dark * 20 - total * 10).abs() + total - 1) / total - 1; result += k * PENALTY_N4; result } @@ -869,7 +869,7 @@ impl FinderPenalty { // Must be called at the end of a line (row or column) of modules. pub fn terminate_and_count(mut self, currentruncolor: bool, mut currentrunlength: i32) -> i32 { - if currentruncolor { // Terminate black run + if currentruncolor { // Terminate dark run self.add_history(currentrunlength); currentrunlength = 0; } diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 77f07bc..2d3e717 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -36,7 +36,7 @@ namespace qrcodegen { /* * A QR Code symbol, which is a type of two-dimension barcode. * Invented by Denso Wave and described in the ISO/IEC 18004 standard. - * Instances of this class represent an immutable square grid of black and light cells. + * Instances of this class represent an immutable square grid of dark and light cells. * The class provides static factory functions to create a QR Code from text or binary data. * The class covers the QR Code Model 2 specification, supporting all versions (sizes) * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. @@ -155,7 +155,7 @@ namespace qrcodegen { // 21 and 177 (inclusive). This is equal to version * 4 + 17. public readonly size: int; - // The modules of this QR Code (false = light, true = black). + // The modules of this QR Code (false = light, true = dark). // Immutable after constructor finishes. Accessed through getModule(). private readonly modules : Array> = []; @@ -232,7 +232,7 @@ namespace qrcodegen { /*-- Accessor methods --*/ // Returns the color of the module (pixel) at the given coordinates, which is false - // for light or true for black. The top left corner has the coordinates (x=0, y=0). + // for light or true for dark. The top left corner has the coordinates (x=0, y=0). // If the given coordinates are out of bounds, then false (light) is returned. public getModule(x: int, y: int): boolean { return 0 <= x && x < this.size && 0 <= y && y < this.size && this.modules[y][x]; @@ -243,7 +243,7 @@ namespace qrcodegen { // Draws this QR Code, with the given module scale and border modules, onto the given HTML // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. - // The drawn image is be purely black and light, and fully opaque. + // The drawn image is be purely dark and light, and fully opaque. // The scale must be a positive integer and the border must be a non-negative integer. public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) @@ -341,7 +341,7 @@ namespace qrcodegen { this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i)); for (let i = 8; i < 15; i++) this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i)); - this.setFunctionModule(8, this.size - 8, true); // Always black + this.setFunctionModule(8, this.size - 8, true); // Always dark } @@ -397,8 +397,8 @@ namespace qrcodegen { // Sets the color of a module and marks it as a function module. // Only used by the constructor. Coordinates must be in bounds. - private setFunctionModule(x: int, y: int, isBlack: boolean): void { - this.modules[y][x] = isBlack; + private setFunctionModule(x: int, y: int, isDark: boolean): void { + this.modules[y][x] = isDark; this.isFunction[y][x] = true; } @@ -566,13 +566,13 @@ namespace qrcodegen { } } - // Balance of black and light modules - let black: int = 0; + // Balance of dark and light modules + let dark: int = 0; for (const row of this.modules) - black = row.reduce((sum, color) => sum + (color ? 1 : 0), black); - const total: int = this.size * this.size; // Note that size is odd, so black/total != 1/2 - // Compute the smallest integer k >= 0 such that (45-5k)% <= black/total <= (55+5k)% - const k: int = Math.ceil(Math.abs(black * 20 - total * 10) / total) - 1; + dark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark); + const total: int = this.size * this.size; // Note that size is odd, so dark/total != 1/2 + // Compute the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + const k: int = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1; result += k * QrCode.PENALTY_N4; return result; } @@ -700,7 +700,7 @@ namespace qrcodegen { // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). private finderPenaltyTerminateAndCount(currentRunColor: boolean, currentRunLength: int, runHistory: Array): int { - if (currentRunColor) { // Terminate black run + if (currentRunColor) { // Terminate dark run this.finderPenaltyAddHistory(currentRunLength, runHistory); currentRunLength = 0; } From 010410be0244769db0a1efbc7eb78833e31b85bf Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:05:35 +0000 Subject: [PATCH 710/810] In Java version: moved QrCode.toImage() out of the library and into the runnable demo program, slightly adapted some code, updated documentation comments. --- Readme.markdown | 4 +- .../main/java/io/nayuki/qrcodegen/QrCode.java | 29 -------- .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 66 ++++++++++++++----- .../io/nayuki/qrcodegen/package-info.java | 4 +- 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 7e8b15f..12571c9 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), `BufferedImage` raster bitmap (Java only), HTML5 canvas (TypeScript/JavaScript only) +* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), HTML5 canvas (TypeScript/JavaScript only) * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License @@ -51,7 +51,7 @@ Java language: // Simple operation QrCode qr0 = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); - BufferedImage img = qr0.toImage(4, 10); + BufferedImage img = toImage(qr0, 4, 10); // See QrCodeGeneratorDemo ImageIO.write(img, "png", new File("qr-code.png")); // Manual operation diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 3bd3f4b..49742f7 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -23,7 +23,6 @@ package io.nayuki.qrcodegen; -import java.awt.image.BufferedImage; import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -285,34 +284,6 @@ public final class QrCode { } - /** - * 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 light - * border modules on all four sides, and use 10×10 pixels to represent each module. - * The resulting image only contains the hex colors 000000 and FFFFFF. - * @param scale the side length (measured in pixels, must be positive) of each module - * @param border the number of border modules to add, which must be non-negative - * @return a new image representing this QR Code, with padding and scaling - * @throws IllegalArgumentException if the scale or border is out of range, or if - * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE - */ - public BufferedImage toImage(int scale, int border) { - if (scale <= 0 || border < 0) - 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++) { - boolean color = getModule(x / scale - border, y / scale - border); - result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); - } - } - 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. diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 3c50bda..2b6b619 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -33,6 +33,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.Arrays; import java.util.List; +import java.util.Objects; import javax.imageio.ImageIO; @@ -57,7 +58,7 @@ public final class QrCodeGeneratorDemo { QrCode qr = QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - BufferedImage img = qr.toImage(10, 4); // Convert to bitmap image + BufferedImage img = toImage(qr, 10, 4); // Convert to bitmap image File imgFile = new File("hello-world-QR.png"); // File path for output ImageIO.write(img, "png", imgFile); // Write image to file @@ -74,15 +75,15 @@ public final class QrCodeGeneratorDemo { // Numeric mode encoding (3.33 bits per digit) qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); - writePng(qr.toImage(13, 1), "pi-digits-QR.png"); + writePng(toImage(qr, 13, 1), "pi-digits-QR.png"); // Alphanumeric mode encoding (5.5 bits per character) qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); - writePng(qr.toImage(10, 2), "alphanumeric-QR.png"); + writePng(toImage(qr, 10, 2), "alphanumeric-QR.png"); // Unicode text as UTF-8 qr = QrCode.encodeText("こんにちwa、世界! αβγδ", QrCode.Ecc.QUARTILE); - writePng(qr.toImage(10, 3), "unicode-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-QR.png"); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) qr = QrCode.encodeText( @@ -93,7 +94,7 @@ public final class QrCodeGeneratorDemo { + "for the hot day made her feel very sleepy and stupid), whether the pleasure of making a " + "daisy-chain would be worth the trouble of getting up and picking the daisies, when suddenly " + "a White Rabbit with pink eyes ran close by her.", QrCode.Ecc.HIGH); - writePng(qr.toImage(6, 10), "alice-wonderland-QR.png"); + writePng(toImage(qr, 6, 10), "alice-wonderland-QR.png"); } @@ -106,36 +107,36 @@ public final class QrCodeGeneratorDemo { String silver0 = "THE SQUARE ROOT OF 2 IS 1."; String silver1 = "41421356237309504880168872420969807856967187537694807317667973799"; qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); - writePng(qr.toImage(10, 3), "sqrt2-monolithic-QR.png"); + writePng(toImage(qr, 10, 3), "sqrt2-monolithic-QR.png"); segs = Arrays.asList( QrSegment.makeAlphanumeric(silver0), QrSegment.makeNumeric(silver1)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(qr.toImage(10, 3), "sqrt2-segmented-QR.png"); + writePng(toImage(qr, 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); - writePng(qr.toImage(8, 5), "phi-monolithic-QR.png"); + writePng(toImage(qr, 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); - writePng(qr.toImage(8, 5), "phi-segmented-QR.png"); + writePng(toImage(qr, 8, 5), "phi-segmented-QR.png"); // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); - writePng(qr.toImage(9, 4), "madoka-utf8-QR.png"); + writePng(toImage(qr, 9, 4), "madoka-utf8-QR.png"); segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(qr.toImage(9, 4), "madoka-kanji-QR.png"); + writePng(toImage(qr, 9, 4), "madoka-kanji-QR.png"); } @@ -147,26 +148,57 @@ 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 - writePng(qr.toImage(8, 6), "project-nayuki-automask-QR.png"); + writePng(toImage(qr, 8, 6), "project-nayuki-automask-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 - writePng(qr.toImage(8, 6), "project-nayuki-mask3-QR.png"); + writePng(toImage(qr, 8, 6), "project-nayuki-mask3-QR.png"); // Chinese text as UTF-8 segs = QrSegment.makeSegments("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 - writePng(qr.toImage(10, 3), "unicode-mask0-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask0-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 - writePng(qr.toImage(10, 3), "unicode-mask1-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask1-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 - writePng(qr.toImage(10, 3), "unicode-mask5-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask5-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 - writePng(qr.toImage(10, 3), "unicode-mask7-QR.png"); + writePng(toImage(qr, 10, 3), "unicode-mask7-QR.png"); } /*---- Utilities ----*/ + /** + * Returns a raster image depicting the specified QR Code, with the specified module scale and border modules. + *

      For example, toImage(qr, scale=10, border=4) means to pad the QR Code with 4 light + * border modules on all four sides, and use 10×10 pixels to represent each module. + * The resulting image only contains the hex colors 000000 and FFFFFF. + * @param qr the QR Code to render (not {@code null}) + * @param scale the side length (measured in pixels, must be positive) of each module + * @param border the number of border modules to add, which must be non-negative + * @return a new image representing the QR Code, with padding and scaling + * @throws NullPointerException if the QR Code is {@code null} + * @throws IllegalArgumentException if the scale or border is out of range, or if + * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE + */ + private static BufferedImage toImage(QrCode qr, int scale, int border) { + Objects.requireNonNull(qr); + if (scale <= 0 || border < 0) + throw new IllegalArgumentException("Value out of range"); + if (border > Integer.MAX_VALUE / 2 || qr.size + border * 2L > Integer.MAX_VALUE / scale) + throw new IllegalArgumentException("Scale or border too large"); + + BufferedImage result = new BufferedImage((qr.size + border * 2) * scale, (qr.size + border * 2) * scale, BufferedImage.TYPE_INT_RGB); + for (int y = 0; y < result.getHeight(); y++) { + for (int x = 0; x < result.getWidth(); x++) { + boolean color = qr.getModule(x / scale - border, y / scale - border); + result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); + } + } + return result; + } + + // Helper function to reduce code duplication. private static void writePng(BufferedImage img, String filepath) throws IOException { ImageIO.write(img, "png", new File(filepath)); diff --git a/java/src/main/java/io/nayuki/qrcodegen/package-info.java b/java/src/main/java/io/nayuki/qrcodegen/package-info.java index 124b438..f741b64 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/package-info.java +++ b/java/src/main/java/io/nayuki/qrcodegen/package-info.java @@ -10,7 +10,7 @@ *

    • Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C

    • *
    • Significantly shorter code but more documentation comments compared to competing libraries

    • *
    • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

    • - *
    • Output formats: Raw modules/pixels of the QR symbol, SVG XML string, {@code BufferedImage} raster bitmap

    • + *
    • Output formats: Raw modules/pixels of the QR symbol, SVG XML string

    • *
    • Encodes numeric and special-alphanumeric text in less space than general text

    • *
    • Open source code under the permissive MIT License

    • *
    @@ -35,7 +35,7 @@ *import io.nayuki.qrcodegen.*; * *QrCode qr = QrCode.encodeText("Hello, world!", QrCode.Ecc.MEDIUM); - *BufferedImage img = qr.toImage(4, 10); + *BufferedImage img = toImage(qr, 4, 10); // See QrCodeGeneratorDemo *ImageIO.write(img, "png", new File("qr-code.png")); *

    Manual operation:

    *
    import java.util.List;
    
    From 6cd17bb9fb53792e532dfd74d048cc41568a97ec Mon Sep 17 00:00:00 2001
    From: Project Nayuki 
    Date: Wed, 28 Jul 2021 17:06:27 +0000
    Subject: [PATCH 711/810] In Java version: moved QrCode.toSvgString() out of
     the library and into the runnable demo program, slightly adapted some code,
     updated documentation comments.
    
    ---
     .../main/java/io/nayuki/qrcodegen/QrCode.java | 34 ----------------
     .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 39 ++++++++++++++++++-
     .../io/nayuki/qrcodegen/package-info.java     |  2 +-
     3 files changed, 39 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 49742f7..2fb8b97 100644
    --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java
    +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java
    @@ -284,40 +284,6 @@ public final class QrCode {
     	}
     	
     	
    -	/**
    -	 * 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.
    -	 * @param border the number of border modules to add, which must be non-negative
    -	 * @return a string representing this QR Code as an SVG XML document
    -	 * @throws IllegalArgumentException if the border is negative
    -	 */
    -	public String toSvgString(int border) {
    -		if (border < 0)
    -			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")
    -			.toString();
    -	}
    -	
    -	
     	
     	/*---- Private helper methods for constructor: Drawing function modules ----*/
     	
    diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java
    index 2b6b619..5f70344 100644
    --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java
    +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java
    @@ -62,7 +62,7 @@ public final class QrCodeGeneratorDemo {
     		File imgFile = new File("hello-world-QR.png");   // File path for output
     		ImageIO.write(img, "png", imgFile);              // Write image to file
     		
    -		String svg = qr.toSvgString(4);                  // Convert to SVG XML code
    +		String svg = toSvgString(qr, 4);                 // Convert to SVG XML code
     		File svgFile = new File("hello-world-QR.svg");   // File path for output
     		Files.write(svgFile.toPath(),                    // Write image to file
     			svg.getBytes(StandardCharsets.UTF_8));
    @@ -204,4 +204,41 @@ public final class QrCodeGeneratorDemo {
     		ImageIO.write(img, "png", new File(filepath));
     	}
     	
    +	
    +	/**
    +	 * Returns a string of SVG code for an image depicting the specified QR Code, with the specified
    +	 * number of border modules. The string always uses Unix newlines (\n), regardless of the platform.
    +	 * @param qr the QR Code to render (not {@code null})
    +	 * @param border the number of border modules to add, which must be non-negative
    +	 * @return a string representing the QR Code as an SVG XML document
    +	 * @throws NullPointerException if the QR Code is {@code null}
    +	 * @throws IllegalArgumentException if the border is negative
    +	 */
    +	private static String toSvgString(QrCode qr, int border) {
    +		Objects.requireNonNull(qr);
    +		if (border < 0)
    +			throw new IllegalArgumentException("Border must be non-negative");
    +		long brd = border;
    +		StringBuilder sb = new StringBuilder()
    +			.append("\n")
    +			.append("\n")
    +			.append(String.format("\n",
    +				qr.size + brd * 2))
    +			.append("\t\n")
    +			.append("\t\n")
    +			.append("\n")
    +			.toString();
    +	}
    +	
     }
    diff --git a/java/src/main/java/io/nayuki/qrcodegen/package-info.java b/java/src/main/java/io/nayuki/qrcodegen/package-info.java
    index f741b64..04ff2cd 100644
    --- a/java/src/main/java/io/nayuki/qrcodegen/package-info.java
    +++ b/java/src/main/java/io/nayuki/qrcodegen/package-info.java
    @@ -10,7 +10,7 @@
      *   
  • Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C

  • *
  • Significantly shorter code but more documentation comments compared to competing libraries

  • *
  • Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard

  • - *
  • Output formats: Raw modules/pixels of the QR symbol, SVG XML string

  • + *
  • Output format: Raw modules/pixels of the QR symbol

  • *
  • Encodes numeric and special-alphanumeric text in less space than general text

  • *
  • Open source code under the permissive MIT License

  • *
From 1cc4617d57162d99ba27f04d5075ce2ac08562a7 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:06:31 +0000 Subject: [PATCH 712/810] In TypeScript version: moved QrCode.drawCanvas() out of the library and into demo programs, slightly adapted some code, updated documentation comments. --- Readme.markdown | 2 +- typescript-javascript/qrcodegen-input-demo.ts | 22 +++++++- .../qrcodegen-output-demo.ts | 54 +++++++++++++------ typescript-javascript/qrcodegen.ts | 20 ------- 4 files changed, 59 insertions(+), 39 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 12571c9..0282416 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C), HTML5 canvas (TypeScript/JavaScript only) +* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C) * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License diff --git a/typescript-javascript/qrcodegen-input-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts index ea1c4c6..ec4b83e 100644 --- a/typescript-javascript/qrcodegen-input-demo.ts +++ b/typescript-javascript/qrcodegen-input-demo.ts @@ -92,7 +92,7 @@ namespace app { const scale: number = parseInt(getInput("scale-input").value, 10); if (scale <= 0 || scale > 30) return; - qr.drawCanvas(scale, border, canvas); + drawCanvas(qr, scale, border, canvas); canvas.style.removeProperty("display"); } else { const code: string = qr.toSvgString(border); @@ -148,6 +148,26 @@ namespace app { } + // Draws the given QR Code, with the given module scale and border modules, onto the given HTML + // canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale. + // The drawn image is be purely dark and light, and fully opaque. + // The scale must be a positive integer and the border must be a non-negative integer. + function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, canvas: HTMLCanvasElement): void { + if (scale <= 0 || border < 0) + throw "Value out of range"; + const width: number = (qr.size + border * 2) * scale; + canvas.width = width; + canvas.height = width; + let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; + for (let y = -border; y < qr.size + border; y++) { + for (let x = -border; x < qr.size + border; x++) { + ctx.fillStyle = qr.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + } + + export function handleVersionMinMax(which: "min"|"max"): void { const minElem: HTMLInputElement = getInput("version-min-input"); const maxElem: HTMLInputElement = getInput("version-max-input"); diff --git a/typescript-javascript/qrcodegen-output-demo.ts b/typescript-javascript/qrcodegen-output-demo.ts index ffc4fbc..f509596 100644 --- a/typescript-javascript/qrcodegen-output-demo.ts +++ b/typescript-javascript/qrcodegen-output-demo.ts @@ -46,7 +46,7 @@ namespace app { const text: string = "Hello, world!"; // User-supplied Unicode text const errCorLvl: qrcodegen.QrCode.Ecc = qrcodegen.QrCode.Ecc.LOW; // Error correction level const qr: qrcodegen.QrCode = qrcodegen.QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - qr.drawCanvas(10, 4, appendCanvas("hello-world-QR")); // Draw it on screen + drawCanvas(qr, 10, 4, appendCanvas("hello-world-QR")); // Draw it on screen } @@ -58,15 +58,15 @@ namespace app { // Numeric mode encoding (3.33 bits per digit) qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); - qr.drawCanvas(13, 1, appendCanvas("pi-digits-QR")); + drawCanvas(qr, 13, 1, appendCanvas("pi-digits-QR")); // Alphanumeric mode encoding (5.5 bits per character) qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); - qr.drawCanvas(10, 2, appendCanvas("alphanumeric-QR")); + drawCanvas(qr, 10, 2, appendCanvas("alphanumeric-QR")); // Unicode text as UTF-8 qr = QrCode.encodeText("\u3053\u3093\u306B\u3061wa\u3001\u4E16\u754C\uFF01 \u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE); - qr.drawCanvas(10, 3, appendCanvas("unicode-QR")); + drawCanvas(qr, 10, 3, appendCanvas("unicode-QR")); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) qr = QrCode.encodeText( @@ -77,7 +77,7 @@ namespace app { + "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); - qr.drawCanvas(6, 10, appendCanvas("alice-wonderland-QR")); + drawCanvas(qr, 6, 10, appendCanvas("alice-wonderland-QR")); } @@ -93,32 +93,32 @@ namespace app { const silver0: string = "THE SQUARE ROOT OF 2 IS 1."; const silver1: string = "41421356237309504880168872420969807856967187537694807317667973799"; qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); - qr.drawCanvas(10, 3, appendCanvas("sqrt2-monolithic-QR")); + drawCanvas(qr, 10, 3, appendCanvas("sqrt2-monolithic-QR")); segs = [ QrSegment.makeAlphanumeric(silver0), QrSegment.makeNumeric(silver1)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - qr.drawCanvas(10, 3, appendCanvas("sqrt2-segmented-QR")); + drawCanvas(qr, 10, 3, appendCanvas("sqrt2-segmented-QR")); // Illustration "golden" const golden0: string = "Golden ratio \u03C6 = 1."; const golden1: string = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; const golden2: string = "......"; qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); - qr.drawCanvas(8, 5, appendCanvas("phi-monolithic-QR")); + drawCanvas(qr, 8, 5, appendCanvas("phi-monolithic-QR")); segs = [ QrSegment.makeBytes(toUtf8ByteArray(golden0)), QrSegment.makeNumeric(golden1), QrSegment.makeAlphanumeric(golden2)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - qr.drawCanvas(8, 5, appendCanvas("phi-segmented-QR")); + drawCanvas(qr, 8, 5, appendCanvas("phi-segmented-QR")); // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters const madoka: string = "\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); - qr.drawCanvas(9, 4, appendCanvas("madoka-utf8-QR")); + drawCanvas(qr, 9, 4, appendCanvas("madoka-utf8-QR")); const kanjiCharBits: Array = [ // Kanji mode encoding (13 bits per character) 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, @@ -153,7 +153,7 @@ namespace app { ]; segs = [new QrSegment(QrSegment.Mode.KANJI, kanjiCharBits.length / 13, kanjiCharBits)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - qr.drawCanvas(9, 4, appendCanvas("madoka-kanji-QR")); + drawCanvas(qr, 9, 4, appendCanvas("madoka-kanji-QR")); } @@ -167,21 +167,21 @@ namespace app { // Project Nayuki URL segs = qrcodegen.QrSegment.makeSegments("https://www.nayuki.io/"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask - qr.drawCanvas(8, 6, appendCanvas("project-nayuki-automask-QR")); + drawCanvas(qr, 8, 6, appendCanvas("project-nayuki-automask-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 - qr.drawCanvas(8, 6, appendCanvas("project-nayuki-mask3-QR")); + drawCanvas(qr, 8, 6, appendCanvas("project-nayuki-mask3-QR")); // Chinese text as UTF-8 segs = qrcodegen.QrSegment.makeSegments("\u7DAD\u57FA\u767E\u79D1\uFF08Wikipedia\uFF0C\u8046\u807Di/\u02CCw\u026Ak\u1D7B\u02C8pi\u02D0di.\u0259/\uFF09\u662F\u4E00" + "\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08\u756B"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 - qr.drawCanvas(10, 3, appendCanvas("unicode-mask0-QR")); + drawCanvas(qr, 10, 3, appendCanvas("unicode-mask0-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 - qr.drawCanvas(10, 3, appendCanvas("unicode-mask1-QR")); + drawCanvas(qr, 10, 3, appendCanvas("unicode-mask1-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 - qr.drawCanvas(10, 3, appendCanvas("unicode-mask5-QR")); + drawCanvas(qr, 10, 3, appendCanvas("unicode-mask5-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 - qr.drawCanvas(10, 3, appendCanvas("unicode-mask7-QR")); + drawCanvas(qr, 10, 3, appendCanvas("unicode-mask7-QR")); } @@ -200,6 +200,26 @@ namespace app { } + // Draws the given QR Code, with the given module scale and border modules, onto the given HTML + // canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale. + // The drawn image is be purely dark and light, and fully opaque. + // The scale must be a positive integer and the border must be a non-negative integer. + function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, canvas: HTMLCanvasElement): void { + if (scale <= 0 || border < 0) + throw "Value out of range"; + const width: number = (qr.size + border * 2) * scale; + canvas.width = width; + canvas.height = width; + let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; + for (let y = -border; y < qr.size + border; y++) { + for (let x = -border; x < qr.size + border; x++) { + ctx.fillStyle = qr.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); + } + } + } + + function toUtf8ByteArray(str: string): Array { str = encodeURI(str); let result: Array = []; diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 2d3e717..c0e051c 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -241,26 +241,6 @@ namespace qrcodegen { /*-- Public instance methods --*/ - // Draws this QR Code, with the given module scale and border modules, onto the given HTML - // canvas element. The canvas's width and height is resized to (this.size + border * 2) * scale. - // The drawn image is be purely dark and light, and fully opaque. - // The scale must be a positive integer and the border must be a non-negative integer. - public drawCanvas(scale: int, border: int, canvas: HTMLCanvasElement): void { - if (scale <= 0 || border < 0) - throw "Value out of range"; - const width: int = (this.size + border * 2) * scale; - canvas.width = width; - canvas.height = width; - let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; - for (let y = -border; y < this.size + border; y++) { - for (let x = -border; x < this.size + border; x++) { - ctx.fillStyle = this.getModule(x, y) ? "#000000" : "#FFFFFF"; - ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); - } - } - } - - // Returns a string of SVG code for an image depicting this QR Code, with the given number // of border modules. The string always uses Unix newlines (\n), regardless of the platform. public toSvgString(border: int): string { From 3531fda14fa22b5a252b15f65233a41b629da011 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:02 +0000 Subject: [PATCH 713/810] In TypeScript version: moved QrCode.toSvgString() out of the library and into a demo program, slightly adapted some code, updated documentation comments. --- Readme.markdown | 2 +- typescript-javascript/qrcodegen-input-demo.ts | 26 +++++++++++++++++-- typescript-javascript/qrcodegen.ts | 24 ----------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 0282416..19939e7 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -70,7 +70,7 @@ TypeScript/JavaScript languages: // Simple operation var qr0 = QRC.encodeText("Hello, world!", QRC.Ecc.MEDIUM); - var svg = qr0.toSvgString(4); + var svg = toSvgString(qr0, 4); // See qrcodegen-input-demo // Manual operation var segs = qrcodegen.QrSegment.makeSegments("3141592653589793238462643383"); diff --git a/typescript-javascript/qrcodegen-input-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts index ec4b83e..2de8341 100644 --- a/typescript-javascript/qrcodegen-input-demo.ts +++ b/typescript-javascript/qrcodegen-input-demo.ts @@ -95,13 +95,13 @@ namespace app { drawCanvas(qr, scale, border, canvas); canvas.style.removeProperty("display"); } else { - const code: string = qr.toSvgString(border); + const code: string = toSvgString(qr, border); const viewBox: string = (/ viewBox="([^"]*)"/.exec(code) as RegExpExecArray)[1]; const pathD: string = (/ d="([^"]*)"/.exec(code) as RegExpExecArray)[1]; svg.setAttribute("viewBox", viewBox); (svg.querySelector("path") as Element).setAttribute("d", pathD); svg.style.removeProperty("display"); - svgXml.value = qr.toSvgString(border); + svgXml.value = code; } // Returns a string to describe the given list of segments. @@ -168,6 +168,28 @@ namespace app { } + // Returns a string of SVG code for an image depicting the given QR Code, with the given number + // of border modules. The string always uses Unix newlines (\n), regardless of the platform. + function toSvgString(qr: qrcodegen.QrCode, border: number): string { + if (border < 0) + throw "Border must be non-negative"; + let parts: Array = []; + for (let y = 0; y < qr.size; y++) { + for (let x = 0; x < qr.size; x++) { + if (qr.getModule(x, y)) + parts.push(`M${x + border},${y + border}h1v1h-1z`); + } + } + return ` + + + + + +` + } + + export function handleVersionMinMax(which: "min"|"max"): void { const minElem: HTMLInputElement = getInput("version-min-input"); const maxElem: HTMLInputElement = getInput("version-max-input"); diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index c0e051c..053770c 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -239,30 +239,6 @@ namespace qrcodegen { } - /*-- Public instance methods --*/ - - // Returns a string of SVG code for an image depicting this QR Code, with the given number - // of border modules. The string always uses Unix newlines (\n), regardless of the platform. - public toSvgString(border: int): string { - if (border < 0) - throw "Border must be non-negative"; - let parts: Array = []; - for (let y = 0; y < this.size; y++) { - for (let x = 0; x < this.size; x++) { - if (this.getModule(x, y)) - parts.push(`M${x + border},${y + border}h1v1h-1z`); - } - } - return ` - - - - - -` - } - - /*-- Private helper methods for constructor: Drawing function modules --*/ // Reads this object's version field, and draws and marks all function modules. From 3dcac1db78c39f024878bee4a687ffef209b42b9 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:05 +0000 Subject: [PATCH 714/810] In Python version: moved QrCode.to_svg_string() out of the library and into the runnable demo program, slightly adapted some code, updated documentation comments. --- Readme.markdown | 2 +- python/qrcodegen-demo.py | 21 ++++++++++++++++++++- python/qrcodegen.py | 21 --------------------- python/setup.py | 4 ++-- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 19939e7..a060ca7 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -87,7 +87,7 @@ Python language: # Simple operation qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) - svg = qr0.to_svg_str(4) + svg = to_svg_str(qr0, 4) # See qrcodegen-demo # Manual operation segs = QrSegment.make_segments("3141592653589793238462643383") diff --git a/python/qrcodegen-demo.py b/python/qrcodegen-demo.py index 159dd56..ef6c098 100644 --- a/python/qrcodegen-demo.py +++ b/python/qrcodegen-demo.py @@ -46,7 +46,7 @@ def do_basic_demo() -> None: # Make and print the QR Code symbol qr = QrCode.encode_text(text, errcorlvl) print_qr(qr) - print(qr.to_svg_str(4)) + print(to_svg_str(qr, 4)) def do_variety_demo() -> None: @@ -170,6 +170,25 @@ def do_mask_demo() -> None: # ---- Utilities ---- +def to_svg_str(qr: QrCode, border: int) -> str: + """Returns a string of SVG code for an image depicting the given QR Code, with the given number + of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" + if border < 0: + raise ValueError("Border must be non-negative") + parts: List[str] = [] + for y in range(qr.get_size()): + for x in range(qr.get_size()): + if qr.get_module(x, y): + parts.append("M{},{}h1v1h-1z".format(x + border, y + border)) + return """ + + + + + +""".format(qr.get_size() + border * 2, " ".join(parts)) + + def print_qr(qrcode: QrCode) -> None: """Prints the given QrCode object to the console.""" border = 4 diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 7cbea57..48bf020 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -232,27 +232,6 @@ class QrCode: return (0 <= x < self._size) and (0 <= y < self._size) and self._modules[y][x] - # ---- Public instance methods ---- - - def to_svg_str(self, border: int) -> str: - """Returns a string of SVG code for an image depicting this QR Code, with the given number - of border modules. The string always uses Unix newlines (\n), regardless of the platform.""" - if border < 0: - raise ValueError("Border must be non-negative") - parts: List[str] = [] - for y in range(self._size): - for x in range(self._size): - if self.get_module(x, y): - parts.append("M{},{}h1v1h-1z".format(x + border, y + border)) - return """ - - - - - -""".format(self._size + border * 2, " ".join(parts)) - - # ---- Private helper methods for constructor: Drawing function modules ---- def _draw_function_patterns(self) -> None: diff --git a/python/setup.py b/python/setup.py index 50272bd..787c79a 100644 --- a/python/setup.py +++ b/python/setup.py @@ -69,7 +69,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Output format: Raw modules/pixels of the QR symbol * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License @@ -94,7 +94,7 @@ Examples: # Simple operation qr0 = QrCode.encode_text("Hello, world!", QrCode.Ecc.MEDIUM) - svg = qr0.to_svg_str(4) + svg = to_svg_str(qr0, 4) # See qrcodegen-demo # Manual operation segs = QrSegment.make_segments("3141592653589793238462643383") From 5bc7bce3c3a5cce9b7bb67522e67f7c8111224ce Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:05 +0000 Subject: [PATCH 715/810] In Rust version: moved QrCode.to_svg_string() out of the library and into the runnable demo program, slightly adapted some code, updated documentation comments. --- Readme.markdown | 2 +- rust/Readme.markdown | 4 ++-- rust/examples/qrcodegen-demo.rs | 31 +++++++++++++++++++++++++++++- rust/src/lib.rs | 34 ++------------------------------- 4 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index a060ca7..6fe5a38 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -158,7 +158,7 @@ Rust language: // Simple operation let qr = QrCode::encode_text("Hello, world!", QrCodeEcc::Medium).unwrap(); - let svg = qr.to_svg_string(4); + let svg = to_svg_string(&qr, 4); // See qrcodegen-demo // Manual operation let chrs: Vec = "3141592653589793238462643383".chars().collect(); diff --git a/rust/Readme.markdown b/rust/Readme.markdown index 7a20c4a..8c5309b 100644 --- a/rust/Readme.markdown +++ b/rust/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol, SVG XML string +* Output format: Raw modules/pixels of the QR symbol * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License @@ -42,7 +42,7 @@ Examples // Simple operation let qr = QrCode::encode_text("Hello, world!", QrCodeEcc::Medium).unwrap(); - let svg = qr.to_svg_string(4); + let svg = to_svg_string(&qr, 4); // See qrcodegen-demo // Manual operation let chrs: Vec = "3141592653589793238462643383".chars().collect(); diff --git a/rust/examples/qrcodegen-demo.rs b/rust/examples/qrcodegen-demo.rs index 91fee9d..ed37c65 100644 --- a/rust/examples/qrcodegen-demo.rs +++ b/rust/examples/qrcodegen-demo.rs @@ -52,7 +52,7 @@ fn do_basic_demo() { // Make and print the QR Code symbol let qr: QrCode = QrCode::encode_text(text, errcorlvl).unwrap(); print_qr(&qr); - println!("{}", qr.to_svg_string(4)); + println!("{}", to_svg_string(&qr, 4)); } @@ -163,6 +163,35 @@ fn do_mask_demo() { /*---- Utilities ----*/ +// Returns a string of SVG code for an image depicting +// the given QR Code, with the given number of border modules. +// The string always uses Unix newlines (\n), regardless of the platform. +fn to_svg_string(qr: &QrCode, border: i32) -> String { + assert!(border >= 0, "Border must be non-negative"); + let mut result = String::new(); + result += "\n"; + result += "\n"; + let dimension = qr.size().checked_add(border.checked_mul(2).unwrap()).unwrap(); + result += &format!( + "\n", dimension); + result += "\t\n"; + result += "\t\n"; + result += "\n"; + result +} + + // Prints the given QrCode object to the console. fn print_qr(qr: &QrCode) { let border: i32 = 4; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 2999e29..546f0a8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -38,7 +38,7 @@ //! - Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C //! - Significantly shorter code but more documentation comments compared to competing libraries //! - Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -//! - Output formats: Raw modules/pixels of the QR symbol, SVG XML string +//! - Output format: Raw modules/pixels of the QR symbol //! - Detects finder-like penalty patterns more accurately than other implementations //! - Encodes numeric and special-alphanumeric text in less space than general text //! - Open source code under the permissive MIT License @@ -64,7 +64,7 @@ //! ``` //! let qr = QrCode::encode_text("Hello, world!", //! QrCodeEcc::Medium).unwrap(); -//! let svg = qr.to_svg_string(4); +//! let svg = to_svg_string(&qr, 4); // See qrcodegen-demo //! ``` //! //! Manual operation: @@ -368,36 +368,6 @@ impl QrCode { } - /// Returns a string of SVG code for an image depicting - /// this QR Code, with the given number of border modules. - /// - /// The string always uses Unix newlines (\n), regardless of the platform. - pub fn to_svg_string(&self, border: i32) -> String { - assert!(border >= 0, "Border must be non-negative"); - let mut result = String::new(); - result += "\n"; - result += "\n"; - let dimension = self.size.checked_add(border.checked_mul(2).unwrap()).unwrap(); - result += &format!( - "\n", dimension); - result += "\t\n"; - result += "\t\n"; - result += "\n"; - result - } - - /*---- Private helper methods for constructor: Drawing function modules ----*/ // Reads this object's version field, and draws and marks all function modules. From cd037d914920420f611e8ec06e4f2fec33e6f61d Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:16 +0000 Subject: [PATCH 716/810] In C++ version: moved QrCode.toSvgString() out of the library and into the runnable demo program, slightly adapted some code, updated documentation comments. --- Readme.markdown | 4 ++-- cpp/QrCode.cpp | 28 ---------------------------- cpp/QrCode.hpp | 7 ------- cpp/QrCodeGeneratorDemo.cpp | 35 ++++++++++++++++++++++++++++++++++- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/Readme.markdown b/Readme.markdown index 6fe5a38..c48fb61 100644 --- a/Readme.markdown +++ b/Readme.markdown @@ -18,7 +18,7 @@ Core features: * Available in 6 programming languages, all with nearly equal functionality: Java, TypeScript/JavaScript, Python, Rust, C++, C * Significantly shorter code but more documentation comments compared to competing libraries * Supports encoding all 40 versions (sizes) and all 4 error correction levels, as per the QR Code Model 2 standard -* Output formats: Raw modules/pixels of the QR symbol (all languages), SVG XML string (all languages except C) +* Output format: Raw modules/pixels of the QR symbol * Detects finder-like penalty patterns more accurately than other implementations * Encodes numeric and special-alphanumeric text in less space than general text * Open source code under the permissive MIT License @@ -105,7 +105,7 @@ C++ language: // Simple operation QrCode qr0 = QrCode::encodeText("Hello, world!", QrCode::Ecc::MEDIUM); - std::string svg = qr0.toSvgString(4); + std::string svg = toSvgString(qr0, 4); // See QrCodeGeneratorDemo // Manual operation std::vector segs = diff --git a/cpp/QrCode.cpp b/cpp/QrCode.cpp index 538204e..ada5969 100644 --- a/cpp/QrCode.cpp +++ b/cpp/QrCode.cpp @@ -385,34 +385,6 @@ bool QrCode::getModule(int x, int y) const { } -std::string QrCode::toSvgString(int border) const { - if (border < 0) - throw std::domain_error("Border must be non-negative"); - if (border > INT_MAX / 2 || border * 2 > INT_MAX - size) - throw std::overflow_error("Border too large"); - - std::ostringstream sb; - sb << "\n"; - sb << "\n"; - sb << "\n"; - sb << "\t\n"; - sb << "\t\n"; - sb << "\n"; - return sb.str(); -} - - void QrCode::drawFunctionPatterns() { // Draw horizontal and vertical timing patterns for (int i = 0; i < size; i++) { diff --git a/cpp/QrCode.hpp b/cpp/QrCode.hpp index 2a1e987..8a71501 100644 --- a/cpp/QrCode.hpp +++ b/cpp/QrCode.hpp @@ -369,13 +369,6 @@ class QrCode final { public: bool getModule(int x, int y) const; - /* - * Returns a string of SVG code for an image depicting this QR Code, with the given number - * of border modules. The string always uses Unix newlines (\n), regardless of the platform. - */ - public: std::string toSvgString(int border) const; - - /*---- Private helper methods for constructor: Drawing function modules ----*/ diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 017dcbe..3419902 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -24,10 +24,12 @@ * Software. */ +#include #include #include #include #include +#include #include #include #include "QrCode.hpp" @@ -42,6 +44,7 @@ static void doBasicDemo(); static void doVarietyDemo(); static void doSegmentDemo(); static void doMaskDemo(); +static std::string toSvgString(const QrCode &qr, int border); static void printQr(const QrCode &qr); @@ -66,7 +69,7 @@ static void doBasicDemo() { // Make and print the QR Code symbol const QrCode qr = QrCode::encodeText(text, errCorLvl); printQr(qr); - std::cout << qr.toSvgString(4) << std::endl; + std::cout << toSvgString(qr, 4) << std::endl; } @@ -186,6 +189,36 @@ static void doMaskDemo() { /*---- Utilities ----*/ +// Returns a string of SVG code for an image depicting the given QR Code, with the given number +// of border modules. The string always uses Unix newlines (\n), regardless of the platform. +static std::string toSvgString(const QrCode &qr, int border) { + if (border < 0) + throw std::domain_error("Border must be non-negative"); + if (border > INT_MAX / 2 || border * 2 > INT_MAX - qr.getSize()) + throw std::overflow_error("Border too large"); + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + return sb.str(); +} + + // Prints the given QrCode object to the console. static void printQr(const QrCode &qr) { int border = 4; From 89895daf1d3d84ecdae0150f97d33ed879e9ea49 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:18 +0000 Subject: [PATCH 717/810] Changed Java classpath in Python batch tester to match Maven's convention. --- python/qrcodegen-batch-test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py index babd9d8..e24c07e 100644 --- a/python/qrcodegen-batch-test.py +++ b/python/qrcodegen-batch-test.py @@ -30,7 +30,7 @@ from typing import List, Optional, TypeVar CHILD_PROGRAMS: List[List[str]] = [ ["python3", "-B", "../python/qrcodegen-worker.py"], # Python program - ["java", "-cp", "../java/src/main/java", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program + ["java", "-cp", "../java/bin", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program ["../c/qrcodegen-worker"], # C program ["../cpp/QrCodeGeneratorWorker"], # C++ program From d4080974f21f317e23192ec45ec00c293b17d74f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:18 +0000 Subject: [PATCH 718/810] Renamed the C++ library source files to qrcodegen, because there's no longer a file per class, related to commit 096c70cd4d4b. --- cpp/Makefile | 2 +- cpp/QrCodeGeneratorDemo.cpp | 2 +- cpp/QrCodeGeneratorWorker.cpp | 2 +- cpp/{QrCode.cpp => qrcodegen.cpp} | 2 +- cpp/{QrCode.hpp => qrcodegen.hpp} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename cpp/{QrCode.cpp => qrcodegen.cpp} (99%) rename cpp/{QrCode.hpp => qrcodegen.hpp} (100%) diff --git a/cpp/Makefile b/cpp/Makefile index 374bfe9..0a8fa6e 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -52,7 +52,7 @@ CXXFLAGS += -std=c++11 -O LIB = qrcodegen LIBFILE = lib$(LIB).a -LIBOBJ = QrCode.o +LIBOBJ = qrcodegen.o MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker # Build all binaries diff --git a/cpp/QrCodeGeneratorDemo.cpp b/cpp/QrCodeGeneratorDemo.cpp index 3419902..b64bf5e 100644 --- a/cpp/QrCodeGeneratorDemo.cpp +++ b/cpp/QrCodeGeneratorDemo.cpp @@ -32,7 +32,7 @@ #include #include #include -#include "QrCode.hpp" +#include "qrcodegen.hpp" using std::uint8_t; using qrcodegen::QrCode; diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp index 073107f..5c5342b 100644 --- a/cpp/QrCodeGeneratorWorker.cpp +++ b/cpp/QrCodeGeneratorWorker.cpp @@ -32,7 +32,7 @@ #include #include #include -#include "QrCode.hpp" +#include "qrcodegen.hpp" using qrcodegen::QrCode; using qrcodegen::QrSegment; diff --git a/cpp/QrCode.cpp b/cpp/qrcodegen.cpp similarity index 99% rename from cpp/QrCode.cpp rename to cpp/qrcodegen.cpp index ada5969..e8a6a1a 100644 --- a/cpp/QrCode.cpp +++ b/cpp/qrcodegen.cpp @@ -29,7 +29,7 @@ #include #include #include -#include "QrCode.hpp" +#include "qrcodegen.hpp" using std::int8_t; using std::uint8_t; diff --git a/cpp/QrCode.hpp b/cpp/qrcodegen.hpp similarity index 100% rename from cpp/QrCode.hpp rename to cpp/qrcodegen.hpp From 22612489576d94250b7c7edb082354a31f2a6482 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:09:18 +0000 Subject: [PATCH 719/810] Removed the test worker program in every language and the Python batch tester, because this is not core functionality and is hard to explain. --- c/Makefile | 2 +- c/qrcodegen-worker.c | 118 -------------- cpp/Makefile | 2 +- cpp/QrCodeGeneratorWorker.cpp | 101 ------------ .../qrcodegen/QrCodeGeneratorWorker.java | 100 ------------ python/qrcodegen-batch-test.py | 144 ------------------ python/qrcodegen-worker.py | 82 ---------- rust/examples/qrcodegen-worker.rs | 113 -------------- typescript-javascript/build.sh | 5 - typescript-javascript/qrcodegen-worker.ts | 130 ---------------- 10 files changed, 2 insertions(+), 795 deletions(-) delete mode 100644 c/qrcodegen-worker.c delete mode 100644 cpp/QrCodeGeneratorWorker.cpp delete mode 100644 java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java delete mode 100644 python/qrcodegen-batch-test.py delete mode 100644 python/qrcodegen-worker.py delete mode 100644 rust/examples/qrcodegen-worker.rs delete mode 100644 typescript-javascript/qrcodegen-worker.ts diff --git a/c/Makefile b/c/Makefile index 314d175..661b1f7 100644 --- a/c/Makefile +++ b/c/Makefile @@ -53,7 +53,7 @@ CFLAGS += -std=c99 -O LIB = qrcodegen LIBFILE = lib$(LIB).a LIBOBJ = qrcodegen.o -MAINS = qrcodegen-demo qrcodegen-test qrcodegen-worker +MAINS = qrcodegen-demo qrcodegen-test # Build all binaries all: $(LIBFILE) $(MAINS) diff --git a/c/qrcodegen-worker.c b/c/qrcodegen-worker.c deleted file mode 100644 index dc16888..0000000 --- a/c/qrcodegen-worker.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * QR Code generator test worker (C) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include -#include -#include "qrcodegen.h" - - -int main(void) { - while (true) { - - // Read data length or exit - size_t length; - { - int temp; - if (scanf("%d", &temp) != 1) - return EXIT_FAILURE; - if (temp == -1) - break; - length = (size_t)temp; - } - - // Read data bytes - bool isAscii = true; - uint8_t *data = malloc(length * sizeof(uint8_t)); - if (data == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - for (size_t i = 0; i < length; i++) { - int b; - if (scanf("%d", &b) != 1) - return EXIT_FAILURE; - data[i] = (uint8_t)b; - isAscii &= 0 < b && b < 128; - } - - // Read encoding parameters - int errCorLvl, minVersion, maxVersion, mask, boostEcl; - if (scanf("%d %d %d %d %d", &errCorLvl, &minVersion, &maxVersion, &mask, &boostEcl) != 5) - return EXIT_FAILURE; - - // Allocate memory for QR Code - size_t bufferLen = (size_t)qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion); - uint8_t *qrcode = malloc(bufferLen * sizeof(uint8_t)); - uint8_t *tempBuffer = malloc(bufferLen * sizeof(uint8_t)); - if (qrcode == NULL || tempBuffer == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - - // Try to make QR Code symbol - bool ok; - if (isAscii) { - char *text = malloc((length + 1) * sizeof(char)); - if (text == NULL) { - perror("malloc"); - return EXIT_FAILURE; - } - for (size_t i = 0; i < length; i++) - text[i] = (char)data[i]; - text[length] = '\0'; - ok = qrcodegen_encodeText(text, tempBuffer, qrcode, (enum qrcodegen_Ecc)errCorLvl, - minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); - free(text); - } else if (length <= bufferLen) { - memcpy(tempBuffer, data, length * sizeof(data[0])); - ok = qrcodegen_encodeBinary(tempBuffer, length, qrcode, (enum qrcodegen_Ecc)errCorLvl, - minVersion, maxVersion, (enum qrcodegen_Mask)mask, boostEcl == 1); - } else - ok = false; - free(data); - free(tempBuffer); - - if (ok) { - // Print grid of modules - int size = qrcodegen_getSize(qrcode); - printf("%d\n", (size - 17) / 4); - for (int y = 0; y < size; y++) { - for (int x = 0; x < size; x++) - printf("%d\n", qrcodegen_getModule(qrcode, x, y) ? 1 : 0); - } - } else - printf("-1\n"); - free(qrcode); - fflush(stdout); - } - return EXIT_SUCCESS; -} diff --git a/cpp/Makefile b/cpp/Makefile index 0a8fa6e..cdcb809 100644 --- a/cpp/Makefile +++ b/cpp/Makefile @@ -53,7 +53,7 @@ CXXFLAGS += -std=c++11 -O LIB = qrcodegen LIBFILE = lib$(LIB).a LIBOBJ = qrcodegen.o -MAINS = QrCodeGeneratorDemo QrCodeGeneratorWorker +MAINS = QrCodeGeneratorDemo # Build all binaries all: $(LIBFILE) $(MAINS) diff --git a/cpp/QrCodeGeneratorWorker.cpp b/cpp/QrCodeGeneratorWorker.cpp deleted file mode 100644 index 5c5342b..0000000 --- a/cpp/QrCodeGeneratorWorker.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * QR Code generator test worker (C++) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -#include -#include -#include -#include -#include -#include -#include "qrcodegen.hpp" - -using qrcodegen::QrCode; -using qrcodegen::QrSegment; - - -static const std::vector ECC_LEVELS{ - QrCode::Ecc::LOW, - QrCode::Ecc::MEDIUM, - QrCode::Ecc::QUARTILE, - QrCode::Ecc::HIGH, -}; - - -int main() { - while (true) { - - // Read data length or exit - int length; - std::cin >> length; - if (length == -1) - break; - - // Read data bytes - bool isAscii = true; - std::vector data; - for (int i = 0; i < length; i++) { - int b; - std::cin >> b; - data.push_back(static_cast(b)); - isAscii &= 0 < b && b < 128; - } - - // Read encoding parameters - int errCorLvl, minVersion, maxVersion, mask, boostEcl; - std::cin >> errCorLvl; - std::cin >> minVersion; - std::cin >> maxVersion; - std::cin >> mask; - std::cin >> boostEcl; - - // Make list of segments - std::vector segs; - if (isAscii) { - std::vector text(data.cbegin(), data.cend()); - text.push_back('\0'); - segs = QrSegment::makeSegments(text.data()); - } else - segs.push_back(QrSegment::makeBytes(data)); - - try { // Try to make QR Code symbol - const QrCode qr = QrCode::encodeSegments(segs, - ECC_LEVELS.at(static_cast(errCorLvl)), minVersion, maxVersion, mask, boostEcl == 1); - // Print grid of modules - std::cout << qr.getVersion() << std::endl; - for (int y = 0; y < qr.getSize(); y++) { - for (int x = 0; x < qr.getSize(); x++) - std::cout << (qr.getModule(x, y) ? 1 : 0) << std::endl; - } - - } catch (const qrcodegen::data_too_long &ex) { - std::cout << -1 << std::endl; - } - std::cout << std::flush; - } - return EXIT_SUCCESS; -} diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java deleted file mode 100644 index 4cf9de3..0000000 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorWorker.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * QR Code generator test worker (Java) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -package io.nayuki.qrcodegen; - -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.List; -import java.util.Scanner; - - -public final class QrCodeGeneratorWorker { - - public static void main(String[] args) { - // Set up input stream and start loop - try (Scanner input = new Scanner(System.in, "US-ASCII")) { - input.useDelimiter("\r\n|\n|\r"); - while (processCase(input)); - } - } - - - private static boolean processCase(Scanner input) { - // Read data length or exit - int length = input.nextInt(); - if (length == -1) - return false; - if (length > Short.MAX_VALUE) - throw new RuntimeException(); - - // Read data bytes - boolean isAscii = true; - byte[] data = new byte[length]; - for (int i = 0; i < data.length; i++) { - int b = input.nextInt(); - if (b < 0 || b > 255) - throw new RuntimeException(); - data[i] = (byte)b; - isAscii &= b < 128; - } - - // Read encoding parameters - int errCorLvl = input.nextInt(); - int minVersion = input.nextInt(); - int maxVersion = input.nextInt(); - int mask = input.nextInt(); - int boostEcl = input.nextInt(); - if (!(0 <= errCorLvl && errCorLvl <= 3) || !(-1 <= mask && mask <= 7) || (boostEcl >>> 1) != 0 - || !(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION)) - throw new RuntimeException(); - - // Make segments for encoding - List segs; - if (isAscii) - segs = QrSegment.makeSegments(new String(data, StandardCharsets.US_ASCII)); - else - 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); - // Print grid of modules - System.out.println(qr.version); - for (int y = 0; y < qr.size; y++) { - for (int x = 0; x < qr.size; x++) - System.out.println(qr.getModule(x, y) ? 1 : 0); - } - - } catch (DataTooLongException e) { - System.out.println(-1); - } - System.out.flush(); - return true; - } - -} diff --git a/python/qrcodegen-batch-test.py b/python/qrcodegen-batch-test.py deleted file mode 100644 index e24c07e..0000000 --- a/python/qrcodegen-batch-test.py +++ /dev/null @@ -1,144 +0,0 @@ -# -# QR Code generator batch test (Python) -# -# Runs various versions of the QR Code generator test worker as subprocesses, -# feeds each one the same random input, and compares their output for equality. -# -# Copyright (c) Project Nayuki. (MIT License) -# https://www.nayuki.io/page/qr-code-generator-library -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# - The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# - The Software is provided "as is", without warranty of any kind, express or -# implied, including but not limited to the warranties of merchantability, -# fitness for a particular purpose and noninfringement. In no event shall the -# authors or copyright holders be liable for any claim, damages or other -# liability, whether in an action of contract, tort or otherwise, arising from, -# out of or in connection with the Software or the use or other dealings in the -# Software. -# - -import itertools, random, subprocess, sys, time -from typing import List, Optional, TypeVar - - -CHILD_PROGRAMS: List[List[str]] = [ - ["python3", "-B", "../python/qrcodegen-worker.py"], # Python program - ["java", "-cp", "../java/bin", "-ea:io.nayuki.qrcodegen...", "io/nayuki/qrcodegen/QrCodeGeneratorWorker"], # Java program - ["node", "../typescript-javascript/qrcodegen-worker.js"], # TypeScript program - ["../c/qrcodegen-worker"], # C program - ["../cpp/QrCodeGeneratorWorker"], # C++ program - ["../rust/target/debug/examples/qrcodegen-worker"], # Rust program -] - - -subprocs: List[subprocess.Popen] = [] - -def main() -> None: - # Launch workers - global subprocs - try: - for args in CHILD_PROGRAMS: - subprocs.append(subprocess.Popen(args, universal_newlines=True, - stdin=subprocess.PIPE, stdout=subprocess.PIPE)) - except FileNotFoundError: - write_all(-1) - raise - - # Check if any died - time.sleep(0.3) - if any(proc.poll() is not None for proc in subprocs): - for proc in subprocs: - if proc.poll() is None: - print(-1, file=proc.stdin) - not_none(proc.stdin).flush() - sys.exit("Error: One or more workers failed to start") - - # Do tests - for i in itertools.count(): - print("Trial {}: ".format(i), end="") - do_trial() - print() - - -def do_trial() -> None: - mode: int = random.randrange(4) - length: int - data: List[int] - if mode == 0: # Numeric - length = round((2 * 7089) ** random.random()) - data = random.choices(b"0123456789", k=length) - elif mode == 1: # Alphanumeric - length = round((2 * 4296) ** random.random()) - data = random.choices(b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", k=length) - elif mode == 2: # ASCII - length = round((2 * 2953) ** random.random()) - data = [random.randrange(128) for _ in range(length)] - elif mode == 3: # Byte - length = round((2 * 2953) ** random.random()) - data = [random.randrange(256) for _ in range(length)] - else: - raise AssertionError() - - write_all(length) - for b in data: - write_all(b) - - errcorlvl: int = random.randrange(4) - minversion: int = random.randint(1, 40) - maxversion: int = random.randint(1, 40) - if minversion > maxversion: - minversion, maxversion = maxversion, minversion - mask: int = -1 - if random.random() < 0.5: - mask = random.randrange(8) - boostecl = int(random.random() < 0.2) - print("mode={} len={} ecl={} minv={} maxv={} mask={} boost={}".format(mode, length, errcorlvl, minversion, maxversion, mask, boostecl), end="") - - write_all(errcorlvl) - write_all(minversion) - write_all(maxversion) - write_all(mask) - write_all(boostecl) - flush_all() - - version: int = read_verify() - print(" version={}".format(version), end="") - if version == -1: - return - size: int = version * 4 + 17 - for _ in range(size**2): - read_verify() - - -def write_all(val: int) -> None: - for proc in subprocs: - print(val, file=proc.stdin) - -def flush_all() -> None: - for proc in subprocs: - not_none(proc.stdin).flush() - -def read_verify() -> int: - val: str = not_none(subprocs[0].stdout).readline().rstrip("\r\n") - for proc in subprocs[1 : ]: - if not_none(proc.stdout).readline().rstrip("\r\n") != val: - raise ValueError("Mismatch") - return int(val) - - -T = TypeVar("T") -def not_none(obj: Optional[T]) -> T: - if obj is None: - raise TypeError() - return obj - - -if __name__ == "__main__": - main() diff --git a/python/qrcodegen-worker.py b/python/qrcodegen-worker.py deleted file mode 100644 index 7ea4a65..0000000 --- a/python/qrcodegen-worker.py +++ /dev/null @@ -1,82 +0,0 @@ -# -# QR Code generator test worker (Python) -# -# This program reads data and encoding parameters from standard input and writes -# QR Code bitmaps to standard output. The I/O format is one integer per line. -# Run with no command line arguments. The program is intended for automated -# batch testing of end-to-end functionality of this QR Code generator library. -# -# Copyright (c) Project Nayuki. (MIT License) -# https://www.nayuki.io/page/qr-code-generator-library -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -# the Software, and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# - The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# - The Software is provided "as is", without warranty of any kind, express or -# implied, including but not limited to the warranties of merchantability, -# fitness for a particular purpose and noninfringement. In no event shall the -# authors or copyright holders be liable for any claim, damages or other -# liability, whether in an action of contract, tort or otherwise, arising from, -# out of or in connection with the Software or the use or other dealings in the -# Software. -# - -import sys -from typing import List, Sequence -import qrcodegen - - -def read_int() -> int: - return int(input()) - - -def main() -> None: - while True: - - # Read data or exit - length: int = read_int() - if length == -1: - break - data = bytearray(read_int() for _ in range(length)) - - # Read encoding parameters - errcorlvl : int = read_int() - minversion: int = read_int() - maxversion: int = read_int() - mask : int = read_int() - boostecl : int = read_int() - - # Make segments for encoding - if all((b < 128) for b in data): # Is ASCII - segs: List[qrcodegen.QrSegment] = qrcodegen.QrSegment.make_segments(data.decode("ASCII")) - else: - segs = [qrcodegen.QrSegment.make_bytes(data)] - - try: # Try to make QR Code symbol - qr: qrcodegen.QrCode = qrcodegen.QrCode.encode_segments(segs, ECC_LEVELS[errcorlvl], minversion, maxversion, mask, boostecl != 0) - # Print grid of modules - print(qr.get_version()) - for y in range(qr.get_size()): - for x in range(qr.get_size()): - print(1 if qr.get_module(x, y) else 0) - - except qrcodegen.DataTooLongError: - print(-1) - sys.stdout.flush() - - -ECC_LEVELS: Sequence[qrcodegen.QrCode.Ecc] = ( - qrcodegen.QrCode.Ecc.LOW, - qrcodegen.QrCode.Ecc.MEDIUM, - qrcodegen.QrCode.Ecc.QUARTILE, - qrcodegen.QrCode.Ecc.HIGH, -) - - -if __name__ == "__main__": - main() diff --git a/rust/examples/qrcodegen-worker.rs b/rust/examples/qrcodegen-worker.rs deleted file mode 100644 index 7238392..0000000 --- a/rust/examples/qrcodegen-worker.rs +++ /dev/null @@ -1,113 +0,0 @@ -/* - * QR Code generator test worker (Rust) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -extern crate qrcodegen; -use qrcodegen::Mask; -use qrcodegen::QrCode; -use qrcodegen::QrCodeEcc; -use qrcodegen::QrSegment; -use qrcodegen::Version; - - -fn main() { - loop { - - // Read data length or exit - let length: i16 = read_int(); - if length == -1 { - break; - } - - // Read data bytes - let mut data = Vec::::with_capacity(length as usize); - for _ in 0 .. length { - let b: i16 = read_int(); - assert_eq!(i16::from(b as u8), b, "Byte value out of range"); - data.push(b as u8); - } - let isascii: bool = data.iter().all(|b| *b < 128); - - // Read encoding parameters - let errcorlvl = read_int(); - let minversion = read_int(); - let maxversion = read_int(); - let mask = read_int(); - let boostecl = read_int(); - assert!(0 <= errcorlvl && errcorlvl <= 3); - assert!(i16::from(Version::MIN.value()) <= minversion - && minversion <= maxversion - && maxversion <= i16::from(Version::MAX.value())); - assert!(-1 <= mask && mask <= 7); - assert!(boostecl >> 1 == 0); - - // Make segments for encoding - let segs: Vec = if isascii { - let chrs: Vec = std::str::from_utf8(&data).unwrap().chars().collect(); - QrSegment::make_segments(&chrs) - } else { - vec![QrSegment::make_bytes(&data)] - }; - - // Try to make QR Code symbol - let msk = if mask == -1 { None } else { Some(Mask::new(mask as u8)) }; - match QrCode::encode_segments_advanced(&segs, ECC_LEVELS[errcorlvl as usize], - Version::new(minversion as u8), Version::new(maxversion as u8), msk, boostecl != 0) { - - Ok(qr) => { - // Print grid of modules - println!("{}", qr.version().value()); - for y in 0 .. qr.size() { - for x in 0 .. qr.size() { - println!("{}", qr.get_module(x, y) as i8); - } - } - }, - Err(_) => println!("-1"), - } - use std::io::Write; - std::io::stdout().flush().unwrap(); - } -} - - -fn read_int() -> i16 { - let mut line = String::new(); - std::io::stdin().read_line(&mut line).unwrap(); - let mut chrs: Vec = line.chars().collect(); - assert_eq!(chrs.pop().unwrap(), '\n'); - let line: String = chrs.iter().cloned().collect(); - line.parse::().expect("Invalid number") -} - - -static ECC_LEVELS: [QrCodeEcc; 4] = [ - QrCodeEcc::Low, - QrCodeEcc::Medium, - QrCodeEcc::Quartile, - QrCodeEcc::High, -]; diff --git a/typescript-javascript/build.sh b/typescript-javascript/build.sh index f1689aa..5875c15 100644 --- a/typescript-javascript/build.sh +++ b/typescript-javascript/build.sh @@ -23,8 +23,3 @@ tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-input-demo.ts tsc --strict --lib DOM,DOM.Iterable,ES6 --target ES6 qrcodegen.ts qrcodegen-output-demo.ts - -if [ '!' -d node_modules ]; then - npm install @types/node -fi -tsc --strict --target ES2017 --outFile qrcodegen-worker.js qrcodegen.ts qrcodegen-worker.ts diff --git a/typescript-javascript/qrcodegen-worker.ts b/typescript-javascript/qrcodegen-worker.ts deleted file mode 100644 index 92a728c..0000000 --- a/typescript-javascript/qrcodegen-worker.ts +++ /dev/null @@ -1,130 +0,0 @@ -/* - * QR Code generator test worker (TypeScript) - * - * This program reads data and encoding parameters from standard input and writes - * QR Code bitmaps to standard output. The I/O format is one integer per line. - * Run with no command line arguments. The program is intended for automated - * batch testing of end-to-end functionality of this QR Code generator library. - * - * Copyright (c) Project Nayuki. (MIT License) - * https://www.nayuki.io/page/qr-code-generator-library - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of - * the Software, and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - The Software is provided "as is", without warranty of any kind, express or - * implied, including but not limited to the warranties of merchantability, - * fitness for a particular purpose and noninfringement. In no event shall the - * authors or copyright holders be liable for any claim, damages or other - * liability, whether in an action of contract, tort or otherwise, arising from, - * out of or in connection with the Software or the use or other dealings in the - * Software. - */ - -"use strict"; - - -async function main(): Promise { - while (true) { - // Read data or exit - const length: number = await input.readInt(); - if (length == -1) - break; - let data: Array = []; - for (let i = 0; i < length; i++) - data.push(await input.readInt()); - - // Read encoding parameters - const errCorLvl : number = await input.readInt(); - const minVersion: number = await input.readInt(); - const maxVersion: number = await input.readInt(); - const mask : number = await input.readInt(); - const boostEcl : number = await input.readInt(); - - // Make segments for encoding - let segs: Array; - if (data.every(b => b < 128)) { // Is ASCII - const s: string = data.map(b => String.fromCharCode(b)).join(""); - segs = qrcodegen.QrSegment.makeSegments(s); - } else - segs = [qrcodegen.QrSegment.makeBytes(data)]; - - try { // Try to make QR Code symbol - const qr = qrcodegen.QrCode.encodeSegments( - segs, ECC_LEVELS[errCorLvl], minVersion, maxVersion, mask, boostEcl != 0); - // Print grid of modules - await printLine(qr.version); - for (let y = 0; y < qr.size; y++) { - for (let x = 0; x < qr.size; x++) - await printLine(qr.getModule(x, y) ? 1 : 0); - } - - } catch (e) { - if (e == "Data too long") - await printLine(-1); - } - } -} - - -namespace input { - - let queue: Array = []; - let callback: ((line:string)=>void)|null = null; - - const readline = require("readline"); - let reader = readline.createInterface({ - input: process.stdin, - terminal: false, - }); - reader.on("line", (line: string) => { - queue.push(line); - if (callback !== null) { - callback(queue.shift() as string); - callback = null; - } - }); - - - async function readLine(): Promise { - return new Promise(resolve => { - if (callback !== null) - throw "Illegal state"; - if (queue.length > 0) - resolve(queue.shift() as string); - else - callback = resolve; - }); - } - - - export async function readInt(): Promise { - let s = await readLine(); - if (!/^-?\d+$/.test(s)) - throw "Invalid number syntax"; - return parseInt(s, 10); - } - -} - - -async function printLine(x: Object): Promise { - return new Promise(resolve => - process.stdout.write(x + "\n", "utf-8", ()=>resolve())); -} - - -const ECC_LEVELS: Array = [ - qrcodegen.QrCode.Ecc.LOW, - qrcodegen.QrCode.Ecc.MEDIUM, - qrcodegen.QrCode.Ecc.QUARTILE, - qrcodegen.QrCode.Ecc.HIGH, -]; - - -main(); From 42bc11c763782ff98e063e9d6c042cc12304c1a8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:21:25 +0000 Subject: [PATCH 720/810] Updated C API comments to match the existing code logic requiring non-overlapping array buffers. --- c/qrcodegen.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/c/qrcodegen.h b/c/qrcodegen.h index 94aaef9..6ce0024 100644 --- a/c/qrcodegen.h +++ b/c/qrcodegen.h @@ -154,8 +154,8 @@ struct qrcodegen_Segment { * - The input text must be encoded in UTF-8 and contain no NULs. * - The variables ecl and mask must correspond to enum constant values. * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays tempBuffer and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - The arrays tempBuffer and qrcode must each have a length of at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion), and cannot overlap. * - After the function returns, tempBuffer contains no useful data. * - If successful, the resulting QR Code may use numeric, * alphanumeric, or byte mode to encode the text. @@ -178,8 +178,8 @@ bool qrcodegen_encodeText(const char *text, uint8_t tempBuffer[], uint8_t qrcode * valid UTF-8 text, but is not required by the QR Code standard. * - The variables ecl and mask must correspond to enum constant values. * - Requires 1 <= minVersion <= maxVersion <= 40. - * - The arrays dataAndTemp and qrcode must each have a length - * of at least qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion). + * - The arrays dataAndTemp and qrcode must each have a length of at least + * qrcodegen_BUFFER_LEN_FOR_VERSION(maxVersion), and cannot overlap. * - After the function returns, the contents of dataAndTemp may have changed, * and does not represent useful data anymore. * - If successful, the resulting QR Code will use byte mode to encode the data. From 764749bd8a6aea9bf5f31e7e288fa98a73b563f0 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:32:33 +0000 Subject: [PATCH 721/810] Added spaces around multiplication operators in QrCode.getAlignmentPatternPositions() for all language versions, for consistency with other code. --- c/qrcodegen.c | 2 +- cpp/qrcodegen.cpp | 2 +- java/src/main/java/io/nayuki/qrcodegen/QrCode.java | 4 ++-- python/qrcodegen.py | 2 +- rust/src/lib.rs | 2 +- typescript-javascript/qrcodegen.ts | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/c/qrcodegen.c b/c/qrcodegen.c index e67a7d3..1208b04 100644 --- a/c/qrcodegen.c +++ b/c/qrcodegen.c @@ -552,7 +552,7 @@ testable int getAlignmentPatternPositions(int version, uint8_t result[7]) { return 0; int numAlign = version / 7 + 2; int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; for (int i = numAlign - 1, pos = version * 4 + 10; i >= 1; i--, pos -= step) result[i] = (uint8_t)pos; result[0] = 6; diff --git a/cpp/qrcodegen.cpp b/cpp/qrcodegen.cpp index e8a6a1a..3e6d81f 100644 --- a/cpp/qrcodegen.cpp +++ b/cpp/qrcodegen.cpp @@ -670,7 +670,7 @@ vector QrCode::getAlignmentPatternPositions() const { else { int numAlign = version / 7 + 2; int step = (version == 32) ? 26 : - (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; vector result; for (int i = 0, pos = size - 7; i < numAlign - 1; i++, pos -= step) result.insert(result.begin(), pos); diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java index 2fb8b97..8e6ef09 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCode.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCode.java @@ -618,8 +618,8 @@ public final class QrCode { int step; if (version == 32) // Special snowflake step = 26; - else // step = ceil[(size - 13) / (numAlign*2 - 2)] * 2 - step = (version*4 + numAlign*2 + 1) / (numAlign*2 - 2) * 2; + else // step = ceil[(size - 13) / (numAlign * 2 - 2)] * 2 + step = (version * 4 + numAlign * 2 + 1) / (numAlign * 2 - 2) * 2; int[] result = new int[numAlign]; result[0] = 6; for (int i = result.length - 1, pos = size - 7; i >= 1; i--, pos -= step) diff --git a/python/qrcodegen.py b/python/qrcodegen.py index 48bf020..ef9eb05 100644 --- a/python/qrcodegen.py +++ b/python/qrcodegen.py @@ -486,7 +486,7 @@ class QrCode: else: numalign: int = ver // 7 + 2 step: int = 26 if (ver == 32) else \ - (ver*4 + numalign*2 + 1) // (numalign*2 - 2) * 2 + (ver * 4 + numalign * 2 + 1) // (numalign * 2 - 2) * 2 result: List[int] = [(self._size - 7 - i * step) for i in range(numalign - 1)] + [6] return list(reversed(result)) diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 546f0a8..f17a7fb 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -695,7 +695,7 @@ impl QrCode { } else { let numalign = i32::from(ver) / 7 + 2; let step: i32 = if ver == 32 { 26 } else - {(i32::from(ver)*4 + numalign*2 + 1) / (numalign*2 - 2) * 2}; + {(i32::from(ver) * 4 + numalign * 2 + 1) / (numalign * 2 - 2) * 2}; let mut result: Vec = (0 .. numalign - 1).map( |i| self.size - 7 - i * step).collect(); result.push(6); diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 053770c..809622b 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -545,7 +545,7 @@ namespace qrcodegen { else { const numAlign: int = Math.floor(this.version / 7) + 2; const step: int = (this.version == 32) ? 26 : - Math.ceil((this.size - 13) / (numAlign*2 - 2)) * 2; + Math.ceil((this.size - 13) / (numAlign * 2 - 2)) * 2; let result: Array = [6]; for (let pos = this.size - 7; result.length < numAlign; pos -= step) result.splice(1, 0, pos); From 87db45ef9cd335de802e9942792e73d03a319867 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:38:03 +0000 Subject: [PATCH 722/810] Tweaked TypeScript code to correspond with other implementations for ease of auditing, without changing behavior. --- typescript-javascript/qrcodegen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript-javascript/qrcodegen.ts b/typescript-javascript/qrcodegen.ts index 809622b..dece093 100644 --- a/typescript-javascript/qrcodegen.ts +++ b/typescript-javascript/qrcodegen.ts @@ -545,7 +545,7 @@ namespace qrcodegen { else { const numAlign: int = Math.floor(this.version / 7) + 2; const step: int = (this.version == 32) ? 26 : - Math.ceil((this.size - 13) / (numAlign * 2 - 2)) * 2; + Math.ceil((this.version * 4 + 4) / (numAlign * 2 - 2)) * 2; let result: Array = [6]; for (let pos = this.size - 7; result.length < numAlign; pos -= step) result.splice(1, 0, pos); From bfd18b2fb81518ab453349b71bb923ef7e69398c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:48:21 +0000 Subject: [PATCH 723/810] In Java version, added parameters for custom module colors when rendering to BufferedImage, changed some demo code to use non-black/white colors. --- .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 5f70344..44f85fd 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -132,11 +132,11 @@ public final class QrCodeGeneratorDemo { // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters String madoka = "「魔法少女まどか☆マギカ」って、 ИАИ desu κα?"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); - writePng(toImage(qr, 9, 4), "madoka-utf8-QR.png"); + writePng(toImage(qr, 9, 4, 0xFFFFE0, 0x303080), "madoka-utf8-QR.png"); segs = Arrays.asList(QrSegmentAdvanced.makeKanji(madoka)); qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - writePng(toImage(qr, 9, 4), "madoka-kanji-QR.png"); + writePng(toImage(qr, 9, 4, 0xE0F0FF, 0x404040), "madoka-kanji-QR.png"); } @@ -148,9 +148,9 @@ 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 - writePng(toImage(qr, 8, 6), "project-nayuki-automask-QR.png"); + writePng(toImage(qr, 8, 6, 0xE0FFE0, 0x206020), "project-nayuki-automask-QR.png"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 - writePng(toImage(qr, 8, 6), "project-nayuki-mask3-QR.png"); + writePng(toImage(qr, 8, 6, 0xFFE0E0, 0x602020), "project-nayuki-mask3-QR.png"); // Chinese text as UTF-8 segs = QrSegment.makeSegments("維基百科(Wikipedia,聆聽i/ˌwɪkᵻˈpiːdi.ə/)是一個自由內容、公開編輯且多語言的網路百科全書協作計畫"); @@ -168,20 +168,27 @@ public final class QrCodeGeneratorDemo { /*---- Utilities ----*/ + private static BufferedImage toImage(QrCode qr, int scale, int border) { + return toImage(qr, scale, border, 0xFFFFFF, 0x000000); + } + + /** - * Returns a raster image depicting the specified QR Code, with the specified module scale and border modules. - *

For example, toImage(qr, scale=10, border=4) means to pad the QR Code with 4 light - * border modules on all four sides, and use 10×10 pixels to represent each module. - * The resulting image only contains the hex colors 000000 and FFFFFF. + * Returns a raster image depicting the specified QR Code, with + * the specified module scale, border modules, and module colors. + *

For example, scale=10 and border=4 means to pad the QR Code with 4 light border + * modules on all four sides, and use 10×10 pixels to represent each module. * @param qr the QR Code to render (not {@code null}) * @param scale the side length (measured in pixels, must be positive) of each module * @param border the number of border modules to add, which must be non-negative + * @param lightColor the color to use for light modules, in 0xRRGGBB format + * @param darkColor the color to use for dark modules, in 0xRRGGBB format * @return a new image representing the QR Code, with padding and scaling * @throws NullPointerException if the QR Code is {@code null} * @throws IllegalArgumentException if the scale or border is out of range, or if * {scale, border, size} cause the image dimensions to exceed Integer.MAX_VALUE */ - private static BufferedImage toImage(QrCode qr, int scale, int border) { + private static BufferedImage toImage(QrCode qr, int scale, int border, int lightColor, int darkColor) { Objects.requireNonNull(qr); if (scale <= 0 || border < 0) throw new IllegalArgumentException("Value out of range"); @@ -192,7 +199,7 @@ public final class QrCodeGeneratorDemo { for (int y = 0; y < result.getHeight(); y++) { for (int x = 0; x < result.getWidth(); x++) { boolean color = qr.getModule(x / scale - border, y / scale - border); - result.setRGB(x, y, color ? 0x000000 : 0xFFFFFF); + result.setRGB(x, y, color ? darkColor : lightColor); } } return result; From d8f0074fabfe3bb02de74fe98ed0c7956a3e9eb8 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 17:55:59 +0000 Subject: [PATCH 724/810] In TypeScript version, added parameters for custom module colors when rendering to canvas, changed some demo code to use non-black/white colors. --- typescript-javascript/qrcodegen-input-demo.ts | 8 ++-- .../qrcodegen-output-demo.ts | 40 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/typescript-javascript/qrcodegen-input-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts index 2de8341..d2d6a81 100644 --- a/typescript-javascript/qrcodegen-input-demo.ts +++ b/typescript-javascript/qrcodegen-input-demo.ts @@ -92,7 +92,7 @@ namespace app { const scale: number = parseInt(getInput("scale-input").value, 10); if (scale <= 0 || scale > 30) return; - drawCanvas(qr, scale, border, canvas); + drawCanvas(qr, scale, border, "#FFFFFF", "#000000", canvas); canvas.style.removeProperty("display"); } else { const code: string = toSvgString(qr, border); @@ -150,9 +150,9 @@ namespace app { // Draws the given QR Code, with the given module scale and border modules, onto the given HTML // canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale. - // The drawn image is be purely dark and light, and fully opaque. + // The drawn image is purely dark and light, and fully opaque. // The scale must be a positive integer and the border must be a non-negative integer. - function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, canvas: HTMLCanvasElement): void { + function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, lightColor: string, darkColor: string, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) throw "Value out of range"; const width: number = (qr.size + border * 2) * scale; @@ -161,7 +161,7 @@ namespace app { let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; for (let y = -border; y < qr.size + border; y++) { for (let x = -border; x < qr.size + border; x++) { - ctx.fillStyle = qr.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillStyle = qr.getModule(x, y) ? darkColor : lightColor; ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); } } diff --git a/typescript-javascript/qrcodegen-output-demo.ts b/typescript-javascript/qrcodegen-output-demo.ts index f509596..eae9630 100644 --- a/typescript-javascript/qrcodegen-output-demo.ts +++ b/typescript-javascript/qrcodegen-output-demo.ts @@ -46,7 +46,7 @@ namespace app { const text: string = "Hello, world!"; // User-supplied Unicode text const errCorLvl: qrcodegen.QrCode.Ecc = qrcodegen.QrCode.Ecc.LOW; // Error correction level const qr: qrcodegen.QrCode = qrcodegen.QrCode.encodeText(text, errCorLvl); // Make the QR Code symbol - drawCanvas(qr, 10, 4, appendCanvas("hello-world-QR")); // Draw it on screen + drawCanvas(qr, 10, 4, "#FFFFFF", "#000000", appendCanvas("hello-world-QR")); // Draw it on screen } @@ -58,15 +58,15 @@ namespace app { // Numeric mode encoding (3.33 bits per digit) qr = QrCode.encodeText("314159265358979323846264338327950288419716939937510", QrCode.Ecc.MEDIUM); - drawCanvas(qr, 13, 1, appendCanvas("pi-digits-QR")); + drawCanvas(qr, 13, 1, "#FFFFFF", "#000000", appendCanvas("pi-digits-QR")); // Alphanumeric mode encoding (5.5 bits per character) qr = QrCode.encodeText("DOLLAR-AMOUNT:$39.87 PERCENTAGE:100.00% OPERATIONS:+-*/", QrCode.Ecc.HIGH); - drawCanvas(qr, 10, 2, appendCanvas("alphanumeric-QR")); + drawCanvas(qr, 10, 2, "#FFFFFF", "#000000", appendCanvas("alphanumeric-QR")); // Unicode text as UTF-8 qr = QrCode.encodeText("\u3053\u3093\u306B\u3061wa\u3001\u4E16\u754C\uFF01 \u03B1\u03B2\u03B3\u03B4", QrCode.Ecc.QUARTILE); - drawCanvas(qr, 10, 3, appendCanvas("unicode-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-QR")); // Moderately large QR Code using longer text (from Lewis Carroll's Alice in Wonderland) qr = QrCode.encodeText( @@ -77,7 +77,7 @@ namespace app { + "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); - drawCanvas(qr, 6, 10, appendCanvas("alice-wonderland-QR")); + drawCanvas(qr, 6, 10, "#FFFFFF", "#000000", appendCanvas("alice-wonderland-QR")); } @@ -93,32 +93,32 @@ namespace app { const silver0: string = "THE SQUARE ROOT OF 2 IS 1."; const silver1: string = "41421356237309504880168872420969807856967187537694807317667973799"; qr = QrCode.encodeText(silver0 + silver1, QrCode.Ecc.LOW); - drawCanvas(qr, 10, 3, appendCanvas("sqrt2-monolithic-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("sqrt2-monolithic-QR")); segs = [ QrSegment.makeAlphanumeric(silver0), QrSegment.makeNumeric(silver1)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - drawCanvas(qr, 10, 3, appendCanvas("sqrt2-segmented-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("sqrt2-segmented-QR")); // Illustration "golden" const golden0: string = "Golden ratio \u03C6 = 1."; const golden1: string = "6180339887498948482045868343656381177203091798057628621354486227052604628189024497072072041893911374"; const golden2: string = "......"; qr = QrCode.encodeText(golden0 + golden1 + golden2, QrCode.Ecc.LOW); - drawCanvas(qr, 8, 5, appendCanvas("phi-monolithic-QR")); + drawCanvas(qr, 8, 5, "#FFFFFF", "#000000", appendCanvas("phi-monolithic-QR")); segs = [ QrSegment.makeBytes(toUtf8ByteArray(golden0)), QrSegment.makeNumeric(golden1), QrSegment.makeAlphanumeric(golden2)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - drawCanvas(qr, 8, 5, appendCanvas("phi-segmented-QR")); + drawCanvas(qr, 8, 5, "#FFFFFF", "#000000", appendCanvas("phi-segmented-QR")); // Illustration "Madoka": kanji, kana, Cyrillic, full-width Latin, Greek characters const madoka: string = "\u300C\u9B54\u6CD5\u5C11\u5973\u307E\u3069\u304B\u2606\u30DE\u30AE\u30AB\u300D\u3063\u3066\u3001\u3000\u0418\u0410\u0418\u3000\uFF44\uFF45\uFF53\uFF55\u3000\u03BA\u03B1\uFF1F"; qr = QrCode.encodeText(madoka, QrCode.Ecc.LOW); - drawCanvas(qr, 9, 4, appendCanvas("madoka-utf8-QR")); + drawCanvas(qr, 9, 4, "#FFFFE0", "#303080", appendCanvas("madoka-utf8-QR")); const kanjiCharBits: Array = [ // Kanji mode encoding (13 bits per character) 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, @@ -153,7 +153,7 @@ namespace app { ]; segs = [new QrSegment(QrSegment.Mode.KANJI, kanjiCharBits.length / 13, kanjiCharBits)]; qr = QrCode.encodeSegments(segs, QrCode.Ecc.LOW); - drawCanvas(qr, 9, 4, appendCanvas("madoka-kanji-QR")); + drawCanvas(qr, 9, 4, "#E0F0FF", "#404040", appendCanvas("madoka-kanji-QR")); } @@ -167,21 +167,21 @@ namespace app { // Project Nayuki URL segs = qrcodegen.QrSegment.makeSegments("https://www.nayuki.io/"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, -1, true); // Automatic mask - drawCanvas(qr, 8, 6, appendCanvas("project-nayuki-automask-QR")); + drawCanvas(qr, 8, 6, "#E0FFE0", "#206020", appendCanvas("project-nayuki-automask-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.HIGH, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 3, true); // Force mask 3 - drawCanvas(qr, 8, 6, appendCanvas("project-nayuki-mask3-QR")); + drawCanvas(qr, 8, 6, "#FFE0E0", "#602020", appendCanvas("project-nayuki-mask3-QR")); // Chinese text as UTF-8 segs = qrcodegen.QrSegment.makeSegments("\u7DAD\u57FA\u767E\u79D1\uFF08Wikipedia\uFF0C\u8046\u807Di/\u02CCw\u026Ak\u1D7B\u02C8pi\u02D0di.\u0259/\uFF09\u662F\u4E00" + "\u500B\u81EA\u7531\u5167\u5BB9\u3001\u516C\u958B\u7DE8\u8F2F\u4E14\u591A\u8A9E\u8A00\u7684\u7DB2\u8DEF\u767E\u79D1\u5168\u66F8\u5354\u4F5C\u8A08\u756B"); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 0, true); // Force mask 0 - drawCanvas(qr, 10, 3, appendCanvas("unicode-mask0-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask0-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 1, true); // Force mask 1 - drawCanvas(qr, 10, 3, appendCanvas("unicode-mask1-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask1-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 5, true); // Force mask 5 - drawCanvas(qr, 10, 3, appendCanvas("unicode-mask5-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask5-QR")); qr = QrCode.encodeSegments(segs, QrCode.Ecc.MEDIUM, QrCode.MIN_VERSION, QrCode.MAX_VERSION, 7, true); // Force mask 7 - drawCanvas(qr, 10, 3, appendCanvas("unicode-mask7-QR")); + drawCanvas(qr, 10, 3, "#FFFFFF", "#000000", appendCanvas("unicode-mask7-QR")); } @@ -202,9 +202,9 @@ namespace app { // Draws the given QR Code, with the given module scale and border modules, onto the given HTML // canvas element. The canvas's width and height is resized to (qr.size + border * 2) * scale. - // The drawn image is be purely dark and light, and fully opaque. + // The drawn image is purely dark and light, and fully opaque. // The scale must be a positive integer and the border must be a non-negative integer. - function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, canvas: HTMLCanvasElement): void { + function drawCanvas(qr: qrcodegen.QrCode, scale: number, border: number, lightColor: string, darkColor: string, canvas: HTMLCanvasElement): void { if (scale <= 0 || border < 0) throw "Value out of range"; const width: number = (qr.size + border * 2) * scale; @@ -213,7 +213,7 @@ namespace app { let ctx = canvas.getContext("2d") as CanvasRenderingContext2D; for (let y = -border; y < qr.size + border; y++) { for (let x = -border; x < qr.size + border; x++) { - ctx.fillStyle = qr.getModule(x, y) ? "#000000" : "#FFFFFF"; + ctx.fillStyle = qr.getModule(x, y) ? darkColor : lightColor; ctx.fillRect((x + border) * scale, (y + border) * scale, scale, scale); } } From 201993945d632ab87363790d19594fe022f6b79f Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 18:01:41 +0000 Subject: [PATCH 725/810] In Java and TypeScript versions, added parameters for custom module colors when rendering to SVG. --- .../nayuki/qrcodegen/QrCodeGeneratorDemo.java | 18 +++++++++++------- typescript-javascript/qrcodegen-input-demo.ts | 8 ++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java index 44f85fd..eb7c96e 100644 --- a/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java +++ b/java/src/main/java/io/nayuki/qrcodegen/QrCodeGeneratorDemo.java @@ -62,9 +62,9 @@ public final class QrCodeGeneratorDemo { File imgFile = new File("hello-world-QR.png"); // File path for output ImageIO.write(img, "png", imgFile); // Write image to file - String svg = toSvgString(qr, 4); // Convert to SVG XML code - File svgFile = new File("hello-world-QR.svg"); // File path for output - Files.write(svgFile.toPath(), // Write image to file + String svg = toSvgString(qr, 4, "#FFFFFF", "#000000"); // Convert to SVG XML code + File svgFile = new File("hello-world-QR.svg"); // File path for output + Files.write(svgFile.toPath(), // Write image to file svg.getBytes(StandardCharsets.UTF_8)); } @@ -217,12 +217,16 @@ public final class QrCodeGeneratorDemo { * number of border modules. The string always uses Unix newlines (\n), regardless of the platform. * @param qr the QR Code to render (not {@code null}) * @param border the number of border modules to add, which must be non-negative + * @param lightColor the color to use for light modules, in any format supported by CSS, not {@code null} + * @param darkColor the color to use for dark modules, in any format supported by CSS, not {@code null} * @return a string representing the QR Code as an SVG XML document - * @throws NullPointerException if the QR Code is {@code null} + * @throws NullPointerException if any object is {@code null} * @throws IllegalArgumentException if the border is negative */ - private static String toSvgString(QrCode qr, int border) { + private static String toSvgString(QrCode qr, int border, String lightColor, String darkColor) { Objects.requireNonNull(qr); + Objects.requireNonNull(lightColor); + Objects.requireNonNull(darkColor); if (border < 0) throw new IllegalArgumentException("Border must be non-negative"); long brd = border; @@ -231,7 +235,7 @@ public final class QrCodeGeneratorDemo { .append("\n") .append(String.format("\n", qr.size + brd * 2)) - .append("\t\n") + .append("\t\n") .append("\t\n") + .append("\" fill=\"" + darkColor + "\"/>\n") .append("\n") .toString(); } diff --git a/typescript-javascript/qrcodegen-input-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts index d2d6a81..f9a027a 100644 --- a/typescript-javascript/qrcodegen-input-demo.ts +++ b/typescript-javascript/qrcodegen-input-demo.ts @@ -95,7 +95,7 @@ namespace app { drawCanvas(qr, scale, border, "#FFFFFF", "#000000", canvas); canvas.style.removeProperty("display"); } else { - const code: string = toSvgString(qr, border); + const code: string = toSvgString(qr, border, "#FFFFFF", "#000000"); const viewBox: string = (/ viewBox="([^"]*)"/.exec(code) as RegExpExecArray)[1]; const pathD: string = (/ d="([^"]*)"/.exec(code) as RegExpExecArray)[1]; svg.setAttribute("viewBox", viewBox); @@ -170,7 +170,7 @@ namespace app { // Returns a string of SVG code for an image depicting the given QR Code, with the given number // of border modules. The string always uses Unix newlines (\n), regardless of the platform. - function toSvgString(qr: qrcodegen.QrCode, border: number): string { + function toSvgString(qr: qrcodegen.QrCode, border: number, lightColor: string, darkColor: string): string { if (border < 0) throw "Border must be non-negative"; let parts: Array = []; @@ -183,8 +183,8 @@ namespace app { return ` - - + + ` } From f29c84a2e7e5500cea0b5e2115749f36ff8f429c Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 18:11:31 +0000 Subject: [PATCH 726/810] Added custom colors support to the TypeScript input demo web page and program. --- typescript-javascript/qrcodegen-input-demo.html | 7 +++++++ typescript-javascript/qrcodegen-input-demo.ts | 10 +++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/typescript-javascript/qrcodegen-input-demo.html b/typescript-javascript/qrcodegen-input-demo.html index d136412..3c77c9e 100644 --- a/typescript-javascript/qrcodegen-input-demo.html +++ b/typescript-javascript/qrcodegen-input-demo.html @@ -97,6 +97,13 @@ Scale: pixels per module + + Colors: + + Light = , + dark = + + Version range: diff --git a/typescript-javascript/qrcodegen-input-demo.ts b/typescript-javascript/qrcodegen-input-demo.ts index f9a027a..b87fe3c 100644 --- a/typescript-javascript/qrcodegen-input-demo.ts +++ b/typescript-javascript/qrcodegen-input-demo.ts @@ -29,7 +29,7 @@ namespace app { function initialize(): void { getElem("loading").style.display = "none"; getElem("loaded").style.removeProperty("display"); - let elems = document.querySelectorAll("input[type=number], textarea"); + let elems = document.querySelectorAll("input[type=number], input[type=text], textarea"); for (let el of elems) { if (el.id.indexOf("version-") != 0) (el as any).oninput = redrawQrCode; @@ -86,20 +86,24 @@ namespace app { // Draw image output const border: number = parseInt(getInput("border-input").value, 10); + const lightColor: string = getInput("light-color-input").value; + const darkColor : string = getInput("dark-color-input" ).value; if (border < 0 || border > 100) return; if (bitmapOutput) { const scale: number = parseInt(getInput("scale-input").value, 10); if (scale <= 0 || scale > 30) return; - drawCanvas(qr, scale, border, "#FFFFFF", "#000000", canvas); + drawCanvas(qr, scale, border, lightColor, darkColor, canvas); canvas.style.removeProperty("display"); } else { - const code: string = toSvgString(qr, border, "#FFFFFF", "#000000"); + const code: string = toSvgString(qr, border, lightColor, darkColor); const viewBox: string = (/ viewBox="([^"]*)"/.exec(code) as RegExpExecArray)[1]; const pathD: string = (/ d="([^"]*)"/.exec(code) as RegExpExecArray)[1]; svg.setAttribute("viewBox", viewBox); (svg.querySelector("path") as Element).setAttribute("d", pathD); + (svg.querySelector("rect") as Element).setAttribute("fill", lightColor); + (svg.querySelector("path") as Element).setAttribute("fill", darkColor); svg.style.removeProperty("display"); svgXml.value = code; } From 6fb34fddc19dccd0e76fe5d12e00fa0bd2ef13e2 Mon Sep 17 00:00:00 2001 From: Project Nayuki Date: Wed, 28 Jul 2021 18:31:33 +0000 Subject: [PATCH 727/810] Slightly improved the visual appearance of web pages for TypeScript/JavaScript demos, also tweaked a bit of HTML code. --- .../qrcodegen-input-demo.html | 21 ++++++++++++++++--- .../qrcodegen-output-demo.html | 11 +++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/typescript-javascript/qrcodegen-input-demo.html b/typescript-javascript/qrcodegen-input-demo.html index 3c77c9e..cc46d6e 100644 --- a/typescript-javascript/qrcodegen-input-demo.html +++ b/typescript-javascript/qrcodegen-input-demo.html @@ -29,6 +29,12 @@ html { font-family: sans-serif; } + h1 { + text-align: center; + } + table { + border-collapse: collapse; + } td { vertical-align: top; padding-top: 0.2em; @@ -38,6 +44,10 @@ white-space: pre; padding-right: 0.5em; } + input[type=number], input[type=text], textarea { + font-size: inherit; + font-family: inherit; + } input[type=radio], input[type=checkbox] { margin: 0em; padding: 0em; @@ -46,6 +56,11 @@ margin-right: 0.8em; padding-left: 0.2em; } + hr { + margin: 2em 0em; + border: none; + border-top: 0.1em solid #A0A0A0; + } @@ -56,12 +71,12 @@

(Are the JavaScript files missing?)

(The JavaScript code needs to be compiled from the TypeScript code.)

-