I just finished up a project for my dad. He's been doing a lot of writing, and he wanted to put some links to videos hosted on my server into his documents. Given that he likes to print out his documents, I suggested that he use QR codes, since those work on paper. So for the last week, I've spent a lot of time figuring out how to generate QR codes.
At first, I just had him send me the links, and I wrote a small
script to to generate QR codes using
qrencode(1) from libqrencode.
That worked fine, but quickly got tedious because it required a lot
of manual work as the links piled up. I really wanted something
more automated; something where he could just input his link and
get a QR code. As a rough, hacky solution to get something out there
right away, I wrote a script that used
find(1) to generate a QR
code for every file on my public web server and store it in a /QR
folder using the same name and path as the original file, so all
he had to do was go to the link he wanted a QR code for, and then
put /QR/ in front of the path, and put .png on the end. This worked
too, but it had the unfortunate problem of taking forever. I ran
that script on a cron job, set to scan my web root and generate new
QR codes every half hour, which means that it could possibly take
up to 30 minutes after a file is uploaded before a QR code is
The ideal solution was to write a small CGI script that generates
a QR code on the fly. I didn't want to add any dependencies such
as PHP or Perl to my web server, since its
chroot(8)-ed, making it
hard to configure those things. Luckily, with the help of libqrencode,
qrencode(1) as a reference, it wasn't too difficult to
write a small C program that reads the PATH_INFO CGI variable,
appends it to the request scheme and the SERVER_NAME, and then
generates a QR code and outputs it to the page. So that is exactly
what I did.
I borrowed the
writeSVG() 1 function from
code, and then wrote a simple
main() that passes in the necessary
parameters. I statically linked the whole package so it can run
chroot(8), and then dropped it into my web root as
qr.cgi. I then enabled the
slowcgi(8) daemon and told
qr.cgi so it could execute it instead of just serving it.
When a browser hits
qr.cgi, it spits out an SVG QR code, and nothing
more. You can append a path or a complete URL to the
qr.cgi URL to
get a custom QR code, generated on the fly, for that path or URL,
regardless of whether or not it actually exists. If you specify a
path, the path is put relative to the root of my server. This is
useful for quickly changing links on my server into QR codes, by
just making a small edit to the URL. Otherwise, if you want to
link to another server, specify a complete URL.
Here's some examples. If you want to generate a QR code that points to the following page on my server:
Just go to:
If you want to generate a QR code for the following page:
Then go to:
This system is pretty straightforward for both humans and machines. You can use it as a quick-and-dirty way to generate a QR code, or you can use it as an API. This QR code generator is intended to be extremely simple, fast, and stable.
By itself, this generator can be useful if SVG is an acceptable output format. You can easily add QR codes to your web pages or application by just making an HTTP GET request. However, my dad really wants to insert his QR codes into Microsoft Word directly from Chrome. Unfortunately, browsers aren't set up to allow SVG images to be dragged and dropped, or copied to the clipboard natively. Additionally, MS Word doesn't support importing SVG images, at least not in a way that we could figure out. So in order to set it up so that my dad can just copy the QR code to his clipboard, and then paste it into Word, I had to write a small wrapper frontend.
This frontend works by making a simple HTTP request to the CGI program, and then base64 encoding the response and storing it in an HTML image tag. This allows browsers to recognize the SVG as an image, and thus allow saving and copying it to the clipboard. However, that doesn't solve the problem of Word not accepting SVG as a valid format. To make that work, and to save my dad a right-click, I added a "Copy as PNG" button that works by converting the SVG into an HTML canvas, and then into a PNG blob, which is then passed directly into the clipboard.
The frontend is highly experimental, and subject to breaking. It is also highly specific in scope, designed to cater to the needs of my dad only. If you're not my dad and you're interested in what I'm doing here, you'd best stick to just the CGI API described above, which will remain stable. Nonetheless, the URL is here if you want to play around with it:
As you'll notice, you can use the simple web form to submit URLs and paths to the server, or you can just directly write the query string. If you want to embed QR codes in your own pages, or convert them to PNG images on the browser side, you may take a look at how I did this, and of course feel free to contact me with any questions.
I chose not to borrow writePNG, because I didn't want to depend
libpng, mainly for the reason that I couldn't figure out how to
statically link it (cc was throwing linking errors when I added the
-static flag). My guess is that
libpng on OpenBSD doesn't actually
provide a static archive of all the symbols for
© 2019-2023 Jordan Bancino.