diff mupdf-source/thirdparty/zxing-cpp/core/src/WriteBarcode.cpp @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mupdf-source/thirdparty/zxing-cpp/core/src/WriteBarcode.cpp	Mon Sep 15 11:43:07 2025 +0200
@@ -0,0 +1,486 @@
+/*
+* Copyright 2024 Axel Waggershauser
+*/
+// SPDX-License-Identifier: Apache-2.0
+
+#ifdef ZXING_EXPERIMENTAL_API
+
+#include "WriteBarcode.h"
+#include "BitMatrix.h"
+
+#if !defined(ZXING_READERS) && !defined(ZXING_WRITERS)
+#include "Version.h"
+#endif
+
+#include <sstream>
+
+#ifdef ZXING_USE_ZINT
+
+#include <zint.h>
+
+#else
+
+struct zint_symbol {};
+
+#endif // ZXING_USE_ZINT
+
+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 unique_zint_symbol zint;
+
+#ifndef __cpp_aggregate_paren_init
+	Data(BarcodeFormat f) : format(f) {}
+#endif
+};
+
+#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;
+
+static bool IsLinearCode(BarcodeFormat format)
+{
+	return BarcodeFormats(BarcodeFormat::LinearCodes).testFlag(format);
+}
+
+static std::string ToSVG(ImageView iv)
+{
+	if (!iv.data())
+		return {};
+
+	// see https://stackoverflow.com/questions/10789059/create-qr-code-in-vector-image/60638350#60638350
+
+	std::ostringstream res;
+
+	res << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+		<< "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 " << iv.width() << " " << iv.height()
+		<< "\" stroke=\"none\">\n"
+		<< "<path d=\"";
+
+	for (int y = 0; y < iv.height(); ++y)
+		for (int x = 0; x < iv.width(); ++x)
+			if (*iv.data(x, y) == 0)
+				res << "M" << x << "," << y << "h1v1h-1z";
+
+	res << "\"/>\n</svg>";
+
+	return res.str();
+}
+
+static Image ToImage(BitMatrix bits, bool isLinearCode, const WriterOptions& opts)
+{
+	bits.flipAll();
+	auto symbol = Inflate(std::move(bits), opts.sizeHint(),
+						  isLinearCode ? 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
+
+
+#ifdef ZXING_WRITERS
+
+#ifdef ZXING_USE_ZINT
+#include "ECI.h"
+#include "ReadBarcode.h"
+
+#include <charconv>
+#include <zint.h>
+
+namespace ZXing {
+
+struct BarcodeFormatZXing2Zint
+{
+	BarcodeFormat zxing;
+	int zint;
+};
+
+static constexpr BarcodeFormatZXing2Zint barcodeFormatZXing2Zint[] = {
+	{BarcodeFormat::Aztec, BARCODE_AZTEC},
+	{BarcodeFormat::Codabar, BARCODE_CODABAR},
+	{BarcodeFormat::Code39, BARCODE_CODE39},
+	{BarcodeFormat::Code93, BARCODE_CODE93},
+	{BarcodeFormat::Code128, BARCODE_CODE128},
+	{BarcodeFormat::DataBar, BARCODE_DBAR_OMN},
+	{BarcodeFormat::DataBarExpanded, BARCODE_DBAR_EXP},
+	{BarcodeFormat::DataBarLimited, BARCODE_DBAR_LTD},
+	{BarcodeFormat::DataMatrix, BARCODE_DATAMATRIX},
+	{BarcodeFormat::DXFilmEdge, BARCODE_DXFILMEDGE},
+	{BarcodeFormat::EAN8, BARCODE_EANX},
+	{BarcodeFormat::EAN13, BARCODE_EANX},
+	{BarcodeFormat::ITF, BARCODE_C25INTER},
+	{BarcodeFormat::MaxiCode, BARCODE_MAXICODE},
+	{BarcodeFormat::MicroQRCode, BARCODE_MICROQR},
+	{BarcodeFormat::PDF417, BARCODE_PDF417},
+	{BarcodeFormat::QRCode, BARCODE_QRCODE},
+	{BarcodeFormat::RMQRCode, BARCODE_RMQR},
+	{BarcodeFormat::UPCA, BARCODE_UPCA},
+	{BarcodeFormat::UPCE, BARCODE_UPCE},
+};
+
+struct String2Int
+{
+	const char* str;
+	int val;
+};
+
+static int ParseECLevel(int symbology, std::string_view s)
+{
+	constexpr std::string_view EC_LABELS_QR[4] = {"L", "M", "Q", "H"};
+
+	int res = 0;
+	if (Contains({BARCODE_QRCODE, BARCODE_MICROQR, BARCODE_RMQR}, symbology))
+		if ((res = IndexOf(EC_LABELS_QR, s) != -1))
+			return res + 1;
+
+	if (std::from_chars(s.data(), s.data() + s.size() - (s.back() == '%'), res).ec != std::errc{})
+		throw std::invalid_argument("Invalid ecLevel: '" + std::string(s) + "'");
+
+	auto findClosestECLevel = [](const std::vector<int>& list, int val) {
+		int mIdx = -2, mAbs = 100;
+		for (int i = 0; i < Size(list); ++i)
+		if (int abs = std::abs(val - list[i]); abs < mAbs) {
+				mIdx = i;
+				mAbs = abs;
+		}
+		return mIdx + 1;
+	};
+
+	if (s.back()=='%'){
+		switch (symbology) {
+		case BARCODE_QRCODE:
+		case BARCODE_MICROQR:
+		case BARCODE_RMQR:
+			return findClosestECLevel({20, 37, 55, 65}, res);
+		case BARCODE_AZTEC:
+			return findClosestECLevel({10, 23, 26, 50}, res);
+		case BARCODE_PDF417:
+			// TODO: do something sensible with PDF417?
+		default:
+			return -1;
+		}
+	}
+
+	return res;
+};
+
+zint_symbol* CreatorOptions::zint() const
+{
+	auto& zint = d->zint;
+
+	if (!zint) {
+#ifdef PRINT_DEBUG
+		printf("zint version: %d, sizeof(zint_symbol): %ld\n", ZBarcode_Version(), sizeof(zint_symbol));
+#endif
+		zint.reset(ZBarcode_Create());
+
+		auto i = FindIf(barcodeFormatZXing2Zint, [zxing = format()](auto& v) { return v.zxing == zxing; });
+		if (i == std::end(barcodeFormatZXing2Zint))
+			throw std::invalid_argument("unsupported barcode format: " + ToString(format()));
+		zint->symbology = i->zint;
+
+		zint->scale = 0.5f;
+
+		if (!ecLevel().empty())
+			zint->option_1 = ParseECLevel(zint->symbology, ecLevel());
+	}
+
+	return zint.get();
+}
+
+#define CHECK(ZINT_CALL) \
+	if (int err = (ZINT_CALL); err >= ZINT_ERROR) \
+		throw std::invalid_argument(zint->errtxt);
+
+Barcode CreateBarcode(const void* data, int size, int mode, const CreatorOptions& opts)
+{
+	auto zint = opts.zint();
+
+	zint->input_mode = mode;
+	zint->output_options |= OUT_BUFFER_INTERMEDIATE | BARCODE_QUIET_ZONES;
+
+	if (mode == DATA_MODE && ZBarcode_Cap(zint->symbology, ZINT_CAP_ECI))
+		zint->eci = static_cast<int>(ECI::Binary);
+
+	CHECK(ZBarcode_Encode_and_Buffer(zint, (uint8_t*)data, size, 0));
+
+#ifdef PRINT_DEBUG
+	printf("create symbol with size: %dx%d\n", zint->width, zint->rows);
+#endif
+
+#ifdef ZXING_READERS
+	auto buffer = std::vector<uint8_t>(zint->bitmap_width * zint->bitmap_height);
+	std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, buffer.data(),
+				   [](unsigned char v) { return (v == '0') * 0xff; });
+
+	auto res = ReadBarcode({buffer.data(), zint->bitmap_width, zint->bitmap_height, ImageFormat::Lum},
+						   ReaderOptions().setFormats(opts.format()).setIsPure(true).setBinarizer(Binarizer::BoolCast));
+#else
+	//TODO: replace by proper construction from encoded data from within zint
+	auto res = Barcode(std::string((const char*)data, size), 0, 0, 0, opts.format(), {});
+#endif
+
+	auto bits = BitMatrix(zint->bitmap_width, zint->bitmap_height);
+	std::transform(zint->bitmap, zint->bitmap + zint->bitmap_width * zint->bitmap_height, bits.row(0).begin(),
+				   [](unsigned char v) { return (v == '1') * BitMatrix::SET_V; });
+	res.symbol(std::move(bits));
+	res.zint(std::move(opts.d->zint));
+
+	return res;
+}
+
+Barcode CreateBarcodeFromText(std::string_view contents, const CreatorOptions& opts)
+{
+	return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
+}
+
+#if __cplusplus > 201703L
+Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
+{
+	return CreateBarcode(contents.data(), contents.size(), UNICODE_MODE, opts);
+}
+#endif
+
+Barcode CreateBarcodeFromBytes(const void* data, int size, const CreatorOptions& opts)
+{
+	return CreateBarcode(data, size, DATA_MODE, opts);
+}
+
+// Writer ========================================================================
+
+struct SetCommonWriterOptions
+{
+	zint_symbol* zint;
+
+	SetCommonWriterOptions(zint_symbol* zint, const WriterOptions& opts) : zint(zint)
+	{
+		zint->show_hrt = opts.withHRT();
+
+		zint->output_options &= ~OUT_BUFFER_INTERMEDIATE;
+		zint->output_options |= opts.withQuietZones() ? BARCODE_QUIET_ZONES : BARCODE_NO_QUIET_ZONES;
+
+		if (opts.scale())
+			zint->scale = opts.scale() / 2.f;
+		else if (opts.sizeHint()) {
+			int size = std::max(zint->width, zint->rows);
+			zint->scale = std::max(1, int(float(opts.sizeHint()) / size)) / 2.f;
+		}
+	}
+
+	// reset the defaults such that consecutive write calls don't influence each other
+	~SetCommonWriterOptions() { zint->scale = 0.5f; }
+};
+
+} // ZXing
+
+#else // ZXING_USE_ZINT
+
+#include "MultiFormatWriter.h"
+#include "ReadBarcode.h"
+
+namespace ZXing {
+
+zint_symbol* CreatorOptions::zint() const { return nullptr; }
+
+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;
+}
+
+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()));
+	writer.setEncoding(CharacterSet::UTF8); // write UTF8 (ECI value 26) for maximum compatibility
+
+	return CreateBarcode(writer.encode(std::string(contents), 0, IsLinearCode(opts.format()) ? 50 : 0), opts);
+}
+
+#if __cplusplus > 201703L
+Barcode CreateBarcodeFromText(std::u8string_view contents, const CreatorOptions& opts)
+{
+	return CreateBarcodeFromText({reinterpret_cast<const char*>(contents.data()), contents.size()}, opts);
+}
+#endif
+
+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);
+}
+
+} // namespace ZXing
+
+#endif // ZXING_USE_ZINT
+
+#else // ZXING_WRITERS
+
+namespace ZXing {
+
+zint_symbol* CreatorOptions::zint() const { return nullptr; }
+
+Barcode CreateBarcodeFromText(std::string_view, const CreatorOptions&)
+{
+	throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
+}
+
+#if __cplusplus > 201703L
+Barcode CreateBarcodeFromText(std::u8string_view, const CreatorOptions&)
+{
+	throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
+}
+#endif
+
+Barcode CreateBarcodeFromBytes(const void*, int, const CreatorOptions&)
+{
+	throw std::runtime_error("This build of zxing-cpp does not support creating barcodes.");
+}
+
+} // namespace ZXing
+
+#endif // ZXING_WRITERS
+
+namespace ZXing {
+
+std::string WriteBarcodeToSVG(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
+{
+	auto zint = barcode.zint();
+
+	if (!zint)
+		return ToSVG(barcode.symbol());
+
+#if defined(ZXING_WRITERS) && defined(ZXING_USE_ZINT)
+	auto resetOnExit = SetCommonWriterOptions(zint, opts);
+
+	zint->output_options |= BARCODE_MEMORY_FILE;// | EMBED_VECTOR_FONT;
+	strcpy(zint->outfile, "null.svg");
+
+	CHECK(ZBarcode_Print(zint, opts.rotate()));
+
+	return std::string(reinterpret_cast<const char*>(zint->memfile), zint->memfile_size);
+#else
+	return {}; // unreachable code
+#endif
+}
+
+Image WriteBarcodeToImage(const Barcode& barcode, [[maybe_unused]] const WriterOptions& opts)
+{
+	auto zint = barcode.zint();
+
+	if (!zint)
+		return ToImage(barcode._symbol->copy(), IsLinearCode(barcode.format()), opts);
+
+#if defined(ZXING_WRITERS) && defined(ZXING_USE_ZINT)
+	auto resetOnExit = SetCommonWriterOptions(zint, opts);
+
+	CHECK(ZBarcode_Buffer(zint, opts.rotate()));
+
+#ifdef PRINT_DEBUG
+	printf("write symbol with size: %dx%d\n", zint->bitmap_width, zint->bitmap_height);
+#endif
+	auto iv = Image(zint->bitmap_width, zint->bitmap_height);
+	auto* src = zint->bitmap;
+	auto* dst = const_cast<uint8_t*>(iv.data());
+	for(int y = 0; y < iv.height(); ++y)
+		for(int x = 0, w = iv.width(); x < w; ++x, src += 3)
+			*dst++ = RGBToLum(src[0], src[1], src[2]);
+
+	return iv;
+#else
+	return {}; // unreachable code
+#endif
+}
+
+std::string WriteBarcodeToUtf8(const Barcode& barcode, [[maybe_unused]] const WriterOptions& options)
+{
+	auto iv = barcode.symbol();
+	if (!iv.data())
+		return {};
+
+	constexpr auto map = std::array{" ", "▀", "▄", "█"};
+	std::ostringstream res;
+	bool inverted = false; // TODO: take from WriterOptions
+
+	for (int y = 0; y < iv.height(); y += 2) {
+		for (int x = 0; x < iv.width(); ++x) {
+			int tp = bool(*iv.data(x, y)) ^ inverted;
+			int bt = (iv.height() == 1 && tp) || (y + 1 < iv.height() && (bool(*iv.data(x, y + 1)) ^ inverted));
+			res << map[tp | (bt << 1)];
+		}
+		res << '\n';
+	}
+
+	return res.str();
+}
+
+} // namespace ZXing
+
+#endif // ZXING_EXPERIMENTAL_API