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