6f1a1a274e
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
642 lines
16 KiB
C
642 lines
16 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <SDL.h>
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
#include "../config.h"
|
|
#include "../qrspec.h"
|
|
#include "../qrinput.h"
|
|
#include "../split.h"
|
|
#include "../qrencode_inner.h"
|
|
|
|
static SDL_Window *window;
|
|
static SDL_Renderer *renderer;
|
|
static SDL_Texture *texture = NULL;
|
|
static SDL_Surface *surface = NULL;
|
|
static int casesensitive = 1;
|
|
static int eightbit = 0;
|
|
static int version = 0;
|
|
static int size = 4;
|
|
static int margin = -1;
|
|
static int structured = 0;
|
|
static int micro = 0;
|
|
static int colorize = 0;
|
|
static QRecLevel level = QR_ECLEVEL_L;
|
|
static QRencodeMode hint = QR_MODE_8;
|
|
|
|
static char **textv;
|
|
static int textc;
|
|
|
|
static const struct option options[] = {
|
|
{"help" , no_argument , NULL, 'h'},
|
|
{"level" , required_argument, NULL, 'l'},
|
|
{"size" , required_argument, NULL, 's'},
|
|
{"symversion" , required_argument, NULL, 'v'},
|
|
{"margin" , required_argument, NULL, 'm'},
|
|
{"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 = "hl:s:v:m:Skci8MV";
|
|
|
|
static char levelChar[4] = {'L', 'M', 'Q', 'H'};
|
|
static void usage(int help, int longopt)
|
|
{
|
|
fprintf(stderr,
|
|
"view_qrcode version %s\n"
|
|
"Copyright (C) 2008, 2009, 2010 Kentaro Fukuchi\n", VERSION);
|
|
if(help) {
|
|
if(longopt) {
|
|
fprintf(stderr,
|
|
"Usage: view_qrcode [OPTION]... [STRING]\n"
|
|
"Encode input data in a QR Code and display.\n\n"
|
|
" -h, --help display the help message. -h displays only the help of short\n"
|
|
" options.\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 version of the symbol. See SYMBOL VERSIONS for more\n"
|
|
" information. (default=auto)\n\n"
|
|
" -m NUMBER, --margin=NUMBER\n"
|
|
" specify the width of the margins. (default=4 (2 for Micro QR)))\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\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"
|
|
" http://www.qrcode.com/en/about/version.html for a detailed\n"
|
|
" version list.\n"
|
|
);
|
|
} else {
|
|
fprintf(stderr,
|
|
"Usage: view_qrcode [OPTION]... [STRING]\n"
|
|
"Encode input data in a QR Code and display.\n\n"
|
|
" -h display this message.\n"
|
|
" --help display the usage of long options.\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"
|
|
" -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 void draw_QRcode(QRcode *qrcode, int ox, int oy)
|
|
{
|
|
int x, y, width;
|
|
unsigned char *p;
|
|
SDL_Rect rect;
|
|
Uint32 color[8];
|
|
int col;
|
|
|
|
color[0] = SDL_MapRGBA(surface->format, 255, 255, 255, 255);
|
|
color[1] = SDL_MapRGBA(surface->format, 0, 0, 0, 255);
|
|
color[2] = SDL_MapRGBA(surface->format, 192, 192, 255, 255);
|
|
color[3] = SDL_MapRGBA(surface->format, 0, 0, 64, 255);
|
|
color[4] = SDL_MapRGBA(surface->format, 255, 255, 192, 255);
|
|
color[5] = SDL_MapRGBA(surface->format, 64, 64, 0, 255);
|
|
color[6] = SDL_MapRGBA(surface->format, 255, 192, 192, 255);
|
|
color[7] = SDL_MapRGBA(surface->format, 64, 0, 0, 255);
|
|
|
|
ox += margin * size;
|
|
oy += margin * size;
|
|
width = qrcode->width;
|
|
p = qrcode->data;
|
|
for(y=0; y<width; y++) {
|
|
for(x=0; x<width; x++) {
|
|
rect.x = ox + x * size;
|
|
rect.y = oy + y * size;
|
|
rect.w = size;
|
|
rect.h = size;
|
|
if(!colorize) {
|
|
col = 0;
|
|
} else {
|
|
if(*p & 0x80) {
|
|
col = 6;
|
|
} else if(*p & 0x02) {
|
|
col = 4;
|
|
} else {
|
|
col = 2;
|
|
}
|
|
}
|
|
col += (*p & 1);
|
|
SDL_FillRect(surface, &rect, color[col]);
|
|
p++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void draw_singleQRcode(QRinput *stream, int mask)
|
|
{
|
|
QRcode *qrcode;
|
|
int width;
|
|
|
|
QRinput_setVersionAndErrorCorrectionLevel(stream, version, level);
|
|
if(micro) {
|
|
qrcode = QRcode_encodeMaskMQR(stream, mask);
|
|
} else {
|
|
qrcode = QRcode_encodeMask(stream, mask);
|
|
}
|
|
if(qrcode == NULL) {
|
|
width = (11 + margin * 2) * size;
|
|
fprintf(stderr, "Input data does not fit to this setting.\n");
|
|
} else {
|
|
version = qrcode->version;
|
|
width = (qrcode->width + margin * 2) * size;
|
|
}
|
|
|
|
SDL_SetWindowSize(window, width, width);
|
|
if(surface != NULL) {
|
|
SDL_FreeSurface(surface);
|
|
}
|
|
surface = SDL_CreateRGBSurface(0, width, width, 32, 0, 0, 0, 0);
|
|
SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 255, 255, 255, 255));
|
|
if(qrcode) {
|
|
draw_QRcode(qrcode, 0, 0);
|
|
}
|
|
if(texture != NULL) {
|
|
SDL_DestroyTexture(texture);
|
|
}
|
|
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
|
QRcode_free(qrcode);
|
|
}
|
|
|
|
static void draw_structuredQRcode(QRinput_Struct *s)
|
|
{
|
|
int i, w, h, n, x, y;
|
|
int swidth;
|
|
QRcode_List *qrcodes, *p;
|
|
|
|
qrcodes = QRcode_encodeInputStructured(s);
|
|
if(qrcodes == NULL) return;
|
|
|
|
swidth = (qrcodes->code->width + margin * 2) * size;
|
|
n = QRcode_List_size(qrcodes);
|
|
w = (n < 4)?n:4;
|
|
h = (n - 1) / 4 + 1;
|
|
|
|
SDL_SetWindowSize(window, swidth * w, swidth * h);
|
|
if(surface != NULL) {
|
|
SDL_FreeSurface(surface);
|
|
}
|
|
surface = SDL_CreateRGBSurface(0, swidth * w, swidth * h, 32, 0, 0, 0, 0);
|
|
SDL_FillRect(surface, NULL, SDL_MapRGBA(surface->format, 255, 255, 255, 255));
|
|
|
|
p = qrcodes;
|
|
for(i=0; i<n; i++) {
|
|
x = (i % 4) * swidth;
|
|
y = (i / 4) * swidth;
|
|
draw_QRcode(p->code, x, y);
|
|
p = p->next;
|
|
}
|
|
if(texture != NULL) {
|
|
SDL_DestroyTexture(texture);
|
|
}
|
|
texture = SDL_CreateTextureFromSurface(renderer, surface);
|
|
QRcode_List_free(qrcodes);
|
|
}
|
|
|
|
static void draw_structuredQRcodeFromText(int argc, char **argv)
|
|
{
|
|
QRinput_Struct *s;
|
|
QRinput *input;
|
|
int i, ret;
|
|
|
|
s = QRinput_Struct_new();
|
|
if(s == NULL) {
|
|
fprintf(stderr, "Failed to allocate memory.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
for(i=0; i<argc; i++) {
|
|
input = QRinput_new2(version, level);
|
|
if(input == NULL) {
|
|
fprintf(stderr, "Failed to allocate memory.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if(eightbit) {
|
|
ret = QRinput_append(input, QR_MODE_8, strlen(argv[i]), (unsigned char *)argv[i]);
|
|
} else {
|
|
ret = Split_splitStringToQRinput(argv[i], input, hint, casesensitive);
|
|
}
|
|
if(ret < 0) {
|
|
perror("Encoding the input string");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
ret = QRinput_Struct_appendInput(s, input);
|
|
if(ret < 0) {
|
|
perror("Encoding the input string");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
ret = QRinput_Struct_insertStructuredAppendHeaders(s);
|
|
if(ret < 0) {
|
|
fprintf(stderr, "Too many inputs.\n");
|
|
}
|
|
|
|
draw_structuredQRcode(s);
|
|
QRinput_Struct_free(s);
|
|
}
|
|
|
|
static void draw_structuredQRcodeFromQRinput(QRinput *stream)
|
|
{
|
|
QRinput_Struct *s;
|
|
|
|
QRinput_setVersion(stream, version);
|
|
QRinput_setErrorCorrectionLevel(stream, level);
|
|
s = QRinput_splitQRinputToStruct(stream);
|
|
if(s != NULL) {
|
|
draw_structuredQRcode(s);
|
|
QRinput_Struct_free(s);
|
|
} else {
|
|
fprintf(stderr, "Input data is too large for this setting.\n");
|
|
}
|
|
}
|
|
|
|
static void view(int mode, QRinput *input)
|
|
{
|
|
int flag = 1;
|
|
int mask = -1;
|
|
SDL_Event event;
|
|
int loop;
|
|
int codeChanged = 1;
|
|
|
|
while(flag) {
|
|
if(codeChanged) {
|
|
if(mode) {
|
|
draw_structuredQRcodeFromText(textc, textv);
|
|
} else {
|
|
if(structured) {
|
|
draw_structuredQRcodeFromQRinput(input);
|
|
} else {
|
|
draw_singleQRcode(input, mask);
|
|
}
|
|
}
|
|
if(mode || structured) {
|
|
printf("Version %d, Level %c.\n", version, levelChar[level]);
|
|
} else {
|
|
printf("Version %d, Level %c, Mask %d.\n", version, levelChar[level], mask);
|
|
}
|
|
}
|
|
SDL_RenderClear(renderer);
|
|
SDL_RenderCopy(renderer, texture, NULL, NULL);
|
|
SDL_RenderPresent(renderer);
|
|
loop = 1;
|
|
codeChanged = 0;
|
|
while(loop) {
|
|
SDL_WaitEvent(&event);
|
|
if(event.type == SDL_KEYDOWN) {
|
|
codeChanged = 1;
|
|
switch(event.key.keysym.sym) {
|
|
case SDLK_RIGHT:
|
|
version++;
|
|
if(version > QRSPEC_VERSION_MAX)
|
|
version = QRSPEC_VERSION_MAX;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_LEFT:
|
|
version--;
|
|
if(version < 1)
|
|
version = 1;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_UP:
|
|
size++;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_DOWN:
|
|
size--;
|
|
if(size < 1) size = 1;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_0:
|
|
case SDLK_1:
|
|
case SDLK_2:
|
|
case SDLK_3:
|
|
case SDLK_4:
|
|
case SDLK_5:
|
|
case SDLK_6:
|
|
case SDLK_7:
|
|
if(!mode && !structured) {
|
|
mask = (event.key.keysym.sym - SDLK_0);
|
|
loop = 0;
|
|
}
|
|
break;
|
|
case SDLK_8:
|
|
if(!mode && !structured) {
|
|
mask = -1;
|
|
loop = 0;
|
|
}
|
|
break;
|
|
case SDLK_9:
|
|
if(!mode && !structured) {
|
|
mask = -2;
|
|
loop = 0;
|
|
}
|
|
break;
|
|
case SDLK_l:
|
|
level = QR_ECLEVEL_L;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_m:
|
|
level = QR_ECLEVEL_M;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_h:
|
|
level = QR_ECLEVEL_H;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_q:
|
|
level = QR_ECLEVEL_Q;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_c:
|
|
colorize ^= 1;
|
|
loop = 0;
|
|
break;
|
|
case SDLK_ESCAPE:
|
|
loop = 0;
|
|
flag = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if(event.type == SDL_QUIT) {
|
|
loop = 0;
|
|
flag = 0;
|
|
}
|
|
}
|
|
if (event.type == SDL_WINDOWEVENT) {
|
|
switch (event.window.event) {
|
|
case SDL_WINDOWEVENT_SHOWN:
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
loop = 0;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void view_simple(const unsigned char *str, int length)
|
|
{
|
|
QRinput *input;
|
|
int ret;
|
|
|
|
if(micro) {
|
|
input = QRinput_newMQR(version, level);
|
|
} else {
|
|
input = QRinput_new2(version, level);
|
|
}
|
|
if(input == NULL) {
|
|
fprintf(stderr, "Memory allocation error.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
if(eightbit) {
|
|
ret = QRinput_append(input, QR_MODE_8, length, str);
|
|
} else {
|
|
ret = Split_splitStringToQRinput((char *)str, input, hint, casesensitive);
|
|
}
|
|
if(ret < 0) {
|
|
perror("Encoding the input string");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
view(0, input);
|
|
|
|
QRinput_free(input);
|
|
}
|
|
|
|
static void view_multiText(char **argv, int argc)
|
|
{
|
|
textc = argc;
|
|
textv = argv;
|
|
|
|
view(1, NULL);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
int opt, lindex = -1;
|
|
unsigned char *intext = NULL;
|
|
int length = 0;
|
|
int ret;
|
|
|
|
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 '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 '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 `view_qrcode --help' for more information.\n");
|
|
exit(EXIT_FAILURE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(argc == 1) {
|
|
usage(1, 0);
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
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 && version == 0) {
|
|
fprintf(stderr, "Version must be specified to encode structured symbols.\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
fprintf(stderr, "Failed initializing SDL: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
ret = SDL_CreateWindowAndRenderer(100, 100, SDL_WINDOW_SHOWN, &window, &renderer);
|
|
if(ret < 0) {
|
|
fprintf(stderr, "Failed to create a window: %s\n", SDL_GetError());
|
|
return -1;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
|
|
|
|
if(structured && (argc - optind > 1)) {
|
|
view_multiText(argv + optind, argc - optind);
|
|
} else {
|
|
view_simple(intext, length);
|
|
}
|
|
|
|
SDL_Quit();
|
|
|
|
return 0;
|
|
}
|