diff options
Diffstat (limited to 'genqrcode/qrenc.c')
| -rw-r--r-- | genqrcode/qrenc.c | 1451 |
1 files changed, 1451 insertions, 0 deletions
diff --git a/genqrcode/qrenc.c b/genqrcode/qrenc.c new file mode 100644 index 0000000000..1ce8c989b0 --- /dev/null +++ b/genqrcode/qrenc.c @@ -0,0 +1,1451 @@ +/** + * qrencode - QR Code encoder + * + * QR Code encoding tool + * Copyright (C) 2006-2018, 2020 Kentaro Fukuchi <kentaro@fukuchi.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if HAVE_CONFIG_H +# include "config.h" +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> +#include <errno.h> +#if HAVE_PNG +#include <png.h> +#endif + +#include "qrencode.h" + +#define INCHES_PER_METER (100.0/2.54) + +static int casesensitive = 1; +static int eightbit = 0; +static int version = 0; +static int size = 3; +static int margin = -1; +static int dpi = 72; +static int structured = 0; +static int rle = 0; +static int svg_path = 0; +static int micro = 0; +static int inline_svg = 0; +static int strict_versioning = 0; +static QRecLevel level = QR_ECLEVEL_L; +static QRencodeMode hint = QR_MODE_8; +static unsigned char fg_color[4] = {0, 0, 0, 255}; +static unsigned char bg_color[4] = {255, 255, 255, 255}; + +static int verbose = 0; + +enum imageType { + PNG_TYPE, + PNG32_TYPE, + EPS_TYPE, + SVG_TYPE, + XPM_TYPE, + ANSI_TYPE, + ANSI256_TYPE, + ASCII_TYPE, + ASCIIi_TYPE, + UTF8_TYPE, + ANSIUTF8_TYPE, + ANSI256UTF8_TYPE, + UTF8i_TYPE, + ANSIUTF8i_TYPE +}; + +static enum imageType image_type = PNG_TYPE; + +static const struct option options[] = { + {"help" , no_argument , NULL, 'h'}, + {"output" , required_argument, NULL, 'o'}, + {"read-from" , required_argument, NULL, 'r'}, + {"level" , required_argument, NULL, 'l'}, + {"size" , required_argument, NULL, 's'}, + {"symversion" , required_argument, NULL, 'v'}, + {"margin" , required_argument, NULL, 'm'}, + {"dpi" , required_argument, NULL, 'd'}, + {"type" , required_argument, NULL, 't'}, + {"structured" , no_argument , NULL, 'S'}, + {"kanji" , no_argument , NULL, 'k'}, + {"casesensitive" , no_argument , NULL, 'c'}, + {"ignorecase" , no_argument , NULL, 'i'}, + {"8bit" , no_argument , NULL, '8'}, + {"micro" , no_argument , NULL, 'M'}, + {"rle" , no_argument , &rle, 1}, + {"svg-path" , no_argument , &svg_path, 1}, + {"inline" , no_argument , &inline_svg, 1}, + {"strict-version", no_argument , &strict_versioning, 1}, + {"foreground" , required_argument, NULL, 'f'}, + {"background" , required_argument, NULL, 'b'}, + {"version" , no_argument , NULL, 'V'}, + {"verbose" , no_argument , &verbose, 1}, + {NULL, 0, NULL, 0} +}; + +static char *optstring = "ho:r:l:s:v:m:d:t:Skci8MV"; + +static void usage(int help, int longopt, int status) +{ + FILE *out = status ? stderr : stdout; + fprintf(out, +"qrencode version %s\n" +"Copyright (C) 2006-2018, 2020 Kentaro Fukuchi\n", QRcode_APIVersionString()); + if(help) { + if(longopt) { + fprintf(out, +"Usage: qrencode [-o FILENAME] [OPTION]... [STRING]\n" +"Encode input data in a QR Code and save as a PNG or EPS image.\n\n" +" -h, --help display the help message. -h displays only the help of short\n" +" options.\n\n" +" -o FILENAME, --output=FILENAME\n" +" write image to FILENAME. If '-' is specified, the result\n" +" will be output to standard output. If -S is given, structured\n" +" symbols are written to FILENAME-01.png, FILENAME-02.png, ...\n" +" (suffix is removed from FILENAME, if specified)\n\n" +" -r FILENAME, --read-from=FILENAME\n" +" read input data from FILENAME.\n\n" +" -s NUMBER, --size=NUMBER\n" +" specify module size in dots (pixels). (default=3)\n\n" +" -l {LMQH}, --level={LMQH}\n" +" specify error correction level from L (lowest) to H (highest).\n" +" (default=L)\n\n" +" -v NUMBER, --symversion=NUMBER\n" +" specify the minimum version of the symbol. See SYMBOL VERSIONS\n" +" for more information. (default=auto)\n\n" +" -m NUMBER, --margin=NUMBER\n" +" specify the width of the margins. (default=4 (2 for Micro QR)))\n\n" +" -d NUMBER, --dpi=NUMBER\n" +" specify the DPI of the generated PNG. (default=72)\n\n" +" -t {PNG,PNG32,EPS,SVG,XPM,ANSI,ANSI256,ASCII,ASCIIi,UTF8,UTF8i,ANSIUTF8,ANSIUTF8i,ANSI256UTF8},\n" +" --type={PNG,PNG32,EPS,SVG,XPM,ANSI,ANSI256,ASCII,ASCIIi,UTF8,UTF8i,ANSIUTF8,ANSIUTF8i,ANSI256UTF8}\n" +" specify the type of the generated image. (default=PNG)\n" +" If ASCII*, UTF8*, or ANSI* is specified, the image will be disp-\n" +" layed in the terminal by using text characters instead of gene-\n" +" rating an image file. If the name of the type is followed by\n" +" 'i', the light/dark character will be reversed.\n" +" -S, --structured\n" +" make structured symbols. Version must be specified with '-v'.\n\n" +" -k, --kanji assume that the input text contains kanji (shift-jis).\n\n" +" -c, --casesensitive\n" +" encode lower-case alphabet characters in 8-bit mode. (default)\n\n" +" -i, --ignorecase\n" +" ignore case distinctions and use only upper-case characters.\n\n" +" -8, --8bit encode entire data in 8-bit mode. -k, -c and -i will be ignored.\n\n" +" -M, --micro encode in a Micro QR Code.\n\n" +" --rle enable run-length encoding for SVG.\n\n" +" --svg-path\n" +" use single path to draw modules for SVG.\n\n" +" --inline only useful for SVG output, generates an SVG without the XML tag.\n\n" +" --foreground=RRGGBB[AA]\n" +" --background=RRGGBB[AA]\n" +" specify foreground/background color in hexadecimal notation.\n" +" 6-digit (RGB) or 8-digit (RGBA) form are supported.\n" +" Color output support available only in PNG, EPS and SVG.\n\n" +" --strict-version\n" +" disable automatic version number adjustment. If the input data is\n" +" too large for the specified version, the program exits with the\n" +" code of 1.\n\n" +" -V, --version\n" +" display the version number and copyrights of the qrencode.\n\n" +" --verbose\n" +" display verbose information to stderr.\n\n" +" [STRING] input data. If it is not specified, data will be taken from\n" +" standard input.\n\n" +"SYMBOL VERSIONS\n" +" The symbol versions of QR Code range from Version 1 to Version\n" +" 40. Each version has a different module configuration or number\n" +" of modules, ranging from Version 1 (21 x 21 modules) up to\n" +" Version 40 (177 x 177 modules). Each higher version number\n" +" comprises 4 additional modules per side by default. See\n" +" https://www.qrcode.com/en/about/version.html for a detailed\n" +" version list.\n" + + ); + } else { + fprintf(out, +"Usage: qrencode [-o FILENAME] [OPTION]... [STRING]\n" +"Encode input data in a QR Code and save as a PNG or EPS image.\n\n" +" -h display this message.\n" +" --help display the usage of long options.\n" +" -o FILENAME write image to FILENAME. If '-' is specified, the result\n" +" will be output to standard output. If -S is given, structured\n" +" symbols are written to FILENAME-01.png, FILENAME-02.png, ...\n" +" (suffix is removed from FILENAME, if specified)\n" +" -r FILENAME read input data from FILENAME.\n" +" -s NUMBER specify module size in dots (pixels). (default=3)\n" +" -l {LMQH} specify error correction level from L (lowest) to H (highest).\n" +" (default=L)\n" +" -v NUMBER specify the minimum version of the symbol. (default=auto)\n" +" -m NUMBER specify the width of the margins. (default=4 (2 for Micro))\n" +" -d NUMBER specify the DPI of the generated PNG. (default=72)\n" +" -t {PNG,PNG32,EPS,SVG,XPM,ANSI,ANSI256,ASCII,ASCIIi,UTF8,UTF8i,ANSIUTF8,ANSIUTF8i,ANSI256UTF8}\n" +" specify the type of the generated image. (default=PNG)\n" +" -S make structured symbols. Version number must be specified with '-v'.\n" +" -k assume that the input text contains kanji (shift-jis).\n" +" -c encode lower-case alphabet characters in 8-bit mode. (default)\n" +" -i ignore case distinctions and use only upper-case characters.\n" +" -8 encode entire data in 8-bit mode. -k, -c and -i will be ignored.\n" +" -M encode in a Micro QR Code.\n" +" -V display the version number and copyrights of the qrencode.\n" +" [STRING] input data. If it is not specified, data will be taken from\n" +" standard input.\n\n" +" Try \"qrencode --help\" for more options.\n" + ); + } + } +} + +static int color_set(unsigned char color[4], const char *value) +{ + int len = (int)strlen(value); + int i, count; + unsigned int col[4]; + if(len == 6) { + count = sscanf(value, "%02x%02x%02x%n", &col[0], &col[1], &col[2], &len); + if(count < 3 || len != 6) { + return -1; + } + for(i = 0; i < 3; i++) { + color[i] = (unsigned char)col[i]; + } + color[3] = 255; + } else if(len == 8) { + count = sscanf(value, "%02x%02x%02x%02x%n", &col[0], &col[1], &col[2], &col[3], &len); + if(count < 4 || len != 8) { + return -1; + } + for(i = 0; i < 4; i++) { + color[i] = (unsigned char)col[i]; + } + } else { + return -1; + } + return 0; +} + +#define MAX_DATA_SIZE (7090 * 2) /* timed by the safty factor 2 */ +static unsigned char data_buffer[MAX_DATA_SIZE]; +static unsigned char *readFile(FILE *fp, int *length) +{ + int ret; + + ret = (int)fread(data_buffer, 1, MAX_DATA_SIZE, fp); + if(ret == 0) { + fprintf(stderr, "No input data.\n"); + exit(EXIT_FAILURE); + } + if(feof(fp) == 0) { + fprintf(stderr, "Input data is too large.\n"); + exit(EXIT_FAILURE); + } + + data_buffer[ret] = '\0'; + *length = ret; + + return data_buffer; +} + +static FILE *openFile(const char *outfile) +{ + FILE *fp; + + if(outfile == NULL || (outfile[0] == '-' && outfile[1] == '\0')) { + fp = stdout; + } else { + fp = fopen(outfile, "wb"); + if(fp == NULL) { + fprintf(stderr, "Failed to create file: %s\n", outfile); + perror(NULL); + exit(EXIT_FAILURE); + } + } + + return fp; +} + +#if HAVE_PNG +static void fillRow(unsigned char *row, int num, const unsigned char color[]) +{ + int i; + + for(i = 0; i < num; i++) { + memcpy(row, color, 4); + row += 4; + } +} +#endif + +static int writePNG(const QRcode *qrcode, const char *outfile, enum imageType type) +{ +#if HAVE_PNG + static FILE *fp; // avoid clobbering by setjmp. + png_structp png_ptr; + png_infop info_ptr; + png_colorp palette = NULL; + png_byte alpha_values[2]; + unsigned char *row, *p, *q; + int x, y, xx, yy, bit; + int realwidth; + + realwidth = (qrcode->width + margin * 2) * size; + if(type == PNG_TYPE) { + row = (unsigned char *)malloc((size_t)((realwidth + 7) / 8)); + } else if(type == PNG32_TYPE) { + row = (unsigned char *)malloc((size_t)realwidth * 4); + } else { + fprintf(stderr, "Internal error.\n"); + exit(EXIT_FAILURE); + } + if(row == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + + if(outfile[0] == '-' && outfile[1] == '\0') { + fp = stdout; + } else { + fp = fopen(outfile, "wb"); + if(fp == NULL) { + fprintf(stderr, "Failed to create file: %s\n", outfile); + perror(NULL); + exit(EXIT_FAILURE); + } + } + + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if(png_ptr == NULL) { + fprintf(stderr, "Failed to initialize PNG writer.\n"); + exit(EXIT_FAILURE); + } + + info_ptr = png_create_info_struct(png_ptr); + if(info_ptr == NULL) { + fprintf(stderr, "Failed to initialize PNG write.\n"); + exit(EXIT_FAILURE); + } + + if(setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + fprintf(stderr, "Failed to write PNG image.\n"); + exit(EXIT_FAILURE); + } + + if(type == PNG_TYPE) { + palette = (png_colorp) malloc(sizeof(png_color) * 2); + if(palette == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + palette[0].red = fg_color[0]; + palette[0].green = fg_color[1]; + palette[0].blue = fg_color[2]; + palette[1].red = bg_color[0]; + palette[1].green = bg_color[1]; + palette[1].blue = bg_color[2]; + alpha_values[0] = fg_color[3]; + alpha_values[1] = bg_color[3]; + png_set_PLTE(png_ptr, info_ptr, palette, 2); + png_set_tRNS(png_ptr, info_ptr, alpha_values, 2, NULL); + } + + png_init_io(png_ptr, fp); + if(type == PNG_TYPE) { + png_set_IHDR(png_ptr, info_ptr, + (unsigned int)realwidth, (unsigned int)realwidth, + 1, + PNG_COLOR_TYPE_PALETTE, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + } else { + png_set_IHDR(png_ptr, info_ptr, + (unsigned int)realwidth, (unsigned int)realwidth, + 8, + PNG_COLOR_TYPE_RGB_ALPHA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + } + png_set_pHYs(png_ptr, info_ptr, + (png_uint_32)(dpi * INCHES_PER_METER), + (png_uint_32)(dpi * INCHES_PER_METER), + PNG_RESOLUTION_METER); + png_write_info(png_ptr, info_ptr); + + if(type == PNG_TYPE) { + /* top margin */ + memset(row, 0xff, (size_t)((realwidth + 7) / 8)); + for(y = 0; y < margin * size; y++) { + png_write_row(png_ptr, row); + } + + /* data */ + p = qrcode->data; + for(y = 0; y < qrcode->width; y++) { + memset(row, 0xff, (size_t)((realwidth + 7) / 8)); + q = row; + q += margin * size / 8; + bit = 7 - (margin * size % 8); + for(x = 0; x < qrcode->width; x++) { + for(xx = 0; xx < size; xx++) { + *q ^= (*p & 1) << bit; + bit--; + if(bit < 0) { + q++; + bit = 7; + } + } + p++; + } + for(yy = 0; yy < size; yy++) { + png_write_row(png_ptr, row); + } + } + /* bottom margin */ + memset(row, 0xff, (size_t)((realwidth + 7) / 8)); + for(y = 0; y < margin * size; y++) { + png_write_row(png_ptr, row); + } + } else { + /* top margin */ + fillRow(row, realwidth, bg_color); + for(y = 0; y < margin * size; y++) { + png_write_row(png_ptr, row); + } + + /* data */ + p = qrcode->data; + for(y = 0; y < qrcode->width; y++) { + fillRow(row, realwidth, bg_color); + for(x = 0; x < qrcode->width; x++) { + for(xx = 0; xx < size; xx++) { + if(*p & 1) { + memcpy(&row[((margin + x) * size + xx) * 4], fg_color, 4); + } + } + p++; + } + for(yy = 0; yy < size; yy++) { + png_write_row(png_ptr, row); + } + } + /* bottom margin */ + fillRow(row, realwidth, bg_color); + for(y = 0; y < margin * size; y++) { + png_write_row(png_ptr, row); + } + } + + png_write_end(png_ptr, info_ptr); + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(fp); + free(row); + free(palette); + + return 0; +#else + fputs("PNG output is disabled at compile time. No output generated.\n", stderr); + return 0; +#endif +} + +static int writeEPS(const QRcode *qrcode, const char *outfile) +{ + FILE *fp; + unsigned char *row, *p; + int x, y, yy; + int realwidth; + + fp = openFile(outfile); + + realwidth = (qrcode->width + margin * 2) * size; + /* EPS file header */ + fprintf(fp, "%%!PS-Adobe-2.0 EPSF-1.2\n" + "%%%%BoundingBox: 0 0 %d %d\n" + "%%%%Pages: 1 1\n" + "%%%%EndComments\n", realwidth, realwidth); + /* draw point */ + fprintf(fp, "/p { " + "moveto " + "0 1 rlineto " + "1 0 rlineto " + "0 -1 rlineto " + "fill " + "} bind def\n"); + /* set color */ + fprintf(fp, "gsave\n"); + fprintf(fp, "%f %f %f setrgbcolor\n", + (double)bg_color[0] / 255, + (double)bg_color[1] / 255, + (double)bg_color[2] / 255); + fprintf(fp, "%d %d scale\n", realwidth, realwidth); + fprintf(fp, "0 0 p\ngrestore\n"); + fprintf(fp, "%f %f %f setrgbcolor\n", + (double)fg_color[0] / 255, + (double)fg_color[1] / 255, + (double)fg_color[2] / 255); + fprintf(fp, "%d %d scale\n", size, size); + + /* data */ + p = qrcode->data; + for(y = 0; y < qrcode->width; y++) { + row = (p+(y*qrcode->width)); + yy = (margin + qrcode->width - y - 1); + + for(x = 0; x < qrcode->width; x++) { + if(*(row+x)&0x1) { + fprintf(fp, "%d %d p ", margin + x, yy); + } + } + } + + fprintf(fp, "\n%%%%EOF\n"); + fclose(fp); + + return 0; +} + +static void writeSVG_drawModules(FILE *fp, int x, int y, int width, const char* col, double opacity) +{ + if(svg_path) { + fprintf(fp, "M%d,%dh%d", x, y, width); + } else { + if(fg_color[3] != 255) { + fprintf(fp, "\t\t\t<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"1\" "\ + "fill=\"#%s\" fill-opacity=\"%f\"/>\n", + x, y, width, col, opacity ); + } else { + fprintf(fp, "\t\t\t<rect x=\"%d\" y=\"%d\" width=\"%d\" height=\"1\" "\ + "fill=\"#%s\"/>\n", + x, y, width, col ); + } + } +} + +static int writeSVG(const QRcode *qrcode, const char *outfile) +{ + FILE *fp; + unsigned char *row, *p; + int x, y, x0, pen; + int symwidth, realwidth; + double scale; + char fg[7], bg[7]; + double fg_opacity; + double bg_opacity; + + fp = openFile(outfile); + + scale = dpi * INCHES_PER_METER / 100.0; + + symwidth = qrcode->width + margin * 2; + realwidth = symwidth * size; + + snprintf(fg, 7, "%02x%02x%02x", fg_color[0], fg_color[1], fg_color[2]); + snprintf(bg, 7, "%02x%02x%02x", bg_color[0], bg_color[1], bg_color[2]); + fg_opacity = (double)fg_color[3] / 255; + bg_opacity = (double)bg_color[3] / 255; + + /* XML declaration */ + if (!inline_svg) + fputs( "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n", fp ); + + /* DTD + No document type specified because "while a DTD is provided in [the SVG] + specification, the use of DTDs for validating XML documents is known to + be problematic. In particular, DTDs do not handle namespaces gracefully. + It is *not* recommended that a DOCTYPE declaration be included in SVG + documents." + https://www.w3.org/TR/2003/REC-SVG11-20030114/intro.html#Namespace + */ + + /* Vanity remark */ + fprintf(fp, "<!-- Created with qrencode %s (https://fukuchi.org/works/qrencode/index.html) -->\n", QRcode_APIVersionString()); + + /* SVG code start */ + fprintf(fp, + "<svg width=\"%.2fcm\" height=\"%.2fcm\" viewBox=\"0 0 %d %d\""\ + " preserveAspectRatio=\"none\" version=\"1.1\""\ + " xmlns=\"http://www.w3.org/2000/svg\">\n", + (double)realwidth / scale, (double)realwidth / scale, symwidth, symwidth + ); + + /* Make named group */ + fputs("\t<g id=\"QRcode\">\n", fp); + + /* Make solid background */ + if(bg_color[3] != 255) { + fprintf(fp, "\t\t<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\" fill-opacity=\"%f\"/>\n", symwidth, symwidth, bg, bg_opacity); + } else { + fprintf(fp, "\t\t<rect x=\"0\" y=\"0\" width=\"%d\" height=\"%d\" fill=\"#%s\"/>\n", symwidth, symwidth, bg); + } + + if(svg_path) { + if(fg_color[3] != 255) { + fprintf(fp, "\t\t<path style=\"stroke:#%s;stroke-opacity:%f\" transform=\"translate(%d,%d.5)\" d=\"", fg, fg_opacity, margin, margin); + } else { + fprintf(fp, "\t\t<path style=\"stroke:#%s\" transform=\"translate(%d,%d.5)\" d=\"", fg, margin, margin); + } + } else { + /* Create new viewbox for QR data */ + fprintf(fp, "\t\t<g id=\"Pattern\" transform=\"translate(%d,%d)\">\n", margin, margin); + } + + /* Write data */ + p = qrcode->data; + for(y = 0; y < qrcode->width; y++) { + row = (p+(y*qrcode->width)); + + if( !rle ) { + /* no RLE */ + for(x = 0; x < qrcode->width; x++) { + if(*(row+x)&0x1) { + writeSVG_drawModules(fp, x, y, 1, fg, fg_opacity); + } + } + } else { + /* simple RLE */ + pen = 0; + x0 = 0; + for(x = 0; x < qrcode->width; x++) { + if( !pen ) { + pen = *(row+x)&0x1; + x0 = x; + } else if(!(*(row+x)&0x1)) { + writeSVG_drawModules(fp, x0, y, x-x0, fg, fg_opacity); + pen = 0; + } + } + if( pen ) { + writeSVG_drawModules(fp, x0, y, qrcode->width - x0, fg, fg_opacity); + } + } + } + + if(svg_path) { + fputs("\"/>\n", fp); + } else { + /* Close QR data viewbox */ + fputs("\t\t</g>\n", fp); + } + + /* Close group */ + fputs("\t</g>\n", fp); + + /* Close SVG code */ + fputs("</svg>\n", fp); + fclose(fp); + + return 0; +} + +static int writeXPM(const QRcode *qrcode, const char *outfile) +{ + FILE *fp; + int x, xx, y, yy, realwidth, realmargin; + char *row; + char fg[7], bg[7]; + unsigned char *p; + + fp = openFile(outfile); + + realwidth = (qrcode->width + margin * 2) * size; + realmargin = margin * size; + + row = malloc((size_t)realwidth + 1); + if (!row ) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + + snprintf(fg, 7, "%02x%02x%02x", fg_color[0], fg_color[1], fg_color[2]); + snprintf(bg, 7, "%02x%02x%02x", bg_color[0], bg_color[1], bg_color[2]); + + fputs("/* XPM */\n", fp); + fputs("static const char *const qrcode_xpm[] = {\n", fp); + fputs("/* width height ncolors chars_per_pixel */\n", fp); + fprintf(fp, "\"%d %d 2 1\",\n", realwidth, realwidth); + + fputs("/* colors */\n", fp); + fprintf(fp, "\"F c #%s\",\n", fg); + fprintf(fp, "\"B c #%s\",\n", bg); + + fputs("/* pixels */\n", fp); + memset(row, 'B', (size_t)realwidth); + row[realwidth] = '\0'; + + for (y = 0; y < realmargin; y++) { + fprintf(fp, "\"%s\",\n", row); + } + + p = qrcode->data; + for (y = 0; y < qrcode->width; y++) { + for (yy = 0; yy < size; yy++) { + fputs("\"", fp); + + for (x = 0; x < margin; x++) { + for (xx = 0; xx < size; xx++) { + fputs("B", fp); + } + } + + for (x = 0; x < qrcode->width; x++) { + for (xx = 0; xx < size; xx++) { + if (p[(y * qrcode->width) + x] & 0x1) { + fputs("F", fp); + } else { + fputs("B", fp); + } + } + } + + for (x = 0; x < margin; x++) { + for (xx = 0; xx < size; xx++) { + fputs("B", fp); + } + } + + fputs("\",\n", fp); + } + } + + for (y = 0; y < realmargin; y++) { + fprintf(fp, "\"%s\",\n", row); + } + fputs("};\n", fp); + + free(row); + fclose(fp); + + return 0; +} + +static void writeANSI_margin(FILE* fp, int realwidth, + char* buffer, const char* white, int white_s ) +{ + int y; + + strncpy(buffer, white, (size_t)white_s); + memset(buffer + white_s, ' ', (size_t)realwidth * 2); + strcpy(buffer + white_s + realwidth * 2, "\033[0m\n"); // reset to default colors + for(y = 0; y < margin; y++ ){ + fputs(buffer, fp); + } +} + +static int writeANSI(const QRcode *qrcode, const char *outfile) +{ + FILE *fp; + unsigned char *row, *p; + int x, y; + int realwidth; + int last; + + const char *white, *black; + char *buffer; + int white_s, black_s, buffer_s; + + if(image_type == ANSI256_TYPE){ + /* codes for 256 color compatible terminals */ + white = "\033[48;5;231m"; + white_s = 11; + black = "\033[48;5;16m"; + black_s = 10; + } else { + white = "\033[47m"; + white_s = 5; + black = "\033[40m"; + black_s = 5; + } + + size = 1; + + fp = openFile(outfile); + + realwidth = (qrcode->width + margin * 2) * size; + buffer_s = (realwidth * white_s) * 2; + buffer = (char *)malloc((size_t)buffer_s); + if(buffer == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + + /* top margin */ + writeANSI_margin(fp, realwidth, buffer, white, white_s); + + /* data */ + p = qrcode->data; + for(y = 0; y < qrcode->width; y++) { + row = (p+(y*qrcode->width)); + + memset(buffer, 0, (size_t)buffer_s); + strncpy(buffer, white, (size_t)white_s); + for(x = 0; x < margin; x++ ){ + strncat(buffer, " ", 2); + } + last = 0; + + for(x = 0; x < qrcode->width; x++) { + if(*(row+x)&0x1) { + if( last != 1 ){ + strncat(buffer, black, (size_t)black_s); + last = 1; + } + } else if( last != 0 ){ + strncat(buffer, white, (size_t)white_s); + last = 0; + } + strncat(buffer, " ", 2); + } + + if( last != 0 ){ + strncat(buffer, white, (size_t)white_s); + } + for(x = 0; x < margin; x++ ){ + strncat(buffer, " ", 2); + } + strncat(buffer, "\033[0m\n", 5); + fputs(buffer, fp); + } + + /* bottom margin */ + writeANSI_margin(fp, realwidth, buffer, white, white_s); + + fclose(fp); + free(buffer); + + return 0; +} + +static void writeUTF8_margin(FILE* fp, int realwidth, const char* white, + const char *reset, const char* full) +{ + int x, y; + + for (y = 0; y < margin/2; y++) { + fputs(white, fp); + for (x = 0; x < realwidth; x++) + fputs(full, fp); + fputs(reset, fp); + fputc('\n', fp); + } +} + +static int writeUTF8(const QRcode *qrcode, const char *outfile, int use_ansi, int invert) +{ + FILE *fp; + int x, y; + int realwidth; + const char *white, *reset; + const char *empty, *lowhalf, *uphalf, *full; + + empty = " "; + lowhalf = "\342\226\204"; + uphalf = "\342\226\200"; + full = "\342\226\210"; + + if (invert) { + const char *tmp; + + tmp = empty; + empty = full; + full = tmp; + + tmp = lowhalf; + lowhalf = uphalf; + uphalf = tmp; + } + + if (use_ansi){ + if (use_ansi == 2) { + white = "\033[38;5;231m\033[48;5;16m"; + } else { + white = "\033[40;37;1m"; + } + reset = "\033[0m"; + } else { + white = ""; + reset = ""; + } + + fp = openFile(outfile); + + realwidth = (qrcode->width + margin * 2); + + /* top margin */ + writeUTF8_margin(fp, realwidth, white, reset, full); + + /* data */ + for(y = 0; y < qrcode->width; y += 2) { + unsigned char *row1, *row2; + row1 = qrcode->data + y*qrcode->width; + row2 = row1 + qrcode->width; + + fputs(white, fp); + + for (x = 0; x < margin; x++) { + fputs(full, fp); + } + + for (x = 0; x < qrcode->width; x++) { + if(row1[x] & 1) { + if(y < qrcode->width - 1 && row2[x] & 1) { + fputs(empty, fp); + } else { + fputs(lowhalf, fp); + } + } else if(y < qrcode->width - 1 && row2[x] & 1) { + fputs(uphalf, fp); + } else { + fputs(full, fp); + } + } + + for (x = 0; x < margin; x++) + fputs(full, fp); + + fputs(reset, fp); + fputc('\n', fp); + } + + /* bottom margin */ + writeUTF8_margin(fp, realwidth, white, reset, full); + + fclose(fp); + + return 0; +} + +static void writeASCII_margin(FILE* fp, int realwidth, char* buffer, int invert) +{ + int y, h; + + h = margin; + + memset(buffer, (invert?'#':' '), (size_t)realwidth); + buffer[realwidth] = '\n'; + buffer[realwidth + 1] = '\0'; + for(y = 0; y < h; y++ ){ + fputs(buffer, fp); + } +} + +static int writeASCII(const QRcode *qrcode, const char *outfile, int invert) +{ + FILE *fp; + unsigned char *row; + int x, y; + int realwidth; + char *buffer, *p; + int buffer_s; + char black = '#'; + char white = ' '; + + if(invert) { + black = ' '; + white = '#'; + } + + size = 1; + + fp = openFile(outfile); + + realwidth = (qrcode->width + margin * 2) * 2; + buffer_s = realwidth + 2; + buffer = (char *)malloc((size_t)buffer_s); + if(buffer == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + + /* top margin */ + writeASCII_margin(fp, realwidth, buffer, invert); + + /* data */ + for(y = 0; y < qrcode->width; y++) { + row = qrcode->data+(y*qrcode->width); + p = buffer; + + memset(p, white, (size_t)margin * 2); + p += margin * 2; + + for(x = 0; x < qrcode->width; x++) { + if(row[x]&0x1) { + *p++ = black; + *p++ = black; + } else { + *p++ = white; + *p++ = white; + } + } + + memset(p, white, (size_t)margin * 2); + p += margin * 2; + *p++ = '\n'; + *p++ = '\0'; + fputs( buffer, fp ); + } + + /* bottom margin */ + writeASCII_margin(fp, realwidth, buffer, invert); + + fclose(fp); + free(buffer); + + return 0; +} + +static QRcode *encode(const unsigned char *intext, int length) +{ + QRcode *code; + + if(micro) { + if(eightbit) { + code = QRcode_encodeDataMQR(length, intext, version, level); + } else { + code = QRcode_encodeStringMQR((const char *)intext, version, level, hint, casesensitive); + } + } else if(eightbit) { + code = QRcode_encodeData(length, intext, version, level); + } else { + code = QRcode_encodeString((const char *)intext, version, level, hint, casesensitive); + } + + return code; +} + +static void qrencode(const unsigned char *intext, int length, const char *outfile) +{ + QRcode *qrcode; + + qrcode = encode(intext, length); + if(qrcode == NULL) { + if(errno == ERANGE) { + fprintf(stderr, "Failed to encode the input data: Input data too large\n"); + } else { + perror("Failed to encode the input data"); + } + exit(EXIT_FAILURE); + } + if(strict_versioning && version > 0 && qrcode->version != version) { + fprintf(stderr, "Failed to encode the input data: Input data too large\n"); + exit(EXIT_FAILURE); + } + + if(verbose) { + fprintf(stderr, "File: %s, Version: %d\n", (outfile!=NULL)?outfile:"(stdout)", qrcode->version); + } + + switch(image_type) { + case PNG_TYPE: + case PNG32_TYPE: + writePNG(qrcode, outfile, image_type); + break; + case EPS_TYPE: + writeEPS(qrcode, outfile); + break; + case SVG_TYPE: + writeSVG(qrcode, outfile); + break; + case XPM_TYPE: + writeXPM(qrcode, outfile); + break; + case ANSI_TYPE: + case ANSI256_TYPE: + writeANSI(qrcode, outfile); + break; + case ASCII_TYPE: + writeASCII(qrcode, outfile, 1); + break; + case ASCIIi_TYPE: + writeASCII(qrcode, outfile, 0); + break; + case UTF8_TYPE: + writeUTF8(qrcode, outfile, 0, 0); + break; + case ANSIUTF8_TYPE: + writeUTF8(qrcode, outfile, 1, 0); + break; + case ANSI256UTF8_TYPE: + writeUTF8(qrcode, outfile, 2, 0); + break; + case UTF8i_TYPE: + writeUTF8(qrcode, outfile, 0, 1); + break; + case ANSIUTF8i_TYPE: + writeUTF8(qrcode, outfile, 1, 1); + break; + } + + QRcode_free(qrcode); +} + +static QRcode_List *encodeStructured(const unsigned char *intext, int length) +{ + QRcode_List *list; + + if(eightbit) { + list = QRcode_encodeDataStructured(length, intext, version, level); + } else { + list = QRcode_encodeStringStructured((const char *)intext, version, level, hint, casesensitive); + } + + return list; +} + +static void qrencodeStructured(const unsigned char *intext, int length, const char *outfile) +{ + QRcode_List *qrlist, *p; + char filename[FILENAME_MAX]; + char *base, *q, *suffix = NULL; + const char *type_suffix; + int i = 1; + size_t suffix_size; + + switch(image_type) { + case PNG_TYPE: + case PNG32_TYPE: + type_suffix = ".png"; + break; + case EPS_TYPE: + type_suffix = ".eps"; + break; + case SVG_TYPE: + type_suffix = ".svg"; + break; + case XPM_TYPE: + type_suffix = ".xpm"; + break; + case ANSI_TYPE: + case ANSI256_TYPE: + case ANSI256UTF8_TYPE: + case ASCII_TYPE: + case ASCIIi_TYPE: + case UTF8_TYPE: + case ANSIUTF8_TYPE: + case UTF8i_TYPE: + case ANSIUTF8i_TYPE: + type_suffix = ".txt"; + break; + } + + if(outfile == NULL) { + fprintf(stderr, "An output filename must be specified to store the structured images.\n"); + exit(EXIT_FAILURE); + } + base = strdup(outfile); + if(base == NULL) { + fprintf(stderr, "Failed to allocate memory.\n"); + exit(EXIT_FAILURE); + } + suffix_size = strlen(type_suffix); + if(strlen(base) > suffix_size) { + q = base + strlen(base) - suffix_size; + if(strcasecmp(type_suffix, q) == 0) { + suffix = strdup(q); + *q = '\0'; + } + } + + qrlist = encodeStructured(intext, length); + if(qrlist == NULL) { + if(errno == ERANGE) { + fprintf(stderr, "Failed to encode the input data: Input data too large\n"); + } else { + perror("Failed to encode the input data"); + } + exit(EXIT_FAILURE); + } + + for(p = qrlist; p != NULL; p = p->next) { + if(p->code == NULL) { + fprintf(stderr, "Failed to encode the input data.\n"); + exit(EXIT_FAILURE); + } + if(suffix) { + snprintf(filename, FILENAME_MAX, "%s-%02d%s", base, i, suffix); + } else { + snprintf(filename, FILENAME_MAX, "%s-%02d", base, i); + } + + if(verbose) { + fprintf(stderr, "File: %s, Version: %d\n", filename, p->code->version); + } + + switch(image_type) { + case PNG_TYPE: + case PNG32_TYPE: + writePNG(p->code, filename, image_type); + break; + case EPS_TYPE: + writeEPS(p->code, filename); + break; + case SVG_TYPE: + writeSVG(p->code, filename); + break; + case XPM_TYPE: + writeXPM(p->code, filename); + break; + case ANSI_TYPE: + case ANSI256_TYPE: + writeANSI(p->code, filename); + break; + case ASCII_TYPE: + writeASCII(p->code, filename, 1); + break; + case ASCIIi_TYPE: + writeASCII(p->code, filename, 0); + break; + case UTF8_TYPE: + writeUTF8(p->code, filename, 0, 0); + break; + case ANSIUTF8_TYPE: + writeUTF8(p->code, filename, 1, 0); + break; + case ANSI256UTF8_TYPE: + writeUTF8(p->code, filename, 2, 0); + break; + case UTF8i_TYPE: + writeUTF8(p->code, filename, 0, 1); + break; + case ANSIUTF8i_TYPE: + writeUTF8(p->code, filename, 1, 1); + break; + } + i++; + } + + free(base); + if(suffix) { + free(suffix); + } + + QRcode_List_free(qrlist); +} + +int main(int argc, char **argv) +{ + int opt, lindex = -1; + char *outfile = NULL, *infile = NULL; + unsigned char *intext = NULL; + int length = 0; + FILE *fp; + + while((opt = getopt_long(argc, argv, optstring, options, &lindex)) != -1) { + switch(opt) { + case 'h': + if(lindex == 0) { + usage(1, 1, EXIT_SUCCESS); + } else { + usage(1, 0, EXIT_SUCCESS); + } + exit(EXIT_SUCCESS); + case 'o': + outfile = optarg; + break; + case 'r': + infile = optarg; + break; + case 's': + size = atoi(optarg); + if(size <= 0) { + fprintf(stderr, "Invalid size: %d\n", size); + exit(EXIT_FAILURE); + } + break; + case 'v': + version = atoi(optarg); + if(version < 0) { + fprintf(stderr, "Invalid version: %d\n", version); + exit(EXIT_FAILURE); + } + break; + case 'l': + switch(*optarg) { + case 'l': + case 'L': + level = QR_ECLEVEL_L; + break; + case 'm': + case 'M': + level = QR_ECLEVEL_M; + break; + case 'q': + case 'Q': + level = QR_ECLEVEL_Q; + break; + case 'h': + case 'H': + level = QR_ECLEVEL_H; + break; + default: + fprintf(stderr, "Invalid level: %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'm': + margin = atoi(optarg); + if(margin < 0) { + fprintf(stderr, "Invalid margin: %d\n", margin); + exit(EXIT_FAILURE); + } + break; + case 'd': + dpi = atoi(optarg); + if( dpi < 0 ) { + fprintf(stderr, "Invalid DPI: %d\n", dpi); + exit(EXIT_FAILURE); + } + break; + case 't': + if(strcasecmp(optarg, "png32") == 0) { + image_type = PNG32_TYPE; + } else if(strcasecmp(optarg, "png") == 0) { + image_type = PNG_TYPE; + } else if(strcasecmp(optarg, "eps") == 0) { + image_type = EPS_TYPE; + } else if(strcasecmp(optarg, "svg") == 0) { + image_type = SVG_TYPE; + } else if(strcasecmp(optarg, "xpm") == 0) { + image_type = XPM_TYPE; + } else if(strcasecmp(optarg, "ansi") == 0) { + image_type = ANSI_TYPE; + } else if(strcasecmp(optarg, "ansi256") == 0) { + image_type = ANSI256_TYPE; + } else if(strcasecmp(optarg, "asciii") == 0) { + image_type = ASCIIi_TYPE; + } else if(strcasecmp(optarg, "ascii") == 0) { + image_type = ASCII_TYPE; + } else if(strcasecmp(optarg, "utf8") == 0) { + image_type = UTF8_TYPE; + } else if(strcasecmp(optarg, "ansiutf8") == 0) { + image_type = ANSIUTF8_TYPE; + } else if(strcasecmp(optarg, "ansi256utf8") == 0) { + image_type = ANSI256UTF8_TYPE; + } else if(strcasecmp(optarg, "utf8i") == 0) { + image_type = UTF8i_TYPE; + } else if(strcasecmp(optarg, "ansiutf8i") == 0) { + image_type = ANSIUTF8i_TYPE; + } else { + fprintf(stderr, "Invalid image type: %s\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'S': + structured = 1; + break; + case 'k': + hint = QR_MODE_KANJI; + break; + case 'c': + casesensitive = 1; + break; + case 'i': + casesensitive = 0; + break; + case '8': + eightbit = 1; + break; + case 'M': + micro = 1; + break; + case 'f': + if(color_set(fg_color, optarg)) { + fprintf(stderr, "Invalid foreground color value.\n"); + exit(EXIT_FAILURE); + } + break; + case 'b': + if(color_set(bg_color, optarg)) { + fprintf(stderr, "Invalid background color value.\n"); + exit(EXIT_FAILURE); + } + break; + case 'V': + usage(0, 0, EXIT_SUCCESS); + exit(EXIT_SUCCESS); + case 0: + break; + default: + fprintf(stderr, "Try \"qrencode --help\" for more information.\n"); + exit(EXIT_FAILURE); + } + } + + if(argc == 1) { + usage(1, 0, EXIT_FAILURE); + exit(EXIT_FAILURE); + } + + if(outfile == NULL && image_type == PNG_TYPE) { + fprintf(stderr, "No output filename is given.\n"); + exit(EXIT_FAILURE); + } + + if(optind < argc) { + intext = (unsigned char *)argv[optind]; + length = (int)strlen((char *)intext); + } + if(intext == NULL) { + fp = infile == NULL ? stdin : fopen(infile, "r"); + if(fp == 0) { + fprintf(stderr, "Cannot read input file %s.\n", infile); + exit(EXIT_FAILURE); + } + intext = readFile(fp, &length); + + } + + if(micro && version > MQRSPEC_VERSION_MAX) { + fprintf(stderr, "Version number should be less or equal to %d.\n", MQRSPEC_VERSION_MAX); + exit(EXIT_FAILURE); + } else if(!micro && version > QRSPEC_VERSION_MAX) { + fprintf(stderr, "Version number should be less or equal to %d.\n", QRSPEC_VERSION_MAX); + exit(EXIT_FAILURE); + } + + if(margin < 0) { + if(micro) { + margin = 2; + } else { + margin = 4; + } + } + + if(micro) { + if(structured) { + fprintf(stderr, "Micro QR Code does not support structured symbols.\n"); + exit(EXIT_FAILURE); + } + } + + if(structured) { + if(version == 0) { + fprintf(stderr, "Version number must be specified to encode structured symbols.\n"); + exit(EXIT_FAILURE); + } + qrencodeStructured(intext, length, outfile); + } else { + qrencode(intext, length, outfile); + } + + return 0; +} |
