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.
- checksums.yaml +7 -0
- data/BUILD.md +482 -0
- data/CHANGELOG.md +51 -0
- data/CMakeLists.txt +126 -0
- data/CONTRIBUTING.md +63 -0
- data/DISTRIBUTION.md +730 -0
- data/INSTALL.md +171 -0
- data/LICENSE +39 -0
- data/PREBUILT.md +171 -0
- data/README.md +312 -0
- data/VERSION +1 -0
- data/bindings/nodejs/binding.gyp +38 -0
- data/bindings/nodejs/fastqr_node.cpp +125 -0
- data/bindings/nodejs/index.d.ts +80 -0
- data/bindings/nodejs/index.js +187 -0
- data/bindings/nodejs/lib/platform.js +72 -0
- data/bindings/nodejs/package.json +45 -0
- data/bindings/nodejs/test/test.js +45 -0
- data/bindings/php/fastqr_php.cpp +85 -0
- data/bindings/php/src/FastQR.php +316 -0
- data/bindings/php/tests/FastQRTest.php +97 -0
- data/bindings/ruby/extconf.rb +29 -0
- data/bindings/ruby/fastqr_ruby.cpp +122 -0
- data/bindings/ruby/lib/fastqr/platform.rb +70 -0
- data/bindings/ruby/lib/fastqr/version.rb +6 -0
- data/bindings/ruby/lib/fastqr.rb +129 -0
- data/bindings/ruby/prebuilt/macos-arm64.tar.gz +1 -0
- data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +1 -0
- data/build.sh +109 -0
- data/cmake/fastqrConfig.cmake.in +6 -0
- data/composer.json +36 -0
- data/docs/CLI_USAGE.md +478 -0
- data/docs/NODEJS_USAGE.md +694 -0
- data/docs/PHP_USAGE.md +815 -0
- data/docs/README.md +191 -0
- data/docs/RUBY_USAGE.md +537 -0
- data/examples/CMakeLists.txt +7 -0
- data/examples/basic.cpp +58 -0
- data/include/fastqr.h +97 -0
- data/include/stb_image.h +7988 -0
- data/include/stb_image_write.h +1724 -0
- data/phpunit.xml +18 -0
- data/prebuilt/README.md +131 -0
- data/scripts/README.md +248 -0
- data/scripts/build-binaries.sh +98 -0
- data/scripts/build-local.sh +18 -0
- data/scripts/install.sh +87 -0
- data/scripts/release.sh +78 -0
- data/scripts/update-version.sh +58 -0
- data/src/cli.cpp +316 -0
- data/src/fastqr.cpp +665 -0
- data/test.sh +86 -0
- 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
|
+
|