/// <summary> /// Creates a QR code representing the specified segments with the specified encoding parameters. /// <para> /// The smallest possible QR code version (size) is used. The range of versions can be /// restricted by the <paramref name="minVersion"/> and <paramref name="maxVersion"/> parameters. /// </para> /// <para> /// If <paramref name="boostEcl"/> is <c>true</c>, the resulting ECC level will be higher than the /// one specified if it can be achieved without increasing the size (version). /// </para> /// <para> /// The QR code mask is usually automatically chosen. It can be explicitly set with the <paramref name="mask"/> /// parameter by using a value between 0 to 7 (inclusive). -1 is for automatic mode (which may be slow). /// </para> /// <para> /// 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 and gives full control over all /// encoding paramters. /// </para> /// </summary> /// <remarks> /// This is a mid-level API; the high-level APIs are <see cref="EncodeText(string, Ecc)"/> /// and <see cref="EncodeBinary(byte[], Ecc)"/>. /// </remarks> /// <param name="segments">The segments to encode.</param> /// <param name="ecl">The minimal or fixed error correction level to use .</param> /// <param name="minVersion">The minimum version (size) of the QR code (between 1 and 40).</param> /// <param name="maxVersion">The maximum version (size) of the QR code (between 1 and 40).</param> /// <param name="mask">The mask number to use (between 0 and 7), or -1 for automatic mask selection.</param> /// <param name="boostEcl">If <c>true</c> the ECC level wil be increased if it can be achieved without increasing the size (version).</param> /// <returns>The created QR code representing the segments.</returns> /// <exception cref="ArgumentNullException"><paramref name="segments"/>, any list element, or <paramref name="ecl"/> is <c>null</c>.</exception> /// <exception cref="ArgumentOutOfRangeException">1 ≤ minVersion ≤ maxVersion ≤ 40 /// or -1 ≤ mask ≤ 7 is violated.</exception> /// <exception cref="DataTooLongException">The segments are too long to fit in the largest QR code size (version) /// at the specified error correction level.</exception> public static QrCode EncodeSegments(List <QrSegment> segments, Ecc ecl, int minVersion = MinVersion, int maxVersion = MaxVersion, int mask = -1, bool boostEcl = true) { Objects.RequireNonNull(segments); Objects.RequireNonNull(ecl); if (minVersion < MinVersion || minVersion > maxVersion) { throw new ArgumentOutOfRangeException(nameof(minVersion), "Invalid value"); } if (maxVersion > MaxVersion) { throw new ArgumentOutOfRangeException(nameof(maxVersion), "Invalid value"); } if (mask < -1 || mask > 7) { throw new ArgumentOutOfRangeException(nameof(mask), "Invalid value"); } // Find the minimal version number to use int version, dataUsedBits; for (version = minVersion; ; version++) { int numDataBits = GetNumDataCodewords(version, ecl) * 8; // Number of data bits available dataUsedBits = QrSegment.GetTotalBits(segments, version); if (dataUsedBits != -1 && dataUsedBits <= numDataBits) { break; // This version number is found to be suitable } if (version >= maxVersion) { // All versions in the range could not fit the given data string msg = "Segment too long"; if (dataUsedBits != -1) { msg = $"Data length = {dataUsedBits} bits, Max capacity = {numDataBits} bits"; } throw new DataTooLongException(msg); } } Debug.Assert(dataUsedBits != -1); // Increase the error correction level while the data still fits in the current version number foreach (Ecc newEcl in Ecc.AllValues) { // From low to high if (boostEcl && dataUsedBits <= GetNumDataCodewords(version, newEcl) * 8) { ecl = newEcl; } } // Concatenate all segments to create the data bit string BitArray ba = new BitArray(0); foreach (QrSegment seg in segments) { ba.AppendBits(seg.EncodingMode.ModeBits, 4); ba.AppendBits((uint)seg.NumChars, seg.EncodingMode.NumCharCountBits(version)); ba.AppendData(seg.GetData()); } Debug.Assert(ba.Length == dataUsedBits); // Add terminator and pad up to a byte if applicable int dataCapacityBits = GetNumDataCodewords(version, ecl) * 8; Debug.Assert(ba.Length <= dataCapacityBits); ba.AppendBits(0, Math.Min(4, dataCapacityBits - ba.Length)); ba.AppendBits(0, (8 - ba.Length % 8) % 8); Debug.Assert(ba.Length % 8 == 0); // Pad with alternating bytes until data capacity is reached for (uint padByte = 0xEC; ba.Length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) { ba.AppendBits(padByte, 8); } // Pack bits into bytes in big endian byte[] dataCodewords = new byte[ba.Length / 8]; for (int i = 0; i < ba.Length; i++) { if (ba.Get(i)) { dataCodewords[i >> 3] |= (byte)(1 << (7 - (i & 7))); } } // Create the QR code object return(new QrCode(version, ecl, dataCodewords, mask)); }