Как кодировать китайский текст в QR-штрих-кодах, генерируемых с помощью iTextSharp? - PullRequest
1 голос
/ 14 марта 2012

Я пытаюсь нарисовать QR-коды в PDF-файле, используя iTextSharp.Если я использую текст на английском языке, штрих-коды в порядке, они декодируются правильно, но если я использую текст на китайском языке, штрих-код декодируется как вопросительные знаки.Например, этот символ «测» (\ u6D4B) декодируется как «?».Я перепробовал все поддерживаемые наборы символов, но ни один из них не помог.Какую комбинацию параметров следует использовать для штрих-кода QR в iTextSharp, чтобы правильно кодировать китайский текст?

Ответы [ 3 ]

5 голосов
/ 14 марта 2012

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 голос
/ 10 января 2015

По сути, тот же трюк, который Крис сделал в своем ответе, можно реализовать, указав кодировку UTF-8 в подсказках штрих-кода.

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

Если вы хотите быть более безопасным, вы можете начать свою строку сСимвол спецификации '\uFEFF', как предложил Крис, так что это будет "\uFEFF\u6D4B".

UTF-8, к сожалению, не поддерживается спецификацией QR-кодов, и существует множество дискуссий на эту тему, но фактявляется то, что большинство читателей QR-кода будут правильно читать код, созданный этим методом.

1 голос
/ 14 марта 2012

Похоже, вам не повезло.Я тоже пытался и получил те же результаты, что и вы.Затем посмотрел на Java API :

"* CHARACTER_SET. Значения являются строками и могут быть Cp437, Shift_JIS и ISO-8859-1 для ISO-8859-16. По умолчаниюзначение - ISO-8859-1. * "

Наконец, посмотрите на исходный код класса iTextSharp BarcodeQRCode, чтобы убедиться, что поддерживаются только эти наборы символов.Я ни в коем случае не являюсь авторитетом в области Юникода или кодировки, но согласно ИСО / МЭК 8859 наборы символов выше не будут работать для китайского языка.

...