You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
12 KiB
460 lines
12 KiB
/* |
|
* Copyright 2023 siiky |
|
* Copyright 2023 Axel Waggershauser |
|
*/ |
|
// SPDX-License-Identifier: Apache-2.0 |
|
|
|
#include "ZXingC.h" |
|
|
|
#include "ZXingCpp.h" |
|
#include "ZXAlgorithms.h" |
|
#include "ZXConfig.h" |
|
#include "Version.h" |
|
|
|
#include <bit> |
|
#include <cstdlib> |
|
#include <exception> |
|
#include <string> |
|
#include <string_view> |
|
#include <utility> |
|
|
|
using namespace ZXing; |
|
|
|
static ZX_THREAD_LOCAL std::string lastErrorMsg; |
|
static Barcodes emptyBarcodes{}; // used to prevent new heap allocation for each empty result |
|
|
|
template<typename R, typename T> R transmute_cast(const T& v) noexcept |
|
{ |
|
static_assert(sizeof(T) == sizeof(R)); |
|
return *(const R*)(&v); |
|
} |
|
|
|
template<typename C, typename P = typename C::pointer> |
|
P copy(const C& c) noexcept |
|
{ |
|
auto ret = (P)malloc((c.size() + 1) * sizeof(typename C::value_type)); |
|
if (ret) { |
|
memcpy(ret, c.data(), c.size() * sizeof(typename C::value_type)); |
|
ret[c.size()] = typename C::value_type(0); |
|
} |
|
return ret; |
|
} |
|
|
|
template<typename C, typename P = typename C::pointer> |
|
P copy(const C& c, int* len) noexcept |
|
{ |
|
// for convenience and as a safety measure, we NULL terminate even byte arrays |
|
auto ret = copy(c); |
|
if (len) |
|
*len = ret ? Size(c) : 0; |
|
return ret; |
|
} |
|
|
|
#define ZX_CHECK( GOOD, MSG ) \ |
|
if (!(GOOD)) { \ |
|
lastErrorMsg = MSG; \ |
|
return {}; \ |
|
} |
|
|
|
#define ZX_CATCH(...) \ |
|
catch (std::exception & e) { \ |
|
lastErrorMsg = e.what(); \ |
|
} catch (...) { \ |
|
lastErrorMsg = "Unknown error"; \ |
|
} \ |
|
return __VA_ARGS__; |
|
|
|
#define ZX_TRY(...) \ |
|
try { \ |
|
return __VA_ARGS__; \ |
|
} \ |
|
ZX_CATCH({}) |
|
|
|
|
|
extern "C" { |
|
|
|
/* |
|
* MARK: - ImageView.h |
|
*/ |
|
|
|
ZXing_ImageView* ZXing_ImageView_new(const uint8_t* data, int width, int height, ZXing_ImageFormat format, int rowStride, |
|
int pixStride) |
|
{ |
|
auto cppformat = static_cast<ImageFormat>(format); |
|
ZX_TRY(new ImageView(data, width, height, cppformat, rowStride, pixStride)) |
|
} |
|
|
|
ZXing_ImageView* ZXing_ImageView_new_checked(const uint8_t* data, int size, int width, int height, ZXing_ImageFormat format, |
|
int rowStride, int pixStride) |
|
{ |
|
auto cppformat = static_cast<ImageFormat>(format); |
|
ZX_TRY(new ImageView(data, size, width, height, cppformat, rowStride, pixStride)) |
|
} |
|
|
|
void ZXing_ImageView_delete(ZXing_ImageView* iv) |
|
{ |
|
delete iv; |
|
} |
|
|
|
void ZXing_ImageView_crop(ZXing_ImageView* iv, int left, int top, int width, int height) |
|
{ |
|
*iv = iv->cropped(left, top, width, height); |
|
} |
|
|
|
void ZXing_ImageView_rotate(ZXing_ImageView* iv, int degree) |
|
{ |
|
*iv = iv->rotated(degree); |
|
} |
|
|
|
void ZXing_Image_delete(ZXing_Image* img) |
|
{ |
|
delete img; |
|
} |
|
|
|
const uint8_t* ZXing_Image_data(const ZXing_Image* img) |
|
{ |
|
return img->data(); |
|
} |
|
|
|
int ZXing_Image_width(const ZXing_Image* img) |
|
{ |
|
return img->width(); |
|
} |
|
|
|
int ZXing_Image_height(const ZXing_Image* img) |
|
{ |
|
return img->height(); |
|
} |
|
|
|
ZXing_ImageFormat ZXing_Image_format(const ZXing_Image* img) |
|
{ |
|
return transmute_cast<ZXing_ImageFormat>(img->format()); |
|
} |
|
|
|
/* |
|
* MARK: - BarcodeFormat.h |
|
*/ |
|
|
|
ZXing_BarcodeFormat ZXing_BarcodeFormatSymbology(ZXing_BarcodeFormat format) |
|
{ |
|
return transmute_cast<ZXing_BarcodeFormat>(Symbology(transmute_cast<BarcodeFormat>(format))); |
|
} |
|
|
|
ZXing_BarcodeFormat ZXing_BarcodeFormatFromString(const char* str) |
|
{ |
|
try { |
|
return transmute_cast<ZXing_BarcodeFormat>(BarcodeFormatFromString(str)); |
|
} |
|
ZX_CATCH(ZXing_BarcodeFormat_Invalid) |
|
} |
|
|
|
char* ZXing_BarcodeFormatToString(ZXing_BarcodeFormat format) |
|
{ |
|
ZX_TRY(copy(ToString(transmute_cast<BarcodeFormat>(format)))); |
|
} |
|
|
|
ZXing_BarcodeFormat* ZXing_BarcodeFormatsList(ZXing_BarcodeFormat filter, int* outCount) |
|
{ |
|
ZX_TRY((ZXing_BarcodeFormat*)copy(BarcodeFormats::list(transmute_cast<BarcodeFormat>(filter)), outCount)); |
|
} |
|
|
|
ZXing_BarcodeFormat* ZXing_BarcodeFormatsFromString(const char* str, int* outCount) |
|
{ |
|
if (!str) { |
|
if (outCount) |
|
*outCount = 0; |
|
return NULL; |
|
} |
|
ZX_TRY((ZXing_BarcodeFormat*)copy(BarcodeFormats(str), outCount)); |
|
} |
|
|
|
char* ZXing_BarcodeFormatsToString(const ZXing_BarcodeFormat* formats, int count) |
|
{ |
|
if (!formats || count == 0) |
|
return copy(std::string{}); |
|
ZX_TRY(copy(ToString(BarcodeFormats(std::vector((BarcodeFormat*)formats, (BarcodeFormat*)formats + count))))); |
|
} |
|
|
|
/* |
|
* MARK: - Barcode.h |
|
*/ |
|
|
|
char* ZXing_ContentTypeToString(ZXing_ContentType type) |
|
{ |
|
return copy(ToString(static_cast<ContentType>(type))); |
|
} |
|
|
|
char* ZXing_PositionToString(ZXing_Position position) |
|
{ |
|
return copy(ToString(transmute_cast<Position>(position))); |
|
} |
|
|
|
|
|
bool ZXing_Barcode_isValid(const ZXing_Barcode* barcode) |
|
{ |
|
return barcode != NULL && barcode->isValid(); |
|
} |
|
|
|
ZXing_ErrorType ZXing_Barcode_errorType(const ZXing_Barcode* barcode) |
|
{ |
|
return static_cast<ZXing_ErrorType>(barcode->error().type()); |
|
} |
|
|
|
char* ZXing_Barcode_errorMsg(const ZXing_Barcode* barcode) |
|
{ |
|
return copy(ToString(barcode->error())); |
|
} |
|
|
|
uint8_t* ZXing_Barcode_bytes(const ZXing_Barcode* barcode, int* len) |
|
{ |
|
return copy(barcode->bytes(), len); |
|
} |
|
|
|
uint8_t* ZXing_Barcode_bytesECI(const ZXing_Barcode* barcode, int* len) |
|
{ |
|
return copy(barcode->bytesECI(), len); |
|
} |
|
|
|
char* ZXing_Barcode_extra(const ZXing_Barcode* barcode, const char* key) |
|
{ |
|
return copy(barcode->extra(key ? key : "")); |
|
} |
|
|
|
#define ZX_GETTER(TYPE, NAME, TRANS) \ |
|
TYPE ZXing_Barcode_##NAME(const ZXing_Barcode* barcode) { return TRANS(barcode->NAME()); } |
|
|
|
ZX_GETTER(ZXing_BarcodeFormat, format, static_cast<ZXing_BarcodeFormat>) |
|
ZX_GETTER(ZXing_BarcodeFormat, symbology, static_cast<ZXing_BarcodeFormat>) |
|
ZX_GETTER(ZXing_ContentType, contentType, static_cast<ZXing_ContentType>) |
|
ZX_GETTER(char*, text, copy) |
|
ZX_GETTER(char*, ecLevel, copy) |
|
ZX_GETTER(char*, symbologyIdentifier, copy) |
|
ZX_GETTER(char*, sequenceId, copy) |
|
ZX_GETTER(ZXing_Position, position, transmute_cast<ZXing_Position>) |
|
|
|
ZX_GETTER(int, orientation,) |
|
ZX_GETTER(bool, hasECI,) |
|
ZX_GETTER(bool, isInverted,) |
|
ZX_GETTER(bool, isMirrored,) |
|
ZX_GETTER(int, lineCount,) |
|
ZX_GETTER(int, sequenceIndex,) |
|
ZX_GETTER(int, sequenceSize,) |
|
|
|
void ZXing_Barcode_delete(ZXing_Barcode* barcode) |
|
{ |
|
delete barcode; |
|
} |
|
|
|
void ZXing_Barcodes_delete(ZXing_Barcodes* barcodes) |
|
{ |
|
if (barcodes != &emptyBarcodes) |
|
delete barcodes; |
|
} |
|
|
|
int ZXing_Barcodes_size(const ZXing_Barcodes* barcodes) |
|
{ |
|
return barcodes ? Size(*barcodes) : 0; |
|
} |
|
|
|
const ZXing_Barcode* ZXing_Barcodes_at(const ZXing_Barcodes* barcodes, int i) |
|
{ |
|
if (!barcodes || i < 0 || i >= Size(*barcodes)) |
|
return NULL; |
|
return &(*barcodes)[i]; |
|
} |
|
|
|
ZXing_Barcode* ZXing_Barcodes_move(ZXing_Barcodes* barcodes, int i) |
|
{ |
|
if (!barcodes || i < 0 || i >= Size(*barcodes)) |
|
return NULL; |
|
|
|
ZX_TRY(new Barcode(std::move((*barcodes)[i]))); |
|
} |
|
|
|
/* |
|
* MARK: - ReaderOptions.h |
|
*/ |
|
|
|
ZXing_ReaderOptions* ZXing_ReaderOptions_new() |
|
{ |
|
ZX_TRY(new ReaderOptions()); |
|
} |
|
|
|
void ZXing_ReaderOptions_delete(ZXing_ReaderOptions* opts) |
|
{ |
|
delete opts; |
|
} |
|
|
|
#define ZX_PROPERTY(TYPE, NAME, CAP_NAME) \ |
|
TYPE ZXing_ReaderOptions_get##CAP_NAME(const ZXing_ReaderOptions* opts) { return opts->NAME(); } \ |
|
void ZXing_ReaderOptions_set##CAP_NAME(ZXing_ReaderOptions* opts, TYPE NAME) { opts->NAME(NAME); } |
|
|
|
ZX_PROPERTY(bool, tryHarder, TryHarder) |
|
ZX_PROPERTY(bool, tryRotate, TryRotate) |
|
ZX_PROPERTY(bool, tryInvert, TryInvert) |
|
ZX_PROPERTY(bool, tryDownscale, TryDownscale) |
|
#ifdef ZXING_EXPERIMENTAL_API |
|
ZX_PROPERTY(bool, tryDenoise, TryDenoise) |
|
#endif |
|
ZX_PROPERTY(bool, isPure, IsPure) |
|
ZX_PROPERTY(bool, validateOptionalChecksum, ValidateOptionalChecksum) |
|
ZX_PROPERTY(bool, returnErrors, ReturnErrors) |
|
ZX_PROPERTY(int, minLineCount, MinLineCount) |
|
ZX_PROPERTY(int, maxNumberOfSymbols, MaxNumberOfSymbols) |
|
|
|
#undef ZX_PROPERTY |
|
|
|
void ZXing_ReaderOptions_setFormats(ZXing_ReaderOptions* opts, const ZXing_BarcodeFormat* formats, int count) |
|
{ |
|
if (!formats || !count) |
|
return; |
|
if (count == -1) // determine count by looking for null terminator |
|
for (count = 0; formats[count] != ZXing_BarcodeFormat_None; ++count) |
|
; |
|
std::vector<BarcodeFormat> v((BarcodeFormat*)formats, (BarcodeFormat*)formats + count); |
|
opts->formats(std::move(v)); |
|
} |
|
|
|
ZXing_BarcodeFormat* ZXing_ReaderOptions_getFormats(const ZXing_ReaderOptions* opts, int* outCount) |
|
{ |
|
return (ZXing_BarcodeFormat*)copy(opts->formats(), outCount); |
|
} |
|
|
|
#define ZX_ENUM_PROPERTY(TYPE, NAME, CAP_NAME) \ |
|
ZXing_##TYPE ZXing_ReaderOptions_get##CAP_NAME(const ZXing_ReaderOptions* opts) { return static_cast<ZXing_##TYPE>(opts->NAME()); } \ |
|
void ZXing_ReaderOptions_set##CAP_NAME(ZXing_ReaderOptions* opts, ZXing_##TYPE NAME) { opts->NAME(static_cast<TYPE>(NAME)); } |
|
|
|
ZX_ENUM_PROPERTY(Binarizer, binarizer, Binarizer) |
|
ZX_ENUM_PROPERTY(EanAddOnSymbol, eanAddOnSymbol, EanAddOnSymbol) |
|
ZX_ENUM_PROPERTY(TextMode, textMode, TextMode) |
|
|
|
#undef ZX_ENUM_PROPERTY |
|
|
|
/* |
|
* MARK: - ReadBarcode.h |
|
*/ |
|
|
|
ZXing_Barcodes* ZXing_ReadBarcodes(const ZXing_ImageView* iv, const ZXing_ReaderOptions* opts) |
|
{ |
|
ZX_CHECK(iv, "ImageView param is NULL") |
|
try { |
|
auto res = ReadBarcodes(*iv, opts ? *opts : ReaderOptions{}); |
|
return res.empty() ? &emptyBarcodes : new Barcodes(std::move(res)); |
|
} |
|
ZX_CATCH(NULL); |
|
} |
|
|
|
|
|
/* |
|
* MARK: - CreateBarcode.h |
|
*/ |
|
|
|
ZXing_CreatorOptions* ZXing_CreatorOptions_new(ZXing_BarcodeFormat format) |
|
{ |
|
ZX_TRY(new CreatorOptions(static_cast<BarcodeFormat>(format))); |
|
} |
|
|
|
void ZXing_CreatorOptions_delete(ZXing_CreatorOptions* opts) |
|
{ |
|
delete opts; |
|
} |
|
|
|
#define ZX_ENUM_PROPERTY(TYPE, NAME, CAP_NAME) \ |
|
ZXing_##TYPE ZXing_CreatorOptions_get##CAP_NAME(const ZXing_CreatorOptions* opts) { return transmute_cast<ZXing_##TYPE>(opts->NAME()); } \ |
|
void ZXing_CreatorOptions_set##CAP_NAME(ZXing_CreatorOptions* opts, ZXing_##TYPE NAME) { opts->NAME(transmute_cast<TYPE>(NAME)); } |
|
|
|
ZX_ENUM_PROPERTY(BarcodeFormat, format, Format) |
|
|
|
|
|
#undef ZX_ENUM_PROPERTY |
|
|
|
#define ZX_PROPERTY(TYPE, NAME, CAP_NAME) \ |
|
TYPE ZXing_CreatorOptions_get##CAP_NAME(const ZXing_CreatorOptions* opts) { return opts->NAME(); } \ |
|
void ZXing_CreatorOptions_set##CAP_NAME(ZXing_CreatorOptions* opts, TYPE NAME) { opts->NAME(NAME); } |
|
|
|
#undef ZX_PROPERTY |
|
|
|
char* ZXing_CreatorOptions_getOptions(const ZXing_CreatorOptions* opts) |
|
{ |
|
return copy(opts->options()); |
|
} |
|
|
|
void ZXing_CreatorOptions_setOptions(ZXing_CreatorOptions* opts, const char* val) |
|
{ |
|
opts->options(val); |
|
} |
|
|
|
ZXing_Barcode* ZXing_CreateBarcodeFromText(const char* data, int size, const ZXing_CreatorOptions* opts) |
|
{ |
|
ZX_CHECK(opts, "CreatorOptions param is NULL") |
|
ZX_TRY(new Barcode(CreateBarcodeFromText({data, size ? static_cast<size_t>(size) : data ? strlen(data) : 0}, *opts));) |
|
} |
|
|
|
ZXing_Barcode* ZXing_CreateBarcodeFromBytes(const void* data, int size, const ZXing_CreatorOptions* opts) |
|
{ |
|
ZX_CHECK(opts, "CreatorOptions param is NULL") |
|
ZX_TRY(new Barcode(CreateBarcodeFromBytes(data, size, *opts))) |
|
} |
|
|
|
|
|
/* |
|
* MARK: - WriteBarcode.h |
|
*/ |
|
|
|
ZXing_WriterOptions* ZXing_WriterOptions_new() |
|
{ |
|
ZX_TRY(new ZXing_WriterOptions()); |
|
} |
|
|
|
void ZXing_WriterOptions_delete(ZXing_WriterOptions* opts) |
|
{ |
|
delete opts; |
|
} |
|
|
|
#define ZX_PROPERTY(TYPE, NAME, CAP_NAME) \ |
|
TYPE ZXing_WriterOptions_get##CAP_NAME(const ZXing_WriterOptions* opts) { return opts->NAME(); } \ |
|
void ZXing_WriterOptions_set##CAP_NAME(ZXing_WriterOptions* opts, TYPE NAME) { opts->NAME(NAME); } |
|
|
|
ZX_PROPERTY(int, scale, Scale) |
|
ZX_PROPERTY(int, rotate, Rotate) |
|
ZX_PROPERTY(bool, addHRT, AddHRT) |
|
ZX_PROPERTY(bool, addQuietZones, AddQuietZones) |
|
|
|
#undef ZX_PROPERTY |
|
|
|
char* ZXing_WriteBarcodeToSVG(const ZXing_Barcode* barcode, const ZXing_WriterOptions* opts) |
|
{ |
|
ZX_CHECK(barcode, "Barcode param is NULL") |
|
ZX_TRY(copy(opts ? WriteBarcodeToSVG(*barcode, *opts) : WriteBarcodeToSVG(*barcode))) |
|
} |
|
|
|
ZXing_Image* ZXing_WriteBarcodeToImage(const ZXing_Barcode* barcode, const ZXing_WriterOptions* opts) |
|
{ |
|
ZX_CHECK(barcode, "Barcode param is NULL") |
|
ZX_TRY(new Image(opts ? WriteBarcodeToImage(*barcode, *opts) : WriteBarcodeToImage(*barcode))) |
|
} |
|
|
|
/* |
|
* MARK: - ZXingC.h |
|
*/ |
|
|
|
char* ZXing_LastErrorMsg() |
|
{ |
|
if (lastErrorMsg.empty()) |
|
return NULL; |
|
|
|
return copy(std::exchange(lastErrorMsg, {})); |
|
} |
|
|
|
const char* ZXing_Version() |
|
{ |
|
return ZXING_VERSION_STR; |
|
} |
|
|
|
void ZXing_free(void* ptr) |
|
{ |
|
if (ptr != ZXing_Version()) |
|
free(ptr); |
|
} |
|
|
|
} // extern "C"
|
|
|