|
| 1 | +/* |
| 2 | +* Copyright 2016 Nu-book Inc. |
| 3 | +* Copyright 2016 ZXing authors |
| 4 | +*/ |
| 5 | +// SPDX-License-Identifier: Apache-2.0 |
| 6 | + |
| 7 | +#include "Barcode.h" |
| 8 | + |
| 9 | +#include "DecoderResult.h" |
| 10 | +#include "DetectorResult.h" |
| 11 | +#include "JSON.h" |
| 12 | +#include "ZXAlgorithms.h" |
| 13 | + |
| 14 | +#ifdef ZXING_EXPERIMENTAL_API |
| 15 | +#include "BitMatrix.h" |
| 16 | + |
| 17 | +#ifdef ZXING_USE_ZINT |
| 18 | +#include <zint.h> |
| 19 | +void zint_symbol_deleter::operator()(zint_symbol* p) const noexcept |
| 20 | +{ |
| 21 | + ZBarcode_Delete(p); |
| 22 | +} |
| 23 | +#else |
| 24 | +struct zint_symbol {}; |
| 25 | +void zint_symbol_deleter::operator()(zint_symbol*) const noexcept {} |
| 26 | +#endif |
| 27 | + |
| 28 | +#endif |
| 29 | + |
| 30 | +#include <cmath> |
| 31 | +#include <list> |
| 32 | +#include <map> |
| 33 | +#include <utility> |
| 34 | + |
| 35 | +namespace ZXing { |
| 36 | + |
| 37 | +Result::Result(const std::string& text, int y, int xStart, int xStop, BarcodeFormat format, SymbologyIdentifier si, Error error, bool readerInit) |
| 38 | + : _content({ByteArray(text)}, si), |
| 39 | + _error(error), |
| 40 | + _position(Line(y, xStart, xStop)), |
| 41 | + _format(format), |
| 42 | + _readerInit(readerInit) |
| 43 | +{} |
| 44 | + |
| 45 | +Result::Result(DecoderResult&& decodeResult, DetectorResult&& detectorResult, BarcodeFormat format) |
| 46 | + : _content(std::move(decodeResult).content()), |
| 47 | + _error(std::move(decodeResult).error()), |
| 48 | + _position(std::move(detectorResult).position()), |
| 49 | + _sai(decodeResult.structuredAppend()), |
| 50 | + _format(format), |
| 51 | + _lineCount(decodeResult.lineCount()), |
| 52 | + _isMirrored(decodeResult.isMirrored()), |
| 53 | + _readerInit(decodeResult.readerInit()) |
| 54 | +#ifdef ZXING_EXPERIMENTAL_API |
| 55 | + , _symbol(std::make_shared<BitMatrix>(std::move(detectorResult).bits())) |
| 56 | + , _json(std::move(decodeResult).json()) |
| 57 | +#endif |
| 58 | +{ |
| 59 | + if (decodeResult.versionNumber()) |
| 60 | + snprintf(_version, 4, "%d", decodeResult.versionNumber()); |
| 61 | + snprintf(_ecLevel, 4, "%s", decodeResult.ecLevel().data()); |
| 62 | + |
| 63 | + // TODO: add type opaque and code specific 'extra data'? (see DecoderResult::extra()) |
| 64 | +} |
| 65 | + |
| 66 | +Result::Result(DecoderResult&& decodeResult, Position&& position, BarcodeFormat format) |
| 67 | + : Result(std::move(decodeResult), {{}, std::move(position)}, format) |
| 68 | +{} |
| 69 | + |
| 70 | +bool Result::isValid() const |
| 71 | +{ |
| 72 | + return format() != BarcodeFormat::None && !_content.bytes.empty() && !error(); |
| 73 | +} |
| 74 | + |
| 75 | +const ByteArray& Result::bytes() const |
| 76 | +{ |
| 77 | + return _content.bytes; |
| 78 | +} |
| 79 | + |
| 80 | +ByteArray Result::bytesECI() const |
| 81 | +{ |
| 82 | + return _content.bytesECI(); |
| 83 | +} |
| 84 | + |
| 85 | +std::string Result::text(TextMode mode) const |
| 86 | +{ |
| 87 | + return _content.text(mode); |
| 88 | +} |
| 89 | + |
| 90 | +std::string Result::text() const |
| 91 | +{ |
| 92 | + return text(_readerOpts.textMode()); |
| 93 | +} |
| 94 | + |
| 95 | +std::string Result::ecLevel() const |
| 96 | +{ |
| 97 | + return _ecLevel; |
| 98 | +} |
| 99 | + |
| 100 | +ContentType Result::contentType() const |
| 101 | +{ |
| 102 | + return _content.type(); |
| 103 | +} |
| 104 | + |
| 105 | +bool Result::hasECI() const |
| 106 | +{ |
| 107 | + return _content.hasECI; |
| 108 | +} |
| 109 | + |
| 110 | +int Result::orientation() const |
| 111 | +{ |
| 112 | + constexpr auto std_numbers_pi_v = 3.14159265358979323846; // TODO: c++20 <numbers> |
| 113 | + return narrow_cast<int>(std::lround(_position.orientation() * 180 / std_numbers_pi_v)); |
| 114 | +} |
| 115 | + |
| 116 | +std::string Result::symbologyIdentifier() const |
| 117 | +{ |
| 118 | + return _content.symbology.toString(); |
| 119 | +} |
| 120 | + |
| 121 | +int Result::sequenceSize() const |
| 122 | +{ |
| 123 | + return _sai.count; |
| 124 | +} |
| 125 | + |
| 126 | +int Result::sequenceIndex() const |
| 127 | +{ |
| 128 | + return _sai.index; |
| 129 | +} |
| 130 | + |
| 131 | +std::string Result::sequenceId() const |
| 132 | +{ |
| 133 | + return _sai.id; |
| 134 | +} |
| 135 | + |
| 136 | +std::string Result::version() const |
| 137 | +{ |
| 138 | + return _version; |
| 139 | +} |
| 140 | + |
| 141 | +Result& Result::setReaderOptions(const ReaderOptions& opts) |
| 142 | +{ |
| 143 | + if (opts.characterSet() != CharacterSet::Unknown) |
| 144 | + _content.defaultCharset = opts.characterSet(); |
| 145 | + _readerOpts = opts; |
| 146 | + return *this; |
| 147 | +} |
| 148 | + |
| 149 | +#ifdef ZXING_EXPERIMENTAL_API |
| 150 | +void Result::symbol(BitMatrix&& bits) |
| 151 | +{ |
| 152 | + bits.flipAll(); |
| 153 | + _symbol = std::make_shared<BitMatrix>(std::move(bits)); |
| 154 | +} |
| 155 | + |
| 156 | +ImageView Result::symbol() const |
| 157 | +{ |
| 158 | + return _symbol && !_symbol->empty() ? ImageView{_symbol->row(0).begin(), _symbol->width(), _symbol->height(), ImageFormat::Lum} |
| 159 | + : ImageView{}; |
| 160 | +} |
| 161 | + |
| 162 | +void Result::zint(unique_zint_symbol&& z) |
| 163 | +{ |
| 164 | + _zint = std::shared_ptr(std::move(z)); |
| 165 | +} |
| 166 | + |
| 167 | +std::string Result::extra(std::string_view key) const |
| 168 | +{ |
| 169 | + if (key == "ALL") { |
| 170 | + if (format() == BarcodeFormat::None) |
| 171 | + return {}; |
| 172 | + auto res = |
| 173 | + StrCat("{", JsonProp("Text", text(TextMode::Plain)), JsonProp("HRI", text(TextMode::HRI)), |
| 174 | + JsonProp("TextECI", text(TextMode::ECI)), JsonProp("Bytes", text(TextMode::Hex)), |
| 175 | + JsonProp("Identifier", symbologyIdentifier()), JsonProp("Format", ToString(format())), |
| 176 | + JsonProp("ContentType", isValid() ? ToString(contentType()) : ""), JsonProp("Position", ToString(position())), |
| 177 | + JsonProp("HasECI", hasECI()), JsonProp("IsMirrored", isMirrored()), JsonProp("IsInverted", isInverted()), _json, |
| 178 | + JsonProp("Error", ToString(error()))); |
| 179 | + res.back() = '}'; |
| 180 | + return res; |
| 181 | + } |
| 182 | + return _json.empty() ? "" : key.empty() ? StrCat("{", _json.substr(0, _json.size() - 1), "}") : std::string(JsonGetStr(_json, key)); |
| 183 | +} |
| 184 | +#endif |
| 185 | + |
| 186 | +bool Result::operator==(const Result& o) const |
| 187 | +{ |
| 188 | + if (format() != o.format()) |
| 189 | + return false; |
| 190 | + |
| 191 | + // handle MatrixCodes first |
| 192 | + if (!IsLinearBarcode(format())) { |
| 193 | + if (bytes() != o.bytes() && isValid() && o.isValid()) |
| 194 | + return false; |
| 195 | + |
| 196 | + // check for equal position if both are valid with equal bytes or at least one is in error |
| 197 | + return IsInside(Center(o.position()), position()); |
| 198 | + } |
| 199 | + |
| 200 | + if (bytes() != o.bytes() || error() != o.error() || orientation() != o.orientation()) |
| 201 | + return false; |
| 202 | + |
| 203 | + if (lineCount() > 1 && o.lineCount() > 1) |
| 204 | + return HaveIntersectingBoundingBoxes(o.position(), position()); |
| 205 | + |
| 206 | + // the following code is only meant for this or other lineCount == 1 |
| 207 | + assert(lineCount() == 1 || o.lineCount() == 1); |
| 208 | + |
| 209 | + // sl == single line, ml = multi line |
| 210 | + const auto& sl = lineCount() == 1 ? *this : o; |
| 211 | + const auto& ml = lineCount() == 1 ? o : *this; |
| 212 | + |
| 213 | + // If one line is less than half the length of the other away from the |
| 214 | + // latter, we consider it to belong to the same symbol. |
| 215 | + // Additionally, both need to have roughly the same length (see #367). |
| 216 | + auto dTop = maxAbsComponent(ml.position().topLeft() - sl.position().topLeft()); |
| 217 | + auto dBot = maxAbsComponent(ml.position().bottomLeft() - sl.position().topLeft()); |
| 218 | + auto slLength = maxAbsComponent(sl.position().topLeft() - sl.position().bottomRight()); |
| 219 | + bool isHorizontal = sl.position().topLeft().y == sl.position().bottomRight().y; |
| 220 | + // Measure the multi line length in the same direction as the single line one (not diagonaly) |
| 221 | + // to make sure overly tall symbols don't get segmented (see #769). |
| 222 | + auto mlLength = isHorizontal ? std::abs(ml.position().topLeft().x - ml.position().bottomRight().x) |
| 223 | + : std::abs(ml.position().topLeft().y - ml.position().bottomRight().y); |
| 224 | + |
| 225 | + return std::min(dTop, dBot) < slLength / 2 && std::abs(slLength - mlLength) < slLength / 5; |
| 226 | +} |
| 227 | + |
| 228 | +Barcode MergeStructuredAppendSequence(const Barcodes& barcodes) |
| 229 | +{ |
| 230 | + if (barcodes.empty()) |
| 231 | + return {}; |
| 232 | + |
| 233 | + std::list<Barcode> allBarcodes(barcodes.begin(), barcodes.end()); |
| 234 | + allBarcodes.sort([](const Barcode& r1, const Barcode& r2) { return r1.sequenceIndex() < r2.sequenceIndex(); }); |
| 235 | + |
| 236 | + Barcode res = allBarcodes.front(); |
| 237 | + for (auto i = std::next(allBarcodes.begin()); i != allBarcodes.end(); ++i) |
| 238 | + res._content.append(i->_content); |
| 239 | + |
| 240 | + res._position = {}; |
| 241 | + res._sai.index = -1; |
| 242 | + |
| 243 | + if (allBarcodes.back().sequenceSize() != Size(allBarcodes) || |
| 244 | + !std::all_of(allBarcodes.begin(), allBarcodes.end(), |
| 245 | + [&](Barcode& it) { return it.sequenceId() == allBarcodes.front().sequenceId(); })) |
| 246 | + res._error = FormatError("sequenceIDs not matching during structured append sequence merging"); |
| 247 | + |
| 248 | + return res; |
| 249 | +} |
| 250 | + |
| 251 | +Barcodes MergeStructuredAppendSequences(const Barcodes& barcodes) |
| 252 | +{ |
| 253 | + std::map<std::string, Barcodes> sas; |
| 254 | + for (auto& barcode : barcodes) { |
| 255 | + if (barcode.isPartOfSequence()) |
| 256 | + sas[barcode.sequenceId()].push_back(barcode); |
| 257 | + } |
| 258 | + |
| 259 | + Barcodes res; |
| 260 | + for (auto& [id, seq] : sas) { |
| 261 | + auto barcode = MergeStructuredAppendSequence(seq); |
| 262 | + if (barcode.isValid()) |
| 263 | + res.push_back(std::move(barcode)); |
| 264 | + } |
| 265 | + |
| 266 | + return res; |
| 267 | +} |
| 268 | + |
| 269 | +} // namespace ZXing |
0 commit comments