public ResultNode(Mode mode, int fromPosition, int charsetEncoderIndex, int characterLength, MinimalEncoder encoder, ResultList resultList)
 {
     this.mode                = mode;
     this.fromPosition        = fromPosition;
     this.charsetEncoderIndex = charsetEncoderIndex;
     this.characterLength     = characterLength;
     this.encoder             = encoder;
     this.resultList          = resultList;
 }
            public Edge(Mode mode, int fromPosition, int charsetEncoderIndex, int characterLength, Edge previous, Version version, MinimalEncoder encoder)
            {
                this.mode                = mode;
                this.fromPosition        = fromPosition;
                this.charsetEncoderIndex = mode == Mode.BYTE || previous == null ? charsetEncoderIndex :
                                           previous.charsetEncoderIndex; // inherit the encoding if not of type BYTE
                this.characterLength = characterLength;
                this.previous        = previous;

                int size = previous != null ? previous.cachedTotalSize : 0;

                bool needECI = mode == Mode.BYTE &&
                               (previous == null && this.charsetEncoderIndex != 0) || // at the beginning and charset is not ISO-8859-1
                               (previous != null && this.charsetEncoderIndex != previous.charsetEncoderIndex);

                if (previous == null || mode != previous.mode || needECI)
                {
                    size += 4 + mode.getCharacterCountBits(version);
                }
                switch (mode.Name)
                {
                case Mode.Names.KANJI:
                    size += 13;
                    break;

                case Mode.Names.ALPHANUMERIC:
                    size += characterLength == 1 ? 6 : 11;
                    break;

                case Mode.Names.NUMERIC:
                    size += characterLength == 1 ? 4 : characterLength == 2 ? 7 : 10;
                    break;

                case Mode.Names.BYTE:
                    size += 8 * encoder.encoders[charsetEncoderIndex].GetBytes(encoder.stringToEncode.Substring(fromPosition, characterLength)).Length;
                    if (needECI)
                    {
                        size += 4 + 8;     // the ECI assignment numbers for ISO-8859-x, UTF-8 and UTF-16 are all 8 bit long
                    }
                    break;
                }
                cachedTotalSize = size;
            }
            public ResultList(Version version, Edge solution, MinimalEncoder encoder)
            {
                this.encoder = encoder;
                this.version = version;
                var length      = 0;
                var current     = solution;
                var containsECI = false;

                while (current != null)
                {
                    length += current.characterLength;
                    Edge previous = current.previous;

                    bool needECI = current.mode == Mode.BYTE &&
                                   (previous == null && current.charsetEncoderIndex != 0) || // at the beginning and charset is not ISO-8859-1
                                   (previous != null && current.charsetEncoderIndex != previous.charsetEncoderIndex);

                    if (needECI)
                    {
                        containsECI = true;
                    }

                    if (previous == null || previous.mode != current.mode || needECI)
                    {
                        list.Insert(0, new ResultNode(current.mode, current.fromPosition, current.charsetEncoderIndex, length, encoder, this));
                        length = 0;
                    }

                    if (needECI)
                    {
                        list.Insert(0, new ResultNode(Mode.ECI, current.fromPosition, current.charsetEncoderIndex, 0, encoder, this));
                    }
                    current = previous;
                }

                // prepend FNC1 if needed. If the bits contain an ECI then the FNC1 must be preceeded by an ECI.
                // If there is no ECI at the beginning then we put an ECI to the default charset (ISO-8859-1)
                if (encoder.isGS1)
                {
                    var first = list[0];
                    if (first != null && first.mode != Mode.ECI && containsECI)
                    {
                        // prepend a default character set ECI
                        list.Insert(0, new ResultNode(Mode.ECI, 0, 0, 0, encoder, this));
                    }
                    first = list[0];
                    // prepend or insert a FNC1_FIRST_POSITION after the ECI (if any)
                    var node = new ResultNode(Mode.FNC1_FIRST_POSITION, 0, 0, 0, encoder, this);
                    if (first == null || first.mode != Mode.ECI)
                    {
                        list.Insert(0, node);
                    }
                    else
                    {
                        list.Insert(1, node);
                    }
                }

                // set version to smallest version into which the bits fit.
                int versionNumber = version.VersionNumber;
                int lowerLimit;
                int upperLimit;

                switch (getVersionSize(version))
                {
                case VersionSize.SMALL:
                    lowerLimit = 1;
                    upperLimit = 9;
                    break;

                case VersionSize.MEDIUM:
                    lowerLimit = 10;
                    upperLimit = 26;
                    break;

                case VersionSize.LARGE:
                default:
                    lowerLimit = 27;
                    upperLimit = 40;
                    break;
                }
                int size = getSize(version);

                // increase version if needed
                while (versionNumber < upperLimit && !Encoder.willFit(size, Version.getVersionForNumber(versionNumber), encoder.ecLevel))
                {
                    versionNumber++;
                }
                // shrink version if possible
                while (versionNumber > lowerLimit && Encoder.willFit(size, Version.getVersionForNumber(versionNumber - 1), encoder.ecLevel))
                {
                    versionNumber--;
                }
                this.version = Version.getVersionForNumber(versionNumber);
            }
Beispiel #4
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)
        {
            Version  version;
            BitArray headerAndDataBits;
            Mode     mode;

            var hasGS1FormatHint = hints != null && hints.ContainsKey(EncodeHintType.GS1_FORMAT) &&
                                   hints[EncodeHintType.GS1_FORMAT] != null && Convert.ToBoolean(hints[EncodeHintType.GS1_FORMAT].ToString());
            var hasCompactionHint = hints != null && hints.ContainsKey(EncodeHintType.QR_COMPACT) &&
                                    hints[EncodeHintType.QR_COMPACT] != null && Convert.ToBoolean(hints[EncodeHintType.QR_COMPACT].ToString());

            // Determine what character encoding has been specified by the caller, if any
            bool hasEncodingHint = hints != null && hints.ContainsKey(EncodeHintType.CHARACTER_SET);

            var encoding = StringUtils.PLATFORM_DEFAULT_ENCODING_T;
            // 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;

            if (hasCompactionHint)
            {
                mode = Mode.BYTE;

                var priorityEncoding = encoding.Equals(DEFAULT_BYTE_MODE_ENCODING) ? null : encoding;
                var rn = MinimalEncoder.encode(content, null, priorityEncoding, hasGS1FormatHint, ecLevel);

                headerAndDataBits = new BitArray();
                rn.getBits(headerAndDataBits);
                version = rn.getVersion();
            }
            else
            {
                // 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.
                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.getCharacterSetECI(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
                if (hasGS1FormatHint)
                {
                    // 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);

                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);
                }

                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);

            // Enable manual selection of the pattern to be used via hint
            var maskPattern = -1;

            if (hints != null && hints.ContainsKey(EncodeHintType.QR_MASK_PATTERN))
            {
                var hintMaskPattern = Int32.Parse(hints[EncodeHintType.QR_MASK_PATTERN].ToString());
                maskPattern = QRCode.isValidMaskPattern(hintMaskPattern) ? hintMaskPattern : -1;
            }

            if (maskPattern == -1)
            {
                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);
        }