Skip to main content

rxing/qrcode/encoder/
qrcode_encoder.rs

1/*
2 * Copyright 2008 ZXing authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17use unicode_segmentation::UnicodeSegmentation;
18
19use crate::{
20    EncodeHints, Exceptions,
21    common::{
22        BitArray, BitFieldBaseType, CharacterSet, Eci, Result,
23        reedsolomon::{PredefinedGenericGF, ReedSolomonEncoder, get_predefined_genericgf},
24    },
25    qrcode::decoder::{ErrorCorrectionLevel, Mode, Version, VersionRef},
26};
27
28use super::{BlockPair, ByteMatrix, MinimalEncoder, QRCode, mask_util, matrix_util};
29
30static SHIFT_JIS_CHARSET: CharacterSet = CharacterSet::Shift_JIS;
31
32// The original table is defined in the table 5 of JISX0510:2004 (p.19).
33const ALPHANUMERIC_TABLE: [i8; 96] = [
34    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x00-0x0f
35    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0x10-0x1f
36    36, -1, -1, -1, 37, 38, -1, -1, -1, -1, 39, 40, -1, 41, 42, 43, // 0x20-0x2f
37    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 44, -1, -1, -1, -1, -1, // 0x30-0x3f
38    -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f
39    25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, // 0x50-0x5f
40];
41
42pub const DEFAULT_BYTE_MODE_ENCODING: CharacterSet = CharacterSet::ISO8859_1;
43
44// The mask penalty calculation is complicated.  See Table 21 of JISX0510:2004 (p.45) for details.
45// Basically it applies four rules and summate all penalties.
46pub fn calculateMaskPenalty(matrix: &ByteMatrix) -> u32 {
47    mask_util::applyMaskPenaltyRule1(matrix)
48        + mask_util::applyMaskPenaltyRule2(matrix)
49        + mask_util::applyMaskPenaltyRule3(matrix)
50        + mask_util::applyMaskPenaltyRule4(matrix)
51}
52
53/**
54 * @param content text to encode
55 * @param ecLevel error correction level to use
56 * @return {@link QRCode} representing the encoded QR code
57 * @throws WriterException if encoding can't succeed, because of for example invalid content
58 *   or configuration
59 */
60pub fn encode(content: &str, ecLevel: ErrorCorrectionLevel) -> Result<QRCode> {
61    encode_with_hints(content, ecLevel, &EncodeHints::default())
62}
63
64pub fn encode_with_hints(
65    content: &str,
66    ec_level: ErrorCorrectionLevel,
67    hints: &EncodeHints,
68) -> Result<QRCode> {
69    let version;
70    let mut header_and_data_bits;
71    let mode;
72
73    let has_gs1_format_hint = matches!(hints.Gs1Format, Some(true));
74
75    let has_compaction_hint = if let Some(vb) = &hints.QrCompact {
76        vb.parse::<bool>().unwrap_or_default()
77    } else {
78        false
79    };
80
81    // Determine what character encoding has been specified by the caller, if any
82    let mut encoding = None; //DEFAULT_BYTE_MODE_ENCODING;
83    let mut has_encoding_hint = hints.CharacterSet.is_some();
84    if has_encoding_hint {
85        if let Some(v) = &hints.CharacterSet {
86            encoding = Some(CharacterSet::get_character_set_by_name(v).ok_or(Exceptions::WRITER)?)
87        }
88    }
89
90    if has_compaction_hint {
91        mode = Mode::BYTE;
92
93        // dbg!("consider this a huge risk, not sure if it should be defaulting to default");
94        let priority_encoding = encoding; //if encoding.name() == DEFAULT_BYTE_MODE_ENCODING.name()  {None} else {Some(encoding)};
95        let rn = MinimalEncoder::encode_with_details(
96            content,
97            None,
98            priority_encoding,
99            has_gs1_format_hint,
100            ec_level,
101        )?;
102
103        header_and_data_bits = BitArray::new();
104        rn.getBits(&mut header_and_data_bits)?;
105        version = rn.getVersion();
106    } else {
107        //Switch to default encoding
108        let encoding = if let Some(encoding) = encoding {
109            encoding
110        } else if let Ok(_encs) = DEFAULT_BYTE_MODE_ENCODING.encode(content) {
111            DEFAULT_BYTE_MODE_ENCODING
112        } else {
113            has_encoding_hint = true;
114            CharacterSet::UTF8
115        };
116
117        // Pick an encoding mode appropriate for the content. Note that this will not attempt to use
118        // multiple modes / segments even if that were more efficient.
119        mode = chooseModeWithEncoding(content, encoding);
120
121        // This will store the header information, like mode and
122        // length, as well as "header" segments like an ECI segment.
123        let mut header_bits = BitArray::new();
124
125        // Append ECI segment if applicable
126        if mode == Mode::BYTE && has_encoding_hint {
127            appendECI(encoding.into(), &mut header_bits)?;
128        }
129
130        // Append the FNC1 mode header for GS1 formatted data if applicable
131        if has_gs1_format_hint {
132            // GS1 formatted codes are prefixed with a FNC1 in first position mode header
133            appendModeInfo(Mode::FNC1_FIRST_POSITION, &mut header_bits)?;
134        }
135
136        // (With ECI in place,) Write the mode marker
137        appendModeInfo(mode, &mut header_bits)?;
138
139        // Collect data within the main segment, separately, to count its size if needed. Don't add it to
140        // main payload yet.
141        let mut data_bits = BitArray::new();
142        appendBytes(content, mode, &mut data_bits, encoding)?;
143
144        if hints.QrVersion.is_some() {
145            let versionNumber = if let Some(v) = &hints.QrVersion {
146                v.parse::<u32>().unwrap_or_default()
147            } else {
148                0
149            };
150
151            version = Version::getVersionForNumber(versionNumber)?;
152            let bitsNeeded = calculateBitsNeeded(mode, &header_bits, &data_bits, version);
153            if !willFit(bitsNeeded, version, &ec_level) {
154                return Err(Exceptions::writer_with(
155                    "Data too big for requested version",
156                ));
157            }
158        } else {
159            version = recommendVersion(&ec_level, mode, &header_bits, &data_bits)?;
160        }
161
162        header_and_data_bits = BitArray::new();
163        header_and_data_bits.appendBitArray(header_bits);
164        // Find "length" of main segment and write it
165        let num_letters = if mode == Mode::BYTE {
166            data_bits.getSizeInBytes()
167        } else {
168            content.graphemes(true).count()
169        };
170        appendLengthInfo(num_letters as u32, version, mode, &mut header_and_data_bits)?;
171        // Put data together into the overall payload
172        header_and_data_bits.appendBitArray(data_bits);
173    }
174
175    let ec_blocks = version.getECBlocksForLevel(ec_level);
176    let num_data_bytes = version.getTotalCodewords() - ec_blocks.getTotalECCodewords();
177
178    // Terminate the bits properly.
179    terminateBits(num_data_bytes, &mut header_and_data_bits)?;
180
181    // Interleave data bits with error correction code.
182    let final_bits = interleaveWithECBytes(
183        &header_and_data_bits,
184        version.getTotalCodewords(),
185        num_data_bytes,
186        ec_blocks.getNumBlocks(),
187    )?;
188
189    let mut qrCode = QRCode::new();
190
191    qrCode.setECLevel(ec_level);
192    qrCode.setMode(mode);
193    qrCode.setVersion(version);
194
195    //  Choose the mask pattern and set to "qrCode".
196    let dimension = version.getDimensionForVersion();
197    let mut matrix = ByteMatrix::new(dimension, dimension);
198
199    // Enable manual selection of the pattern to be used via hint
200    let mut mask_pattern = -1;
201    if let Some(v) = &hints.QrMaskPattern {
202        let hint_mask_pattern = v.parse::<i32>().unwrap_or(-1);
203
204        mask_pattern = if QRCode::isValidMaskPattern(hint_mask_pattern) {
205            hint_mask_pattern
206        } else {
207            -1
208        };
209    }
210
211    if mask_pattern == -1 {
212        mask_pattern = chooseMaskPattern(&final_bits, &ec_level, version, &mut matrix)? as i32;
213    }
214    qrCode.setMaskPattern(mask_pattern);
215
216    // Build the matrix and set it to "qrCode".
217    matrix_util::buildMatrix(&final_bits, &ec_level, version, mask_pattern, &mut matrix)?;
218    qrCode.setMatrix(matrix);
219
220    Ok(qrCode)
221}
222
223/**
224 * Decides the smallest version of QR code that will contain all of the provided data.
225 *
226 * @throws WriterException if the data cannot fit in any version
227 */
228fn recommendVersion(
229    ec_level: &ErrorCorrectionLevel,
230    mode: Mode,
231    header_bits: &BitArray,
232    data_bits: &BitArray,
233) -> Result<VersionRef> {
234    // Hard part: need to know version to know how many bits length takes. But need to know how many
235    // bits it takes to know version. First we take a guess at version by assuming version will be
236    // the minimum, 1:
237    let provisional_bits_needed = calculateBitsNeeded(
238        mode,
239        header_bits,
240        data_bits,
241        Version::getVersionForNumber(1)?,
242    );
243    let provisional_version = chooseVersion(provisional_bits_needed, ec_level)?;
244
245    // Use that guess to calculate the right version. I am still not sure this works in 100% of cases.
246    let bits_needed = calculateBitsNeeded(mode, header_bits, data_bits, provisional_version);
247
248    chooseVersion(bits_needed, ec_level)
249}
250
251fn calculateBitsNeeded(
252    mode: Mode,
253    header_bits: &BitArray,
254    data_bits: &BitArray,
255    version: VersionRef,
256) -> u32 {
257    (header_bits.get_size() + mode.getCharacterCountBits(version) as usize + data_bits.get_size())
258        as u32
259}
260
261/**
262 * @return the code point of the table used in alphanumeric mode or
263 *  -1 if there is no corresponding code in the table.
264 */
265pub fn getAlphanumericCode(code: u32) -> i8 {
266    let code = code as usize;
267    if code < ALPHANUMERIC_TABLE.len() {
268        ALPHANUMERIC_TABLE[code]
269    } else {
270        -1
271    }
272}
273
274pub fn chooseMode(content: &str) -> Mode {
275    chooseModeWithEncoding(content, CharacterSet::ISO8859_1)
276}
277
278/**
279 * Choose the best mode by examining the content. Note that 'encoding' is used as a hint;
280 * if it is Shift_JIS, and the input is only double-byte Kanji, then we return {@link Mode#KANJI}.
281 */
282fn chooseModeWithEncoding(content: &str, encoding: CharacterSet) -> Mode {
283    if SHIFT_JIS_CHARSET == encoding && isOnlyDoubleByteKanji(content) {
284        // Choose Kanji mode if all input are double-byte characters
285        return Mode::KANJI;
286    }
287    let mut has_numeric = false;
288    let mut has_alphanumeric = false;
289    for c in content.chars() {
290        if c.is_ascii_digit() {
291            has_numeric = true;
292        } else if getAlphanumericCode(c as u32) != -1 {
293            has_alphanumeric = true;
294        } else {
295            return Mode::BYTE;
296        }
297    }
298    if has_alphanumeric {
299        return Mode::ALPHANUMERIC;
300    }
301    if has_numeric {
302        return Mode::NUMERIC;
303    }
304    Mode::BYTE
305}
306
307pub fn isOnlyDoubleByteKanji(content: &str) -> bool {
308    let bytes = if let Ok(byt) = SHIFT_JIS_CHARSET.encode(content) {
309        byt
310    } else {
311        return false;
312    };
313
314    let length = bytes.len();
315    if length % 2 != 0 {
316        return false;
317    }
318    let mut i = 0;
319    while i < length {
320        let byte1 = bytes[i];
321        if !(0x81..=0x9F).contains(&byte1) && !(0xE0..=0xEB).contains(&byte1) {
322            return false;
323        }
324        i += 2;
325    }
326    true
327}
328
329fn chooseMaskPattern(
330    bits: &BitArray,
331    ec_level: &ErrorCorrectionLevel,
332    version: VersionRef,
333    matrix: &mut ByteMatrix,
334) -> Result<u32> {
335    let mut min_penalty = u32::MAX; // Lower penalty is better.
336    let mut best_mask_pattern = -1;
337    // We try all mask patterns to choose the best one.
338    for maskPattern in 0..QRCode::NUM_MASK_PATTERNS {
339        let mut matrix = matrix.clone();
340        matrix_util::buildMatrix(bits, ec_level, version, maskPattern, &mut matrix)?;
341        let penalty = calculateMaskPenalty(&matrix);
342        if penalty < min_penalty {
343            min_penalty = penalty;
344            best_mask_pattern = maskPattern;
345        }
346    }
347    Ok(best_mask_pattern as u32)
348}
349
350fn chooseVersion(numInputBits: u32, ecLevel: &ErrorCorrectionLevel) -> Result<VersionRef> {
351    for versionNum in 1..=40 {
352        let version = Version::getVersionForNumber(versionNum)?;
353        if willFit(numInputBits, version, ecLevel) {
354            return Ok(version);
355        }
356    }
357    Err(Exceptions::writer_with(format!(
358        "data too big {numInputBits}/{ecLevel:?}"
359    )))
360}
361
362/**
363 * @return true if the number of input bits will fit in a code with the specified version and
364 * error correction level.
365 */
366pub fn willFit(numInputBits: u32, version: VersionRef, ecLevel: &ErrorCorrectionLevel) -> bool {
367    // In the following comments, we use numbers of Version 7-H.
368    // numBytes = 196
369    let num_bytes = version.getTotalCodewords();
370    // getNumECBytes = 130
371    let ec_blocks = version.getECBlocksForLevel(*ecLevel);
372    let num_ec_bytes = ec_blocks.getTotalECCodewords();
373    // getNumDataBytes = 196 - 130 = 66
374    let num_data_bytes = num_bytes - num_ec_bytes;
375    let total_input_bytes = numInputBits.div_ceil(8);
376    num_data_bytes >= total_input_bytes
377}
378
379/**
380 * Terminate bits as described in 8.4.8 and 8.4.9 of JISX0510:2004 (p.24).
381 */
382pub fn terminateBits(num_data_bytes: u32, bits: &mut BitArray) -> Result<()> {
383    let capacity = num_data_bytes * 8;
384    if bits.get_size() > capacity as usize {
385        return Err(Exceptions::writer_with(format!(
386            "data bits cannot fit in the QR Code{capacity} > "
387        )));
388    }
389    // Append Mode.TERMINATE if there is enough space (value is 0000)
390    for _i in 0..4 {
391        if bits.get_size() >= capacity as usize {
392            break;
393        }
394        bits.appendBit(false);
395    }
396    // Append termination bits. See 8.4.8 of JISX0510:2004 (p.24) for details.
397    // If the last byte isn't 8-bit aligned, we'll add padding bits.
398    let num_bits_in_last_byte = bits.get_size() & 0x07;
399    if num_bits_in_last_byte > 0 {
400        for _i in num_bits_in_last_byte..8 {
401            bits.appendBit(false);
402        }
403    }
404    // If we have more space, we'll fill the space with padding patterns defined in 8.4.9 (p.24).
405    let num_padding_bytes = num_data_bytes as isize - bits.getSizeInBytes() as isize;
406    for i in 0..num_padding_bytes {
407        if i >= num_padding_bytes {
408            break;
409        }
410        bits.appendBits(if (i & 0x01) == 0 { 0xEC } else { 0x11 }, 8)?;
411    }
412    if bits.get_size() != capacity as usize {
413        return Err(Exceptions::writer_with("Bits size does not equal capacity"));
414    }
415    Ok(())
416}
417
418/**
419 * Get number of data bytes and number of error correction bytes for block id "blockID". Store
420 * the result in "numDataBytesInBlock", and "numECBytesInBlock". See table 12 in 8.5.1 of
421 * JISX0510:2004 (p.30)
422 */
423pub fn getNumDataBytesAndNumECBytesForBlockID(
424    num_total_bytes: u32,
425    num_data_bytes: u32,
426    num_rsblocks: u32,
427    block_id: u32,
428    // numDataBytesInBlock: &mut [u32],
429    // numECBytesInBlock: &mut [u32],
430) -> Result<(u32, u32)> {
431    if block_id >= num_rsblocks {
432        return Err(Exceptions::writer_with("Block ID too large"));
433    }
434    // numRsBlocksInGroup2 = 196 % 5 = 1
435    let num_rs_blocks_in_group2 = num_total_bytes % num_rsblocks;
436    // numRsBlocksInGroup1 = 5 - 1 = 4
437    let num_rs_blocks_in_group1 = num_rsblocks - num_rs_blocks_in_group2;
438    // numTotalBytesInGroup1 = 196 / 5 = 39
439    let num_total_bytes_in_group1 = num_total_bytes / num_rsblocks;
440    // numTotalBytesInGroup2 = 39 + 1 = 40
441    let num_total_bytes_in_group2 = num_total_bytes_in_group1 + 1;
442    // numDataBytesInGroup1 = 66 / 5 = 13
443    let num_data_bytes_in_group1 = num_data_bytes / num_rsblocks;
444    // numDataBytesInGroup2 = 13 + 1 = 14
445    let num_data_bytes_in_group2 = num_data_bytes_in_group1 + 1;
446    // numEcBytesInGroup1 = 39 - 13 = 26
447    let num_ec_bytes_in_group1 = num_total_bytes_in_group1 - num_data_bytes_in_group1;
448    // numEcBytesInGroup2 = 40 - 14 = 26
449    let numEcBytesInGroup2 = num_total_bytes_in_group2 - num_data_bytes_in_group2;
450    // Sanity checks.
451    // 26 = 26
452    if num_ec_bytes_in_group1 != numEcBytesInGroup2 {
453        return Err(Exceptions::writer_with("EC bytes mismatch"));
454    }
455    // 5 = 4 + 1.
456    if num_rsblocks != num_rs_blocks_in_group1 + num_rs_blocks_in_group2 {
457        return Err(Exceptions::writer_with("RS blocks mismatch"));
458    }
459    // 196 = (13 + 26) * 4 + (14 + 26) * 1
460    if num_total_bytes
461        != ((num_data_bytes_in_group1 + num_ec_bytes_in_group1) * num_rs_blocks_in_group1)
462            + ((num_data_bytes_in_group2 + numEcBytesInGroup2) * num_rs_blocks_in_group2)
463    {
464        return Err(Exceptions::writer_with("total bytes mismatch"));
465    }
466
467    Ok(if block_id < num_rs_blocks_in_group1 {
468        (num_data_bytes_in_group1, num_ec_bytes_in_group1)
469    } else {
470        (num_data_bytes_in_group2, numEcBytesInGroup2)
471    })
472}
473
474/**
475 * Interleave "bits" with corresponding error correction bytes. On success, store the result in
476 * "result". The interleave rule is complicated. See 8.6 of JISX0510:2004 (p.37) for details.
477 */
478pub fn interleaveWithECBytes(
479    bits: &BitArray,
480    num_total_bytes: u32,
481    num_data_bytes: u32,
482    num_rsblocks: u32,
483) -> Result<BitArray> {
484    // "bits" must have "getNumDataBytes" bytes of data.
485    if bits.getSizeInBytes() as u32 != num_data_bytes {
486        return Err(Exceptions::writer_with(
487            "Number of bits and data bytes does not match",
488        ));
489    }
490
491    // Step 1.  Divide data bytes into blocks and generate error correction bytes for them. We'll
492    // store the divided data bytes blocks and error correction bytes blocks into "blocks".
493    let mut data_bytes_offset = 0;
494    let mut max_num_data_bytes = 0;
495    let mut max_num_ec_bytes = 0;
496
497    // Since, we know the number of reedsolmon blocks, we can initialize the vector with the number.
498    let mut blocks = Vec::new();
499
500    for i in 0..num_rsblocks {
501        let (numDataBytesInBlock, numEcBytesInBlock) = getNumDataBytesAndNumECBytesForBlockID(
502            num_total_bytes,
503            num_data_bytes,
504            num_rsblocks,
505            i,
506            // &mut numDataBytesInBlock,
507            // &mut numEcBytesInBlock,
508        )?;
509
510        let size = numDataBytesInBlock;
511        let mut dataBytes = vec![0u8; size as usize];
512        bits.toBytes(8 * data_bytes_offset, &mut dataBytes, 0, size as usize);
513        let ec_bytes = generateECBytes(&dataBytes, numEcBytesInBlock as usize)?;
514        blocks.push(BlockPair::new(dataBytes, ec_bytes.clone()));
515
516        max_num_data_bytes = max_num_data_bytes.max(size);
517        max_num_ec_bytes = max_num_ec_bytes.max(ec_bytes.len());
518        data_bytes_offset += numDataBytesInBlock as usize;
519    }
520    if num_data_bytes != data_bytes_offset as u32 {
521        return Err(Exceptions::writer_with("Data bytes does not match offset"));
522    }
523
524    let mut result = BitArray::new();
525
526    // First, place data blocks.
527    for i in 0..max_num_data_bytes as usize {
528        for block in &blocks {
529            let data_bytes = block.getDataBytes();
530            if i < data_bytes.len() {
531                result.appendBits(data_bytes[i] as BitFieldBaseType, 8)?;
532            }
533        }
534    }
535    // Then, place error correction blocks.
536    for i in 0..max_num_ec_bytes {
537        for block in &blocks {
538            let ec_bytes = block.getErrorCorrectionBytes();
539            if i < ec_bytes.len() {
540                result.appendBits(ec_bytes[i] as BitFieldBaseType, 8)?;
541            }
542        }
543    }
544    if num_total_bytes != result.getSizeInBytes() as u32 {
545        // Should be same.
546        return Err(Exceptions::writer_with(format!(
547            "Interleaving error: {} and {} differ.",
548            num_total_bytes,
549            result.getSizeInBytes()
550        )));
551    }
552
553    Ok(result)
554}
555
556pub fn generateECBytes(dataBytes: &[u8], num_ec_bytes_in_block: usize) -> Result<Vec<u8>> {
557    let num_data_bytes = dataBytes.len();
558    let mut to_encode = vec![0; num_data_bytes + num_ec_bytes_in_block];
559    for i in 0..num_data_bytes {
560        to_encode[i] = dataBytes[i] as i32;
561    }
562
563    ReedSolomonEncoder::new(get_predefined_genericgf(
564        PredefinedGenericGF::QrCodeField256,
565    ))?
566    .encode(&mut to_encode, num_ec_bytes_in_block)?;
567
568    let mut ecBytes = vec![0u8; num_ec_bytes_in_block];
569    for i in 0..num_ec_bytes_in_block {
570        ecBytes[i] = to_encode[num_data_bytes + i] as u8;
571    }
572    Ok(ecBytes)
573}
574
575/**
576 * Append mode info. On success, store the result in "bits".
577 */
578pub fn appendModeInfo(mode: Mode, bits: &mut BitArray) -> Result<()> {
579    bits.appendBits(mode.getBits() as BitFieldBaseType, 4)
580}
581
582/**
583 * Append length info. On success, store the result in "bits".
584 */
585pub fn appendLengthInfo(
586    num_letters: u32,
587    version: VersionRef,
588    mode: Mode,
589    bits: &mut BitArray,
590) -> Result<()> {
591    let numBits = mode.getCharacterCountBits(version);
592    if num_letters >= (1 << numBits) {
593        return Err(Exceptions::writer_with(format!(
594            "{} is bigger than {}",
595            num_letters,
596            ((1 << numBits) - 1)
597        )));
598    }
599    bits.appendBits(num_letters as BitFieldBaseType, numBits as usize)
600}
601
602/**
603 * Append "bytes" in "mode" mode (encoding) into "bits". On success, store the result in "bits".
604 */
605pub fn appendBytes(
606    content: &str,
607    mode: Mode,
608    bits: &mut BitArray,
609    encoding: CharacterSet,
610) -> Result<()> {
611    match mode {
612        Mode::NUMERIC => appendNumericBytes(content, bits),
613        Mode::ALPHANUMERIC => appendAlphanumericBytes(content, bits),
614        Mode::BYTE => append8BitBytes(content, bits, encoding),
615        Mode::KANJI => appendKanjiBytes(content, bits),
616        _ => Err(Exceptions::writer_with(format!("Invalid mode: {mode:?}"))),
617    }
618}
619
620pub fn appendNumericBytes(content: &str, bits: &mut BitArray) -> Result<()> {
621    let length = content.len();
622    let mut i = 0;
623    let content_byte_cache: Vec<u8> = content.chars().map(|c| c as u8).collect();
624    while i < length {
625        let num1 = content_byte_cache[i] - b'0';
626        if i + 2 < length {
627            // Encode three numeric letters in ten bits.
628            let num2 = content_byte_cache[i + 1] - b'0';
629            let num3 = content_byte_cache[i + 2] - b'0';
630            bits.appendBits(
631                num1 as BitFieldBaseType * 100
632                    + num2 as BitFieldBaseType * 10
633                    + num3 as BitFieldBaseType,
634                10,
635            )?;
636            i += 3;
637        } else if i + 1 < length {
638            // Encode two numeric letters in seven bits.
639            let num2 = content_byte_cache[i + 1] - b'0';
640            bits.appendBits(num1 as BitFieldBaseType * 10 + num2 as BitFieldBaseType, 7)?;
641            i += 2;
642        } else {
643            // Encode one numeric letter in four bits.
644            bits.appendBits(num1 as BitFieldBaseType, 4)?;
645            i += 1;
646        }
647    }
648    Ok(())
649}
650
651pub fn appendAlphanumericBytes(content: &str, bits: &mut BitArray) -> Result<()> {
652    let length = content.len();
653    let content_byte_cache: Vec<u32> = content.chars().map(|c| c as u32).collect();
654    let mut i = 0;
655    while i < length {
656        let code1 = getAlphanumericCode(content_byte_cache[i]);
657        if code1 == -1 {
658            return Err(Exceptions::WRITER);
659        }
660        if i + 1 < length {
661            let code2 = getAlphanumericCode(content_byte_cache[i + 1]);
662            if code2 == -1 {
663                return Err(Exceptions::WRITER);
664            }
665            // Encode two alphanumeric letters in 11 bits.
666            bits.appendBits((code1 as i16 * 45 + code2 as i16) as BitFieldBaseType, 11)?;
667            i += 2;
668        } else {
669            // Encode one alphanumeric letter in six bits.
670            bits.appendBits(code1 as BitFieldBaseType, 6)?;
671            i += 1;
672        }
673    }
674    Ok(())
675}
676
677pub fn append8BitBytes(content: &str, bits: &mut BitArray, encoding: CharacterSet) -> Result<()> {
678    let bytes = encoding
679        .encode(content)
680        .map_err(|e| Exceptions::writer_with(format!("error {e}")))?;
681    for b in bytes {
682        bits.appendBits(b as BitFieldBaseType, 8)?;
683    }
684    Ok(())
685}
686
687pub fn appendKanjiBytes(content: &str, bits: &mut BitArray) -> Result<()> {
688    let sjis = &SHIFT_JIS_CHARSET;
689
690    let bytes = sjis
691        .encode(content)
692        .map_err(|e| Exceptions::writer_with(format!("error {e}")))?;
693    if bytes.len() % 2 != 0 {
694        return Err(Exceptions::writer_with("Kanji byte size not even"));
695    }
696    let max_i = bytes.len() - 1; // bytes.length must be even
697    let mut i = 0;
698    while i < max_i {
699        let byte1 = bytes[i]; // & 0xFF;
700        let byte2 = bytes[i + 1]; // & 0xFF;
701        let code: u16 = ((byte1 as u16) << 8u16) | byte2 as u16;
702        let mut subtracted: i32 = -1;
703        if (0x8140..=0x9ffc).contains(&code) {
704            subtracted = code as i32 - 0x8140;
705        } else if (0xe040..=0xebbf).contains(&code) {
706            subtracted = code as i32 - 0xc140;
707        }
708        if subtracted == -1 {
709            return Err(Exceptions::writer_with("Invalid byte sequence"));
710        }
711        let encoded = ((subtracted >> 8) * 0xc0) + (subtracted & 0xff);
712        bits.appendBits(encoded as BitFieldBaseType, 13)?;
713
714        i += 2;
715    }
716    Ok(())
717}
718
719fn appendECI(eci: Eci, bits: &mut BitArray) -> Result<()> {
720    bits.appendBits(Mode::ECI.getBits() as BitFieldBaseType, 4)?;
721    // This is correct for values up to 127, which is all we need now.
722    bits.appendBits(eci as BitFieldBaseType, 8)
723}