// Returns a new list of segments based on the given text and modes, such that // consecutive code points in the same mode are put into the same segment. private static List <QrSegment> SplitIntoSegments(int[] codePoints, QrSegment.Mode[] charModes) { if (codePoints.Length == 0) { throw new ArgumentOutOfRangeException((codePoints.GetType().Name)); } List <QrSegment> result = new List <QrSegment>(); // Accumulate run of modes QrSegment.Mode curMode = charModes[0]; int start = 0; for (int i = 1;; i++) { if (i < codePoints.Length && charModes[i] == curMode) { continue; } string s = FromCodePoints(codePoints, start, i - start); if (curMode == QrSegment.Mode.Byte) { result.Add(QrSegment.MakeBytes(Encoding.UTF8.GetBytes(s))); } else if (curMode == QrSegment.Mode.Numeric) { result.Add(QrSegment.MakeNumeric(s)); } else if (curMode == QrSegment.Mode.Alphanumeric) { result.Add(QrSegment.MakeAlphanumeric(s)); } else if (curMode == QrSegment.Mode.Kanji) { result.Add(MakeKanji(s)); } else { Debug.Assert(false); } if (i >= codePoints.Length) { return(result); } curMode = charModes[i]; start = i; } }
/// <summary> /// Creates a list of zero or more segments to represent the specified text string. /// The resulting list optimally minimizes the total encoded bit length, subjected to the constraints /// of the specified error correction level, minimum and maximum version number. /// <para> /// This function potentially uses all four text encoding modes: numeric, alphanumeric, byte (UTF-8), /// and Kanji. It is a more sophisticated but slower replacement for <see cref="MakeSegments"/>. /// </para> /// <para> /// The text to be encoded can contain the full set of Unicode characters (code points). /// </para> /// </summary> /// <param name="text">The text to be encoded.</param> /// <param name="ecl">The 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> /// <returns>The created mutable list of segments encoding the specified text with a minimal bit length.</returns> /// <exception cref="ArgumentNullException"><paramref name="text"/> or <paramref name="ecl"/> is <c>null</c>.</exception> /// <exception cref="ArgumentOutOfRangeException">1 ≤ minVersion ≤ maxVersion ≤ 40 is violated.</exception> /// <exception cref="DataTooLongException">The text is too long to fit into the QR code with the given encoding parameters.</exception> public static List <QrSegment> MakeSegmentsOptimally(string text, QrCode.Ecc ecl, int minVersion, int maxVersion) { // Check arguments Objects.RequireNonNull(text); Objects.RequireNonNull(ecl); if (minVersion < QrCode.MinVersion || minVersion > maxVersion) { throw new ArgumentOutOfRangeException(nameof(minVersion), "Invalid value"); } if (maxVersion > QrCode.MaxVersion) { throw new ArgumentOutOfRangeException(nameof(maxVersion), "Invalid value"); } // Iterate through version numbers, and make tentative segments List <QrSegment> segs = null; int[] codePoints = ToCodePoints(text); for (int version = minVersion;; version++) { if (version == minVersion || version == 10 || version == 27) { segs = MakeSegmentsOptimally(codePoints, version); } Debug.Assert(segs != null); // Check if the segments fit int dataCapacityBits = QrCode.GetNumDataCodewords(version, ecl) * 8; int dataUsedBits = QrSegment.GetTotalBits(segs, version); if (dataUsedBits != -1 && dataUsedBits <= dataCapacityBits) { return(segs); // This version number is found to be suitable } if (version < maxVersion) { continue; } // All versions in the range could not fit the given text string msg = "Segment too long"; if (dataUsedBits != -1) { msg = "Data length = {dataUsedBits} bits, Max capacity = {dataCapacityBits} bits"; } throw new DataTooLongException(msg); } }