1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "qrcode-util.h"
4
5 #if HAVE_QRENCODE
6 #include <qrencode.h>
7
8 #include "dlfcn-util.h"
9 #include "locale-util.h"
10 #include "log.h"
11 #include "terminal-util.h"
12
13 #define ANSI_WHITE_ON_BLACK "\033[40;37;1m"
14 #define UNICODE_FULL_BLOCK u8"█"
15 #define UNICODE_LOWER_HALF_BLOCK u8"▄"
16 #define UNICODE_UPPER_HALF_BLOCK u8"▀"
17
18 static void *qrcode_dl = NULL;
19
20 static QRcode* (*sym_QRcode_encodeString)(const char *string, int version, QRecLevel level, QRencodeMode hint, int casesensitive) = NULL;
21 static void (*sym_QRcode_free)(QRcode *qrcode) = NULL;
22
dlopen_qrencode(void)23 int dlopen_qrencode(void) {
24 return dlopen_many_sym_or_warn(
25 &qrcode_dl, "libqrencode.so.4", LOG_DEBUG,
26 DLSYM_ARG(QRcode_encodeString),
27 DLSYM_ARG(QRcode_free));
28 }
29
print_border(FILE * output,unsigned width)30 static void print_border(FILE *output, unsigned width) {
31 /* Four rows of border */
32 for (unsigned y = 0; y < 4; y += 2) {
33 fputs(ANSI_WHITE_ON_BLACK, output);
34
35 for (unsigned x = 0; x < 4 + width + 4; x++)
36 fputs(UNICODE_FULL_BLOCK, output);
37
38 fputs(ANSI_NORMAL "\n", output);
39 }
40 }
41
write_qrcode(FILE * output,QRcode * qr)42 static void write_qrcode(FILE *output, QRcode *qr) {
43 assert(qr);
44
45 if (!output)
46 output = stdout;
47
48 print_border(output, qr->width);
49
50 for (unsigned y = 0; y < (unsigned) qr->width; y += 2) {
51 const uint8_t *row1 = qr->data + qr->width * y;
52 const uint8_t *row2 = row1 + qr->width;
53
54 fputs(ANSI_WHITE_ON_BLACK, output);
55 for (unsigned x = 0; x < 4; x++)
56 fputs(UNICODE_FULL_BLOCK, output);
57
58 for (unsigned x = 0; x < (unsigned) qr->width; x++) {
59 bool a, b;
60
61 a = row1[x] & 1;
62 b = (y+1) < (unsigned) qr->width ? (row2[x] & 1) : false;
63
64 if (a && b)
65 fputc(' ', output);
66 else if (a)
67 fputs(UNICODE_LOWER_HALF_BLOCK, output);
68 else if (b)
69 fputs(UNICODE_UPPER_HALF_BLOCK, output);
70 else
71 fputs(UNICODE_FULL_BLOCK, output);
72 }
73
74 for (unsigned x = 0; x < 4; x++)
75 fputs(UNICODE_FULL_BLOCK, output);
76 fputs(ANSI_NORMAL "\n", output);
77 }
78
79 print_border(output, qr->width);
80 fflush(output);
81 }
82
print_qrcode(FILE * out,const char * header,const char * string)83 int print_qrcode(FILE *out, const char *header, const char *string) {
84 QRcode* qr;
85 int r;
86
87 /* If this is not an UTF-8 system or ANSI colors aren't supported/disabled don't print any QR
88 * codes */
89 if (!is_locale_utf8() || !colors_enabled())
90 return -EOPNOTSUPP;
91
92 r = dlopen_qrencode();
93 if (r < 0)
94 return r;
95
96 qr = sym_QRcode_encodeString(string, 0, QR_ECLEVEL_L, QR_MODE_8, 1);
97 if (!qr)
98 return -ENOMEM;
99
100 if (header)
101 fprintf(out, "\n%s:\n\n", header);
102
103 write_qrcode(out, qr);
104
105 fputc('\n', out);
106
107 sym_QRcode_free(qr);
108 return 0;
109 }
110 #endif
111