如何用iTextSharp生成的QR条码对中文文本进行编码?

1 qr-code itextsharp

我正在尝试使用iTextSharp在PDF文件中绘制QR条形码.如果我使用英文文本条形码很好,它们会被正确解码,但如果我使用中文文本,条形码会被解码为问号.例如,该字符'测'(\ u6D4B)被解码为'?'.我尝试了所有支持的字符集,但没有一个帮助.
为了正确编码中文文本,我应该将哪些参数组合用于iTextSharp中的QR条码?

Chr*_*aas 5

iText和iTextSharp显然本身不支持此功能,但您可以编写一些代码来自行处理.诀窍是让QR代码解析器只使用任意字节数组而不是字符串.真正好的是iTextSharp代码几乎已经准备就绪,但没有公开功能.不幸的是,许多必需的类都是sealed如此,你不能只是将它们子类化,你必须重新创建它们.您可以下载整个源并添加这些更改,也可以只创建具有相同名称的单独类.(请检查许可证以确保您可以执行此操作.)下面的更改没有任何错误更正,因此请确保您也这样做.

您需要重新创建的第一个类是,您需要做iTextSharp.text.pdf.qrcode.BlockPair的唯一更改是创建构造函数public而不是internal.(如果要创建自己的代码而不修改现有代码,则只需执行此操作.)

第二堂课是iTextSharp.text.pdf.qrcode.Encoder.这是我们将做出最大改变的地方.添加一个重载Append8BitBytes,如下所示:

static void Append8BitBytes(byte[] bytes, BitVector bits) {
    for (int i = 0; i < bytes.Length; ++i) {
        bits.AppendBits(bytes[i], 8);
    }
}
Run Code Online (Sandbox Code Playgroud)

这个方法的字符串版本将文本转换为字节数组,然后使用上面的代码,所以我们只是剪掉了中间人.接下来,向构造函数添加一个新的重载,它接受一个字节数组而不是字符串.然后我们将切断字符串检测部分并强制系统进入字节模式,否则下面的代码几乎相同.

    public static void Encode(byte[] bytes, ErrorCorrectionLevel ecLevel, IDictionary<EncodeHintType, Object> hints, QRCode qrCode) {
        String encoding = DEFAULT_BYTE_MODE_ENCODING;

        // Step 1: Choose the mode (encoding).
        Mode mode = Mode.BYTE;

        // Step 2: Append "bytes" into "dataBits" in appropriate encoding.
        BitVector dataBits = new BitVector();
        Append8BitBytes(bytes, dataBits);

        // Step 3: Initialize QR code that can contain "dataBits".
        int numInputBytes = dataBits.SizeInBytes();
        InitQRCode(numInputBytes, ecLevel, mode, qrCode);

        // Step 4: Build another bit vector that contains header and data.
        BitVector headerAndDataBits = new BitVector();

        // Step 4.5: Append ECI message if applicable
        if (mode == Mode.BYTE && !DEFAULT_BYTE_MODE_ENCODING.Equals(encoding)) {
            CharacterSetECI eci = CharacterSetECI.GetCharacterSetECIByName(encoding);
            if (eci != null) {
                AppendECI(eci, headerAndDataBits);
            }
        }

        AppendModeInfo(mode, headerAndDataBits);

        int numLetters = dataBits.SizeInBytes();
        AppendLengthInfo(numLetters, qrCode.GetVersion(), mode, headerAndDataBits);
        headerAndDataBits.AppendBitVector(dataBits);

        // Step 5: Terminate the bits properly.
        TerminateBits(qrCode.GetNumDataBytes(), headerAndDataBits);

        // Step 6: Interleave data bits with error correction code.
        BitVector finalBits = new BitVector();
        InterleaveWithECBytes(headerAndDataBits, qrCode.GetNumTotalBytes(), qrCode.GetNumDataBytes(),
            qrCode.GetNumRSBlocks(), finalBits);

        // Step 7: Choose the mask pattern and set to "qrCode".
        ByteMatrix matrix = new ByteMatrix(qrCode.GetMatrixWidth(), qrCode.GetMatrixWidth());
        qrCode.SetMaskPattern(ChooseMaskPattern(finalBits, qrCode.GetECLevel(), qrCode.GetVersion(),
            matrix));

        // Step 8.  Build the matrix and set it to "qrCode".
        MatrixUtil.BuildMatrix(finalBits, qrCode.GetECLevel(), qrCode.GetVersion(),
            qrCode.GetMaskPattern(), matrix);
        qrCode.SetMatrix(matrix);
        // Step 9.  Make sure we have a valid QR Code.
        if (!qrCode.IsValid()) {
            throw new WriterException("Invalid QR code: " + qrCode.ToString());
        }
    }
Run Code Online (Sandbox Code Playgroud)

第三个类是iTextSharp.text.pdf.qrcode.QRCodeWriter,我们只需要添加一个重载Encode方法支持一个字节数组,并且调用是上面创建的新构造函数:

    public ByteMatrix Encode(byte[] bytes, int width, int height, IDictionary<EncodeHintType, Object> hints) {
        ErrorCorrectionLevel errorCorrectionLevel = ErrorCorrectionLevel.L;
        if (hints != null && hints.ContainsKey(EncodeHintType.ERROR_CORRECTION))
            errorCorrectionLevel = (ErrorCorrectionLevel)hints[EncodeHintType.ERROR_CORRECTION];

        QRCode code = new QRCode();
        Encoder.Encode(bytes, errorCorrectionLevel, hints, code);
        return RenderResult(code, width, height);
    }
Run Code Online (Sandbox Code Playgroud)

最后一个类是iTextSharp.text.pdf.BarcodeQRCode我们再次添加我们的新构造函数重载:

    public BarcodeQRCode(byte[] bytes, int width, int height, IDictionary<EncodeHintType, Object> hints) {
        newCode.QRCodeWriter qc = new newCode.QRCodeWriter();
        bm = qc.Encode(bytes, width, height, hints);
    }
Run Code Online (Sandbox Code Playgroud)

最后一个技巧是确保在调用时包含字节顺序标记(BOM),以便解码器知道正确解码,在本例中为UTF-8.

//Create an encoder that supports outputting a BOM
System.Text.Encoding enc = new System.Text.UTF8Encoding(true, true);

//Get the BOM
byte[] bom = enc.GetPreamble();

//Get the raw bytes for the string
byte[] bytes = enc.GetBytes("?");

//Combine the byte arrays
byte[] final = new byte[bom.Length + bytes.Length];
System.Buffer.BlockCopy(bom, 0, final, 0, bom.Length);
System.Buffer.BlockCopy(bytes, 0, final, bom.Length, bytes.Length);

//Create are barcode using our new constructor
var q = new BarcodeQRCode(final, 100, 100, null);

//Add it to the document
doc.Add(q.GetImage());
Run Code Online (Sandbox Code Playgroud)