/** * qrencode - QR Code encoder * * QR Code encoding tool * Copyright (C) 2006-2011 Kentaro Fukuchi * * 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 #include #include #include #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 micro = 0; static QRecLevel level = QR_ECLEVEL_L; static QRencodeMode hint = QR_MODE_8; static enum imageType { PNG_TYPE, EPS_TYPE }; static enum imageType image_type = PNG_TYPE; static const struct option options[] = { {"help" , no_argument , NULL, 'h'}, {"output" , required_argument, NULL, 'o'}, {"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'}, {"version" , no_argument , NULL, 'V'}, {NULL, 0, NULL, 0} }; static char *optstring = "ho:l:s:v:m:d:t:Skci8MV"; static void usage(int help, int longopt) { fprintf(stderr, "qrencode version %s\n" "Copyright (C) 2006-2011 Kentaro Fukuchi\n", VERSION); if(help) { if(longopt) { fprintf(stderr, "Usage: qrencode [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" " -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 version of the symbol. (default=auto)\n\n" " -m NUMBER, --margin=NUMBER\n" " specify the width of the margins. (default=4 (2 for Micro)))\n\n" " -d NUMBER, --dpi=NUMBER\n" " specify the DPI of the generated PNG. (default=72)\n\n" " -t {PNG,EPS}, --type={PNG,EPS}\n" " specify the type of the generated image. (default=PNG)\n\n" " -S, --structured\n" " make structured symbols. Version must be specified.\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. (experimental)\n\n" " -V, --version\n" " display the version number and copyrights of the qrencode.\n\n" " [STRING] input data. If it is not specified, data will be taken from\n" " standard input.\n" ); } else { fprintf(stderr, "Usage: qrencode [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" " -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 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,EPS} specify the type of the generated image. (default=PNG)\n" " -S make structured symbols. Version must be specified.\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" ); } } } #define MAX_DATA_SIZE (7090 * 16) /* from the specification */ static unsigned char *readStdin(int *length) { unsigned char *buffer; int ret; buffer = (unsigned char *)malloc(MAX_DATA_SIZE + 1); if(buffer == NULL) { fprintf(stderr, "Memory allocation failed.\n"); exit(EXIT_FAILURE); } ret = fread(buffer, 1, MAX_DATA_SIZE, stdin); if(ret == 0) { fprintf(stderr, "No input data.\n"); exit(EXIT_FAILURE); } if(feof(stdin) == 0) { fprintf(stderr, "Input data is too large.\n"); exit(EXIT_FAILURE); } buffer[ret] = '\0'; *length = ret; return buffer; } static int writePNG(QRcode *qrcode, const char *outfile) { static FILE *fp; // avoid clobbering by setjmp. png_structp png_ptr; png_infop info_ptr; unsigned char *row, *p, *q; int x, y, xx, yy, bit; int realwidth; realwidth = (qrcode->width + margin * 2) * size; row = (unsigned char *)malloc((realwidth + 7) / 8); 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); } png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, realwidth, realwidth, 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_set_pHYs(png_ptr, info_ptr, dpi * INCHES_PER_METER, dpi * INCHES_PER_METER, PNG_RESOLUTION_METER); png_write_info(png_ptr, info_ptr); /* top margin */ memset(row, 0xff, (realwidth + 7) / 8); for(y=0; ydata; for(y=0; ywidth; y++) { bit = 7; memset(row, 0xff, (realwidth + 7) / 8); q = row; q += margin * size / 8; bit = 7 - (margin * size % 8); for(x=0; xwidth; x++) { for(xx=0; xxwidth + 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 " "3 3 scale "); /* data */ p = qrcode->data; for(y=0; ywidth; y++) { row = (p+(y*qrcode->width)); yy = (margin + qrcode->width - y - 1); for(x=0; xwidth; x++) { if(*(row+x)&0x1) { fprintf(fp, "%d %d p ", margin + x, yy); } } } fprintf(fp, "\n%%%%EOF\n"); fclose(fp); 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((char *)intext, version, level, hint, casesensitive); } } else { if(eightbit) { code = QRcode_encodeData(length, intext, version, level); } else { code = QRcode_encodeString((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) { perror("Failed to encode the input data"); exit(EXIT_FAILURE); } switch(image_type) { case PNG_TYPE: writePNG(qrcode, outfile); break; case EPS_TYPE: writeEPS(qrcode, outfile); break; default: fprintf(stderr, "Unknown image type.\n"); exit(EXIT_FAILURE); } 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((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; int suffix_size; switch(image_type) { case PNG_TYPE: type_suffix = ".png"; break; case EPS_TYPE: type_suffix = ".eps"; break; default: fprintf(stderr, "Unknown image type.\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) { 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); } switch(image_type) { case PNG_TYPE: writePNG(p->code, filename); break; case EPS_TYPE: writeEPS(p->code, filename); break; default: fprintf(stderr, "Unknown image type.\n"); exit(EXIT_FAILURE); } i++; } free(base); if(suffix) { free(suffix); } QRcode_List_free(qrlist); } int main(int argc, char **argv) { int opt, lindex = -1; char *outfile = NULL; unsigned char *intext = NULL; int length = 0; while((opt = getopt_long(argc, argv, optstring, options, &lindex)) != -1) { switch(opt) { case 'h': if(lindex == 0) { usage(1, 1); } else { usage(1, 0); } exit(EXIT_SUCCESS); break; case 'o': outfile = 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; } 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, "png") == 0) { image_type = PNG_TYPE; } else if(strcasecmp(optarg, "eps") == 0) { image_type = EPS_TYPE; } else { fprintf(stderr, "Invalid image type: %s\n", optarg); exit(EXIT_FAILURE); } break; case 'S': structured = 1; 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 'V': usage(0, 0); exit(EXIT_SUCCESS); break; default: fprintf(stderr, "Try `qrencode --help' for more information.\n"); exit(EXIT_FAILURE); break; } } if(argc == 1) { usage(1, 0); exit(EXIT_SUCCESS); } if(outfile == NULL) { fprintf(stderr, "No output filename is given.\n"); exit(EXIT_FAILURE); } if(optind < argc) { intext = (unsigned char *)argv[optind]; length = strlen((char *)intext); } if(intext == NULL) { intext = readStdin(&length); } if(micro && version > MQRSPEC_VERSION_MAX) { fprintf(stderr, "Version should be less or equal to %d.\n", MQRSPEC_VERSION_MAX); exit(EXIT_FAILURE); } else if(!micro && version > QRSPEC_VERSION_MAX) { fprintf(stderr, "Version 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(version == 0) { fprintf(stderr, "Version must be specified to encode a Micro QR Code symbol.\n"); exit(EXIT_FAILURE); } if(structured) { fprintf(stderr, "Micro QR Code does not support structured symbols.\n"); exit(EXIT_FAILURE); } } if(structured) { if(version == 0) { fprintf(stderr, "Version must be specified to encode structured symbols.\n"); exit(EXIT_FAILURE); } qrencodeStructured(intext, length, outfile); } else { qrencode(intext, length, outfile); } return 0; }