Skip to content

Commit

Permalink
Writer: implement new experimental API (Create + Write)
Browse files Browse the repository at this point in the history
This adds two new operations 'create' and 'write' next to the existing
'read'. Each new operation is accompanied by an Options class:

 * ReadBarcodes(..., ReaderOptions)
 * CreateBarcodeFromText(..., CreatorOptions)
 * WriteBarcodeToImage(..., WriterOptions)

Background info can be found here:
 * zxing-cpp#724
 * zxing-cpp#739
  • Loading branch information
axxel committed Mar 3, 2024
1 parent 8d7a5a2 commit 3a79c38
Show file tree
Hide file tree
Showing 8 changed files with 381 additions and 56 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:
run: ctest -V --test-dir build

build-ios:
runs-on: macos-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v4
with:
Expand Down
15 changes: 8 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,6 @@ if (BUILD_SHARED_LIBS)
set (CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
endif()

if (NOT CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 17)
endif()
if (NOT CMAKE_CXX_EXTENSIONS)
set (CMAKE_CXX_EXTENSIONS OFF)
endif()

if (NOT (BUILD_READERS OR BUILD_WRITERS))
message(FATAL_ERROR "At least one of BUILD_READERS/BUILD_WRITERS must be enabled.")
endif()
Expand All @@ -55,6 +48,7 @@ if (BUILD_UNIT_TESTS AND (NOT BUILD_WRITERS OR NOT BUILD_READERS))
endif()

if (BUILD_EXPERIMENTAL_API)
set (CMAKE_CXX_STANDARD 20)
add_definitions (-DZXING_BUILD_EXPERIMENTAL_API)
endif()

Expand All @@ -64,6 +58,13 @@ if(NOT BUILD_DEPENDENCIES IN_LIST BUILD_DEPENDENCIES_LIST)
message(FATAL_ERROR "BUILD_DEPENDENCIES must be one of ${BUILD_DEPENDENCIES_LIST}")
endif()

if (NOT CMAKE_CXX_STANDARD)
set (CMAKE_CXX_STANDARD 17)
endif()
if (NOT CMAKE_CXX_EXTENSIONS)
set (CMAKE_CXX_EXTENSIONS OFF)
endif()

add_subdirectory (core)

enable_testing()
Expand Down
3 changes: 3 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ if (BUILD_WRITERS)
src/TextEncoder.cpp
src/MultiFormatWriter.h
src/MultiFormatWriter.cpp
$<$<BOOL:${BUILD_EXPERIMENTAL_API}>:src/WriteBarcode.h>
$<$<BOOL:${BUILD_EXPERIMENTAL_API}>:src/WriteBarcode.cpp>
)
endif()

Expand Down Expand Up @@ -191,6 +193,7 @@ if (BUILD_READERS)
src/ReaderOptions.h
src/Result.h # [[deprecated]]
src/StructuredAppend.h
$<$<BOOL:${BUILD_EXPERIMENTAL_API}>:${CMAKE_CURRENT_SOURCE_DIR}/src/WriteBarcode.h>
$<$<BOOL:${BUILD_C_API}>:${CMAKE_CURRENT_SOURCE_DIR}/src/ZXingC.h>
)
endif()
Expand Down
2 changes: 1 addition & 1 deletion core/src/Barcode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,4 +206,4 @@ Barcodes MergeStructuredAppendSequences(const Barcodes& barcodes)
return res;
}

} // ZXing
} // namespace ZXing
1 change: 1 addition & 0 deletions core/src/Barcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ class Result
std::string version() const;

#ifdef ZXING_BUILD_EXPERIMENTAL_API
void symbol(BitMatrix&& bits) { _symbol = std::make_shared<BitMatrix>(std::move(bits)); }
const BitMatrix& symbol() const { return *_symbol; }
#endif

Expand Down
142 changes: 142 additions & 0 deletions core/src/WriteBarcode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2024 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0

#ifdef ZXING_BUILD_EXPERIMENTAL_API

#include "WriteBarcode.h"

struct zint_symbol {};

namespace ZXing {

struct CreatorOptions::Data
{
BarcodeFormat format;
bool readerInit = false;
bool forceSquareDataMatrix = false;
std::string ecLevel;

// symbol size (qrcode, datamatrix, etc), map from I, 'WxH'
// structured_append (idx, cnt, ID)

mutable std::unique_ptr<zint_symbol> zint;
};

#define ZX_PROPERTY(TYPE, NAME) \
TYPE CreatorOptions::NAME() const noexcept { return d->NAME; } \
CreatorOptions& CreatorOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \
CreatorOptions&& CreatorOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); }

ZX_PROPERTY(BarcodeFormat, format)
ZX_PROPERTY(bool, readerInit)
ZX_PROPERTY(bool, forceSquareDataMatrix)
ZX_PROPERTY(std::string, ecLevel)

#undef ZX_PROPERTY

CreatorOptions::CreatorOptions(BarcodeFormat format) : d(std::make_unique<Data>(format)) {}
CreatorOptions::~CreatorOptions() = default;
CreatorOptions::CreatorOptions(CreatorOptions&&) = default;
CreatorOptions& CreatorOptions::operator=(CreatorOptions&&) = default;


struct WriterOptions::Data
{
int scale = 0;
int sizeHint = 0;
int rotate = 0;
bool withHRT = false;
bool withQuietZones = true;
};

#define ZX_PROPERTY(TYPE, NAME) \
TYPE WriterOptions::NAME() const noexcept { return d->NAME; } \
WriterOptions& WriterOptions::NAME(TYPE v)& { return d->NAME = std::move(v), *this; } \
WriterOptions&& WriterOptions::NAME(TYPE v)&& { return d->NAME = std::move(v), std::move(*this); }

ZX_PROPERTY(int, scale)
ZX_PROPERTY(int, sizeHint)
ZX_PROPERTY(int, rotate)
ZX_PROPERTY(bool, withHRT)
ZX_PROPERTY(bool, withQuietZones)

#undef ZX_PROPERTY

WriterOptions::WriterOptions() : d(std::make_unique<Data>()) {}
WriterOptions::~WriterOptions() = default;
WriterOptions::WriterOptions(WriterOptions&&) = default;
WriterOptions& WriterOptions::operator=(WriterOptions&&) = default;

} // namespace ZXing


#include "BitMatrix.h"
#include "MultiFormatWriter.h"
#include "ReadBarcode.h"

namespace ZXing {

static Barcode CreateBarcode(BitMatrix&& bits, const CreatorOptions& opts)
{
auto img = ToMatrix<uint8_t>(bits);

auto res = ReadBarcode({img.data(), img.width(), img.height(), ImageFormat::Lum},
ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
res.symbol(std::move(bits));
return res;
}

static bool IsLinearCode(BarcodeFormat format)
{
return BarcodeFormats(BarcodeFormat::LinearCodes).testFlag(format);
}

Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
{
auto writer = MultiFormatWriter(opts.format()).setMargin(0);
if (!opts.ecLevel().empty())
writer.setEccLevel(std::stoi(opts.ecLevel()));

return CreateBarcode(writer.encode(std::string(contents), 0, IsLinearCode(opts.format()) ? 50 : 0), opts);
}

Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
{
return CreateBarcodeFromText({reinterpret_cast<const char*>(contents.data()), contents.size()}, opts);
}

Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
{
std::wstring bytes;
for (uint8_t c : std::basic_string_view<uint8_t>((uint8_t*)data, size))
bytes.push_back(c);

auto writer = MultiFormatWriter(opts.format()).setMargin(0);
if (!opts.ecLevel().empty())
writer.setEccLevel(std::stoi(opts.ecLevel()));
writer.setEncoding(CharacterSet::BINARY);

return CreateBarcode(writer.encode(bytes, 0, IsLinearCode(opts.format()) ? 50 : 0), opts);
}

std::string WriteBarcodeToSVG(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
{
return ToSVG(barcode.symbol());
}

Image WriteBarcodeToImage(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
{
auto symbol = Inflate(barcode.symbol().copy(), opts.sizeHint(),
IsLinearCode(barcode.format()) ? std::clamp(opts.sizeHint() / 2, 50, 300) : opts.sizeHint(),
opts.withQuietZones() ? 10 : 0);
auto bitmap = ToMatrix<uint8_t>(symbol);
auto iv = Image(symbol.width(), symbol.height());
std::memcpy(const_cast<uint8_t*>(iv.data()), bitmap.data(), iv.width() * iv.height());
return iv;
}

} // namespace ZXing

#endif // ZXING_BUILD_EXPERIMENTAL_API
129 changes: 129 additions & 0 deletions core/src/WriteBarcode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright 2022 Axel Waggershauser
*/
// SPDX-License-Identifier: Apache-2.0

#pragma once

#ifdef ZXING_BUILD_EXPERIMENTAL_API

#include "Barcode.h"
#include "ImageView.h"

#include <memory>
#include <string_view>

extern "C" struct zint_symbol;

namespace ZXing {

class CreatorOptions
{
struct Data;

std::unique_ptr<Data> d;

friend Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& options);

public:
CreatorOptions(BarcodeFormat format);

~CreatorOptions();
CreatorOptions(CreatorOptions&&);
CreatorOptions& operator=(CreatorOptions&&);

zint_symbol* zint() const;

#define ZX_PROPERTY(TYPE, NAME) \
TYPE NAME() const noexcept; \
CreatorOptions& NAME(TYPE v)&; \
CreatorOptions&& NAME(TYPE v)&&;

ZX_PROPERTY(BarcodeFormat, format)
ZX_PROPERTY(bool, readerInit)
ZX_PROPERTY(bool, forceSquareDataMatrix)
ZX_PROPERTY(std::string, ecLevel)

#undef ZX_PROPERTY
};

/**
* Generate barcode from unicode text
*
* @param contents UTF-8 string to encode into a barcode
* @param options CreatorOptions (including BarcodeFormat)
* @return #Barcode generated barcode
*/
Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& options);

/**
* Generate barcode from raw binary data
*
* @param data array of bytes to encode into a barcode
* @param size size of byte array
* @param options CreatorOptions (including BarcodeFormat)
* @return #Barcode generated barcode
*/
Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& options);

#if __cplusplus > 201703L
Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& options);

template <typename R>
requires std::ranges::contiguous_range<R> && std::ranges::sized_range<R> && (sizeof(std::ranges::range_value_t<R>) == 1)
Barcode CreateBarcodeFromBytes(const R& contents, const CreatorOptions& options)
{
return CreateBarcodeFromBytes(std::ranges::data(contents), std::ranges::size(contents), options);
}
#endif

// =================================================================================

class WriterOptions
{
struct Data;

std::unique_ptr<Data> d;

public:
WriterOptions();
~WriterOptions();
WriterOptions(WriterOptions&&);
WriterOptions& operator=(WriterOptions&&);

#define ZX_PROPERTY(TYPE, NAME) \
TYPE NAME() const noexcept; \
WriterOptions& NAME(TYPE v)&; \
WriterOptions&& NAME(TYPE v)&&;

ZX_PROPERTY(int, scale)
ZX_PROPERTY(int, sizeHint)
ZX_PROPERTY(int, rotate)
ZX_PROPERTY(bool, withHRT)
ZX_PROPERTY(bool, withQuietZones)

#undef ZX_PROPERTY
};


/**
* Write barcode symbol to SVG
*
* @param barcode Barcode to write
* @param options WriterOptions to parameterize rendering
* @return std::string SVG representation of barcode symbol
*/
std::string WriteBarcodeToSVG(const Barcode& barcode, const WriterOptions& options = {});

/**
* Write barcode symbol to Image (Bitmap)
*
* @param barcode Barcode to write
* @param options WriterOptions to parameterize rendering
* @return Image Bitmap reprentation of barcode symbol
*/
Image WriteBarcodeToImage(const Barcode& barcode, const WriterOptions& options = {});

} // ZXing

#endif // ZXING_BUILD_EXPERIMENTAL_API
Loading

0 comments on commit 3a79c38

Please sign in to comment.