diff options
Diffstat (limited to 'lpg/libqr/qrgen.c')
-rw-r--r-- | lpg/libqr/qrgen.c | 402 |
1 files changed, 402 insertions, 0 deletions
diff --git a/lpg/libqr/qrgen.c b/lpg/libqr/qrgen.c new file mode 100644 index 0000000..cf518e5 --- /dev/null +++ b/lpg/libqr/qrgen.c @@ -0,0 +1,402 @@ +#include <assert.h> +#include <ctype.h> +#include <getopt.h> +#include <limits.h> +#include <png.h> +#include <setjmp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <qr/bitmap.h> +#include <qr/bitstream.h> +#include <qr/code.h> +#include <qr/data.h> +#include <qr/version.h> + +struct config { + int version; + enum qr_ec_level ec; + enum qr_data_type dtype; + enum { + FORMAT_ANSI, + FORMAT_PBM, + FORMAT_PNG + } format; + const char * file; + const char * outfile; + const char * input; +}; + +struct qr_code * create(int version, + enum qr_ec_level ec, + enum qr_data_type dtype, + const char * input, + size_t len) +{ + struct qr_data * data; + struct qr_code * code; + + data = qr_data_create(version, ec, dtype, input, len); + + if (!data) { + /* BUG: this could also indicate OOM or + * some other error. + */ + fprintf(stderr, "Invalid data\n"); + exit(1); + } + + code = qr_code_create(data); + qr_data_destroy(data); + + if (!code) { + perror("Failed to create code"); + exit(2); + } + + return code; +} + +void output_pbm(FILE * file, const struct qr_bitmap * bmp, const char * comment) +{ + unsigned char * row; + int x, y; + + fputs("P1\n", file); + + if (comment) + fprintf(file, "# %s\n", comment); + + fprintf(file, "%u %u\n", + (unsigned)bmp->width + 8, + (unsigned)bmp->height + 8); + + row = bmp->bits; + + for (y = -4; y < (int)bmp->height + 4; ++y) { + + if (y < 0 || y >= (int)bmp->height) { + for (x = 0; x < (int)bmp->width + 8; ++x) + fputs("0 ", file); + fputc('\n', file); + continue; + } + + fputs("0 0 0 0 ", file); + + for (x = 0; x < (int)bmp->width; ++x) { + + int mask = 1 << x % CHAR_BIT; + int byte = row[x / CHAR_BIT]; + + fprintf(file, "%c ", (byte & mask) ? '1' : '0'); + } + + fputs("0 0 0 0\n", file); + row += bmp->stride; + } +} + +void output_ansi(FILE * file, const struct qr_bitmap * bmp) +{ + const char * out[2] = { + " ", + "\033[7m \033[0m", + }; + + unsigned char * line; + size_t x, y; + + line = bmp->bits; + + for (y = 0; y < bmp->height; ++y) { + + for (x = 0; x < bmp->width; ++x) { + + int mask = 1 << (x % CHAR_BIT); + int byte = line[x / CHAR_BIT]; + + fprintf(file, "%s", out[!!(byte & mask)]); + } + + fputc('\n', file); + + line += bmp->stride; + } +} + +void output_png(FILE * file, const struct qr_bitmap * bmp, const char * comment) +{ + const int px_size = 4; + png_structp png_ptr; + png_infop info_ptr; + png_text text; + int x, y, p; + unsigned char * row; + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) + goto err; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) + goto err; + + if (setjmp(png_jmpbuf(png_ptr))) + goto err; + + png_init_io(png_ptr, file); + png_set_IHDR(png_ptr, info_ptr, + (bmp->width + 8) * px_size, + (bmp->height + 8) * px_size, + 1, + PNG_COLOR_TYPE_GRAY, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + text.compression = PNG_TEXT_COMPRESSION_NONE; + text.key = "Software"; + text.text = (char *) comment; + text.text_length = strlen(comment); + png_set_text(png_ptr, info_ptr, &text, 1); + + png_write_info(png_ptr, info_ptr); + png_set_packing(png_ptr); + + row = malloc((bmp->width + 8) * px_size); + memset(row, 1, (bmp->width + 8) * px_size); + for (y = 0; y < 4; ++y) + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + + for (y = 0; y < bmp->height; ++y) { + const unsigned char * bmp_row = bmp->bits + y * bmp->stride; + unsigned char * out = row + 4 * px_size; + for (x = 0; x < bmp->width; ++x) { + int px = bmp_row[x / CHAR_BIT] & (1 << (x % CHAR_BIT)); + for (p = 0; p < px_size; ++p) + *out++ = !px; + } + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + } + + memset(row, 1, (bmp->width + 8) * px_size); + for (y = 0; y < 4; ++y) + for (p = 0; p < px_size; ++p) + png_write_row(png_ptr, row); + + free(row); + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, NULL); + return; + +err: + fprintf(stderr, "error writing PNG\n"); + exit(2); +} + +void show_help() { + fprintf(stderr, + "Usage:\n\t%s [options] <data>\n\n" + "\t-h Display this help message\n" + "\t-f <file> File containing data to encode (- for stdin)\n" + "\t-v <n> Specify QR version (size) 1 <= n <= 40\n" + "\t-e <type> Specify EC type: L, M, Q, H\n" + "\t-a Output as ANSI graphics (default)\n" + "\t-p Output as PBM\n" + "\t-g Output as PNG\n" + "\t-o <file> File to write (- for stdout)\n\n", + "qrgen"); +} + +void set_default_config(struct config * conf) +{ + conf->version = 0; + conf->ec = QR_EC_LEVEL_M; + conf->dtype = QR_DATA_8BIT; + conf->format = FORMAT_ANSI; + conf->file = NULL; + conf->outfile = NULL; + conf->input = NULL; +} + +void parse_options(int argc, char ** argv, struct config * conf) +{ + int c; + + for (;;) { + c = getopt(argc, argv, ":hf:v:e:t:apgo:"); + + if (c == -1) /* no more options */ + break; + + switch (c) { + case 'h': /* help */ + show_help(); + exit(0); + break; + case 'f': /* file */ + conf->file = optarg; + break; + case 'v': /* version */ + conf->version = atoi(optarg); + if (conf->version < 1 || conf->version > 40) { + fprintf(stderr, + "Version must be between 1 and 40\n"); + exit(1); + } + break; + case 'e': /* ec */ + switch (tolower(optarg[0])) { + case 'l': conf->ec = QR_EC_LEVEL_L; break; + case 'm': conf->ec = QR_EC_LEVEL_M; break; + case 'q': conf->ec = QR_EC_LEVEL_Q; break; + case 'h': conf->ec = QR_EC_LEVEL_H; break; + default: + fprintf(stderr, + "Invalid EC type (%c). Choose from" + " L, M, Q or H.\n", optarg[0]); + } + break; + case 't': /* type */ + fprintf(stderr, "XXX: ignored \"type\"\n"); + break; + case 'a': /* ansi */ + conf->format = FORMAT_ANSI; break; + case 'p': /* pnm */ + conf->format = FORMAT_PBM; break; + case 'g': /* png */ + conf->format = FORMAT_PNG; break; + case 'o': /* output file */ + conf->outfile = optarg; break; + case ':': + fprintf(stderr, + "Argument \"%s\" missing parameter\n", + argv[optind-1]); + exit(1); + break; + case '?': default: + fprintf(stderr, + "Invalid argument: \"%s\"\n" + "Try -h for help\n", + argv[optind-1]); + exit(1); + break; + } + } + + + if (optind < argc) + conf->input = argv[optind++]; + + if (!conf->file && !conf->input) { + fprintf(stderr, "No data (try -h for help)\n"); + exit(1); + } +} + +void slurp_file(const char * path, char ** data, size_t * len) +{ + const size_t chunk_size = 65536; + FILE * file; + char * tmpbuf; + size_t count; + + if (strcmp(path, "-") == 0) + file = stdin; + else + file = fopen(path, "rb"); + + if (!file) { + fprintf(stderr, "Failed to open %s\n", path); + exit(2); + } + + *data = NULL; + *len = 0; + + do { + tmpbuf = realloc(*data, *len + chunk_size); + if (!tmpbuf) { + perror("realloc"); + exit(2); + } + *data = tmpbuf; + count = fread(*data + *len, 1, chunk_size, file); + if (count == 0 && !feof(file)) { + perror("fread"); + exit(2); + } + *len += count; + } while (*len == chunk_size); + + fclose(file); +} + +int main(int argc, char ** argv) { + + struct config conf; + struct qr_code * code; + char * file_data; + size_t len; + FILE * outfile; + + set_default_config(&conf); + parse_options(argc, argv, &conf); + + if (conf.file) + slurp_file(conf.file, &file_data, &len); + else + len = strlen(conf.input); + + if (!conf.outfile) { + switch (conf.format) { + case FORMAT_ANSI: + conf.outfile = "-"; break; + case FORMAT_PBM: + conf.outfile = "qr.pbm"; break; + case FORMAT_PNG: + conf.outfile = "qr.png"; break; + } + } + + if (strcmp(conf.outfile, "-") == 0) { + outfile = stdout; + } else { + outfile = fopen(conf.outfile, "wb"); + if (!outfile) { + perror("fopen"); + exit(2); + } + } + + code = create(conf.version, + conf.ec, + conf.dtype, + conf.file ? file_data : conf.input, + len); + + if (conf.file) + free(file_data); + + switch (conf.format) { + case FORMAT_ANSI: + output_ansi(outfile, code->modules); + break; + case FORMAT_PBM: + output_pbm(outfile, code->modules, "libqr v" QR_VERSION); + break; + case FORMAT_PNG: + output_png(outfile, code->modules, "libqr v" QR_VERSION); + break; + } + + fclose(outfile); + qr_code_destroy(code); + + return 0; +} + |