Java Code Examples for com.google.zxing.common.BitArray#getSize()

The following examples show how to use com.google.zxing.common.BitArray#getSize() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: MatrixUtil.java    From reacteu-app with MIT License 6 votes vote down vote up
static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits)
    throws WriterException {
  if (!QRCode.isValidMaskPattern(maskPattern)) {
    throw new WriterException("Invalid mask pattern");
  }
  int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
  bits.appendBits(typeInfo, 5);

  int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
  bits.appendBits(bchCode, 10);

  BitArray maskBits = new BitArray();
  maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
  bits.xor(maskBits);

  if (bits.getSize() != 15) {  // Just in case.
    throw new WriterException("should not happen but we got: " + bits.getSize());
  }
}
 
Example 2
Source File: CodaBarReader.java    From Tesseract-OCR-Scanner with Apache License 2.0 6 votes vote down vote up
/**
 * Records the size of all runs of white and black pixels, starting with white.
 * This is just like recordPattern, except it records all the counters, and
 * uses our builtin "counters" member for storage.
 * @param row row to count from
 */
private void setCounters(BitArray row) throws NotFoundException {
  counterLength = 0;
  // Start from the first white bit.
  int i = row.getNextUnset(0);
  int end = row.getSize();
  if (i >= end) {
    throw NotFoundException.getNotFoundInstance();
  }
  boolean isWhite = true;
  int count = 0;
  while (i < end) {
    if (row.get(i) != isWhite) {
      count++;
    } else {
      counterAppend(count);
      count = 1;
      isWhite = !isWhite;
    }
    i++;
  }
  counterAppend(count);
}
 
Example 3
Source File: MatrixUtil.java    From RipplePower with Apache License 2.0 6 votes vote down vote up
static void makeTypeInfoBits(ErrorCorrectionLevel ecLevel, int maskPattern, BitArray bits) throws WriterException {
	if (!QRCode.isValidMaskPattern(maskPattern)) {
		throw new WriterException("Invalid mask pattern");
	}
	int typeInfo = (ecLevel.getBits() << 3) | maskPattern;
	bits.appendBits(typeInfo, 5);

	int bchCode = calculateBCHCode(typeInfo, TYPE_INFO_POLY);
	bits.appendBits(bchCode, 10);

	BitArray maskBits = new BitArray();
	maskBits.appendBits(TYPE_INFO_MASK_PATTERN, 15);
	bits.xor(maskBits);

	if (bits.getSize() != 15) { // Just in case.
		throw new WriterException("should not happen but we got: " + bits.getSize());
	}
}
 
Example 4
Source File: d.java    From MiBandDecompiled with Apache License 2.0 5 votes vote down vote up
static void a(int j, BitArray bitarray)
{
    bitarray.appendBits(j, 6);
    bitarray.appendBits(a(j, 7973), 12);
    if (bitarray.getSize() != 18)
    {
        throw new WriterException((new StringBuilder()).append("should not happen but we got: ").append(bitarray.getSize()).toString());
    } else
    {
        return;
    }
}
 
Example 5
Source File: UPCEReader.java    From RipplePower with Apache License 2.0 5 votes vote down vote up
@Override
protected int decodeMiddle(BitArray row, int[] startRange, StringBuilder result) throws NotFoundException {
	int[] counters = decodeMiddleCounters;
	counters[0] = 0;
	counters[1] = 0;
	counters[2] = 0;
	counters[3] = 0;
	int end = row.getSize();
	int rowOffset = startRange[1];

	int lgPatternFound = 0;

	for (int x = 0; x < 6 && rowOffset < end; x++) {
		int bestMatch = decodeDigit(row, counters, rowOffset, L_AND_G_PATTERNS);
		result.append((char) ('0' + bestMatch % 10));
		for (int counter : counters) {
			rowOffset += counter;
		}
		if (bestMatch >= 10) {
			lgPatternFound |= 1 << (5 - x);
		}
	}

	determineNumSysAndCheckDigit(result, lgPatternFound);

	return rowOffset;
}
 
Example 6
Source File: OneDReader.java    From Tesseract-OCR-Scanner with Apache License 2.0 5 votes vote down vote up
/**
 * Records the size of successive runs of white and black pixels in a row, starting at a given point.
 * The values are recorded in the given array, and the number of runs recorded is equal to the size
 * of the array. If the row starts on a white pixel at the given start point, then the first count
 * recorded is the run of white pixels starting from that point; likewise it is the count of a run
 * of black pixels if the row begin on a black pixels at that point.
 *
 * @param row row to count from
 * @param start offset into row to start at
 * @param counters array into which to record counts
 * @throws NotFoundException if counters cannot be filled entirely from row before running out
 *  of pixels
 */
protected static void recordPattern(BitArray row,
                                    int start,
                                    int[] counters) throws NotFoundException {
  int numCounters = counters.length;
  Arrays.fill(counters, 0, numCounters, 0);
  int end = row.getSize();
  if (start >= end) {
    throw NotFoundException.getNotFoundInstance();
  }
  boolean isWhite = !row.get(start);
  int counterPosition = 0;
  int i = start;
  while (i < end) {
    if (row.get(i) != isWhite) {
      counters[counterPosition]++;
    } else {
      if (++counterPosition == numCounters) {
        break;
      } else {
        counters[counterPosition] = 1;
        isWhite = !isWhite;
      }
    }
    i++;
  }
  // If we read fully the last section of pixels and filled up our counters -- or filled
  // the last counter but ran off the side of the image, OK. Otherwise, a problem.
  if (!(counterPosition == numCounters || (counterPosition == numCounters - 1 && i == end))) {
    throw NotFoundException.getNotFoundInstance();
  }
}
 
Example 7
Source File: MatrixUtil.java    From android-quick-response-code with Apache License 2.0 5 votes vote down vote up
static void makeVersionInfoBits(int version, BitArray bits) throws WriterException {
    bits.appendBits(version, 6);
    int bchCode = calculateBCHCode(version, VERSION_INFO_POLY);
    bits.appendBits(bchCode, 12);

    if (bits.getSize() != 18) { // Just in case.
        throw new WriterException("should not happen but we got: " + bits.getSize());
    }
}
 
Example 8
Source File: Encoder.java    From QrCodeScanner with GNU General Public License v3.0 5 votes vote down vote up
/**
 * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
 */
static void terminateBits(int numDataBytes, BitArray bits) throws WriterException {
  int capacity = numDataBytes * 8;
  if (bits.getSize() > capacity) {
    throw new WriterException("data bits cannot fit in the QR Code" + bits.getSize() + " > " +
        capacity);
  }
  for (int i = 0; i < 4 && bits.getSize() < capacity; ++i) {
    bits.appendBit(false);
  }
  // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
  // If the last byte isn't 8-bit aligned, we'll add padding bits.
  int numBitsInLastByte = bits.getSize() & 0x07;    
  if (numBitsInLastByte > 0) {
    for (int i = numBitsInLastByte; i < 8; i++) {
      bits.appendBit(false);
    }
  }
  // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
  int numPaddingBytes = numDataBytes - bits.getSizeInBytes();
  for (int i = 0; i < numPaddingBytes; ++i) {
    bits.appendBits((i & 0x01) == 0 ? 0xEC : 0x11, 8);
  }
  if (bits.getSize() != capacity) {
    throw new WriterException("Bits size does not equal capacity");
  }
}
 
Example 9
Source File: ITFReader.java    From weex with Apache License 2.0 5 votes vote down vote up
/**
 * Skip all whitespace until we get to the first black line.
 *
 * @param row row of black/white values to search
 * @return index of the first black line.
 * @throws NotFoundException Throws exception if no black lines are found in the row
 */
private static int skipWhiteSpace(BitArray row) throws NotFoundException {
  int width = row.getSize();
  int endStart = row.getNextSet(0);
  if (endStart == width) {
    throw NotFoundException.getNotFoundInstance();
  }

  return endStart;
}
 
Example 10
Source File: ITFReader.java    From MiBandDecompiled with Apache License 2.0 5 votes vote down vote up
private static int c(BitArray bitarray)
{
    int j = bitarray.getSize();
    int k = bitarray.getNextSet(0);
    if (k == j)
    {
        throw NotFoundException.getNotFoundInstance();
    } else
    {
        return k;
    }
}
 
Example 11
Source File: Encoder.java    From ZXing-Orient with Apache License 2.0 5 votes vote down vote up
private static BitArray generateCheckWords(BitArray bitArray, int totalBits, int wordSize) {
  // bitArray is guaranteed to be a multiple of the wordSize, so no padding needed
  int messageSizeInWords = bitArray.getSize() / wordSize;
  ReedSolomonEncoder rs = new ReedSolomonEncoder(getGF(wordSize));
  int totalWords = totalBits / wordSize;
  int[] messageWords = bitsToWords(bitArray, wordSize, totalWords);
  rs.encode(messageWords, totalWords - messageSizeInWords);
  int startPad = totalBits % wordSize;
  BitArray messageBits = new BitArray();
  messageBits.appendBits(0, startPad);
  for (int messageWord : messageWords) {
    messageBits.appendBits(messageWord, wordSize);
  }
  return messageBits;
}
 
Example 12
Source File: ITFReader.java    From ScreenCapture with MIT License 5 votes vote down vote up
/**
 * @param row       row of black/white values to search
 * @param rowOffset position to start search
 * @param pattern   pattern of counts of number of black and white pixels that are
 *                  being searched for as a pattern
 * @return start/end horizontal offset of guard pattern, as an array of two
 *         ints
 * @throws NotFoundException if pattern is not found
 */
private static int[] findGuardPattern(BitArray row,
                                      int rowOffset,
                                      int[] pattern) throws NotFoundException {
  int patternLength = pattern.length;
  int[] counters = new int[patternLength];
  int width = row.getSize();
  boolean isWhite = false;

  int counterPosition = 0;
  int patternStart = rowOffset;
  for (int x = rowOffset; x < width; x++) {
    if (row.get(x) != isWhite) {
      counters[counterPosition]++;
    } else {
      if (counterPosition == patternLength - 1) {
        if (patternMatchVariance(counters, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) {
          return new int[]{patternStart, x};
        }
        patternStart += counters[0] + counters[1];
        System.arraycopy(counters, 2, counters, 0, counterPosition - 1);
        counters[counterPosition - 1] = 0;
        counters[counterPosition] = 0;
        counterPosition--;
      } else {
        counterPosition++;
      }
      counters[counterPosition] = 1;
      isWhite = !isWhite;
    }
  }
  throw NotFoundException.getNotFoundInstance();
}
 
Example 13
Source File: Code39Reader.java    From Tesseract-OCR-Scanner with Apache License 2.0 5 votes vote down vote up
private static int[] findAsteriskPattern(BitArray row, int[] counters) throws NotFoundException {
  int width = row.getSize();
  int rowOffset = row.getNextSet(0);

  int counterPosition = 0;
  int patternStart = rowOffset;
  boolean isWhite = false;
  int patternLength = counters.length;

  for (int i = rowOffset; i < width; i++) {
    if (row.get(i) != isWhite) {
      counters[counterPosition]++;
    } else {
      if (counterPosition == patternLength - 1) {
        // Look for whitespace before start pattern, >= 50% of width of start pattern
        if (toNarrowWidePattern(counters) == ASTERISK_ENCODING &&
            row.isRange(Math.max(0, patternStart - ((i - patternStart) / 2)), patternStart, false)) {
          return new int[]{patternStart, i};
        }
        patternStart += counters[0] + counters[1];
        System.arraycopy(counters, 2, counters, 0, counterPosition - 1);
        counters[counterPosition - 1] = 0;
        counters[counterPosition] = 0;
        counterPosition--;
      } else {
        counterPosition++;
      }
      counters[counterPosition] = 1;
      isWhite = !isWhite;
    }
  }
  throw NotFoundException.getNotFoundInstance();
}
 
Example 14
Source File: Code39Reader.java    From RipplePower with Apache License 2.0 5 votes vote down vote up
private static int[] findAsteriskPattern(BitArray row, int[] counters) throws NotFoundException {
	int width = row.getSize();
	int rowOffset = row.getNextSet(0);

	int counterPosition = 0;
	int patternStart = rowOffset;
	boolean isWhite = false;
	int patternLength = counters.length;

	for (int i = rowOffset; i < width; i++) {
		if (row.get(i) ^ isWhite) {
			counters[counterPosition]++;
		} else {
			if (counterPosition == patternLength - 1) {
				// Look for whitespace before start pattern, >= 50% of width
				// of start pattern
				if (toNarrowWidePattern(counters) == ASTERISK_ENCODING
						&& row.isRange(Math.max(0, patternStart - ((i - patternStart) / 2)), patternStart, false)) {
					return new int[] { patternStart, i };
				}
				patternStart += counters[0] + counters[1];
				System.arraycopy(counters, 2, counters, 0, patternLength - 2);
				counters[patternLength - 2] = 0;
				counters[patternLength - 1] = 0;
				counterPosition--;
			} else {
				counterPosition++;
			}
			counters[counterPosition] = 1;
			isWhite = !isWhite;
		}
	}
	throw NotFoundException.getNotFoundInstance();
}
 
Example 15
Source File: EAN8Reader.java    From MiBandDecompiled with Apache License 2.0 5 votes vote down vote up
protected int decodeMiddle(BitArray bitarray, int ai[], StringBuilder stringbuilder)
{
    int ai1[] = a;
    ai1[0] = 0;
    ai1[1] = 0;
    ai1[2] = 0;
    ai1[3] = 0;
    int i = bitarray.getSize();
    int j = ai[1];
    int j2;
    for (int k = 0; k < 4 && j < i; j = j2)
    {
        stringbuilder.append((char)(48 + a(bitarray, ai1, j, d)));
        int i2 = ai1.length;
        j2 = j;
        for (int k2 = 0; k2 < i2; k2++)
        {
            j2 += ai1[k2];
        }

        k++;
    }

    int l = a(bitarray, j, true, c)[1];
    int k1;
    for (int i1 = 0; i1 < 4 && l < i; l = k1)
    {
        stringbuilder.append((char)(48 + a(bitarray, ai1, l, d)));
        int j1 = ai1.length;
        k1 = l;
        for (int l1 = 0; l1 < j1; l1++)
        {
            k1 += ai1[l1];
        }

        i1++;
    }

    return l;
}
 
Example 16
Source File: UPCEANExtension5Support.java    From ZXing-Orient with Apache License 2.0 5 votes vote down vote up
int decodeMiddle(BitArray row, int[] startRange, StringBuilder resultString) throws NotFoundException {
  int[] counters = decodeMiddleCounters;
  counters[0] = 0;
  counters[1] = 0;
  counters[2] = 0;
  counters[3] = 0;
  int end = row.getSize();
  int rowOffset = startRange[1];

  int lgPatternFound = 0;

  for (int x = 0; x < 5 && rowOffset < end; x++) {
    int bestMatch = UPCEANReader.decodeDigit(row, counters, rowOffset, UPCEANReader.L_AND_G_PATTERNS);
    resultString.append((char) ('0' + bestMatch % 10));
    for (int counter : counters) {
      rowOffset += counter;
    }
    if (bestMatch >= 10) {
      lgPatternFound |= 1 << (4 - x);
    }
    if (x != 4) {
      // Read off separator if not last
      rowOffset = row.getNextSet(rowOffset);
      rowOffset = row.getNextUnset(rowOffset);
    }
  }

  if (resultString.length() != 5) {
    throw NotFoundException.getNotFoundInstance();
  }

  int checkDigit = determineCheckDigit(lgPatternFound);
  if (extensionChecksum(resultString.toString()) != checkDigit) {
    throw NotFoundException.getNotFoundInstance();
  }
  
  return rowOffset;
}
 
Example 17
Source File: Encoder.java    From analyzer-of-android-for-Apache-Weex with Apache License 2.0 4 votes vote down vote up
public static QRCode encode(String content,
                            ErrorCorrectionLevel ecLevel,
                            Map<EncodeHintType,?> hints) throws WriterException {

  // Determine what character encoding has been specified by the caller, if any
  String encoding = DEFAULT_BYTE_MODE_ENCODING;
  if (hints != null && hints.containsKey(EncodeHintType.CHARACTER_SET)) {
    encoding = hints.get(EncodeHintType.CHARACTER_SET).toString();
  }

  // Pick an encoding mode appropriate for the content. Note that this will not attempt to use
  // multiple modes / segments even if that were more efficient. Twould be nice.
  Mode mode = chooseMode(content, encoding);

  // This will store the header information, like mode and
  // length, as well as "header" segments like an ECI segment.
  BitArray headerBits = new BitArray();

  // Append ECI segment if applicable
  if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
    CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
    if (eci != null) {
      appendECI(eci, headerBits);
    }
  }

  // (With ECI in place,) Write the mode marker
  appendModeInfo(mode, headerBits);

  // Collect data within the main segment, separately, to count its size if needed. Don't add it to
  // main payload yet.
  BitArray dataBits = new BitArray();
  appendBytes(content, mode, dataBits, encoding);

  // Hard part: need to know version to know how many bits length takes. But need to know how many
  // bits it takes to know version. First we take a guess at version by assuming version will be
  // the minimum, 1:

  int provisionalBitsNeeded = headerBits.getSize()
      + mode.getCharacterCountBits(Version.getVersionForNumber(1))
      + dataBits.getSize();
  Version provisionalVersion = chooseVersion(provisionalBitsNeeded, ecLevel);

  // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.

  int bitsNeeded = headerBits.getSize()
      + mode.getCharacterCountBits(provisionalVersion)
      + dataBits.getSize();
  Version version = chooseVersion(bitsNeeded, ecLevel);

  BitArray headerAndDataBits = new BitArray();
  headerAndDataBits.appendBitArray(headerBits);
  // Find "length" of main segment and write it
  int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();
  appendLengthInfo(numLetters, version, mode, headerAndDataBits);
  // Put data together into the overall payload
  headerAndDataBits.appendBitArray(dataBits);

  Version.ECBlocks ecBlocks = version.getECBlocksForLevel(ecLevel);
  int numDataBytes = version.getTotalCodewords() - ecBlocks.getTotalECCodewords();

  // Terminate the bits properly.
  terminateBits(numDataBytes, headerAndDataBits);

  // Interleave data bits with error correction code.
  BitArray finalBits = interleaveWithECBytes(headerAndDataBits,
                                             version.getTotalCodewords(),
                                             numDataBytes,
                                             ecBlocks.getNumBlocks());

  QRCode qrCode = new QRCode();

  qrCode.setECLevel(ecLevel);
  qrCode.setMode(mode);
  qrCode.setVersion(version);

  //  Choose the mask pattern and set to "qrCode".
  int dimension = version.getDimensionForVersion();
  ByteMatrix matrix = new ByteMatrix(dimension, dimension);
  int maskPattern = chooseMaskPattern(finalBits, ecLevel, version, matrix);
  qrCode.setMaskPattern(maskPattern);

  // Build the matrix and set it to "qrCode".
  MatrixUtil.buildMatrix(finalBits, ecLevel, version, maskPattern, matrix);
  qrCode.setMatrix(matrix);

  return qrCode;
}
 
Example 18
Source File: Encoder.java    From QrCodeScanner with GNU General Public License v3.0 4 votes vote down vote up
private static int calculateBitsNeeded(Mode mode,
                                       BitArray headerBits,
                                       BitArray dataBits,
                                       Version version) {
  return headerBits.getSize() + mode.getCharacterCountBits(version) + dataBits.getSize();
}
 
Example 19
Source File: MatrixUtil.java    From barterli_android with Apache License 2.0 4 votes vote down vote up
static void embedDataBits(BitArray dataBits, int maskPattern, ByteMatrix matrix) throws WriterException {
    int bitIndex = 0;
    int direction = -1;
    // Start from the right bottom cell.
    int x = matrix.getWidth() - 1;
    int y = matrix.getHeight() - 1;
    while (x > 0) {
        // Skip the vertical timing pattern.
        if (x == 6) {
            x -= 1;
        }
        while (y >= 0 && y < matrix.getHeight()) {
            for (int i = 0; i < 2; ++i) {
                int xx = x - i;
                // Skip the cell if it's not empty.
                if (!isEmpty(matrix.get(xx, y))) {
                    continue;
                }
                boolean bit;
                if (bitIndex < dataBits.getSize()) {
                    bit = dataBits.get(bitIndex);
                    ++bitIndex;
                } else {
                    // Padding bit. If there is no bit left, we'll fill the
                    // left cells with 0,
                    // as described
                    // in 8.4.9 of JISX0510:2004 (p. 24).
                    bit = false;
                }

                // Skip masking if mask_pattern is -1.
                if (maskPattern != -1) {
                    if (MaskUtil.getDataMaskBit(maskPattern, xx, y)) {
                        bit = !bit;
                    }
                }
                matrix.set(xx, y, bit);
            }
            y += direction;
        }
        direction = -direction; // Reverse the direction.
        y += direction;
        x -= 2; // Move to the left.
    }
    // All bits should be consumed.
    if (bitIndex != dataBits.getSize()) {
        throw new WriterException("Not all bits consumed: " + bitIndex + '/' + dataBits.getSize());
    }
}
 
Example 20
Source File: Encoder.java    From android-quick-response-code with Apache License 2.0 4 votes vote down vote up
public static void encode(String content, ErrorCorrectionLevel ecLevel, Map<EncodeHintType, ?> hints, QRCode qrCode) throws WriterException {

        String encoding = hints == null ? null : (String) hints.get(EncodeHintType.CHARACTER_SET);
        if (encoding == null) {
            encoding = DEFAULT_BYTE_MODE_ENCODING;
        }

        // Step 1: Choose the mode (encoding).
        Mode mode = chooseMode(content, encoding);

        // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
        BitArray dataBits = new BitArray();
        appendBytes(content, mode, dataBits, encoding);
        // Step 3: Initialize QR code that can contain "dataBits".
        int numInputBits = dataBits.getSize();
        initQRCode(numInputBits, ecLevel, mode, qrCode);

        // Step 4: Build another bit vector that contains header and data.
        BitArray headerAndDataBits = new BitArray();

        // Step 4.5: Append ECI message if applicable
        if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.equals(encoding)) {
            CharacterSetECI eci = CharacterSetECI.getCharacterSetECIByName(encoding);
            if (eci != null) {
                appendECI(eci, headerAndDataBits);
            }
        }

        appendModeInfo(mode, headerAndDataBits);

        int numLetters = mode == Mode.BYTE ? dataBits.getSizeInBytes() : content.length();
        appendLengthInfo(numLetters, qrCode.getVersion(), mode, headerAndDataBits);
        headerAndDataBits.appendBitArray(dataBits);

        // Step 5: Terminate the bits properly.
        terminateBits(qrCode.getNumDataBytes(), headerAndDataBits);

        // Step 6: Interleave data bits with error correction code.
        BitArray finalBits = new BitArray();
        interleaveWithECBytes(headerAndDataBits, qrCode.getNumTotalBytes(), qrCode.getNumDataBytes(), qrCode.getNumRSBlocks(), finalBits);

        // Step 7: Choose the mask pattern and set to "qrCode".
        ByteMatrix matrix = new ByteMatrix(qrCode.getMatrixWidth(), qrCode.getMatrixWidth());
        qrCode.setMaskPattern(chooseMaskPattern(finalBits, ecLevel, qrCode.getVersion(), matrix));

        // Step 8. Build the matrix and set it to "qrCode".
        MatrixUtil.buildMatrix(finalBits, ecLevel, qrCode.getVersion(), qrCode.getMaskPattern(), matrix);
        qrCode.setMatrix(matrix);
        // Step 9. Make sure we have a valid QR Code.
        if (!qrCode.isValid()) {
            throw new WriterException("Invalid QR code: " + qrCode.toString());
        }
    }