Port r1623 changes to C++

This provide C++ support for Hanzi from issue 582. With it the C++
qrcode blackbox test results again match the Java results.

The changes are mostly a re-port of various pieces of the Java code
for supporting modes and bit stream parsing in QR codes. It expands
the modes/parsing including ECI and FNC, refactors StringUtils as was
done in Java, and makes DecoderResult include the fields that the Java
code has.

Although the non-qrcode blackbox tests don't all match the Java code
(a known issue) the entire blackbox suite was run under valgrind and
has no errors, including invalid memory references and leaks.

This commit also includes a few cleanups for things like files that
had mixed line endings, i.e., a combination of LF and CR-LF.

git-svn-id: https://zxing.googlecode.com/svn/trunk@1966 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
smparkes@smparkes.net 2011-10-13 15:21:13 +00:00
parent 492d773f59
commit 2c787d8440
35 changed files with 909 additions and 320 deletions

14
cpp/.valgrind.supp Normal file
View file

@ -0,0 +1,14 @@
{
zxing::common::CharacterSetECI::init_tables
Memcheck:Leak
fun:malloc
...
fun:_ZN5zxing6common15CharacterSetECI11init_tablesEv
}
{
ImageMagick
Memcheck:Leak
...
fun:AcquireImage
}

1
cpp/.valgrindrc Normal file
View file

@ -0,0 +1 @@
--memcheck:leak-check=full --suppressions=.valgrind.supp --gen-suppressions=all

View file

@ -28,7 +28,7 @@ def all_files(dir, ext='.cpp', level=5):
magick_include = ['/usr/include/ImageMagick/']
magick_include = ['/usr/include/ImageMagick/', '/opt/local/include/ImageMagick/']
magick_libs = ['Magick++', 'MagickWand', 'MagickCore']
cppunit_libs = ['cppunit']

View file

@ -21,6 +21,8 @@
#include <zxing/common/IllegalArgumentException.h>
namespace zxing {
const DecodeHintType DecodeHints::CHARACTER_SET;
const DecodeHints DecodeHints::PRODUCT_HINT(
BARCODEFORMAT_UPC_E_HINT |
BARCODEFORMAT_UPC_A_HINT |

View file

@ -30,6 +30,12 @@ class DecodeHints {
private:
DecodeHintType hints;
Ref<ResultPointCallback> callback;
public:
static const DecodeHintType BARCODEFORMAT_QR_CODE_HINT = 1 << BarcodeFormat_QR_CODE;
static const DecodeHintType BARCODEFORMAT_DATA_MATRIX_HINT = 1 << BarcodeFormat_DATA_MATRIX;
static const DecodeHintType BARCODEFORMAT_UPC_E_HINT = 1 << BarcodeFormat_UPC_E;
@ -39,14 +45,9 @@ class DecodeHints {
static const DecodeHintType BARCODEFORMAT_CODE_128_HINT = 1 << BarcodeFormat_CODE_128;
static const DecodeHintType BARCODEFORMAT_CODE_39_HINT = 1 << BarcodeFormat_CODE_39;
static const DecodeHintType BARCODEFORMAT_ITF_HINT = 1 << BarcodeFormat_ITF;
static const DecodeHintType CHARACTER_SET = 1 << 30;
static const DecodeHintType TRYHARDER_HINT = 1 << 31;
DecodeHintType hints;
Ref<ResultPointCallback> callback;
public:
static const DecodeHints PRODUCT_HINT;
static const DecodeHints ONED_HINT;
static const DecodeHints DEFAULT_HINT;

View file

@ -1,16 +1,31 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Exception.cpp
* ZXing
*
* Created by Christian Brunschen on 03/06/2008.
* Copyright 2008 ZXing authors All rights reserved.
* Copyright 2008-2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/Exception.h>
namespace zxing {
Exception::Exception() {}
Exception::Exception(const char *msg) :
message(msg) {
}

View file

@ -30,6 +30,7 @@ private:
std::string message;
public:
Exception();
Exception(const char *msg);
virtual const char* what() const throw();
virtual ~Exception() throw();

View file

@ -0,0 +1,35 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* FormatException.cpp
* zxing
*
* Created by Christian Brunschen on 13/05/2008.
* Copyright 2008 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/FormatException.h>
namespace zxing {
FormatException::FormatException() {}
FormatException::FormatException(const char *msg) :
ReaderException(msg) {
}
FormatException::~FormatException() throw() {
}
}

View file

@ -0,0 +1,35 @@
#ifndef __FORMAT_EXCEPTION_H__
#define __FORMAT_EXCEPTION_H__
/*
* FormatException.h
* zxing
*
* Copyright 2010 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/ReaderException.h>
namespace zxing {
class FormatException : public ReaderException {
public:
FormatException();
FormatException(const char *msg);
~FormatException() throw();
};
}
#endif // __FORMAT_EXCEPTION_H__

View file

@ -89,7 +89,7 @@ namespace zxing {
for (unsigned int i = 0; i < readers_.size(); i++) {
try {
return readers_[i]->decode(image, hints_);
} catch (ReaderException re) {
} catch (ReaderException const& re) {
// continue
}
}

View file

@ -1,9 +1,10 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* ReaderException.cpp
* zxing
*
* Created by Christian Brunschen on 13/05/2008.
* Copyright 2008 ZXing authors All rights reserved.
* Copyright 2008-2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,6 +23,8 @@
namespace zxing {
ReaderException::ReaderException() {}
ReaderException::ReaderException(const char *msg) :
Exception(msg) {
}

View file

@ -26,6 +26,7 @@ namespace zxing {
class ReaderException : public Exception {
public:
ReaderException();
ReaderException(const char *msg);
~ReaderException() throw();
};

View file

@ -93,7 +93,7 @@ public:
}
};
template<typename T> class ArrayRef {
template<typename T> class ArrayRef : public Counted {
private:
public:
Array<T> *array_;

View file

@ -0,0 +1,87 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Copyright 2008-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/common/CharacterSetECI.h>
#include <zxing/common/IllegalArgumentException.h>
using std::string;
using zxing::common::CharacterSetECI;
using zxing::IllegalArgumentException;
std::map<int, CharacterSetECI*> CharacterSetECI::VALUE_TO_ECI;
std::map<std::string, CharacterSetECI*> CharacterSetECI::NAME_TO_ECI;
const bool CharacterSetECI::inited = CharacterSetECI::init_tables();
bool CharacterSetECI::init_tables() {
addCharacterSet(0, "Cp437");
{ char const* s[] = {"ISO8859_1", "ISO-8859-1", 0};
addCharacterSet(1, s); }
addCharacterSet(2, "Cp437");
{ char const* s[] = {"ISO8859_1", "ISO-8859-1", 0};
addCharacterSet(3, s); }
addCharacterSet(4, "ISO8859_2");
addCharacterSet(5, "ISO8859_3");
addCharacterSet(6, "ISO8859_4");
addCharacterSet(7, "ISO8859_5");
addCharacterSet(8, "ISO8859_6");
addCharacterSet(9, "ISO8859_7");
addCharacterSet(10, "ISO8859_8");
addCharacterSet(11, "ISO8859_9");
addCharacterSet(12, "ISO8859_10");
addCharacterSet(13, "ISO8859_11");
addCharacterSet(15, "ISO8859_13");
addCharacterSet(16, "ISO8859_14");
addCharacterSet(17, "ISO8859_15");
addCharacterSet(18, "ISO8859_16");
{ char const* s[] = {"SJIS", "Shift_JIS", 0};
addCharacterSet(20, s ); }
return true;
}
CharacterSetECI::CharacterSetECI(int value, char const* encodingName_)
: ECI(value), encodingName(encodingName_) {}
char const* CharacterSetECI::getEncodingName() {
return encodingName;
}
void CharacterSetECI::addCharacterSet(int value, char const* encodingName) {
CharacterSetECI* eci = new CharacterSetECI(value, encodingName);
VALUE_TO_ECI[value] = eci; // can't use valueOf
NAME_TO_ECI[string(encodingName)] = eci;
}
void CharacterSetECI::addCharacterSet(int value, char const* const* encodingNames) {
CharacterSetECI* eci = new CharacterSetECI(value, encodingNames[0]);
VALUE_TO_ECI[value] = eci;
for (int i = 0; encodingNames[i]; i++) {
NAME_TO_ECI[string(encodingNames[i])] = eci;
}
}
CharacterSetECI* CharacterSetECI::getCharacterSetECIByValue(int value) {
if (value < 0 || value >= 900) {
throw IllegalArgumentException("Bad ECI value: " + value);
}
return VALUE_TO_ECI[value];
}
CharacterSetECI* CharacterSetECI::getCharacterSetECIByName(string const& name) {
return NAME_TO_ECI[name];
}

View file

@ -0,0 +1,53 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
#ifndef __CHARACTERSET_ECI__
#define __CHARACTERSET_ECI__
/*
* Copyright 2008-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <map>
#include <zxing/common/ECI.h>
#include <zxing/DecodeHints.h>
namespace zxing {
namespace common {
class CharacterSetECI;
}
}
class zxing::common::CharacterSetECI : public ECI {
private:
static std::map<int, CharacterSetECI*> VALUE_TO_ECI;
static std::map<std::string, CharacterSetECI*> NAME_TO_ECI;
static const bool inited;
static bool init_tables();
char const* const encodingName;
CharacterSetECI(int value, char const* encodingName);
static void addCharacterSet(int value, char const* encodingName);
static void addCharacterSet(int value, char const* const* encodingNames);
public:
char const* getEncodingName();
static CharacterSetECI* getCharacterSetECIByValue(int value);
static CharacterSetECI* getCharacterSetECIByName(std::string const& name);
};
#endif

View file

@ -1,9 +1,10 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* DecoderResult.cpp
* zxing
*
* Created by Christian Brunschen on 20/05/2008.
* Copyright 2008 ZXing authors All rights reserved.
* Copyright 2008-2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,11 +21,21 @@
#include <zxing/common/DecoderResult.h>
namespace zxing {
using namespace std;
using namespace zxing;
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes, Ref<String> text) :
rawBytes_(rawBytes), text_(text) {
}
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
Ref<String> text,
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
string const& ecLevel) :
rawBytes_(rawBytes),
text_(text),
byteSegments_(byteSegments),
ecLevel_(ecLevel) {}
DecoderResult::DecoderResult(ArrayRef<unsigned char> rawBytes,
Ref<String> text)
: rawBytes_(rawBytes), text_(text) {}
ArrayRef<unsigned char> DecoderResult::getRawBytes() {
return rawBytes_;
@ -33,5 +44,3 @@ ArrayRef<unsigned char> DecoderResult::getRawBytes() {
Ref<String> DecoderResult::getText() {
return text_;
}
}

View file

@ -31,9 +31,17 @@ class DecoderResult : public Counted {
private:
ArrayRef<unsigned char> rawBytes_;
Ref<String> text_;
ArrayRef< ArrayRef<unsigned char> > byteSegments_;
std::string ecLevel_;
public:
DecoderResult(ArrayRef<unsigned char> rawBytes,
Ref<String> text,
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
std::string const& ecLevel);
DecoderResult(ArrayRef<unsigned char> rawBytes, Ref<String> text);
ArrayRef<unsigned char> getRawBytes();
Ref<String> getText();
};

View file

@ -0,0 +1,39 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Copyright 2008-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/common/ECI.h>
#include <zxing/common/CharacterSetECI.h>
#include <zxing/common/IllegalArgumentException.h>
using zxing::common::ECI;
using zxing::IllegalArgumentException;
ECI::ECI(int value_) : value(value_) {}
int ECI::getValue() const {
return value;
}
ECI* ECI::getECIByValue(int value) {
if (value < 0 || value > 999999) {
throw IllegalArgumentException("Bad ECI value: " + value);
}
if (value < 900) { // Character set ECIs use 000000 - 000899
return CharacterSetECI::getCharacterSetECIByValue(value);
}
return 0;
}

View file

@ -0,0 +1,40 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
#ifndef __ECI__
#define __ECI__
/*
* Copyright 2008-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace zxing {
namespace common {
class ECI;
}
}
class zxing::common::ECI {
private:
const int value;
protected:
ECI(int value);
public:
int getValue() const;
static ECI* getECIByValue(int value);
};
#endif

View file

@ -19,8 +19,8 @@
*/
#include <zxing/common/GlobalHistogramBinarizer.h>
#include <zxing/common/IllegalArgumentException.h>
#include <zxing/common/Array.h>
namespace zxing {
using namespace std;
@ -107,7 +107,8 @@ Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix() {
// Quickly calculates the histogram by sampling four rows from the image.
// This proved to be more robust on the blackbox tests than sampling a
// diagonal as we used to do.
unsigned char* row = new unsigned char[width];
ArrayRef<unsigned char> ref (width);
unsigned char* row = &ref[0];
for (int y = 1; y < 5; y++) {
int rownum = height * y / 5;
int right = (width << 2) / 5;
@ -130,7 +131,7 @@ Ref<BitMatrix> GlobalHistogramBinarizer::getBlackMatrix() {
}
cached_matrix_ = matrix_ref;
delete [] row;
// delete [] row;
return matrix_ref;
}

View file

@ -70,6 +70,9 @@ Ref<BitMatrix> HybridBinarizer::getBlackMatrix() {
calculateThresholdForBlock(luminances, subWidth, subHeight, width, height, blackPoints, newMatrix);
matrix_ = newMatrix;
// N.B.: these deletes are inadequate if anything between the new and this point can throw.
// As of this writing, it doesn't look like they do.
delete [] blackPoints;
delete [] luminances;
} else {

View file

@ -0,0 +1,175 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* Copyright (C) 2010-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zxing/common/StringUtils.h>
#include <zxing/DecodeHints.h>
using namespace std;
using namespace zxing;
using namespace zxing::common;
// N.B.: these are the iconv strings for at least some versions of iconv
char const* const StringUtils::PLATFORM_DEFAULT_ENCODING = "UTF-8";
char const* const StringUtils::ASCII = "ASCII";
char const* const StringUtils::SHIFT_JIS = "SHIFT_JIS";
char const* const StringUtils::GB2312 = "GBK";
char const* const StringUtils::EUC_JP = "EUC-JP";
char const* const StringUtils::UTF8 = "UTF-8";
char const* const StringUtils::ISO88591 = "ISO8859-1";
const bool StringUtils::ASSUME_SHIFT_JIS = false;
string
StringUtils::guessEncoding(unsigned char* bytes, int length, Hashtable const& hints) {
Hashtable::const_iterator i = hints.find(DecodeHints::CHARACTER_SET);
if (i != hints.end()) {
return i->second;
}
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
if (length > 3 &&
bytes[0] == (unsigned char) 0xEF &&
bytes[1] == (unsigned char) 0xBB &&
bytes[2] == (unsigned char) 0xBF) {
return UTF8;
}
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings. ISO-8859-1
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
// uses this as a first byte of a two-byte character. If we see this
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
// If we see something else in that second byte, we'll make the risky guess
// that it's UTF-8.
bool canBeISO88591 = true;
bool canBeShiftJIS = true;
bool canBeUTF8 = true;
int utf8BytesLeft = 0;
int maybeDoubleByteCount = 0;
int maybeSingleByteKatakanaCount = 0;
bool sawLatin1Supplement = false;
bool sawUTF8Start = false;
bool lastWasPossibleDoubleByteStart = false;
for (int i = 0;
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
i++) {
int value = bytes[i] & 0xFF;
// UTF-8 stuff
if (value >= 0x80 && value <= 0xBF) {
if (utf8BytesLeft > 0) {
utf8BytesLeft--;
}
} else {
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
if (value >= 0xC0 && value <= 0xFD) {
sawUTF8Start = true;
int valueCopy = value;
while ((valueCopy & 0x40) != 0) {
utf8BytesLeft++;
valueCopy <<= 1;
}
}
}
// ISO-8859-1 stuff
if ((value == 0xC2 || value == 0xC3) && i < length - 1) {
// This is really a poor hack. The slightly more exotic characters people might want to put in
// a QR Code, by which I mean the Latin-1 supplement characters (e.g. u-umlaut) have encodings
// that start with 0xC2 followed by [0xA0,0xBF], or start with 0xC3 followed by [0x80,0xBF].
int nextValue = bytes[i + 1] & 0xFF;
if (nextValue <= 0xBF &&
((value == 0xC2 && nextValue >= 0xA0) || (value == 0xC3 && nextValue >= 0x80))) {
sawLatin1Supplement = true;
}
}
if (value >= 0x7F && value <= 0x9F) {
canBeISO88591 = false;
}
// Shift_JIS stuff
if (value >= 0xA1 && value <= 0xDF) {
// count the number of characters that might be a Shift_JIS single-byte Katakana character
if (!lastWasPossibleDoubleByteStart) {
maybeSingleByteKatakanaCount++;
}
}
if (!lastWasPossibleDoubleByteStart &&
((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
canBeShiftJIS = false;
}
if ((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF)) {
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
// second byte.
if (lastWasPossibleDoubleByteStart) {
// If we just checked this and the last byte for being a valid double-byte
// char, don't check starting on this byte. If this and the last byte
// formed a valid pair, then this shouldn't be checked to see if it starts
// a double byte pair of course.
lastWasPossibleDoubleByteStart = false;
} else {
// ... otherwise do check to see if this plus the next byte form a valid
// double byte pair encoding a character.
lastWasPossibleDoubleByteStart = true;
if (i >= length - 1) {
canBeShiftJIS = false;
} else {
int nextValue = bytes[i + 1] & 0xFF;
if (nextValue < 0x40 || nextValue > 0xFC) {
canBeShiftJIS = false;
} else {
maybeDoubleByteCount++;
}
// There is some conflicting information out there about which bytes can follow which in
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
}
}
} else {
lastWasPossibleDoubleByteStart = false;
}
}
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
// Easy -- if assuming Shift_JIS and no evidence it can't be, done
if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
return SHIFT_JIS;
}
if (canBeUTF8 && sawUTF8Start) {
return UTF8;
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
// - If we saw
// - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
// - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
return SHIFT_JIS;
}
// Otherwise, we default to ISO-8859-1 unless we know it can't be
if (!sawLatin1Supplement && canBeISO88591) {
return ISO88591;
}
// Otherwise, we take a wild guess with platform encoding
return PLATFORM_DEFAULT_ENCODING;
}

View file

@ -0,0 +1,52 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
#ifndef __STRING_UTILS__
#define __STRING_UTILS__
/*
* Copyright (C) 2010-2011 ZXing authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <string>
#include <map>
#include <zxing/DecodeHints.h>
namespace zxing {
namespace common {
class StringUtils;
}
}
class zxing::common::StringUtils {
private:
static char const* const PLATFORM_DEFAULT_ENCODING;
StringUtils() {}
public:
static char const* const ASCII;
static char const* const SHIFT_JIS;
static char const* const GB2312;
static char const* const EUC_JP;
static char const* const UTF8;
static char const* const ISO88591;
static const bool ASSUME_SHIFT_JIS;
typedef std::map<DecodeHintType, std::string> Hashtable;
static std::string guessEncoding(unsigned char* bytes, int length, Hashtable const& hints);
};
#endif

View file

@ -34,7 +34,7 @@ using namespace std;
Decoder::Decoder() :
rsDecoder_(GF256::DATA_MATRIX_FIELD) {
}
void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCodewords) {
int numCodewords = codewordBytes->size();
@ -46,7 +46,7 @@ void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCo
try {
rsDecoder_.decode(codewordInts, numECCodewords);
} catch (ReedSolomonException ex) {
} catch (ReedSolomonException const& ex) {
ReaderException rex(ex.what());
throw rex;
}
@ -56,34 +56,34 @@ void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCo
}
}
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
// Construct a parser and read version, error-correction level
BitMatrixParser parser(bits);
Version *version = parser.readVersion(bits);
// Read codewords
ArrayRef<unsigned char> codewords(parser.readCodewords());
// Separate into data blocks
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
// Count total number of data bytes
int totalBytes = 0;
for (unsigned int i = 0; i < dataBlocks.size(); i++) {
totalBytes += dataBlocks[i]->getNumDataCodewords();
}
ArrayRef<unsigned char> resultBytes(totalBytes);
int resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes
for (unsigned int j = 0; j < dataBlocks.size(); j++) {
Ref<DataBlock> dataBlock(dataBlocks[j]);
ArrayRef<unsigned char> codewordBytes = dataBlock->getCodewords();
int numDataCodewords = dataBlock->getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords);
for (int i = 0; i < numDataCodewords; i++) {
resultBytes[resultOffset++] = codewordBytes[i];
}
}
Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
// Construct a parser and read version, error-correction level
BitMatrixParser parser(bits);
Version *version = parser.readVersion(bits);
// Read codewords
ArrayRef<unsigned char> codewords(parser.readCodewords());
// Separate into data blocks
std::vector<Ref<DataBlock> > dataBlocks = DataBlock::getDataBlocks(codewords, version);
// Count total number of data bytes
int totalBytes = 0;
for (unsigned int i = 0; i < dataBlocks.size(); i++) {
totalBytes += dataBlocks[i]->getNumDataCodewords();
}
ArrayRef<unsigned char> resultBytes(totalBytes);
int resultOffset = 0;
// Error-correct and copy data blocks together into a stream of bytes
for (unsigned int j = 0; j < dataBlocks.size(); j++) {
Ref<DataBlock> dataBlock(dataBlocks[j]);
ArrayRef<unsigned char> codewordBytes = dataBlock->getCodewords();
int numDataCodewords = dataBlock->getNumDataCodewords();
correctErrors(codewordBytes, numDataCodewords);
for (int i = 0; i < numDataCodewords; i++) {
resultBytes[resultOffset++] = codewordBytes[i];
}
}
// Decode the contents of that stream of bytes
DecodedBitStreamParser decodedBSParser;

View file

@ -279,7 +279,7 @@ namespace zxing {
// Decode another code from image
try {
code = decodeCode(row, counters, sizeof(counters)/sizeof(int), nextStart);
} catch (ReaderException re) {
} catch (ReaderException const& re) {
throw re;
}

View file

@ -103,7 +103,7 @@ namespace zxing {
delete [] endRange;
ArrayRef<unsigned char> resultBytes(1);
return Ref<Result>(new Result(resultString, resultBytes, resultPoints, BarcodeFormat_ITF));
} catch (ReaderException re) {
} catch (ReaderException const& re) {
delete [] startRange;
delete [] endRange;
return Ref<Result>();
@ -179,7 +179,7 @@ namespace zxing {
narrowLineWidth = (startPattern[1] - startPattern[0]) >> 2;
validateQuietZone(row, startPattern[0]);
return startPattern;
} catch (ReaderException re) {
} catch (ReaderException const& re) {
delete [] startPattern;
throw re;
}
@ -217,7 +217,7 @@ namespace zxing {
row->reverse();
return endPattern;
} catch (ReaderException re) {
} catch (ReaderException const& re) {
delete [] endPattern;
row->reverse();
throw re;

View file

@ -89,9 +89,9 @@ namespace zxing {
// Estimate black point for this row and load it:
try {
row = image->getBlackRow(rowNumber, row);
} catch (ReaderException re) {
} catch (ReaderException const& re) {
continue;
} catch (IllegalArgumentException re) {
} catch (IllegalArgumentException const& re) {
continue;
}

View file

@ -1,9 +1,10 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* ErrorCorrectionLevel.cpp
* zxing
*
* Created by Christian Brunschen on 15/05/2008.
* Copyright 2008 ZXing authors All rights reserved.
* Copyright 2008-2011 ZXing authors All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,15 +21,30 @@
#include <zxing/qrcode/ErrorCorrectionLevel.h>
using std::string;
namespace zxing {
namespace qrcode {
ErrorCorrectionLevel::ErrorCorrectionLevel(int inOrdinal) :
ordinal_(inOrdinal) {
ErrorCorrectionLevel::ErrorCorrectionLevel(int inOrdinal,
int bits,
char const* name) :
ordinal_(inOrdinal), bits_(bits), name_(name) {}
int ErrorCorrectionLevel::ordinal() const {
return ordinal_;
}
int ErrorCorrectionLevel::ordinal() {
return ordinal_;
int ErrorCorrectionLevel::bits() const {
return bits_;
}
string const& ErrorCorrectionLevel::name() const {
return name_;
}
ErrorCorrectionLevel::operator string const& () const {
return name_;
}
ErrorCorrectionLevel& ErrorCorrectionLevel::forBits(int bits) {
@ -38,10 +54,10 @@ ErrorCorrectionLevel& ErrorCorrectionLevel::forBits(int bits) {
return *FOR_BITS[bits];
}
ErrorCorrectionLevel ErrorCorrectionLevel::L(0);
ErrorCorrectionLevel ErrorCorrectionLevel::M(1);
ErrorCorrectionLevel ErrorCorrectionLevel::Q(2);
ErrorCorrectionLevel ErrorCorrectionLevel::H(3);
ErrorCorrectionLevel ErrorCorrectionLevel::L(0, 0x01, "L");
ErrorCorrectionLevel ErrorCorrectionLevel::M(1, 0x00, "M");
ErrorCorrectionLevel ErrorCorrectionLevel::Q(2, 0x03, "Q");
ErrorCorrectionLevel ErrorCorrectionLevel::H(3, 0x02, "H");
ErrorCorrectionLevel *ErrorCorrectionLevel::FOR_BITS[] = { &M, &L, &H, &Q };
int ErrorCorrectionLevel::N_LEVELS = 4;

View file

@ -28,7 +28,9 @@ namespace qrcode {
class ErrorCorrectionLevel {
private:
int ordinal_;
ErrorCorrectionLevel(int inOrdinal);
int bits_;
std::string name_;
ErrorCorrectionLevel(int inOrdinal, int bits, char const* name);
static ErrorCorrectionLevel *FOR_BITS[];
static int N_LEVELS;
public:
@ -37,7 +39,11 @@ public:
static ErrorCorrectionLevel Q;
static ErrorCorrectionLevel H;
int ordinal();
int ordinal() const;
int bits() const;
std::string const& name() const;
operator std::string const& () const;
static ErrorCorrectionLevel& forBits(int bits);
};
}

View file

@ -20,6 +20,9 @@
*/
#include <zxing/qrcode/decoder/DecodedBitStreamParser.h>
#include <zxing/common/CharacterSetECI.h>
#include <zxing/FormatException.h>
#include <zxing/common/StringUtils.h>
#include <iostream>
#ifndef NO_ICONV
#include <iconv.h>
@ -38,6 +41,7 @@
using namespace std;
using namespace zxing;
using namespace zxing::qrcode;
using namespace zxing::common;
const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
@ -46,19 +50,29 @@ const char DecodedBitStreamParser::ALPHANUMERIC_CHARS[] =
'Y', 'Z', ' ', '$', '%', '*', '+', '-', '.', '/', ':'
};
const char *DecodedBitStreamParser::ASCII = "ASCII";
const char *DecodedBitStreamParser::ISO88591 = "ISO-8859-1";
const char *DecodedBitStreamParser::UTF8 = "UTF-8";
const char *DecodedBitStreamParser::SHIFT_JIS = "SHIFT_JIS";
const char *DecodedBitStreamParser::EUC_JP = "EUC-JP";
namespace {int GB2312_SUBSET = 1;}
void DecodedBitStreamParser::append(std::string &result, const unsigned char *bufIn, size_t nIn, const char *src) {
void DecodedBitStreamParser::append(std::string &result,
string const& in,
const char *src) {
append(result, (unsigned char const*)in.c_str(), in.length(), src);
}
void DecodedBitStreamParser::append(std::string &result,
const unsigned char *bufIn,
size_t nIn,
const char *src) {
#ifndef NO_ICONV
if (nIn == 0) {
return;
}
iconv_t cd = iconv_open(UTF8, src);
iconv_t cd = iconv_open(StringUtils::UTF8, src);
if (cd == (iconv_t)-1) {
result.append((const char *)bufIn, nIn);
return;
}
const int maxOut = 4 * nIn + 1;
unsigned char* bufOut = new unsigned char[maxOut];
@ -86,6 +100,47 @@ void DecodedBitStreamParser::append(std::string &result, const unsigned char *bu
#endif
}
void DecodedBitStreamParser::decodeHanziSegment(Ref<BitSource> bits_,
string& result,
int count) {
BitSource& bits (*bits_);
// Don't crash trying to read more bits than we have available.
if (count * 13 > bits.available()) {
throw FormatException();
}
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as GB2312 afterwards
size_t nBytes = 2 * count;
unsigned char* buffer = new unsigned char[nBytes];
int offset = 0;
while (count > 0) {
// Each 13 bits encodes a 2-byte character
int twoBytes = bits.readBits(13);
int assembledTwoBytes = ((twoBytes / 0x060) << 8) | (twoBytes % 0x060);
if (assembledTwoBytes < 0x003BF) {
// In the 0xA1A1 to 0xAAFE range
assembledTwoBytes += 0x0A1A1;
} else {
// In the 0xB0A1 to 0xFAFE range
assembledTwoBytes += 0x0A6A1;
}
buffer[offset] = (unsigned char) ((assembledTwoBytes >> 8) & 0xFF);
buffer[offset + 1] = (unsigned char) (assembledTwoBytes & 0xFF);
offset += 2;
count--;
}
try {
append(result, buffer, nBytes, StringUtils::GB2312);
} catch (ReaderException const& re) {
delete [] buffer;
throw FormatException();
}
delete [] buffer;
}
void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string &result, int count) {
// Each character will require 2 bytes. Read the characters as 2-byte pairs
// and decode as Shift_JIS afterwards
@ -110,30 +165,45 @@ void DecodedBitStreamParser::decodeKanjiSegment(Ref<BitSource> bits, std::string
count--;
}
append(result, buffer, nBytes, SHIFT_JIS);
append(result, buffer, nBytes, StringUtils::SHIFT_JIS);
delete[] buffer;
}
void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits, std::string &result, int count) {
void DecodedBitStreamParser::decodeByteSegment(Ref<BitSource> bits_,
string& result,
int count,
CharacterSetECI* currentCharacterSetECI,
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
Hashtable const& hints) {
int nBytes = count;
unsigned char* readBytes = new unsigned char[nBytes];
if (count << 3 > bits->available()) {
ostringstream s;
s << "Count too large: " << count;
delete[] readBytes;
throw ReaderException(s.str().c_str());
BitSource& bits (*bits_);
// Don't crash trying to read more bits than we have available.
if (count << 3 > bits.available()) {
throw FormatException();
}
ArrayRef<unsigned char> bytes_ (count);
unsigned char* readBytes = &(*bytes_)[0];
for (int i = 0; i < count; i++) {
readBytes[i] = (unsigned char)bits->readBits(8);
readBytes[i] = (unsigned char) bits.readBits(8);
}
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
const char *encoding = guessEncoding(readBytes, nBytes);
append(result, readBytes, nBytes, encoding);
delete[] readBytes;
string encoding;
if (currentCharacterSetECI == 0) {
// The spec isn't clear on this mode; see
// section 6.4.5: t does not say which encoding to assuming
// upon decoding. I have seen ISO-8859-1 used as well as
// Shift_JIS -- without anything like an ECI designator to
// give a hint.
encoding = StringUtils::guessEncoding(readBytes, count, hints);
} else {
encoding = currentCharacterSetECI->getEncodingName();
}
try {
append(result, readBytes, nBytes, encoding.c_str());
} catch (ReaderException const& re) {
throw FormatException();
}
byteSegments->values().push_back(bytes_);
}
void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count) {
@ -186,249 +256,147 @@ void DecodedBitStreamParser::decodeNumericSegment(Ref<BitSource> bits, std::stri
}
bytes[i++] = ALPHANUMERIC_CHARS[digitBits];
}
append(result, bytes, nBytes, ASCII);
append(result, bytes, nBytes, StringUtils::ASCII);
delete[] bytes;
}
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits, std::string &result, int count) {
int nBytes = count;
unsigned char* bytes = new unsigned char[nBytes];
int i = 0;
char DecodedBitStreamParser::toAlphaNumericChar(size_t value) {
if (value >= sizeof(DecodedBitStreamParser::ALPHANUMERIC_CHARS)) {
throw FormatException();
}
return ALPHANUMERIC_CHARS[value];
}
void DecodedBitStreamParser::decodeAlphanumericSegment(Ref<BitSource> bits_,
string& result,
int count,
bool fc1InEffect) {
BitSource& bits (*bits_);
ostringstream bytes;
// Read two characters at a time
while (count > 1) {
int nextTwoCharsBits = bits->readBits(11);
bytes[i++] = ALPHANUMERIC_CHARS[nextTwoCharsBits / 45];
bytes[i++] = ALPHANUMERIC_CHARS[nextTwoCharsBits % 45];
int nextTwoCharsBits = bits.readBits(11);
bytes << toAlphaNumericChar(nextTwoCharsBits / 45);
bytes << toAlphaNumericChar(nextTwoCharsBits % 45);
count -= 2;
}
if (count == 1) {
bytes[i++] = ALPHANUMERIC_CHARS[bits->readBits(6)];
// special case: one character left
bytes << toAlphaNumericChar(bits.readBits(6));
}
append(result, bytes, nBytes, ASCII);
delete[] bytes;
}
const char *
DecodedBitStreamParser::guessEncoding(unsigned char *bytes, int length) {
const bool ASSUME_SHIFT_JIS = false;
char const* const PLATFORM_DEFAULT_ENCODING="UTF-8";
// Does it start with the UTF-8 byte order mark? then guess it's UTF-8
if (length > 3 && bytes[0] == (unsigned char)0xEF && bytes[1] == (unsigned char)0xBB && bytes[2]
== (unsigned char)0xBF) {
return UTF8;
}
// For now, merely tries to distinguish ISO-8859-1, UTF-8 and Shift_JIS,
// which should be by far the most common encodings. ISO-8859-1
// should not have bytes in the 0x80 - 0x9F range, while Shift_JIS
// uses this as a first byte of a two-byte character. If we see this
// followed by a valid second byte in Shift_JIS, assume it is Shift_JIS.
// If we see something else in that second byte, we'll make the risky guess
// that it's UTF-8.
bool canBeISO88591 = true;
bool canBeShiftJIS = true;
bool canBeUTF8 = true;
int utf8BytesLeft = 0;
int maybeDoubleByteCount = 0;
int maybeSingleByteKatakanaCount = 0;
bool sawLatin1Supplement = false;
bool sawUTF8Start = false;
bool lastWasPossibleDoubleByteStart = false;
for (int i = 0;
i < length && (canBeISO88591 || canBeShiftJIS || canBeUTF8);
i++) {
int value = bytes[i] & 0xFF;
// UTF-8 stuff
if (value >= 0x80 && value <= 0xBF) {
if (utf8BytesLeft > 0) {
utf8BytesLeft--;
}
} else {
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
if (value >= 0xC0 && value <= 0xFD) {
sawUTF8Start = true;
int valueCopy = value;
while ((valueCopy & 0x40) != 0) {
utf8BytesLeft++;
valueCopy <<= 1;
}
}
}
// Shift_JIS stuff
if (value >= 0xA1 && value <= 0xDF) {
// count the number of characters that might be a Shift_JIS single-byte Katakana character
if (!lastWasPossibleDoubleByteStart) {
maybeSingleByteKatakanaCount++;
}
}
if (!lastWasPossibleDoubleByteStart &&
((value >= 0xF0 && value <= 0xFF) || value == 0x80 || value == 0xA0)) {
canBeShiftJIS = false;
}
if (((value >= 0x81 && value <= 0x9F) || (value >= 0xE0 && value <= 0xEF))) {
// These start double-byte characters in Shift_JIS. Let's see if it's followed by a valid
// second byte.
if (lastWasPossibleDoubleByteStart) {
// If we just checked this and the last byte for being a valid double-byte
// char, don't check starting on this byte. If this and the last byte
// formed a valid pair, then this shouldn't be checked to see if it starts
// a double byte pair of course.
lastWasPossibleDoubleByteStart = false;
// 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 {
// ... otherwise do check to see if this plus the next byte form a valid
// double byte pair encoding a character.
lastWasPossibleDoubleByteStart = true;
if (i >= length - 1) {
canBeShiftJIS = false;
if (i < s.length() - 1 && s[i + 1] == '%') {
// %% is rendered as %
r << s[i++];
} else {
int nextValue = bytes[i + 1] & 0xFF;
if (nextValue < 0x40 || nextValue > 0xFC) {
canBeShiftJIS = false;
} else {
maybeDoubleByteCount++;
}
// There is some conflicting information out there about which bytes can follow which in
// double-byte Shift_JIS characters. The rule above seems to be the one that matches practice.
// In alpha mode, % should be converted to FNC1 separator 0x1D
r << (char)0x1D;
}
}
} else {
lastWasPossibleDoubleByteStart = false;
}
s = r.str();
}
if (utf8BytesLeft > 0) {
canBeUTF8 = false;
}
// Easy -- if assuming Shift_JIS and no evidence it can't be, done
if (canBeShiftJIS && ASSUME_SHIFT_JIS) {
return SHIFT_JIS;
}
if (canBeUTF8 && sawUTF8Start) {
return UTF8;
}
// Distinguishing Shift_JIS and ISO-8859-1 can be a little tough. The crude heuristic is:
// - If we saw
// - at least 3 bytes that starts a double-byte value (bytes that are rare in ISO-8859-1), or
// - over 5% of bytes could be single-byte Katakana (also rare in ISO-8859-1),
// - and, saw no sequences that are invalid in Shift_JIS, then we conclude Shift_JIS
if (canBeShiftJIS && (maybeDoubleByteCount >= 3 || 20 * maybeSingleByteKatakanaCount > length)) {
return SHIFT_JIS;
}
// Otherwise, we default to ISO-8859-1 unless we know it can't be
if (!sawLatin1Supplement && canBeISO88591) {
return ISO88591;
}
// Otherwise, we take a wild guess with platform encoding
return PLATFORM_DEFAULT_ENCODING;
append(result, s, StringUtils::ASCII);
}
/*
string DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes, Version *version) {
namespace {
int parseECIValue(BitSource bits) {
int firstByte = bits.readBits(8);
if ((firstByte & 0x80) == 0) {
// just one byte
return firstByte & 0x7F;
}
if ((firstByte & 0xC0) == 0x80) {
// two bytes
int secondByte = bits.readBits(8);
return ((firstByte & 0x3F) << 8) | secondByte;
}
if ((firstByte & 0xE0) == 0xC0) {
// three bytes
int secondThirdBytes = bits.readBits(16);
return ((firstByte & 0x1F) << 16) | secondThirdBytes;
}
throw IllegalArgumentException("Bad ECI bits starting with byte " + firstByte);
}
}
Ref<DecoderResult>
DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes,
Version* version,
ErrorCorrectionLevel const& ecLevel,
Hashtable const& hints) {
Ref<BitSource> bits_ (new BitSource(bytes));
BitSource& bits (*bits_);
string result;
Ref<BitSource> bits(new BitSource(bytes));
Mode *mode = &Mode::TERMINATOR;
do {
// While still another segment to read...
if (bits->available() < 4) {
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
mode = &Mode::TERMINATOR;
} else {
mode = &Mode::forBits(bits->readBits(4)); // mode is encoded by 4 bits
}
if (mode != &Mode::TERMINATOR) {
// 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);
} else if (mode == &Mode::BYTE) {
decodeByteSegment(bits, result, count);
} else if (mode == &Mode::KANJI) {
decodeKanjiSegment(bits, result, count);
} else {
throw ReaderException("Unsupported mode indicator");
}
}
} while (mode != &Mode::TERMINATOR);
return result;
}
*/
DecoderResult DecodedBitStreamParser::decode(ArrayRef<unsigned char> bytes,
Version* version,
ErrorCorrectionLevel ecLevel,
Hashtable hints) {
BitSource bits = new BitSource(bytes);
StringBuffer result = new StringBuffer(50);
CharacterSetECI currentCharacterSetECI = null;
boolean fc1InEffect = false;
Vector byteSegments = new Vector(1);
Mode mode;
CharacterSetECI* currentCharacterSetECI = 0;
bool fc1InEffect = false;
ArrayRef< ArrayRef<unsigned char> > byteSegments (size_t(0));
Mode* mode = 0;
do {
// While still another segment to read...
if (bits.available() < 4) {
// OK, assume we're done. Really, a TERMINATOR mode should have been recorded here
mode = Mode.TERMINATOR;
mode = &Mode::TERMINATOR;
} else {
try {
mode = Mode.forBits(bits.readBits(4)); // mode is encoded by 4 bits
} catch (IllegalArgumentException iae) {
throw FormatException.getFormatInstance();
mode = &Mode::forBits(bits.readBits(4)); // mode is encoded by 4 bits
} catch (IllegalArgumentException const& iae) {
throw iae;
// throw FormatException.getFormatInstance();
}
}
if (!mode.equals(Mode.TERMINATOR)) {
if (mode.equals(Mode.FNC1_FIRST_POSITION) || mode.equals(Mode.FNC1_SECOND_POSITION)) {
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.equals(Mode.STRUCTURED_APPEND)) {
} else if (mode == &Mode::STRUCTURED_APPEND) {
// 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.equals(Mode.ECI)) {
} else if (mode == &Mode::ECI) {
// Count doesn't apply to ECI
int value = parseECIValue(bits);
currentCharacterSetECI = CharacterSetECI.getCharacterSetECIByValue(value);
if (currentCharacterSetECI == null) {
throw FormatException.getFormatInstance();
currentCharacterSetECI = CharacterSetECI::getCharacterSetECIByValue(value);
if (currentCharacterSetECI == 0) {
throw FormatException();
}
} else {
// First handle Hanzi mode which does not start with character count
if (mode.equals(Mode.HANZI)) {
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));
int countHanzi = bits.readBits(mode->getCharacterCountBits(version));
if (subset == GB2312_SUBSET) {
decodeHanziSegment(bits, result, countHanzi);
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.equals(Mode.NUMERIC)) {
decodeNumericSegment(bits, result, count);
} else if (mode.equals(Mode.ALPHANUMERIC)) {
decodeAlphanumericSegment(bits, result, count, fc1InEffect);
} else if (mode.equals(Mode.BYTE)) {
decodeByteSegment(bits, result, count, currentCharacterSetECI, byteSegments, hints);
} else if (mode.equals(Mode.KANJI)) {
decodeKanjiSegment(bits, result, count);
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 {
throw FormatException.getFormatInstance();
throw FormatException();
}
}
}
}
} while (!mode.equals(Mode.TERMINATOR));
} while (mode != &Mode::TERMINATOR);
return new DecoderResult(bytes,
result.toString(),
byteSegments.isEmpty() ? null : byteSegments,
ecLevel == null ? null : ecLevel.toString());
return Ref<DecoderResult>(new DecoderResult(bytes, Ref<String>(new String(result)), byteSegments, (string)ecLevel));
}

View file

@ -24,33 +24,46 @@
#include <string>
#include <sstream>
#include <map>
#include <zxing/qrcode/decoder/Mode.h>
#include <zxing/common/BitSource.h>
#include <zxing/common/Counted.h>
#include <zxing/common/Array.h>
#include <zxing/common/DecoderResult.h>
#include <zxing/common/CharacterSetECI.h>
#include <zxing/DecodeHints.h>
namespace zxing {
namespace qrcode {
class DecodedBitStreamParser {
public:
typedef std::map<DecodeHintType, std::string> Hashtable;
private:
static const char ALPHANUMERIC_CHARS[];
static const char *ASCII;
static const char *ISO88591;
static const char *UTF8;
static const char *SHIFT_JIS;
static const char *EUC_JP;
static char const ALPHANUMERIC_CHARS[];
static char toAlphaNumericChar(size_t value);
static void decodeHanziSegment(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 decodeAlphanumericSegment(Ref<BitSource> bits, std::string &result, int count);
static void decodeByteSegment(Ref<BitSource> bits_,
std::string& result,
int count,
zxing::common::CharacterSetECI* currentCharacterSetECI,
ArrayRef< ArrayRef<unsigned char> >& byteSegments,
Hashtable const& hints);
static void decodeAlphanumericSegment(Ref<BitSource> bits, std::string &result, int count, bool fc1InEffect);
static void decodeNumericSegment(Ref<BitSource> bits, std::string &result, int count);
static const char *guessEncoding(unsigned char *bytes, int length);
static void append(std::string &ost, const unsigned char *bufIn, size_t nIn, const char *src);
static void append(std::string &ost, std::string const& in, const char *src);
public:
static DecoderResulta decode(ArrayRef<unsigned char> bytes, Version *version);
static Ref<DecoderResult> decode(ArrayRef<unsigned char> bytes,
Version *version,
ErrorCorrectionLevel const& ecLevel,
Hashtable const& hints);
};
}

View file

@ -46,7 +46,7 @@ void Decoder::correctErrors(ArrayRef<unsigned char> codewordBytes, int numDataCo
try {
rsDecoder_.decode(codewordInts, numECCodewords);
} catch (ReedSolomonException ex) {
} catch (ReedSolomonException const& ex) {
ReaderException rex(ex.what());
throw rex;
}
@ -92,11 +92,10 @@ Ref<DecoderResult> Decoder::decode(Ref<BitMatrix> bits) {
}
}
// Decode the contents of that stream of bytes
Ref<String> text(new String(DecodedBitStreamParser::decode(resultBytes, version)));
Ref<DecoderResult> result(new DecoderResult(resultBytes, text));
return result;
return DecodedBitStreamParser::decode(resultBytes,
version,
ecLevel,
DecodedBitStreamParser::Hashtable());
}
}

View file

@ -45,22 +45,33 @@ Mode::Mode(int cbv0_9, int cbv10_26, int cbv27, int bits, char const* name) :
}
Mode& Mode::forBits(int bits) {
switch (bits) {
case 0x0:
return TERMINATOR;
case 0x1:
return NUMERIC;
case 0x2:
return ALPHANUMERIC;
case 0x4:
return BYTE;
case 0x8:
return KANJI;
default:
ostringstream s;
s << "Illegal mode bits: " << bits;
throw ReaderException(s.str().c_str());
}
switch (bits) {
case 0x0:
return TERMINATOR;
case 0x1:
return NUMERIC;
case 0x2:
return ALPHANUMERIC;
case 0x3:
return STRUCTURED_APPEND;
case 0x4:
return BYTE;
case 0x5:
return FNC1_FIRST_POSITION;
case 0x7:
return ECI;
case 0x8:
return KANJI;
case 0x9:
return FNC1_SECOND_POSITION;
case 0xD:
// 0xD is defined in GBT 18284-2000, may not be supported in foreign country
return HANZI;
default:
ostringstream s;
s << "Illegal mode bits: " << bits;
throw ReaderException(s.str().c_str());
}
}
int Mode::getCharacterCountBits(Version *version) {

View file

@ -82,7 +82,7 @@ Ref<DetectorResult> Detector::detect(DecodeHints const& hints) {
try {
alignmentPattern = findAlignmentInRegion(moduleSize, estAlignmentX, estAlignmentY, (float)i);
break;
} catch (zxing::ReaderException re) {
} catch (zxing::ReaderException const& re) {
// try next round
}
}

View file

@ -1,3 +1,4 @@
// -*- mode:c++; tab-width:2; indent-tabs-mode:nil; c-basic-offset:2 -*-
/*
* main.cpp
* zxing