fastqr 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/BUILD.md +482 -0
  3. data/CHANGELOG.md +51 -0
  4. data/CMakeLists.txt +126 -0
  5. data/CONTRIBUTING.md +63 -0
  6. data/DISTRIBUTION.md +730 -0
  7. data/INSTALL.md +171 -0
  8. data/LICENSE +39 -0
  9. data/PREBUILT.md +171 -0
  10. data/README.md +312 -0
  11. data/VERSION +1 -0
  12. data/bindings/nodejs/binding.gyp +38 -0
  13. data/bindings/nodejs/fastqr_node.cpp +125 -0
  14. data/bindings/nodejs/index.d.ts +80 -0
  15. data/bindings/nodejs/index.js +187 -0
  16. data/bindings/nodejs/lib/platform.js +72 -0
  17. data/bindings/nodejs/package.json +45 -0
  18. data/bindings/nodejs/test/test.js +45 -0
  19. data/bindings/php/fastqr_php.cpp +85 -0
  20. data/bindings/php/src/FastQR.php +316 -0
  21. data/bindings/php/tests/FastQRTest.php +97 -0
  22. data/bindings/ruby/extconf.rb +29 -0
  23. data/bindings/ruby/fastqr_ruby.cpp +122 -0
  24. data/bindings/ruby/lib/fastqr/platform.rb +70 -0
  25. data/bindings/ruby/lib/fastqr/version.rb +6 -0
  26. data/bindings/ruby/lib/fastqr.rb +129 -0
  27. data/bindings/ruby/prebuilt/macos-arm64.tar.gz +1 -0
  28. data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +1 -0
  29. data/build.sh +109 -0
  30. data/cmake/fastqrConfig.cmake.in +6 -0
  31. data/composer.json +36 -0
  32. data/docs/CLI_USAGE.md +478 -0
  33. data/docs/NODEJS_USAGE.md +694 -0
  34. data/docs/PHP_USAGE.md +815 -0
  35. data/docs/README.md +191 -0
  36. data/docs/RUBY_USAGE.md +537 -0
  37. data/examples/CMakeLists.txt +7 -0
  38. data/examples/basic.cpp +58 -0
  39. data/include/fastqr.h +97 -0
  40. data/include/stb_image.h +7988 -0
  41. data/include/stb_image_write.h +1724 -0
  42. data/phpunit.xml +18 -0
  43. data/prebuilt/README.md +131 -0
  44. data/scripts/README.md +248 -0
  45. data/scripts/build-binaries.sh +98 -0
  46. data/scripts/build-local.sh +18 -0
  47. data/scripts/install.sh +87 -0
  48. data/scripts/release.sh +78 -0
  49. data/scripts/update-version.sh +58 -0
  50. data/src/cli.cpp +316 -0
  51. data/src/fastqr.cpp +665 -0
  52. data/test.sh +86 -0
  53. metadata +155 -0
@@ -0,0 +1,58 @@
1
+ #!/bin/bash
2
+ # Update version across all project files
3
+
4
+ set -e
5
+
6
+ if [ -z "$1" ]; then
7
+ echo "Usage: $0 <version>"
8
+ echo "Example: $0 1.0.1"
9
+ exit 1
10
+ fi
11
+
12
+ NEW_VERSION="$1"
13
+
14
+ echo "🔄 Updating version to $NEW_VERSION..."
15
+
16
+ # Update VERSION file
17
+ echo "$NEW_VERSION" > VERSION
18
+
19
+ # Update CMakeLists.txt
20
+ sed -i.bak "s/project(fastqr VERSION [0-9.]\+ /project(fastqr VERSION $NEW_VERSION /" CMakeLists.txt
21
+ rm -f CMakeLists.txt.bak
22
+
23
+ # Update src/fastqr.cpp
24
+ sed -i.bak "s/#define FASTQR_VERSION \"[^\"]*\"/#define FASTQR_VERSION \"$NEW_VERSION\"/" src/fastqr.cpp
25
+ rm -f src/fastqr.cpp.bak
26
+
27
+ # Update Ruby version
28
+ sed -i.bak "s/VERSION = \"[^\"]*\"/VERSION = \"$NEW_VERSION\"/" bindings/ruby/lib/fastqr/version.rb
29
+ rm -f bindings/ruby/lib/fastqr/version.rb.bak
30
+
31
+ # Update gemspec
32
+ sed -i.bak "s/spec.version[[:space:]]*=[[:space:]]*\"[^\"]*\"/spec.version = \"$NEW_VERSION\"/" fastqr.gemspec
33
+ rm -f fastqr.gemspec.bak
34
+
35
+ # Update Node.js package.json
36
+ sed -i.bak "s/\"version\": \"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" bindings/nodejs/package.json
37
+ rm -f bindings/nodejs/package.json.bak
38
+
39
+ # Update composer.json (PHP)
40
+ if [ -f composer.json ]; then
41
+ sed -i.bak "s/\"version\": \"[^\"]*\"/\"version\": \"$NEW_VERSION\"/" composer.json
42
+ rm -f composer.json.bak
43
+ fi
44
+
45
+ # Update documentation
46
+ for doc in README.md docs/*.md; do
47
+ if [ -f "$doc" ]; then
48
+ # Update version in installation examples
49
+ sed -i.bak "s/v[0-9]\+\.[0-9]\+\.[0-9]\+/v$NEW_VERSION/g" "$doc"
50
+ sed -i.bak "s/fastqr-[0-9]\+\.[0-9]\+\.[0-9]\+/fastqr-$NEW_VERSION/g" "$doc"
51
+ rm -f "$doc.bak"
52
+ fi
53
+ done
54
+
55
+ echo "✅ Version updated to $NEW_VERSION in all files"
56
+ echo ""
57
+ echo "Changed files:"
58
+ git diff --name-only
data/src/cli.cpp ADDED
@@ -0,0 +1,316 @@
1
+ /*
2
+ * FastQR - Fast QR Code Generator Library
3
+ * Copyright (C) 2025 FastQR Project
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ */
10
+
11
+ #include "fastqr.h"
12
+ #include <iostream>
13
+ #include <fstream>
14
+ #include <vector>
15
+ #include <cstring>
16
+ #include <cstdlib>
17
+ #include <sys/stat.h>
18
+ #include <errno.h>
19
+
20
+ #ifdef _OPENMP
21
+ #include <omp.h>
22
+ #endif
23
+
24
+ void print_usage(const char* program_name) {
25
+ std::cout << "FastQR v" << fastqr::version() << " - Fast QR Code Generator\n\n";
26
+ std::cout << "Usage: " << program_name << " [OPTIONS] <data> <output_file>\n";
27
+ std::cout << " " << program_name << " [OPTIONS] -F <input.txt> <output_dir>\n\n";
28
+ std::cout << "Options:\n";
29
+ std::cout << " -s, --size SIZE Output size in pixels (default: 300)\n";
30
+ std::cout << " -o, --optimize Auto round-up size for best performance\n";
31
+ std::cout << " -f, --foreground R,G,B QR code color (default: 0,0,0)\n";
32
+ std::cout << " -b, --background R,G,B Background color (default: 255,255,255)\n";
33
+ std::cout << " -e, --error-level L|M|Q|H Error correction level (default: M)\n";
34
+ std::cout << " -l, --logo PATH Path to logo image\n";
35
+ std::cout << " -p, --logo-size N Logo size percentage (default: 20)\n";
36
+ std::cout << " -q, --quality N Image quality 1-100 (default: 95)\n";
37
+ std::cout << " -F, --file PATH Batch mode: process text file (one QR per line)\n";
38
+ std::cout << " -h, --help Show this help\n";
39
+ std::cout << " -v, --version Show version\n\n";
40
+ std::cout << "Examples:\n";
41
+ std::cout << " " << program_name << " \"Hello World\" output.png\n";
42
+ std::cout << " " << program_name << " -s 500 \"Large QR\" large.png\n";
43
+ std::cout << " " << program_name << " -s 500 -o \"Optimized\" fast.png\n";
44
+ std::cout << " " << program_name << " -s 500 -f 255,0,0 \"Red QR\" red_qr.png\n";
45
+ std::cout << " " << program_name << " -l logo.png \"Company\" qr_with_logo.png\n";
46
+ std::cout << " " << program_name << " -F batch.txt output_dir/ -s 500 -o\n";
47
+ }
48
+
49
+ bool parse_color(const char* str, fastqr::QROptions::Color& color) {
50
+ int r, g, b;
51
+ if (sscanf(str, "%d,%d,%d", &r, &g, &b) != 3) {
52
+ return false;
53
+ }
54
+ if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
55
+ return false;
56
+ }
57
+ color.r = static_cast<uint8_t>(r);
58
+ color.g = static_cast<uint8_t>(g);
59
+ color.b = static_cast<uint8_t>(b);
60
+ return true;
61
+ }
62
+
63
+ bool parse_size(const char* str, int& size) {
64
+ if (sscanf(str, "%d", &size) != 1) {
65
+ return false;
66
+ }
67
+ if (size <= 0 || size > 10000) {
68
+ return false;
69
+ }
70
+ return true;
71
+ }
72
+
73
+ // Create directory recursively
74
+ bool mkdir_p(const std::string& path) {
75
+ struct stat st;
76
+ if (stat(path.c_str(), &st) == 0) {
77
+ return S_ISDIR(st.st_mode);
78
+ }
79
+
80
+ // Try to create directory
81
+ if (mkdir(path.c_str(), 0755) == 0) {
82
+ return true;
83
+ }
84
+
85
+ if (errno != ENOENT) {
86
+ return false;
87
+ }
88
+
89
+ // Parent doesn't exist, create it
90
+ size_t pos = path.find_last_of('/');
91
+ if (pos == std::string::npos) {
92
+ return false;
93
+ }
94
+
95
+ if (!mkdir_p(path.substr(0, pos))) {
96
+ return false;
97
+ }
98
+
99
+ return mkdir(path.c_str(), 0755) == 0;
100
+ }
101
+
102
+ // Read batch file (one QR text per line)
103
+ bool read_batch_file(const std::string& filename, std::vector<std::string>& lines) {
104
+ std::ifstream file(filename);
105
+ if (!file.is_open()) {
106
+ std::cerr << "Error: Cannot open file: " << filename << std::endl;
107
+ return false;
108
+ }
109
+
110
+ std::string line;
111
+ while (std::getline(file, line)) {
112
+ if (!line.empty()) {
113
+ lines.push_back(line);
114
+ }
115
+ }
116
+
117
+ if (lines.empty()) {
118
+ std::cerr << "Error: File is empty: " << filename << std::endl;
119
+ return false;
120
+ }
121
+
122
+ return true;
123
+ }
124
+
125
+ // Process batch with parallel processing
126
+ bool process_batch(const std::string& input_file, const std::string& output_dir,
127
+ const fastqr::QROptions& options) {
128
+ // Read input file
129
+ std::vector<std::string> lines;
130
+ if (!read_batch_file(input_file, lines)) {
131
+ return false;
132
+ }
133
+
134
+ // Create output directory
135
+ if (!mkdir_p(output_dir)) {
136
+ std::cerr << "Error: Cannot create directory: " << output_dir << std::endl;
137
+ return false;
138
+ }
139
+
140
+ std::cout << "Processing " << lines.size() << " QR codes..." << std::endl;
141
+
142
+ int success_count = 0;
143
+ int fail_count = 0;
144
+
145
+ // Parallel processing with OpenMP
146
+ #pragma omp parallel for schedule(dynamic, 10) reduction(+:success_count,fail_count)
147
+ for (size_t i = 0; i < lines.size(); i++) {
148
+ // Generate output filename: 1.png, 2.png, ...
149
+ std::string output_path = output_dir;
150
+ if (output_path.back() != '/') {
151
+ output_path += '/';
152
+ }
153
+ output_path += std::to_string(i + 1) + ".png";
154
+
155
+ // Generate QR code (reusing single-QR generation - no overhead!)
156
+ if (fastqr::generate(lines[i], output_path, options)) {
157
+ success_count++;
158
+ } else {
159
+ fail_count++;
160
+ #pragma omp critical
161
+ {
162
+ std::cerr << "Error: Failed to generate QR " << (i + 1) << std::endl;
163
+ }
164
+ }
165
+ }
166
+
167
+ std::cout << "Done: " << success_count << " success, " << fail_count << " failed" << std::endl;
168
+
169
+ return fail_count == 0;
170
+ }
171
+
172
+ int main(int argc, char* argv[]) {
173
+ if (argc < 2) {
174
+ print_usage(argv[0]);
175
+ return 1;
176
+ }
177
+
178
+ fastqr::QROptions options;
179
+ std::string data;
180
+ std::string output_path;
181
+ std::string batch_file; // For batch mode
182
+
183
+ // Parse arguments
184
+ for (int i = 1; i < argc; i++) {
185
+ std::string arg = argv[i];
186
+
187
+ if (arg == "-h" || arg == "--help") {
188
+ print_usage(argv[0]);
189
+ return 0;
190
+ } else if (arg == "-v" || arg == "--version") {
191
+ std::cout << "FastQR v" << fastqr::version() << std::endl;
192
+ return 0;
193
+ } else if (arg == "-s" || arg == "--size") {
194
+ if (++i >= argc) {
195
+ std::cerr << "Error: " << arg << " requires an argument\n";
196
+ return 1;
197
+ }
198
+ if (!parse_size(argv[i], options.size)) {
199
+ std::cerr << "Error: Invalid size format. Use SIZE (e.g., 500)\n";
200
+ return 1;
201
+ }
202
+ } else if (arg == "-o" || arg == "--optimize") {
203
+ options.optimize_size = true;
204
+ } else if (arg == "-f" || arg == "--foreground") {
205
+ if (++i >= argc) {
206
+ std::cerr << "Error: " << arg << " requires an argument\n";
207
+ return 1;
208
+ }
209
+ if (!parse_color(argv[i], options.foreground)) {
210
+ std::cerr << "Error: Invalid color format. Use R,G,B (e.g., 255,0,0)\n";
211
+ return 1;
212
+ }
213
+ } else if (arg == "-b" || arg == "--background") {
214
+ if (++i >= argc) {
215
+ std::cerr << "Error: " << arg << " requires an argument\n";
216
+ return 1;
217
+ }
218
+ if (!parse_color(argv[i], options.background)) {
219
+ std::cerr << "Error: Invalid color format. Use R,G,B (e.g., 255,255,255)\n";
220
+ return 1;
221
+ }
222
+ } else if (arg == "-e" || arg == "--error-level") {
223
+ if (++i >= argc) {
224
+ std::cerr << "Error: " << arg << " requires an argument\n";
225
+ return 1;
226
+ }
227
+ std::string level = argv[i];
228
+ if (level == "L") options.ec_level = fastqr::ErrorCorrectionLevel::LOW;
229
+ else if (level == "M") options.ec_level = fastqr::ErrorCorrectionLevel::MEDIUM;
230
+ else if (level == "Q") options.ec_level = fastqr::ErrorCorrectionLevel::QUARTILE;
231
+ else if (level == "H") options.ec_level = fastqr::ErrorCorrectionLevel::HIGH;
232
+ else {
233
+ std::cerr << "Error: Invalid error level. Use L, M, Q, or H\n";
234
+ return 1;
235
+ }
236
+ } else if (arg == "-l" || arg == "--logo") {
237
+ if (++i >= argc) {
238
+ std::cerr << "Error: " << arg << " requires an argument\n";
239
+ return 1;
240
+ }
241
+ options.logo_path = argv[i];
242
+ } else if (arg == "-p" || arg == "--logo-size") {
243
+ if (++i >= argc) {
244
+ std::cerr << "Error: " << arg << " requires an argument\n";
245
+ return 1;
246
+ }
247
+ options.logo_size_percent = atoi(argv[i]);
248
+ if (options.logo_size_percent < 1 || options.logo_size_percent > 50) {
249
+ std::cerr << "Error: Logo size must be between 1 and 50\n";
250
+ return 1;
251
+ }
252
+ } else if (arg == "-q" || arg == "--quality") {
253
+ if (++i >= argc) {
254
+ std::cerr << "Error: " << arg << " requires an argument\n";
255
+ return 1;
256
+ }
257
+ options.quality = atoi(argv[i]);
258
+ if (options.quality < 1 || options.quality > 100) {
259
+ std::cerr << "Error: Quality must be between 1 and 100\n";
260
+ return 1;
261
+ }
262
+ } else if (arg == "-F" || arg == "--file") {
263
+ if (++i >= argc) {
264
+ std::cerr << "Error: " << arg << " requires an argument\n";
265
+ return 1;
266
+ }
267
+ batch_file = argv[i];
268
+ } else if (arg[0] == '-') {
269
+ std::cerr << "Error: Unknown option: " << arg << std::endl;
270
+ return 1;
271
+ } else {
272
+ // Non-option arguments
273
+ if (data.empty()) {
274
+ data = arg;
275
+ } else if (output_path.empty()) {
276
+ output_path = arg;
277
+ } else {
278
+ std::cerr << "Error: Too many arguments\n";
279
+ return 1;
280
+ }
281
+ }
282
+ }
283
+
284
+ // Batch mode vs single mode
285
+ if (!batch_file.empty()) {
286
+ // Batch mode: --file <input.txt> <output_dir>
287
+ if (data.empty()) {
288
+ std::cerr << "Error: Output directory required for batch mode\n";
289
+ print_usage(argv[0]);
290
+ return 1;
291
+ }
292
+
293
+ // In batch mode, first non-option arg is output_dir
294
+ std::string output_dir = data;
295
+
296
+ if (!process_batch(batch_file, output_dir, options)) {
297
+ return 1;
298
+ }
299
+ } else {
300
+ // Single mode: <data> <output_file>
301
+ if (data.empty() || output_path.empty()) {
302
+ std::cerr << "Error: Missing required arguments\n";
303
+ print_usage(argv[0]);
304
+ return 1;
305
+ }
306
+
307
+ // Generate single QR code (no overhead - same performance as before!)
308
+ if (!fastqr::generate(data, output_path, options)) {
309
+ std::cerr << "Error: Failed to generate QR code\n";
310
+ return 1;
311
+ }
312
+ }
313
+
314
+ return 0;
315
+ }
316
+