• Home
  • Blog
  • Contact
  • Projects
  • Content Mirror
  • Matrix Guide

  • QR Codes For All

    June 2, 2022

    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 available.

    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, and using 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 qrencode(1)'s source code, and then wrote a simple main() that passes in the necessary parameters. I statically linked the whole package so it can run inside the chroot(8), and then dropped it into my web root as qr.cgi. I then enabled the slowcgi(8) daemon and told httpd(8) about 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:

        /blog/rss.xml
    

    Just go to:

        /qr.cgi/blog/rss.xml
    

    If you want to generate a QR code for the following page:

        https://duckduckgo.com
    

    Then go to:

        /qr.cgi/https://duckduckgo.com
    

    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:

        /qr.html
    

    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'm usually not a fan of JavaScript, and if I had the time to do the frontend properly, I'd make it work without JS. But I do think JS is good enough for now. Again, I'm intending the actual CGI API to be more useful. The frontend is really just a proof of concept.


    1. I chose not to borrow writePNG, because I didn't want to depend on 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 libpng


    © 2019-2024 Jordan Bancino.