private static Version ChooseVersion(int numInputBits, ErrorCorrectionLevel ecLevel) { for (int versionNum = 1; versionNum <= 40; versionNum++) { var version = Version.GetVersionForNumber(versionNum); if (WillFit(numInputBits, version, ecLevel)) { return version; } } throw new WriterException("Data too big"); }
/// <summary> /// Decides the smallest version of QR code that will contain all of the provided data. /// </summary> /// <exception cref="WriterException">if the data cannot fit in any version</exception> private static Version RecommendVersion(ErrorCorrectionLevel ecLevel, Mode mode, BitArray headerBits, BitArray dataBits) { // 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: var provisionalBitsNeeded = CalculateBitsNeeded(mode, headerBits, dataBits, Version.GetVersionForNumber(1)); var provisionalVersion = ChooseVersion(provisionalBitsNeeded, ecLevel); // Use that guess to calculate the right version. I am still not sure this works in 100% of cases. var bitsNeeded = CalculateBitsNeeded(mode, headerBits, dataBits, provisionalVersion); return ChooseVersion(bitsNeeded, ecLevel); }
/// <summary></summary> /// <returns>true if the number of input bits will fit in a code with the specified version and error correction level.</returns> private static bool WillFit(int numInputBits, Version version, ErrorCorrectionLevel ecLevel) { // In the following comments, we use numbers of Version 7-H. // numBytes = 196 var numBytes = version.TotalCodewords; // getNumECBytes = 130 var ecBlocks = version.GetECBlocksForLevel(ecLevel); var numEcBytes = ecBlocks.TotalECCodewords; // getNumDataBytes = 196 - 130 = 66 var numDataBytes = numBytes - numEcBytes; var totalInputBytes = (numInputBits + 7) / 8; return numDataBytes >= totalInputBytes; }
static int ChooseMaskPattern(BitArray bits, ErrorCorrectionLevel ecLevel, Version version, ByteMatrix matrix) { int minPenalty = Int32.MaxValue; // Lower penalty is better. int bestMaskPattern = -1; // We try all mask patterns to choose the best one. for (int maskPattern = 0; maskPattern < QRCode.NUM_MASK_PATTERNS; maskPattern++) { MatrixUtil.BuildMatrix(bits, ecLevel, version, maskPattern, matrix); int penalty = CalculateMaskPenalty(matrix); if (penalty < minPenalty) { minPenalty = penalty; bestMaskPattern = maskPattern; } } return bestMaskPattern; }
internal static DecoderResult Decode(byte[] bytes, Version version, ErrorCorrectionLevel ecLevel) { var bits = new BitSource(bytes); var result = new StringBuilder(50); var byteSegments = new List <byte[]>(1); var symbolSequence = -1; var parityData = -1; try { CharacterSetECI currentCharacterSetECI = null; bool fc1InEffect = false; Mode mode; do { // While still another segment to read... if (bits.Available() < 4) { // OK, assume we're done. Really, a TERMINATOR mode should have been recorded here mode = Mode.TERMINATOR; } else { try { mode = Mode.ForBits(bits.ReadBits(4)); // mode is encoded by 4 bits } catch (ArgumentException) { return(null); } } switch (mode.Name) { case Mode.Names.TERMINATOR: break; case Mode.Names.FNC1_FIRST_POSITION: case Mode.Names.FNC1_SECOND_POSITION: // We do little with FNC1 except alter the parsed result a bit according to the spec fc1InEffect = true; break; case Mode.Names.STRUCTURED_APPEND: if (bits.Available() < 16) { return(null); } // not really supported; but sequence number and parity is added later to the result metadata // Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue symbolSequence = bits.ReadBits(8); parityData = bits.ReadBits(8); break; case Mode.Names.ECI: // Count doesn't apply to ECI int value = ParseECIValue(bits); currentCharacterSetECI = CharacterSetECI.GetCharacterSetECIByValue(value); if (currentCharacterSetECI == null) { return(null); } break; case Mode.Names.HANZI: // First handle Hanzi mode which does not start with character count //chinese mode contains a sub set indicator right after mode indicator int subset = bits.ReadBits(4); int countHanzi = bits.ReadBits(mode.GetCharacterCountBits(version)); if (subset == GB2312_SUBSET) { if (!DecodeHanziSegment(bits, result, countHanzi)) { return(null); } } break; default: // "Normal" QR code modes: // How many characters will follow, encoded in this mode? int count = bits.ReadBits(mode.GetCharacterCountBits(version)); switch (mode.Name) { case Mode.Names.NUMERIC: if (!DecodeNumericSegment(bits, result, count)) { return(null); } break; case Mode.Names.ALPHANUMERIC: if (!DecodeAlphanumericSegment(bits, result, count, fc1InEffect)) { return(null); } break; case Mode.Names.BYTE: if (!DecodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments)) { return(null); } break; case Mode.Names.KANJI: if (!DecodeKanjiSegment(bits, result, count)) { return(null); } break; default: return(null); } break; } } while (mode != Mode.TERMINATOR); } catch (ArgumentException) { // from readBits() calls return(null); } var resultString = result.ToString().Replace("\r\n", "\n").Replace("\n", Environment.NewLine); return(new DecoderResult(bytes, resultString)); }
/// <summary> /// Gets the EC blocks for level. /// </summary> /// <param name="ecLevel">The ec level.</param> /// <returns></returns> public ECBlocks GetECBlocksForLevel(ErrorCorrectionLevel ecLevel) { return(ecBlocks[ecLevel.Ordinal]); }
/// <summary> /// Encodes the specified content. /// </summary> /// <param name="content">The content.</param> /// <param name="ecLevel">The ec level.</param> /// <param name="hints">The hints.</param> /// <returns></returns> public static QRCode Encode(String content, ErrorCorrectionLevel ecLevel) { var encoding = DEFAULT_BYTE_MODE_ENCODING; var generateECI = !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding); // 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. var mode = ChooseMode(content, encoding); // This will store the header information, like mode and // length, as well as "header" segments like an ECI segment. var headerBits = new BitArray(); // Append ECI segment if applicable if (mode == Mode.BYTE && generateECI) { var 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. var dataBits = new BitArray(); AppendBytes(content, mode, dataBits, encoding); Version version = RecommendVersion(ecLevel, mode, headerBits, dataBits); var headerAndDataBits = new BitArray(); headerAndDataBits.AppendBitArray(headerBits); // Find "length" of main segment and write it var numLetters = mode == Mode.BYTE ? dataBits.SizeInBytes : content.Length; AppendLengthInfo(numLetters, version, mode, headerAndDataBits); // Put data together into the overall payload headerAndDataBits.AppendBitArray(dataBits); var ecBlocks = version.GetECBlocksForLevel(ecLevel); var numDataBytes = version.TotalCodewords - ecBlocks.TotalECCodewords; // Terminate the bits properly. TerminateBits(numDataBytes, headerAndDataBits); // Interleave data bits with error correction code. var finalBits = InterleaveWithECBytes(headerAndDataBits, version.TotalCodewords, numDataBytes, ecBlocks.NumBlocks); var qrCode = new QRCode { ECLevel = ecLevel, Mode = mode, Version = version }; // Choose the mask pattern and set to "qrCode". var dimension = version.DimensionForVersion; var matrix = new ByteMatrix(dimension, dimension); var maskPattern = ChooseMaskPattern(finalBits, ecLevel, version, matrix); qrCode.MaskPattern = maskPattern; // Build the matrix and set it to "qrCode". MatrixUtil.BuildMatrix(finalBits, ecLevel, version, maskPattern, matrix); qrCode.Matrix = matrix; return qrCode; }