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.
235 lines
7.3 KiB
235 lines
7.3 KiB
/* |
|
* Copyright 2016 Nu-book Inc. |
|
*/ |
|
// SPDX-License-Identifier: Apache-2.0 |
|
|
|
// #define USE_OLD_WRITER_API |
|
|
|
#ifndef USE_OLD_WRITER_API |
|
#include "CreateBarcode.h" |
|
#include "WriteBarcode.h" |
|
#else |
|
#include "BitMatrix.h" |
|
#include "BitMatrixIO.h" |
|
#include "CharacterSet.h" |
|
#include "MultiFormatWriter.h" |
|
#endif |
|
#include "Version.h" |
|
|
|
#include <algorithm> |
|
#include <cctype> |
|
#include <cstring> |
|
#include <fstream> |
|
#include <iostream> |
|
#include <iomanip> |
|
#include <string> |
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION |
|
#include <stb_image_write.h> |
|
|
|
using namespace ZXing; |
|
|
|
static void PrintUsage(const char* exePath) |
|
{ |
|
std::cout << "Usage: " << exePath |
|
<< " [-options <creator-options>] [-scale <factor>] [-binary] [-noqz] [-hrt] [-invert] <format> <text> <output>\n" |
|
<< " -options Comma separated list of format specific options and flags\n" |
|
<< " -scale module size of generated image / negative numbers mean 'target size in pixels'\n" |
|
<< " -rotate Rotate image by given angle (90, 180 or 270)\n" |
|
// << " -encoding Encoding used to encode input text\n" |
|
<< " -binary Interpret <text> as a file name containing binary data\n" |
|
<< " -noqz Print barcode without quiet zone\n" |
|
<< " -hrt Print human readable text below the barcode (if supported)\n" |
|
<< " -invert Invert colors (switch black and white)\n" |
|
<< " -help Print usage information\n" |
|
<< " -version Print version information\n" |
|
<< "\n" |
|
<< "Supported formats are (Symbology : Variants):"; |
|
for (auto f : BarcodeFormats::list(BarcodeFormat::AllCreatable)) { |
|
if (Symbology(f) == f || f == BarcodeFormat::DXFilmEdge) |
|
std::cout << "\n " << std::setw(13) << ToString(f) << " : "; |
|
else |
|
std::cout << ToString(f) << ", "; |
|
} |
|
std::cout << "\n\n"; |
|
|
|
std::cout << "Format can be lowercase letters, with or without any of ' -_/'.\n" |
|
<< "Output format is determined by file name, supported are png, jpg and svg.\n"; |
|
} |
|
|
|
struct CLI |
|
{ |
|
BarcodeFormat format = BarcodeFormat::None; |
|
int scale = 0; |
|
int rotate = 0; |
|
std::string input; |
|
std::string outPath; |
|
std::string options; |
|
bool inputIsFile = false; |
|
bool invert = false; |
|
bool addHRT = false; |
|
bool addQZs = true; |
|
bool verbose = false; |
|
// CharacterSet encoding = CharacterSet::Unknown; |
|
}; |
|
|
|
static bool ParseOptions(int argc, char* argv[], CLI& cli) |
|
{ |
|
int nonOptArgCount = 0; |
|
for (int i = 1; i < argc; ++i) { |
|
auto is = [&](const char* str) { return strncmp(argv[i], str, strlen(argv[i])) == 0; }; |
|
if (is("-scale")) { |
|
if (++i == argc) |
|
return false; |
|
cli.scale = std::stoi(argv[i]); |
|
} else if (is("-rotate")) { |
|
if (++i == argc) |
|
return false; |
|
cli.rotate = std::stoi(argv[i]); |
|
// } else if (is("-encoding")) { |
|
// if (++i == argc) |
|
// return false; |
|
// cli.encoding = CharacterSetFromString(argv[i]); |
|
} else if (is("-binary")) { |
|
cli.inputIsFile = true; |
|
} else if (is("-hrt")) { |
|
cli.addHRT = true; |
|
} else if (is("-noqz")) { |
|
cli.addQZs = false; |
|
} else if (is("-invert")) { |
|
cli.invert = true; |
|
} else if (is("-options")) { |
|
if (++i == argc) |
|
return false; |
|
cli.options = argv[i]; |
|
} else if (is("-verbose")) { |
|
cli.verbose = true; |
|
} else if (is("-help") || is("--help")) { |
|
PrintUsage(argv[0]); |
|
exit(0); |
|
} else if (is("-version") || is("--version")) { |
|
std::cout << "ZXingWriter version " << ZXING_VERSION_STR << " (with " |
|
#ifdef ZXING_USE_ZINT |
|
<< "zint" |
|
#else |
|
<< "zxing-cpp" |
|
#endif |
|
<< " backend) \n"; |
|
exit(0); |
|
} else if (nonOptArgCount == 0) { |
|
try { |
|
cli.format = BarcodeFormatFromString(argv[i]); |
|
} catch (const std::exception& e) { |
|
std::cerr << "Error: " << e.what() << "\n\n"; |
|
return false; |
|
} |
|
++nonOptArgCount; |
|
} else if (nonOptArgCount == 1) { |
|
cli.input = argv[i]; |
|
++nonOptArgCount; |
|
} else if (nonOptArgCount == 2) { |
|
cli.outPath = argv[i]; |
|
++nonOptArgCount; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
return nonOptArgCount == 3; |
|
} |
|
|
|
static std::string GetExtension(const std::string& path) |
|
{ |
|
auto fileNameStart = path.find_last_of("/\\"); |
|
auto fileName = fileNameStart == std::string::npos ? path : path.substr(fileNameStart + 1); |
|
auto extStart = fileName.find_last_of('.'); |
|
auto ext = extStart == std::string::npos ? "" : fileName.substr(extStart + 1); |
|
std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return std::tolower(c); }); |
|
return ext; |
|
} |
|
|
|
template <typename T = char> |
|
std::vector<T> ReadFile(const std::string& fn) |
|
{ |
|
std::basic_ifstream<T> ifs(fn, std::ios::binary); |
|
if (!ifs.good()) |
|
throw std::runtime_error("failed to open/read file " + fn); |
|
return ifs ? std::vector(std::istreambuf_iterator<T>(ifs), std::istreambuf_iterator<T>()) : std::vector<T>(); |
|
}; |
|
|
|
int main(int argc, char* argv[]) |
|
{ |
|
CLI cli; |
|
|
|
if (!ParseOptions(argc, argv, cli)) { |
|
PrintUsage(argv[0]); |
|
return -1; |
|
} |
|
|
|
try { |
|
#if 1 |
|
auto cOpts = CreatorOptions(cli.format, cli.options); |
|
auto barcode = cli.inputIsFile ? CreateBarcodeFromBytes(ReadFile(cli.input), cOpts) : CreateBarcodeFromText(cli.input, cOpts); |
|
|
|
auto wOpts = WriterOptions().scale(cli.scale).addQuietZones(cli.addQZs).addHRT(cli.addHRT).invert(cli.invert).rotate(cli.rotate); |
|
auto bitmap = WriteBarcodeToImage(barcode, wOpts); |
|
|
|
if (cli.verbose) { |
|
std::cout.setf(std::ios::boolalpha); |
|
std::cout << "Text: \"" << barcode.text() << "\"\n" |
|
<< "Bytes: " << barcode.text(TextMode::Hex) << "\n" |
|
<< "Format: " << ToString(barcode.format()) << "\n" |
|
<< "Symbology: " << ToString(barcode.symbology()) << "\n" |
|
<< "Identifier: " << barcode.symbologyIdentifier() << "\n" |
|
<< "Content: " << ToString(barcode.contentType()) << "\n" |
|
<< "HasECI: " << barcode.hasECI() << "\n" |
|
<< "Position: " << ToString(barcode.position()) << "\n" |
|
<< "Rotation: " << barcode.orientation() << " deg\n" |
|
<< "IsMirrored: " << barcode.isMirrored() << "\n" |
|
<< "IsInverted: " << barcode.isInverted() << "\n" |
|
<< "ECLevel: " << barcode.ecLevel() << "\n"; |
|
std::cout << WriteBarcodeToUtf8(barcode, wOpts); |
|
} |
|
#else // 'old' writer API (non zint based) |
|
auto writer = MultiFormatWriter(cli.format).setMargin(cli.addQZs ? 10 : 0); |
|
|
|
BitMatrix matrix; |
|
if (cli.inputIsFile) { |
|
auto file = ReadFile(cli.input); |
|
std::wstring bytes; |
|
for (uint8_t c : file) |
|
bytes.push_back(c); |
|
writer.setEncoding(CharacterSet::BINARY); |
|
matrix = writer.encode(bytes, cli.scale, std::clamp(cli.scale / 2, 50, 300)); |
|
} else { |
|
writer.setEncoding(CharacterSet::UTF8); |
|
matrix = writer.encode(cli.input, cli.scale, std::clamp(cli.scale / 2, 50, 300)); |
|
} |
|
auto bitmap = ToMatrix<uint8_t>(matrix); |
|
#endif |
|
|
|
auto ext = GetExtension(cli.outPath); |
|
int success = 0; |
|
if (ext == "" || ext == "png") { |
|
success = stbi_write_png(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0); |
|
} else if (ext == "jpg" || ext == "jpeg") { |
|
success = stbi_write_jpg(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0); |
|
} else if (ext == "svg") { |
|
#ifndef USE_OLD_WRITER_API |
|
success = (std::ofstream(cli.outPath) << WriteBarcodeToSVG(barcode, wOpts)).good(); |
|
#else |
|
success = (std::ofstream(cli.outPath) << ToSVG(matrix)).good(); |
|
#endif |
|
} |
|
|
|
if (!success) { |
|
std::cerr << "Failed to write image: " << cli.outPath << "\n"; |
|
return -1; |
|
} |
|
} catch (const std::exception& e) { |
|
std::cerr << e.what() << "\n"; |
|
return -1; |
|
} |
|
|
|
return 0; |
|
}
|
|
|