我正在尝试使用iTextSharp在PDF文件中绘制QR条形码。如果我使用英文文本条形码很好,它们会被正确解码,但如果我使用中文文本,条形码会被解码为问号。例如,该字符'测'(\ u6D4B)被解码为'?'。我尝试了所有支持的字符集,但没有一个帮助。
为了正确编码中文文本,我应该将哪些参数组合用于iTextSharp中的QR条码?
答案 0 :(得分: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);
}
}
此方法的字符串版本将文本转换为字节数组,然后使用上面的内容,因此我们只是删除了中间人。接下来,向构造函数添加一个新的重载,它接受一个字节数组而不是字符串。然后我们将切断字符串检测部分并强制系统进入字节模式,否则下面的代码几乎相同。
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());
}
}
第三个类是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);
}
最后一个类是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);
}
最后一个技巧是确保在调用时包含字节顺序标记(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());
答案 1 :(得分:1)
看起来你可能运气不好。我也尝试了,并得到了与你相同的结果。然后看了Java API:
“* CHARACTER_SET值是字符串,可以是Cp437,Shift_JIS和 ISO-8859-1至ISO-8859-16。默认值为ISO-8859-1。*“
最后,查看了iTextSharp BarcodeQRCode
类源代码,以确认只支持那些字符集。我绝不是Unicode或编码的权威,但根据ISO/IEC 8859,上面的字符集对中文不起作用。
答案 2 :(得分:1)
基本上与Chris在答案中所做的相同的技巧可以通过在条形码提示中指定UTF-8字符集来实现。
var hints = new Dictionary<EncodeHintType, Object>() {{EncodeHintType.CHARACTER_SET, "UTF-8"}};
var q = new BarcodeQRCode("\u6D4B", 100, 100, hints);
如果您想更安全,可以像Chris建议的那样使用BOM字符'\uFEFF'
开始字符串,因此它将是"\uFEFF\u6D4B"
。