(segs: Readonly<Array<QrSegment>>, ecl: QrCode.Ecc, minVersion: int = 1, maxVersion: int = 40, mask: int = -1, boostEcl: boolean = true)
| 69 | // between modes (such as alphanumeric and byte) to encode text in less space. |
| 70 | // This is a mid-level API; the high-level API is encodeText() and encodeBinary(). |
| 71 | public static encodeSegments(segs: Readonly<Array<QrSegment>>, ecl: QrCode.Ecc, |
| 72 | minVersion: int = 1, maxVersion: int = 40, |
| 73 | mask: int = -1, boostEcl: boolean = true): QrCode { |
| 74 | |
| 75 | if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION) |
| 76 | || mask < -1 || mask > 7) |
| 77 | throw new RangeError("Invalid value"); |
| 78 | |
| 79 | // Find the minimal version number to use |
| 80 | let version: int; |
| 81 | let dataUsedBits: int; |
| 82 | for (version = minVersion; ; version++) { |
| 83 | const dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; // Number of data bits available |
| 84 | const usedBits: number = QrSegment.getTotalBits(segs, version); |
| 85 | if (usedBits <= dataCapacityBits) { |
| 86 | dataUsedBits = usedBits; |
| 87 | break; // This version number is found to be suitable |
| 88 | } |
| 89 | if (version >= maxVersion) // All versions in the range could not fit the given data |
| 90 | throw new RangeError("Data too long"); |
| 91 | } |
| 92 | |
| 93 | // Increase the error correction level while the data still fits in the current version number |
| 94 | for (const newEcl of [QrCode.Ecc.MEDIUM, QrCode.Ecc.QUARTILE, QrCode.Ecc.HIGH]) { // From low to high |
| 95 | if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) |
| 96 | ecl = newEcl; |
| 97 | } |
| 98 | |
| 99 | // Concatenate all segments to create the data bit string |
| 100 | let bb: Array<bit> = [] |
| 101 | for (const seg of segs) { |
| 102 | appendBits(seg.mode.modeBits, 4, bb); |
| 103 | appendBits(seg.numChars, seg.mode.numCharCountBits(version), bb); |
| 104 | for (const b of seg.getData()) |
| 105 | bb.push(b); |
| 106 | } |
| 107 | assert(bb.length == dataUsedBits); |
| 108 | |
| 109 | // Add terminator and pad up to a byte if applicable |
| 110 | const dataCapacityBits: int = QrCode.getNumDataCodewords(version, ecl) * 8; |
| 111 | assert(bb.length <= dataCapacityBits); |
| 112 | appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb); |
| 113 | appendBits(0, (8 - bb.length % 8) % 8, bb); |
| 114 | assert(bb.length % 8 == 0); |
| 115 | |
| 116 | // Pad with alternating bytes until data capacity is reached |
| 117 | for (let padByte = 0xEC; bb.length < dataCapacityBits; padByte ^= 0xEC ^ 0x11) |
| 118 | appendBits(padByte, 8, bb); |
| 119 | |
| 120 | // Pack bits into bytes in big endian |
| 121 | let dataCodewords: Array<byte> = []; |
| 122 | while (dataCodewords.length * 8 < bb.length) |
| 123 | dataCodewords.push(0); |
| 124 | bb.forEach((b: bit, i: int) => |
| 125 | dataCodewords[i >>> 3] |= b << (7 - (i & 7))); |
| 126 | |
| 127 | // Create the QR Code object |
| 128 | return new QrCode(version, ecl, dataCodewords, mask); |
no test coverage detected