2012-03-14 76 views
1

我想使用iTextSharp在PDF文件中繪製QR條形碼。如果我使用的是英文文本,條形碼就可以正常解碼,但如果使用的是中文文本,則條形碼將被解碼爲問號。例如,這個字符'測'(\ u6D4B)被解碼爲'?'。我嘗試了所有支持的字符集,但都沒有幫助。
爲了正確編碼中文文本,我應該在iTextSharp中使用QR條形碼的參數組合嗎?如何在iTextSharp生成的QR條碼中編碼中文文本?

+0

您可以嘗試使用[link](http://kuujinbo.info/cs/itext_cjk.aspx)中的不同字體並使用[this](http://sourceforge.net/project/shownotes.php?group_id = 72954&release_id = 368247)字體 – Vinay 2012-03-14 11:37:20

+0

我不想繪製中文文本,我試圖在QR條形碼中編碼中文文本。 – iPDFdev 2012-03-14 12:13:22

回答

4

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()); 
+0

謝謝,它工作。 – 2012-03-15 09:24:20

+0

偉大的解決方案,它的工作原理! – dev 2015-01-07 15:19:18

1

看起來你可能不走運。我也試過,得到了和你一樣的結果。然後看了看Java API:。

「* CHARACTER_SET的值是字符串,可以是CP437,SHIFT_JIS和 ISO-8859-1到ISO-8859-16默認值爲ISO-8859-1 * 「

最後,看了看iTextSharp的BarcodeQRCode類的源代碼,以確認只有那些字符集支持。我絕不是Unicode或編碼方面的權威,但根據ISO/IEC 8859,上述字符集不適用於中文。

1

本質上同樣的伎倆,克里斯在他的回答做了可以通過條形碼提示指定UTF-8字符集來實現。

var hints = new Dictionary<EncodeHintType, Object>() {{EncodeHintType.CHARACTER_SET, "UTF-8"}}; 
var q = new BarcodeQRCode("\u6D4B", 100, 100, hints); 

如果你想更安全,你可以用BOM字符'\uFEFF'開始你的字符串,像克里斯建議,所以這將是"\uFEFF\u6D4B"

不幸的是,QR碼規範並不支持UTF-8,並且在這個主題上有很多討論,但事實是大多數QR碼閱讀器將正確讀取由此方法創建的代碼。