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