mirror of https://github.com/status-im/qzxing.git
I found that if QR-Code was generated with non standard charSet (and the message language is not English) than decoded QString contains wrong characters. So we must use QTextCodec to make it readable.
Now tagFound signal emits decoded string. If you want to know what code was detected and what charSet is used, use tagFoundAdvanced(QString tag, QString format, QString charSet) signal or functions foundedFormat() and charSet().
This commit is contained in:
parent
13720010fc
commit
4df9512790
|
@ -114,7 +114,7 @@ ArrayRef<char> CameraImageWrapper::getMatrix() const
|
||||||
ArrayRef<char> tmpRow;
|
ArrayRef<char> tmpRow;
|
||||||
tmpRow = getRow(y, ArrayRef<char>(width));
|
tmpRow = getRow(y, ArrayRef<char>(width));
|
||||||
#if __cplusplus > 199711L
|
#if __cplusplus > 199711L
|
||||||
memcpy(m, tmpRow->values()..data(), width);
|
memcpy(m, tmpRow->values().data(), width);
|
||||||
#else
|
#else
|
||||||
memcpy(m, &tmpRow->values()[0], width);
|
memcpy(m, &tmpRow->values()[0], width);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -28,9 +28,9 @@ class ImageHandler;
|
||||||
*/
|
*/
|
||||||
class
|
class
|
||||||
#ifndef DISABLE_LIBRARY_FEATURES
|
#ifndef DISABLE_LIBRARY_FEATURES
|
||||||
QZXINGSHARED_EXPORT
|
QZXINGSHARED_EXPORT
|
||||||
#endif
|
#endif
|
||||||
QZXing : public QObject{
|
QZXing : public QObject {
|
||||||
|
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_ENUMS(DecoderFormat)
|
Q_ENUMS(DecoderFormat)
|
||||||
|
@ -75,6 +75,10 @@ public:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QString decoderFormatToString(int fmt);
|
||||||
|
QString foundedFormat() const;
|
||||||
|
QString charSet() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
/**
|
/**
|
||||||
* The decoding function. Will try to decode the given image based on the enabled decoders.
|
* The decoding function. Will try to decode the given image based on the enabled decoders.
|
||||||
|
@ -85,13 +89,13 @@ public slots:
|
||||||
* The smoothTransformation flag determines whether the transformation will be smooth or fast.
|
* The smoothTransformation flag determines whether the transformation will be smooth or fast.
|
||||||
* Smooth transformation provides better results but fast transformation is...faster.
|
* Smooth transformation provides better results but fast transformation is...faster.
|
||||||
*/
|
*/
|
||||||
QString decodeImage(QImage &image, int maxWidth=-1, int maxHeight=-1, bool smoothTransformation = false);
|
QString decodeImage(QImage &image, int maxWidth = -1, int maxHeight = -1, bool smoothTransformation = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The decoding function. Will try to decode the given image based on the enabled decoders.
|
* The decoding function. Will try to decode the given image based on the enabled decoders.
|
||||||
* The input image is read from a local image file.
|
* The input image is read from a local image file.
|
||||||
*/
|
*/
|
||||||
QString decodeImageFromFile(QString imageFilePath, int maxWidth=-1, int maxHeight=-1, bool smoothTransformation = false);
|
QString decodeImageFromFile(QString imageFilePath, int maxWidth = -1, int maxHeight = -1, bool smoothTransformation = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The decoding function accessible from QML. (Suggested for Qt 4.x)
|
* The decoding function accessible from QML. (Suggested for Qt 4.x)
|
||||||
|
@ -102,7 +106,7 @@ public slots:
|
||||||
* The decoding function accessible from QML. Able to set the decoding
|
* The decoding function accessible from QML. Able to set the decoding
|
||||||
* of a portion of the image. (Suggested for Qt 4.x)
|
* of a portion of the image. (Suggested for Qt 4.x)
|
||||||
*/
|
*/
|
||||||
QString decodeSubImageQML(QObject* item,
|
QString decodeSubImageQML(QObject *item,
|
||||||
const double offsetX = 0 , const double offsetY = 0,
|
const double offsetX = 0 , const double offsetY = 0,
|
||||||
const double width = 0, const double height = 0);
|
const double width = 0, const double height = 0);
|
||||||
|
|
||||||
|
@ -123,6 +127,7 @@ public slots:
|
||||||
QString decodeSubImageQML(const QUrl &imageUrl,
|
QString decodeSubImageQML(const QUrl &imageUrl,
|
||||||
const double offsetX = 0, const double offsetY = 0,
|
const double offsetX = 0, const double offsetY = 0,
|
||||||
const double width = 0, const double height = 0);
|
const double width = 0, const double height = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the prossecing time in millisecond of the last decode operation.
|
* Get the prossecing time in millisecond of the last decode operation.
|
||||||
* Added mainly as a statistic measure.
|
* Added mainly as a statistic measure.
|
||||||
|
@ -140,7 +145,7 @@ public slots:
|
||||||
* As argument it is possible to pass conjuction of decoders by using logic OR.
|
* As argument it is possible to pass conjuction of decoders by using logic OR.
|
||||||
* e.x. setDecoder ( DecoderFormat_QR_CODE | DecoderFormat_EAN_13 | DecoderFormat_CODE_39 )
|
* e.x. setDecoder ( DecoderFormat_QR_CODE | DecoderFormat_EAN_13 | DecoderFormat_CODE_39 )
|
||||||
*/
|
*/
|
||||||
void setDecoder(const uint& hint);
|
void setDecoder(const uint &hint);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void decodingStarted();
|
void decodingStarted();
|
||||||
|
@ -149,10 +154,12 @@ signals:
|
||||||
void enabledFormatsChanged();
|
void enabledFormatsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
zxing::MultiFormatReader* decoder;
|
zxing::MultiFormatReader *decoder;
|
||||||
DecoderFormatType enabledDecoders;
|
DecoderFormatType enabledDecoders;
|
||||||
ImageHandler* imageHandler;
|
ImageHandler *imageHandler;
|
||||||
int processingTime;
|
int processingTime;
|
||||||
|
QString foundedFmt;
|
||||||
|
QString charSet_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If true, the decoding operation will take place at a different thread.
|
* If true, the decoding operation will take place at a different thread.
|
||||||
|
|
|
@ -16,7 +16,7 @@ QImage ImageHandler::extractQImage(QObject *imageObj,
|
||||||
{
|
{
|
||||||
QGraphicsObject *item = qobject_cast<QGraphicsObject*>(imageObj);
|
QGraphicsObject *item = qobject_cast<QGraphicsObject*>(imageObj);
|
||||||
|
|
||||||
if (!item ) {
|
if (!item) {
|
||||||
qDebug() << "Item is NULL";
|
qDebug() << "Item is NULL";
|
||||||
return QImage();
|
return QImage();
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,73 @@ QZXing::QZXing(QZXing::DecoderFormat decodeHints, QObject *parent) : QObject(par
|
||||||
setDecoder(decodeHints);
|
setDecoder(decodeHints);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString QZXing::decoderFormatToString(int fmt)
|
||||||
|
{
|
||||||
|
switch (fmt) {
|
||||||
|
case 1:
|
||||||
|
return "AZTEC";
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
return "CODABAR";
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
return "CODE_39";
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
return "CODE_93";
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
return "CODE_128";
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
return "DATA_MATRIX";
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
return "EAN_8";
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
return "EAN_13";
|
||||||
|
|
||||||
|
case 9:
|
||||||
|
return "ITF";
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
return "MAXICODE";
|
||||||
|
|
||||||
|
case 11:
|
||||||
|
return "PDF_417";
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
return "QR_CODE";
|
||||||
|
|
||||||
|
case 13:
|
||||||
|
return "RSS_14";
|
||||||
|
|
||||||
|
case 14:
|
||||||
|
return "RSS_EXPANDED";
|
||||||
|
|
||||||
|
case 15:
|
||||||
|
return "UPC_A";
|
||||||
|
|
||||||
|
case 16:
|
||||||
|
return "UPC_E";
|
||||||
|
|
||||||
|
case 17:
|
||||||
|
return "UPC_EAN_EXTENSION";
|
||||||
|
} // switch
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QZXing::foundedFormat() const
|
||||||
|
{
|
||||||
|
return foundedFmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QZXing::charSet() const
|
||||||
|
{
|
||||||
|
return charSet_;
|
||||||
|
}
|
||||||
|
|
||||||
void QZXing::setDecoder(const uint &hint)
|
void QZXing::setDecoder(const uint &hint)
|
||||||
{
|
{
|
||||||
unsigned int newHints = 0;
|
unsigned int newHints = 0;
|
||||||
|
@ -113,50 +180,59 @@ QString QZXing::decodeImage(QImage &image, int maxWidth, int maxHeight, bool smo
|
||||||
|
|
||||||
if(image.isNull())
|
if(image.isNull())
|
||||||
{
|
{
|
||||||
qDebug() << "Image Null";
|
qDebug() << "Image is Null";
|
||||||
emit decodingFinished(false);
|
emit decodingFinished(false);
|
||||||
processingTime = -1;
|
processingTime = t.elapsed();
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraImageWrapper* ciw;
|
CameraImageWrapper *ciw = NULL;
|
||||||
|
try {
|
||||||
try{
|
if ((maxWidth > 0) || (maxHeight > 0))
|
||||||
if(maxWidth > 0 || maxHeight > 0)
|
|
||||||
ciw = CameraImageWrapper::Factory(image, maxWidth, maxHeight, smoothTransformation);
|
ciw = CameraImageWrapper::Factory(image, maxWidth, maxHeight, smoothTransformation);
|
||||||
else
|
else
|
||||||
ciw = new CameraImageWrapper(image);
|
ciw = new CameraImageWrapper(image);
|
||||||
|
|
||||||
Ref<LuminanceSource> imageRef(ciw);
|
Ref<LuminanceSource> imageRef(ciw);
|
||||||
GlobalHistogramBinarizer* binz = new GlobalHistogramBinarizer(imageRef);
|
GlobalHistogramBinarizer *binz = new GlobalHistogramBinarizer(imageRef);
|
||||||
|
|
||||||
Ref<Binarizer> bz (binz);
|
Ref<Binarizer> bz(binz);
|
||||||
BinaryBitmap* bb = new BinaryBitmap(bz);
|
BinaryBitmap *bb = new BinaryBitmap(bz);
|
||||||
|
|
||||||
Ref<BinaryBitmap> ref(bb);
|
Ref<BinaryBitmap> ref(bb);
|
||||||
|
|
||||||
res = decoder->decode(ref, DecodeHints((int)enabledDecoders));
|
res = decoder->decode(ref, DecodeHints((int)enabledDecoders));
|
||||||
|
|
||||||
QString string = QString(res->getText()->getText().c_str());
|
QString string = QString(res->getText()->getText().c_str());
|
||||||
|
if (!string.isEmpty() && (string.length() > 0)) {
|
||||||
|
int fmt = res->getBarcodeFormat().value;
|
||||||
|
foundedFmt = decoderFormatToString(fmt);
|
||||||
|
charSet_ = QString::fromStdString(res->getCharSet());
|
||||||
|
if (!charSet_.isEmpty()) {
|
||||||
|
QTextCodec *codec = QTextCodec::codecForName(res->getCharSet().c_str());
|
||||||
|
if (codec)
|
||||||
|
string = codec->toUnicode(res->getText()->getText().c_str());
|
||||||
|
}
|
||||||
|
emit tagFound(string);
|
||||||
|
emit tagFoundAdvanced(string, foundedFmt, charSet_);
|
||||||
|
}
|
||||||
processingTime = t.elapsed();
|
processingTime = t.elapsed();
|
||||||
qDebug() << "Deconding succeeded: " << string;
|
|
||||||
emit tagFound(string);
|
|
||||||
emit decodingFinished(true);
|
emit decodingFinished(true);
|
||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
catch(zxing::Exception& /*e*/)
|
catch(zxing::Exception &e)
|
||||||
{
|
{
|
||||||
qDebug() << "Deconding failed";
|
emit error(QString(e.what()));
|
||||||
emit decodingFinished(false);
|
emit decodingFinished(false);
|
||||||
processingTime = -1;
|
processingTime = t.elapsed();
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QZXing::decodeImageFromFile(QString imageFilePath, int maxWidth, int maxHeight, bool smoothTransformation)
|
QString QZXing::decodeImageFromFile(QString imageFilePath, int maxWidth, int maxHeight, bool smoothTransformation)
|
||||||
{
|
{
|
||||||
//used to have a check if this image exists
|
// used to have a check if this image exists
|
||||||
//but was removed because if the image file path doesn't point to a valid image
|
// but was removed because if the image file path doesn't point to a valid image
|
||||||
// then the QImage::isNull will return true and the decoding will fail eitherway.
|
// then the QImage::isNull will return true and the decoding will fail eitherway.
|
||||||
QImage tmpImage = QImage(imageFilePath);
|
QImage tmpImage = QImage(imageFilePath);
|
||||||
return decodeImage(tmpImage, maxWidth, maxHeight, smoothTransformation);
|
return decodeImage(tmpImage, maxWidth, maxHeight, smoothTransformation);
|
||||||
|
@ -167,12 +243,13 @@ QString QZXing::decodeImageQML(QObject *item)
|
||||||
return decodeSubImageQML(item);
|
return decodeSubImageQML(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QZXing::decodeSubImageQML(QObject* item,
|
QString QZXing::decodeSubImageQML(QObject *item,
|
||||||
const double offsetX, const double offsetY,
|
const double offsetX, const double offsetY,
|
||||||
const double width, const double height)
|
const double width, const double height)
|
||||||
{
|
{
|
||||||
if(item == NULL)
|
if(item == NULL)
|
||||||
{
|
{
|
||||||
|
processingTime = 0;
|
||||||
emit decodingFinished(false);
|
emit decodingFinished(false);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -186,6 +263,7 @@ QString QZXing::decodeImageQML(const QUrl &imageUrl)
|
||||||
{
|
{
|
||||||
return decodeSubImageQML(imageUrl);
|
return decodeSubImageQML(imageUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QZXing::decodeSubImageQML(const QUrl &imageUrl,
|
QString QZXing::decodeSubImageQML(const QUrl &imageUrl,
|
||||||
const double offsetX, const double offsetY,
|
const double offsetX, const double offsetY,
|
||||||
const double width, const double height)
|
const double width, const double height)
|
||||||
|
@ -214,7 +292,3 @@ uint QZXing::getEnabledFormats() const
|
||||||
{
|
{
|
||||||
return enabledDecoders;
|
return enabledDecoders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,8 @@ using zxing::BarcodeFormat;
|
||||||
Result::Result(Ref<String> text,
|
Result::Result(Ref<String> text,
|
||||||
ArrayRef<char> rawBytes,
|
ArrayRef<char> rawBytes,
|
||||||
ArrayRef< Ref<ResultPoint> > resultPoints,
|
ArrayRef< Ref<ResultPoint> > resultPoints,
|
||||||
BarcodeFormat format) :
|
BarcodeFormat format, std::string charSet) :
|
||||||
text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format) {
|
text_(text), rawBytes_(rawBytes), resultPoints_(resultPoints), format_(format), charSet_(charSet) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result::~Result() {
|
Result::~Result() {
|
||||||
|
@ -57,5 +57,10 @@ ArrayRef< Ref<ResultPoint> >& Result::getResultPoints() {
|
||||||
}
|
}
|
||||||
|
|
||||||
zxing::BarcodeFormat Result::getBarcodeFormat() const {
|
zxing::BarcodeFormat Result::getBarcodeFormat() const {
|
||||||
return format_;
|
return format_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Result::getCharSet() const
|
||||||
|
{
|
||||||
|
return charSet_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,18 +35,21 @@ private:
|
||||||
ArrayRef<char> rawBytes_;
|
ArrayRef<char> rawBytes_;
|
||||||
ArrayRef< Ref<ResultPoint> > resultPoints_;
|
ArrayRef< Ref<ResultPoint> > resultPoints_;
|
||||||
BarcodeFormat format_;
|
BarcodeFormat format_;
|
||||||
|
//NOTE: My
|
||||||
|
std::string charSet_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Result(Ref<String> text,
|
Result(Ref<String> text,
|
||||||
ArrayRef<char> rawBytes,
|
ArrayRef<char> rawBytes,
|
||||||
ArrayRef< Ref<ResultPoint> > resultPoints,
|
ArrayRef< Ref<ResultPoint> > resultPoints,
|
||||||
BarcodeFormat format);
|
BarcodeFormat format, std::string charSet = "");
|
||||||
~Result();
|
~Result();
|
||||||
Ref<String> getText();
|
Ref<String> getText();
|
||||||
ArrayRef<char> getRawBytes();
|
ArrayRef<char> getRawBytes();
|
||||||
ArrayRef< Ref<ResultPoint> > const& getResultPoints() const;
|
ArrayRef< Ref<ResultPoint> > const& getResultPoints() const;
|
||||||
ArrayRef< Ref<ResultPoint> >& getResultPoints();
|
ArrayRef< Ref<ResultPoint> >& getResultPoints();
|
||||||
BarcodeFormat getBarcodeFormat() const;
|
BarcodeFormat getBarcodeFormat() const;
|
||||||
|
std::string getCharSet() const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream &out, Result& result);
|
friend std::ostream& operator<<(std::ostream &out, Result& result);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,20 +27,25 @@ using namespace zxing;
|
||||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
||||||
Ref<String> text,
|
Ref<String> text,
|
||||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||||
string const& ecLevel) :
|
string const& ecLevel, string charSet) :
|
||||||
rawBytes_(rawBytes),
|
rawBytes_(rawBytes),
|
||||||
text_(text),
|
text_(text),
|
||||||
byteSegments_(byteSegments),
|
byteSegments_(byteSegments),
|
||||||
ecLevel_(ecLevel) {}
|
ecLevel_(ecLevel), charSet_(charSet) {}
|
||||||
|
|
||||||
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
DecoderResult::DecoderResult(ArrayRef<char> rawBytes,
|
||||||
Ref<String> text)
|
Ref<String> text)
|
||||||
: rawBytes_(rawBytes), text_(text) {}
|
: rawBytes_(rawBytes), text_(text),charSet_("") {}
|
||||||
|
|
||||||
ArrayRef<char> DecoderResult::getRawBytes() {
|
ArrayRef<char> DecoderResult::getRawBytes() {
|
||||||
return rawBytes_;
|
return rawBytes_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<String> DecoderResult::getText() {
|
Ref<String> DecoderResult::getText() {
|
||||||
return text_;
|
return text_;
|
||||||
|
}
|
||||||
|
|
||||||
|
string DecoderResult::charSet()
|
||||||
|
{
|
||||||
|
return charSet_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,17 +33,21 @@ private:
|
||||||
Ref<String> text_;
|
Ref<String> text_;
|
||||||
ArrayRef< ArrayRef<char> > byteSegments_;
|
ArrayRef< ArrayRef<char> > byteSegments_;
|
||||||
std::string ecLevel_;
|
std::string ecLevel_;
|
||||||
|
std::string charSet_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DecoderResult(ArrayRef<char> rawBytes,
|
DecoderResult(ArrayRef<char> rawBytes,
|
||||||
Ref<String> text,
|
Ref<String> text,
|
||||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||||
std::string const& ecLevel);
|
std::string const& ecLevel,
|
||||||
|
std::string charSet = "");
|
||||||
|
|
||||||
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text);
|
DecoderResult(ArrayRef<char> rawBytes, Ref<String> text);
|
||||||
|
|
||||||
ArrayRef<char> getRawBytes();
|
ArrayRef<char> getRawBytes();
|
||||||
Ref<String> getText();
|
Ref<String> getText();
|
||||||
|
// NOTE: my
|
||||||
|
std::string charSet();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@ namespace zxing {
|
||||||
|
|
||||||
QRCodeReader::QRCodeReader() :decoder_() {
|
QRCodeReader::QRCodeReader() :decoder_() {
|
||||||
}
|
}
|
||||||
//TODO: see if any of the other files in the qrcode tree need tryHarder
|
//TODO : see if any of the other files in the qrcode tree need tryHarder
|
||||||
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
Ref<Result> QRCodeReader::decode(Ref<BinaryBitmap> image, DecodeHints hints) {
|
||||||
Detector detector(image->getBlackMatrix());
|
Detector detector(image->getBlackMatrix());
|
||||||
Ref<DetectorResult> detectorResult(detector.detect(hints));
|
Ref<DetectorResult> detectorResult(detector.detect(hints));
|
||||||
ArrayRef< Ref<ResultPoint> > points (detectorResult->getPoints());
|
ArrayRef< Ref<ResultPoint> > points (detectorResult->getPoints());
|
||||||
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
Ref<DecoderResult> decoderResult(decoder_.decode(detectorResult->getBits()));
|
||||||
Ref<Result> result(
|
Ref<Result> result(
|
||||||
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat::QR_CODE));
|
new Result(decoderResult->getText(), decoderResult->getRawBytes(), points, BarcodeFormat::QR_CODE, decoderResult->charSet()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ private:
|
||||||
static void decodeHanziSegment(Ref<BitSource> bits, std::string &result, int count);
|
static void decodeHanziSegment(Ref<BitSource> bits, std::string &result, int count);
|
||||||
static void decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count);
|
static void decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count);
|
||||||
static void decodeByteSegment(Ref<BitSource> bits, std::string &result, int count);
|
static void decodeByteSegment(Ref<BitSource> bits, std::string &result, int count);
|
||||||
static void decodeByteSegment(Ref<BitSource> bits_,
|
static std::string decodeByteSegment(Ref<BitSource> bits_,
|
||||||
std::string& result,
|
std::string& result,
|
||||||
int count,
|
int count,
|
||||||
zxing::common::CharacterSetECI* currentCharacterSetECI,
|
zxing::common::CharacterSetECI* currentCharacterSetECI,
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Required for compatibility. TODO: test on Symbian
|
// Required for compatibility. TODO : test on Symbian
|
||||||
#ifdef ZXING_ICONV_CONST
|
#ifdef ZXING_ICONV_CONST
|
||||||
#undef ICONV_CONST
|
#undef ICONV_CONST
|
||||||
#define ICONV_CONST const
|
#define ICONV_CONST const
|
||||||
|
@ -46,18 +46,18 @@ using namespace zxing::qrcode;
|
||||||
using namespace zxing::common;
|
using namespace zxing::common;
|
||||||
|
|
||||||
const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] =
|
const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] =
|
||||||
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
|
||||||
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
|
||||||
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||||
'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {int GB2312_SUBSET = 1;}
|
namespace {int GB2312_SUBSET = 1;}
|
||||||
|
|
||||||
void DecodedBitStreamParser::append(std::string &result,
|
void DecodedBitStreamParser::append(std::string &result,
|
||||||
string const& in,
|
string const& in,
|
||||||
const char *src) {
|
const char *src) {
|
||||||
append(result, (char const*)in.c_str(), in.length(), src);
|
append(result, (char const*)in.c_str(), in.length(), src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::append(std::string &result,
|
void DecodedBitStreamParser::append(std::string &result,
|
||||||
|
@ -65,286 +65,288 @@ void DecodedBitStreamParser::append(std::string &result,
|
||||||
size_t nIn,
|
size_t nIn,
|
||||||
const char *src) {
|
const char *src) {
|
||||||
#ifndef NO_ICONV
|
#ifndef NO_ICONV
|
||||||
if (nIn == 0) {
|
if (nIn == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
iconv_t cd = iconv_open(StringUtils::UTF8, src);
|
iconv_t cd = iconv_open(StringUtils::UTF8, src);
|
||||||
if (cd == (iconv_t)-1) {
|
if (cd == (iconv_t)-1) {
|
||||||
result.append((const char *)bufIn, nIn);
|
result.append((const char *)bufIn, nIn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int maxOut = 4 * nIn + 1;
|
const int maxOut = 4 * nIn + 1;
|
||||||
char* bufOut = new char[maxOut];
|
char* bufOut = new char[maxOut];
|
||||||
|
|
||||||
ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn;
|
ICONV_CONST char *fromPtr = (ICONV_CONST char *)bufIn;
|
||||||
size_t nFrom = nIn;
|
size_t nFrom = nIn;
|
||||||
char *toPtr = (char *)bufOut;
|
char *toPtr = (char *)bufOut;
|
||||||
size_t nTo = maxOut;
|
size_t nTo = maxOut;
|
||||||
|
|
||||||
while (nFrom > 0) {
|
while (nFrom > 0) {
|
||||||
#if defined(Q_OS_SYMBIAN)
|
#if defined(Q_OS_SYMBIAN)
|
||||||
size_t oneway = iconv(cd,(const char**) &fromPtr, &nFrom, &toPtr, &nTo);
|
size_t oneway = iconv(cd,(const char**) &fromPtr, &nFrom, &toPtr, &nTo);
|
||||||
#else
|
#else
|
||||||
size_t oneway = iconv(cd,(char**) &fromPtr, &nFrom, &toPtr, &nTo);
|
size_t oneway = iconv(cd,(char**) &fromPtr, &nFrom, &toPtr, &nTo);
|
||||||
#endif
|
#endif
|
||||||
if (oneway == (size_t)(-1)) {
|
if (oneway == (size_t)(-1)) {
|
||||||
iconv_close(cd);
|
iconv_close(cd);
|
||||||
delete[] bufOut;
|
delete[] bufOut;
|
||||||
throw ReaderException("error converting characters");
|
throw ReaderException("error converting characters");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
iconv_close(cd);
|
||||||
iconv_close(cd);
|
|
||||||
|
|
||||||
int nResult = maxOut - nTo;
|
int nResult = maxOut - nTo;
|
||||||
bufOut[nResult] = '\0';
|
bufOut[nResult] = '\0';
|
||||||
result.append((const char *)bufOut);
|
result.append((const char *)bufOut);
|
||||||
delete[] bufOut;
|
delete[] bufOut;
|
||||||
#else
|
#else
|
||||||
result.append((const char *)bufIn, nIn);
|
Q_UNUSED(src);
|
||||||
|
result.append((const char *)bufIn, nIn);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
|
void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
|
||||||
string& result,
|
string& result,
|
||||||
int count) {
|
int count) {
|
||||||
BitSource& bits (*bits_);
|
BitSource& bits (*bits_);
|
||||||
// Don't crash trying to read more bits than we have available.
|
// Don't crash trying to read more bits than we have available.
|
||||||
if (count * 13 > bits.available()) {
|
if (count * 13 > bits.available()) {
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||||
// and decode as GB2312 afterwards
|
// and decode as GB2312 afterwards
|
||||||
size_t nBytes = 2 * count;
|
size_t nBytes = 2 * count;
|
||||||
char* buffer = new char[nBytes];
|
char* buffer = new char[nBytes];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
// Each 13 bits encodes a 2-byte character
|
// Each 13 bits encodes a 2-byte character
|
||||||
int twoBytes = bits.readBits(13);
|
int twoBytes = bits.readBits(13);
|
||||||
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
|
||||||
if (assembledTwoBytes < 0x003BF) {
|
if (assembledTwoBytes < 0x003BF) {
|
||||||
// In the 0xA1A1 to 0xAAFE range
|
// In the 0xA1A1 to 0xAAFE range
|
||||||
assembledTwoBytes += 0x0A1A1;
|
assembledTwoBytes += 0x0A1A1;
|
||||||
} else {
|
} else {
|
||||||
// In the 0xB0A1 to 0xFAFE range
|
// In the 0xB0A1 to 0xFAFE range
|
||||||
assembledTwoBytes += 0x0A6A1;
|
assembledTwoBytes += 0x0A6A1;
|
||||||
|
}
|
||||||
|
buffer[offset] = (char) ((assembledTwoBytes >> 8) & 0xFF);
|
||||||
|
buffer[offset + 1] = (char) (assembledTwoBytes & 0xFF);
|
||||||
|
offset += 2;
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
append(result, buffer, nBytes, StringUtils::GB2312);
|
||||||
|
} catch (ReaderException const& ignored) {
|
||||||
|
(void)ignored;
|
||||||
|
delete [] buffer;
|
||||||
|
throw FormatException();
|
||||||
}
|
}
|
||||||
buffer[offset] = (char) ((assembledTwoBytes >> 8) & 0xFF);
|
|
||||||
buffer[offset + 1] = (char) (assembledTwoBytes & 0xFF);
|
|
||||||
offset += 2;
|
|
||||||
count--;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
append(result, buffer, nBytes, StringUtils::GB2312);
|
|
||||||
} catch (ReaderException const& ignored) {
|
|
||||||
(void)ignored;
|
|
||||||
delete [] buffer;
|
delete [] buffer;
|
||||||
throw FormatException();
|
|
||||||
}
|
|
||||||
|
|
||||||
delete [] buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count) {
|
void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count) {
|
||||||
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
// Each character will require 2 bytes. Read the characters as 2-byte pairs
|
||||||
// and decode as Shift_JIS afterwards
|
// and decode as Shift_JIS afterwards
|
||||||
size_t nBytes = 2 * count;
|
size_t nBytes = 2 * count;
|
||||||
char* buffer = new char[nBytes];
|
char* buffer = new char[nBytes];
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
// Each 13 bits encodes a 2-byte character
|
// Each 13 bits encodes a 2-byte character
|
||||||
|
|
||||||
int twoBytes = bits->readBits(13);
|
int twoBytes = bits->readBits(13);
|
||||||
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
int assembledTwoBytes = ((twoBytes / 0x0C0) << 8) | (twoBytes % 0x0C0);
|
||||||
if (assembledTwoBytes < 0x01F00) {
|
if (assembledTwoBytes < 0x01F00) {
|
||||||
// In the 0x8140 to 0x9FFC range
|
// In the 0x8140 to 0x9FFC range
|
||||||
assembledTwoBytes += 0x08140;
|
assembledTwoBytes += 0x08140;
|
||||||
} else {
|
} else {
|
||||||
// In the 0xE040 to 0xEBBF range
|
// In the 0xE040 to 0xEBBF range
|
||||||
assembledTwoBytes += 0x0C140;
|
assembledTwoBytes += 0x0C140;
|
||||||
|
}
|
||||||
|
buffer[offset] = (char)(assembledTwoBytes >> 8);
|
||||||
|
buffer[offset + 1] = (char)assembledTwoBytes;
|
||||||
|
offset += 2;
|
||||||
|
count--;
|
||||||
}
|
}
|
||||||
buffer[offset] = (char)(assembledTwoBytes >> 8);
|
try {
|
||||||
buffer[offset + 1] = (char)assembledTwoBytes;
|
append(result, buffer, nBytes, StringUtils::SHIFT_JIS);
|
||||||
offset += 2;
|
} catch (ReaderException const& ignored) {
|
||||||
count--;
|
(void)ignored;
|
||||||
}
|
delete [] buffer;
|
||||||
try {
|
throw FormatException();
|
||||||
append(result, buffer, nBytes, StringUtils::SHIFT_JIS);
|
}
|
||||||
} catch (ReaderException const& ignored) {
|
delete[] buffer;
|
||||||
(void)ignored;
|
|
||||||
delete [] buffer;
|
|
||||||
throw FormatException();
|
|
||||||
}
|
|
||||||
delete[] buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
std::string DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
|
||||||
string& result,
|
string& result,
|
||||||
int count,
|
int count,
|
||||||
CharacterSetECI* currentCharacterSetECI,
|
CharacterSetECI* currentCharacterSetECI,
|
||||||
ArrayRef< ArrayRef<char> >& byteSegments,
|
ArrayRef< ArrayRef<char> >& byteSegments,
|
||||||
Hashtable const& hints) {
|
Hashtable const& hints) {
|
||||||
int nBytes = count;
|
int nBytes = count;
|
||||||
BitSource& bits (*bits_);
|
BitSource& bits (*bits_);
|
||||||
// Don't crash trying to read more bits than we have available.
|
// Don't crash trying to read more bits than we have available.
|
||||||
if (count << 3 > bits.available()) {
|
if (count << 3 > bits.available()) {
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayRef<char> bytes_ (count);
|
ArrayRef<char> bytes_ (count);
|
||||||
char* readBytes = &(*bytes_)[0];
|
char* readBytes = &(*bytes_)[0];
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
readBytes[i] = (char) bits.readBits(8);
|
readBytes[i] = (char) bits.readBits(8);
|
||||||
}
|
}
|
||||||
string encoding;
|
string encoding;
|
||||||
if (currentCharacterSetECI == 0) {
|
if (currentCharacterSetECI == 0) {
|
||||||
// The spec isn't clear on this mode; see
|
// The spec isn't clear on this mode; see
|
||||||
// section 6.4.5: t does not say which encoding to assuming
|
// section 6.4.5: t does not say which encoding to assuming
|
||||||
// upon decoding. I have seen ISO-8859-1 used as well as
|
// upon decoding. I have seen ISO-8859-1 used as well as
|
||||||
// Shift_JIS -- without anything like an ECI designator to
|
// Shift_JIS -- without anything like an ECI designator to
|
||||||
// give a hint.
|
// give a hint.
|
||||||
encoding = StringUtils::guessEncoding(readBytes, count, hints);
|
encoding = StringUtils::guessEncoding(readBytes, count, hints);
|
||||||
} else {
|
} else {
|
||||||
encoding = currentCharacterSetECI->name();
|
encoding = currentCharacterSetECI->name();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
append(result, readBytes, nBytes, encoding.c_str());
|
append(result, readBytes, nBytes, encoding.c_str());
|
||||||
} catch (ReaderException const& ignored) {
|
} catch (ReaderException const& ignored) {
|
||||||
(void)ignored;
|
(void)ignored;
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
}
|
||||||
byteSegments->values().push_back(bytes_);
|
byteSegments->values().push_back(bytes_);
|
||||||
|
return encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count) {
|
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count) {
|
||||||
int nBytes = count;
|
int nBytes = count;
|
||||||
char* bytes = new char[nBytes];
|
char* bytes = new char[nBytes];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
// Read three digits at a time
|
// Read three digits at a time
|
||||||
while (count >= 3) {
|
while (count >= 3) {
|
||||||
// Each 10 bits encodes three digits
|
// Each 10 bits encodes three digits
|
||||||
if (bits->available() < 10) {
|
if (bits->available() < 10) {
|
||||||
throw ReaderException("format exception");
|
throw ReaderException("format exception");
|
||||||
|
}
|
||||||
|
int threeDigitsBits = bits->readBits(10);
|
||||||
|
if (threeDigitsBits >= 1000) {
|
||||||
|
ostringstream s;
|
||||||
|
s << "Illegal value for 3-digit unit: " << threeDigitsBits;
|
||||||
|
delete[] bytes;
|
||||||
|
throw ReaderException(s.str().c_str());
|
||||||
|
}
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100];
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10];
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10];
|
||||||
|
count -= 3;
|
||||||
}
|
}
|
||||||
int threeDigitsBits = bits->readBits(10);
|
if (count == 2) {
|
||||||
if (threeDigitsBits >= 1000) {
|
if (bits->available() < 7) {
|
||||||
ostringstream s;
|
throw ReaderException("format exception");
|
||||||
s << "Illegal value for 3-digit unit: " << threeDigitsBits;
|
}
|
||||||
delete[] bytes;
|
// Two digits left over to read, encoded in 7 bits
|
||||||
throw ReaderException(s.str().c_str());
|
int twoDigitsBits = bits->readBits(7);
|
||||||
|
if (twoDigitsBits >= 100) {
|
||||||
|
ostringstream s;
|
||||||
|
s << "Illegal value for 2-digit unit: " << twoDigitsBits;
|
||||||
|
delete[] bytes;
|
||||||
|
throw ReaderException(s.str().c_str());
|
||||||
|
}
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10];
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10];
|
||||||
|
} else if (count == 1) {
|
||||||
|
if (bits->available() < 4) {
|
||||||
|
throw ReaderException("format exception");
|
||||||
|
}
|
||||||
|
// One digit left over to read
|
||||||
|
int digitBits = bits->readBits(4);
|
||||||
|
if (digitBits >= 10) {
|
||||||
|
ostringstream s;
|
||||||
|
s << "Illegal value for digit unit: " << digitBits;
|
||||||
|
delete[] bytes;
|
||||||
|
throw ReaderException(s.str().c_str());
|
||||||
|
}
|
||||||
|
bytes[i++] = ALPHANUMERIC_CHARS[digitBits];
|
||||||
}
|
}
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits / 100];
|
append(result, bytes, nBytes, StringUtils::ASCII);
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[(threeDigitsBits / 10) % 10];
|
delete[] bytes;
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[threeDigitsBits % 10];
|
|
||||||
count -= 3;
|
|
||||||
}
|
|
||||||
if (count == 2) {
|
|
||||||
if (bits->available() < 7) {
|
|
||||||
throw ReaderException("format exception");
|
|
||||||
}
|
|
||||||
// Two digits left over to read, encoded in 7 bits
|
|
||||||
int twoDigitsBits = bits->readBits(7);
|
|
||||||
if (twoDigitsBits >= 100) {
|
|
||||||
ostringstream s;
|
|
||||||
s << "Illegal value for 2-digit unit: " << twoDigitsBits;
|
|
||||||
delete[] bytes;
|
|
||||||
throw ReaderException(s.str().c_str());
|
|
||||||
}
|
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits / 10];
|
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[twoDigitsBits % 10];
|
|
||||||
} else if (count == 1) {
|
|
||||||
if (bits->available() < 4) {
|
|
||||||
throw ReaderException("format exception");
|
|
||||||
}
|
|
||||||
// One digit left over to read
|
|
||||||
int digitBits = bits->readBits(4);
|
|
||||||
if (digitBits >= 10) {
|
|
||||||
ostringstream s;
|
|
||||||
s << "Illegal value for digit unit: " << digitBits;
|
|
||||||
delete[] bytes;
|
|
||||||
throw ReaderException(s.str().c_str());
|
|
||||||
}
|
|
||||||
bytes[i++] = ALPHANUMERIC_CHARS[digitBits];
|
|
||||||
}
|
|
||||||
append(result, bytes, nBytes, StringUtils::ASCII);
|
|
||||||
delete[] bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char DecodedBitStreamParser::toAlphaNumericChar(size_t value) {
|
char DecodedBitStreamParser::toAlphaNumericChar(size_t value) {
|
||||||
if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) {
|
if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) {
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
}
|
||||||
return ALPHANUMERIC_CHARS[value];
|
return ALPHANUMERIC_CHARS[value];
|
||||||
}
|
}
|
||||||
|
|
||||||
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits_,
|
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits_,
|
||||||
string& result,
|
string& result,
|
||||||
int count,
|
int count,
|
||||||
bool fc1InEffect) {
|
bool fc1InEffect) {
|
||||||
BitSource& bits (*bits_);
|
BitSource& bits (*bits_);
|
||||||
ostringstream bytes;
|
ostringstream bytes;
|
||||||
// Read two characters at a time
|
// Read two characters at a time
|
||||||
while (count > 1) {
|
while (count > 1) {
|
||||||
if (bits.available() < 11) {
|
if (bits.available() < 11) {
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
|
||||||
int nextTwoCharsBits = bits.readBits(11);
|
|
||||||
bytes << toAlphaNumericChar(nextTwoCharsBits / 45);
|
|
||||||
bytes << toAlphaNumericChar(nextTwoCharsBits % 45);
|
|
||||||
count -= 2;
|
|
||||||
}
|
|
||||||
if (count == 1) {
|
|
||||||
// special case: one character left
|
|
||||||
if (bits.available() < 6) {
|
|
||||||
throw FormatException();
|
|
||||||
}
|
|
||||||
bytes << toAlphaNumericChar(bits.readBits(6));
|
|
||||||
}
|
|
||||||
// See section 6.4.8.1, 6.4.8.2
|
|
||||||
string s = bytes.str();
|
|
||||||
if (fc1InEffect) {
|
|
||||||
// We need to massage the result a bit if in an FNC1 mode:
|
|
||||||
ostringstream r;
|
|
||||||
for (size_t i = 0; i < s.length(); i++) {
|
|
||||||
if (s[i] != '%') {
|
|
||||||
r << s[i];
|
|
||||||
} else {
|
|
||||||
if (i < s.length() - 1 && s[i + 1] == '%') {
|
|
||||||
// %% is rendered as %
|
|
||||||
r << s[i++];
|
|
||||||
} else {
|
|
||||||
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
|
||||||
r << (char)0x1D;
|
|
||||||
}
|
}
|
||||||
}
|
int nextTwoCharsBits = bits.readBits(11);
|
||||||
|
bytes << toAlphaNumericChar(nextTwoCharsBits / 45);
|
||||||
|
bytes << toAlphaNumericChar(nextTwoCharsBits % 45);
|
||||||
|
count -= 2;
|
||||||
}
|
}
|
||||||
s = r.str();
|
if (count == 1) {
|
||||||
}
|
// special case: one character left
|
||||||
append(result, s, StringUtils::ASCII);
|
if (bits.available() < 6) {
|
||||||
|
throw FormatException();
|
||||||
|
}
|
||||||
|
bytes << toAlphaNumericChar(bits.readBits(6));
|
||||||
|
}
|
||||||
|
// See section 6.4.8.1, 6.4.8.2
|
||||||
|
string s = bytes.str();
|
||||||
|
if (fc1InEffect) {
|
||||||
|
// We need to massage the result a bit if in an FNC1 mode:
|
||||||
|
ostringstream r;
|
||||||
|
for (size_t i = 0; i < s.length(); i++) {
|
||||||
|
if (s[i] != '%') {
|
||||||
|
r << s[i];
|
||||||
|
} else {
|
||||||
|
if (i < s.length() - 1 && s[i + 1] == '%') {
|
||||||
|
// %% is rendered as %
|
||||||
|
r << s[i++];
|
||||||
|
} else {
|
||||||
|
// In alpha mode, % should be converted to FNC1 separator 0x1D
|
||||||
|
r << (char)0x1D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = r.str();
|
||||||
|
}
|
||||||
|
append(result, s, StringUtils::ASCII);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
int parseECIValue(BitSource& bits) {
|
int parseECIValue(BitSource& bits) {
|
||||||
int firstByte = bits.readBits(8);
|
int firstByte = bits.readBits(8);
|
||||||
if ((firstByte & 0x80) == 0) {
|
if ((firstByte & 0x80) == 0) {
|
||||||
// just one byte
|
// just one byte
|
||||||
return firstByte & 0x7F;
|
return firstByte & 0x7F;
|
||||||
}
|
}
|
||||||
if ((firstByte & 0xC0) == 0x80) {
|
if ((firstByte & 0xC0) == 0x80) {
|
||||||
// two bytes
|
// two bytes
|
||||||
int secondByte = bits.readBits(8);
|
int secondByte = bits.readBits(8);
|
||||||
return ((firstByte & 0x3F) << 8) | secondByte;
|
return ((firstByte & 0x3F) << 8) | secondByte;
|
||||||
}
|
}
|
||||||
if ((firstByte & 0xE0) == 0xC0) {
|
if ((firstByte & 0xE0) == 0xC0) {
|
||||||
// three bytes
|
// three bytes
|
||||||
int secondThirdBytes = bits.readBits(16);
|
int secondThirdBytes = bits.readBits(16);
|
||||||
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
|
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
|
||||||
}
|
}
|
||||||
throw FormatException();
|
throw FormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref<DecoderResult>
|
Ref<DecoderResult>
|
||||||
|
@ -352,80 +354,80 @@ DecodedBitStreamParser::decode(ArrayRef<char> bytes,
|
||||||
Version* version,
|
Version* version,
|
||||||
ErrorCorrectionLevel const& ecLevel,
|
ErrorCorrectionLevel const& ecLevel,
|
||||||
Hashtable const& hints) {
|
Hashtable const& hints) {
|
||||||
Ref<BitSource> bits_ (new BitSource(bytes));
|
Ref<BitSource> bits_ (new BitSource(bytes));
|
||||||
BitSource& bits (*bits_);
|
BitSource& bits (*bits_);
|
||||||
string result;
|
string result;
|
||||||
result.reserve(50);
|
result.reserve(50);
|
||||||
ArrayRef< ArrayRef<char> > byteSegments (0);
|
ArrayRef< ArrayRef<char> > byteSegments (0);
|
||||||
try {
|
|
||||||
CharacterSetECI* currentCharacterSetECI = 0;
|
CharacterSetECI* currentCharacterSetECI = 0;
|
||||||
bool fc1InEffect = false;
|
string charSet = "";
|
||||||
Mode* mode = 0;
|
try {
|
||||||
do {
|
bool fc1InEffect = false;
|
||||||
// While still another segment to read...
|
Mode* mode = 0;
|
||||||
if (bits.available() < 4) {
|
do {
|
||||||
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
// While still another segment to read...
|
||||||
mode = &Mode::TERMINATOR;
|
if (bits.available() < 4) {
|
||||||
} else {
|
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
|
||||||
try {
|
mode = &Mode::TERMINATOR;
|
||||||
mode = &Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
|
||||||
} catch (IllegalArgumentException const& iae) {
|
|
||||||
throw iae;
|
|
||||||
// throw FormatException.getFormatInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mode != &Mode::TERMINATOR) {
|
|
||||||
if ((mode == &Mode::FNC1_FIRST_POSITION) || (mode == &Mode::FNC1_SECOND_POSITION)) {
|
|
||||||
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
|
||||||
fc1InEffect = true;
|
|
||||||
} else if (mode == &Mode::STRUCTURED_APPEND) {
|
|
||||||
if (bits.available() < 16) {
|
|
||||||
throw FormatException();
|
|
||||||
}
|
|
||||||
// not really supported; all we do is ignore it
|
|
||||||
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
|
||||||
bits.readBits(16);
|
|
||||||
} else if (mode == &Mode::ECI) {
|
|
||||||
// Count doesn't apply to ECI
|
|
||||||
int value = parseECIValue(bits);
|
|
||||||
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
|
|
||||||
if (currentCharacterSetECI == 0) {
|
|
||||||
throw FormatException();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// First handle Hanzi mode which does not start with character count
|
|
||||||
if (mode == &Mode::HANZI) {
|
|
||||||
//chinese mode contains a sub set indicator right after mode indicator
|
|
||||||
int subset = bits.readBits(4);
|
|
||||||
int countHanzi = bits.readBits(mode->getCharacterCountBits(version));
|
|
||||||
if (subset == GB2312_SUBSET) {
|
|
||||||
decodeHanziSegment(bits_, result, countHanzi);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// "Normal" QR code modes:
|
|
||||||
// How many characters will follow, encoded in this mode?
|
|
||||||
int count = bits.readBits(mode->getCharacterCountBits(version));
|
|
||||||
if (mode == &Mode::NUMERIC) {
|
|
||||||
decodeNumericSegment(bits_, result, count);
|
|
||||||
} else if (mode == &Mode::ALPHANUMERIC) {
|
|
||||||
decodeAlphanumericSegment(bits_, result, count, fc1InEffect);
|
|
||||||
} else if (mode == &Mode::BYTE) {
|
|
||||||
decodeByteSegment(bits_, result, count, currentCharacterSetECI, byteSegments, hints);
|
|
||||||
} else if (mode == &Mode::KANJI) {
|
|
||||||
decodeKanjiSegment(bits_, result, count);
|
|
||||||
} else {
|
} else {
|
||||||
throw FormatException();
|
try {
|
||||||
|
mode = &Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
|
||||||
|
} catch (IllegalArgumentException const& iae) {
|
||||||
|
throw iae;
|
||||||
|
// throw FormatException.getFormatInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (mode != &Mode::TERMINATOR) {
|
||||||
}
|
if ((mode == &Mode::FNC1_FIRST_POSITION) || (mode == &Mode::FNC1_SECOND_POSITION)) {
|
||||||
}
|
// We do little with FNC1 except alter the parsed result a bit according to the spec
|
||||||
} while (mode != &Mode::TERMINATOR);
|
fc1InEffect = true;
|
||||||
} catch (IllegalArgumentException const& iae) {
|
} else if (mode == &Mode::STRUCTURED_APPEND) {
|
||||||
(void)iae;
|
if (bits.available() < 16) {
|
||||||
// from readBits() calls
|
throw FormatException();
|
||||||
throw FormatException();
|
}
|
||||||
}
|
// not really supported; all we do is ignore it
|
||||||
|
// Read next 8 bits (symbol sequence #) and 8 bits (parity data), then continue
|
||||||
return Ref<DecoderResult>(new DecoderResult(bytes, Ref<String>(new String(result)), byteSegments, (string)ecLevel));
|
bits.readBits(16);
|
||||||
|
} else if (mode == &Mode::ECI) {
|
||||||
|
// Count doesn't apply to ECI
|
||||||
|
int value = parseECIValue(bits);
|
||||||
|
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
|
||||||
|
if (currentCharacterSetECI == 0) {
|
||||||
|
throw FormatException();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// First handle Hanzi mode which does not start with character count
|
||||||
|
if (mode == &Mode::HANZI) {
|
||||||
|
//chinese mode contains a sub set indicator right after mode indicator
|
||||||
|
int subset = bits.readBits(4);
|
||||||
|
int countHanzi = bits.readBits(mode->getCharacterCountBits(version));
|
||||||
|
if (subset == GB2312_SUBSET) {
|
||||||
|
decodeHanziSegment(bits_, result, countHanzi);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// "Normal" QR code modes:
|
||||||
|
// How many characters will follow, encoded in this mode?
|
||||||
|
int count = bits.readBits(mode->getCharacterCountBits(version));
|
||||||
|
if (mode == &Mode::NUMERIC) {
|
||||||
|
decodeNumericSegment(bits_, result, count);
|
||||||
|
} else if (mode == &Mode::ALPHANUMERIC) {
|
||||||
|
decodeAlphanumericSegment(bits_, result, count, fc1InEffect);
|
||||||
|
} else if (mode == &Mode::BYTE) {
|
||||||
|
charSet = decodeByteSegment(bits_, result, count, currentCharacterSetECI, byteSegments, hints);
|
||||||
|
} else if (mode == &Mode::KANJI) {
|
||||||
|
decodeKanjiSegment(bits_, result, count);
|
||||||
|
} else {
|
||||||
|
throw FormatException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (mode != &Mode::TERMINATOR);
|
||||||
|
} catch (IllegalArgumentException const& iae) {
|
||||||
|
(void)iae;
|
||||||
|
// from readBits() calls
|
||||||
|
throw FormatException();
|
||||||
|
}
|
||||||
|
return Ref<DecoderResult>(new DecoderResult(bytes, Ref<String>(new String(result)), byteSegments, (string)ecLevel, charSet));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue