ZXingWriter.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * Copyright 2016 Nu-book Inc.
  3. */
  4. // SPDX-License-Identifier: Apache-2.0
  5. #ifdef ZXING_EXPERIMENTAL_API
  6. #include "WriteBarcode.h"
  7. #else
  8. #include "BitMatrix.h"
  9. #include "BitMatrixIO.h"
  10. #include "CharacterSet.h"
  11. #include "MultiFormatWriter.h"
  12. #endif
  13. #include "Version.h"
  14. #include <algorithm>
  15. #include <cctype>
  16. #include <cstring>
  17. #include <fstream>
  18. #include <iostream>
  19. #include <string>
  20. #define STB_IMAGE_WRITE_IMPLEMENTATION
  21. #include <stb_image_write.h>
  22. using namespace ZXing;
  23. static void PrintUsage(const char* exePath)
  24. {
  25. std::cout << "Usage: " << exePath
  26. << " [-size <width/height>] [-eclevel <level>] [-noqz] [-hrt] <format> <text> <output>\n"
  27. << " -size Size of generated image\n"
  28. // << " -margin Margin around barcode\n"
  29. // << " -encoding Encoding used to encode input text\n"
  30. << " -eclevel Error correction level, [0-8]\n"
  31. << " -binary Interpret <text> as a file name containing binary data\n"
  32. << " -noqz Print barcode witout quiet zone\n"
  33. << " -hrt Print human readable text below the barcode (if supported)\n"
  34. << " -help Print usage information\n"
  35. << " -version Print version information\n"
  36. << "\n"
  37. << "Supported formats are:\n";
  38. #ifdef ZXING_EXPERIMENTAL_API
  39. for (auto f : BarcodeFormats::all())
  40. #else
  41. for (auto f : BarcodeFormatsFromString("Aztec Codabar Code39 Code93 Code128 DataMatrix EAN8 EAN13 ITF PDF417 QRCode UPCA UPCE"))
  42. #endif
  43. std::cout << " " << ToString(f) << "\n";
  44. std::cout << "Format can be lowercase letters, with or without '-'.\n"
  45. << "Output format is determined by file name, supported are png, jpg and svg.\n";
  46. }
  47. static bool ParseSize(std::string str, int* width, int* height)
  48. {
  49. std::transform(str.begin(), str.end(), str.begin(), [](char c) { return (char)std::tolower(c); });
  50. auto xPos = str.find('x');
  51. if (xPos != std::string::npos) {
  52. *width = std::stoi(str.substr(0, xPos));
  53. *height = std::stoi(str.substr(xPos + 1));
  54. return true;
  55. }
  56. return false;
  57. }
  58. struct CLI
  59. {
  60. BarcodeFormat format;
  61. int sizeHint = 0;
  62. std::string input;
  63. std::string outPath;
  64. std::string ecLevel;
  65. bool inputIsFile = false;
  66. bool withHRT = false;
  67. bool withQZ = true;
  68. bool verbose = false;
  69. // CharacterSet encoding = CharacterSet::Unknown;
  70. };
  71. static bool ParseOptions(int argc, char* argv[], CLI& cli)
  72. {
  73. int nonOptArgCount = 0;
  74. for (int i = 1; i < argc; ++i) {
  75. auto is = [&](const char* str) { return strncmp(argv[i], str, strlen(argv[i])) == 0; };
  76. if (is("-size")) {
  77. if (++i == argc)
  78. return false;
  79. cli.sizeHint = std::stoi(argv[i]);
  80. } else if (is("-eclevel")) {
  81. if (++i == argc)
  82. return false;
  83. cli.ecLevel = argv[i];
  84. // } else if (is("-margin")) {
  85. // if (++i == argc)
  86. // return false;
  87. // cli.margin = std::stoi(argv[i]);
  88. // } else if (is("-encoding")) {
  89. // if (++i == argc)
  90. // return false;
  91. // cli.encoding = CharacterSetFromString(argv[i]);
  92. } else if (is("-binary")) {
  93. cli.inputIsFile = true;
  94. } else if (is("-hrt")) {
  95. cli.withHRT = true;
  96. } else if (is("-noqz")) {
  97. cli.withQZ = false;
  98. } else if (is("-verbose")) {
  99. cli.verbose = true;
  100. } else if (is("-help") || is("--help")) {
  101. PrintUsage(argv[0]);
  102. exit(0);
  103. } else if (is("-version") || is("--version")) {
  104. std::cout << "ZXingWriter " << ZXING_VERSION_STR << "\n";
  105. exit(0);
  106. } else if (nonOptArgCount == 0) {
  107. cli.format = BarcodeFormatFromString(argv[i]);
  108. if (cli.format == BarcodeFormat::None) {
  109. std::cerr << "Unrecognized format: " << argv[i] << std::endl;
  110. return false;
  111. }
  112. ++nonOptArgCount;
  113. } else if (nonOptArgCount == 1) {
  114. cli.input = argv[i];
  115. ++nonOptArgCount;
  116. } else if (nonOptArgCount == 2) {
  117. cli.outPath = argv[i];
  118. ++nonOptArgCount;
  119. } else {
  120. return false;
  121. }
  122. }
  123. return nonOptArgCount == 3;
  124. }
  125. static std::string GetExtension(const std::string& path)
  126. {
  127. auto fileNameStart = path.find_last_of("/\\");
  128. auto fileName = fileNameStart == std::string::npos ? path : path.substr(fileNameStart + 1);
  129. auto extStart = fileName.find_last_of('.');
  130. auto ext = extStart == std::string::npos ? "" : fileName.substr(extStart + 1);
  131. std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) { return std::tolower(c); });
  132. return ext;
  133. }
  134. template <typename T = char>
  135. std::vector<T> ReadFile(const std::string& fn)
  136. {
  137. std::basic_ifstream<T> ifs(fn, std::ios::binary);
  138. if (!ifs.good())
  139. throw std::runtime_error("failed to open/read file " + fn);
  140. return ifs ? std::vector(std::istreambuf_iterator<T>(ifs), std::istreambuf_iterator<T>()) : std::vector<T>();
  141. };
  142. int main(int argc, char* argv[])
  143. {
  144. CLI cli;
  145. if (!ParseOptions(argc, argv, cli)) {
  146. PrintUsage(argv[0]);
  147. return -1;
  148. }
  149. try {
  150. #ifdef ZXING_EXPERIMENTAL_API
  151. auto cOpts = CreatorOptions(cli.format).ecLevel(cli.ecLevel);
  152. auto barcode = cli.inputIsFile ? CreateBarcodeFromBytes(ReadFile(cli.input), cOpts) : CreateBarcodeFromText(cli.input, cOpts);
  153. auto wOpts = WriterOptions().sizeHint(cli.sizeHint).withQuietZones(cli.withQZ).withHRT(cli.withHRT).rotate(0);
  154. auto bitmap = WriteBarcodeToImage(barcode, wOpts);
  155. if (cli.verbose) {
  156. std::cout << "Text: \"" << barcode.text() << "\"\n"
  157. << "Bytes: " << ToHex(barcode.bytes()) << "\n"
  158. << "Format: " << ToString(barcode.format()) << "\n"
  159. << "Identifier: " << barcode.symbologyIdentifier() << "\n"
  160. << "Content: " << ToString(barcode.contentType()) << "\n"
  161. << "HasECI: " << barcode.hasECI() << "\n"
  162. << "Position: " << ToString(barcode.position()) << "\n"
  163. << "Rotation: " << barcode.orientation() << " deg\n"
  164. << "IsMirrored: " << barcode.isMirrored() << "\n"
  165. << "IsInverted: " << barcode.isInverted() << "\n"
  166. << "ecLevel: " << barcode.ecLevel() << "\n";
  167. std::cout << WriteBarcodeToUtf8(barcode);
  168. }
  169. #else
  170. auto writer = MultiFormatWriter(cli.format).setMargin(cli.withQZ ? 10 : 0);
  171. if (!cli.ecLevel.empty())
  172. writer.setEccLevel(std::stoi(cli.ecLevel));
  173. BitMatrix matrix;
  174. if (cli.inputIsFile) {
  175. auto file = ReadFile(cli.input);
  176. std::wstring bytes;
  177. for (uint8_t c : file)
  178. bytes.push_back(c);
  179. writer.setEncoding(CharacterSet::BINARY);
  180. matrix = writer.encode(bytes, cli.sizeHint, std::clamp(cli.sizeHint / 2, 50, 300));
  181. } else {
  182. writer.setEncoding(CharacterSet::UTF8);
  183. matrix = writer.encode(cli.input, cli.sizeHint, std::clamp(cli.sizeHint / 2, 50, 300));
  184. }
  185. auto bitmap = ToMatrix<uint8_t>(matrix);
  186. #endif
  187. auto ext = GetExtension(cli.outPath);
  188. int success = 0;
  189. if (ext == "" || ext == "png") {
  190. success = stbi_write_png(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
  191. } else if (ext == "jpg" || ext == "jpeg") {
  192. success = stbi_write_jpg(cli.outPath.c_str(), bitmap.width(), bitmap.height(), 1, bitmap.data(), 0);
  193. } else if (ext == "svg") {
  194. #ifdef ZXING_EXPERIMENTAL_API
  195. success = (std::ofstream(cli.outPath) << WriteBarcodeToSVG(barcode, wOpts)).good();
  196. #else
  197. success = (std::ofstream(cli.outPath) << ToSVG(matrix)).good();
  198. #endif
  199. }
  200. if (!success) {
  201. std::cerr << "Failed to write image: " << cli.outPath << std::endl;
  202. return -1;
  203. }
  204. } catch (const std::exception& e) {
  205. std::cerr << e.what() << std::endl;
  206. return -1;
  207. }
  208. return 0;
  209. }