예제 #1
0
 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");
 }
예제 #2
0
        /// <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));
        }
예제 #3
0
        /// <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);
        }
예제 #4
0
        private 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);
        }
예제 #5
0
 /// <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()]);
 }
        internal static DecoderResult decode(byte[] bytes,
                                             Version version,
                                             ErrorCorrectionLevel ecLevel,
                                             IDictionary <DecodeHintType, object> hints)
        {
            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, hints))
                            {
                                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);
            }

#if WindowsCE
            var resultString = result.ToString().Replace("\n", "\r\n");
#else
            var resultString = result.ToString().Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
#endif
            return(new DecoderResult(bytes,
                                     resultString,
                                     byteSegments.Count == 0 ? null : byteSegments,
                                     ecLevel == null ? null : ecLevel.ToString(),
                                     symbolSequence, parityData));
        }
예제 #7
0
        /// <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,
                                    IDictionary <EncodeHintType, object> hints)
        {
            // Determine what character encoding has been specified by the caller, if any
            bool hasEncodingHint = hints != null && hints.ContainsKey(EncodeHintType.CHARACTER_SET);

#if !SILVERLIGHT || WINDOWS_PHONE
            var encoding = hints == null || !hints.ContainsKey(EncodeHintType.CHARACTER_SET) ? null : (String)hints[EncodeHintType.CHARACTER_SET];
            if (encoding == null)
            {
                encoding = DEFAULT_BYTE_MODE_ENCODING;
            }
            var generateECI = hasEncodingHint || !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding);
#else
            // Silverlight supports only UTF-8 and UTF-16 out-of-the-box
            const string encoding = "UTF-8";
            // caller of the method can only control if the ECI segment should be written
            // character set is fixed to UTF-8; but some scanners doesn't like the ECI segment
            var generateECI = hasEncodingHint;
#endif

            // 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)
                {
                    var eciIsExplicitDisabled = (hints != null && hints.ContainsKey(EncodeHintType.DISABLE_ECI) && hints[EncodeHintType.DISABLE_ECI] != null && Convert.ToBoolean(hints[EncodeHintType.DISABLE_ECI].ToString()));
                    if (!eciIsExplicitDisabled)
                    {
                        appendECI(eci, headerBits);
                    }
                }
            }

            // Append the FNC1 mode header for GS1 formatted data if applicable
            var hasGS1FormatHint = hints != null && hints.ContainsKey(EncodeHintType.GS1_FORMAT);
            if (hasGS1FormatHint && hints[EncodeHintType.GS1_FORMAT] != null && Convert.ToBoolean(hints[EncodeHintType.GS1_FORMAT].ToString()))
            {
                // GS1 formatted codes are prefixed with a FNC1 in first position mode header
                appendModeInfo(Mode.FNC1_FIRST_POSITION, 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;
            if (hints != null && hints.ContainsKey(EncodeHintType.QR_VERSION))
            {
                int versionNumber = Int32.Parse(hints[EncodeHintType.QR_VERSION].ToString());
                version = Version.getVersionForNumber(versionNumber);
                int bitsNeeded = calculateBitsNeeded(mode, headerBits, dataBits, version);
                if (!willFit(bitsNeeded, version, ecLevel))
                {
                    throw new WriterException("Data too big for requested version");
                }
            }
            else
            {
                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);
        }
예제 #8
0
 /// <summary>
 /// Encode "bytes" with the error correction level "ecLevel". The encoding mode will be chosen
 /// internally by chooseMode(). On success, store the result in "qrCode".
 /// We recommend you to use QRCode.EC_LEVEL_L (the lowest level) for
 /// "getECLevel" since our primary use is to show QR code on desktop screens. We don't need very
 /// strong error correction for this purpose.
 /// Note that there is no way to encode bytes in MODE_KANJI. We might want to add EncodeWithMode()
 /// with which clients can specify the encoding mode. For now, we don't need the functionality.
 /// </summary>
 /// <param name="content">text to encode</param>
 /// <param name="ecLevel">error correction level to use</param>
 /// <returns><see cref="QRCode"/> representing the encoded QR code</returns>
 public static QRCode encode(String content, ErrorCorrectionLevel ecLevel)
 {
     return(encode(content, ecLevel, null));
 }