;; qrprinter.fnl ;; Provides utilities for encoding and printing strings as QR codes ;; -------------------------------------------------------------------------- ;; ;; License ;; Copyright (C) 2021 Jessie Hildebrandt ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; -------------------------------------------------------------------------- ;; ;; Local modules (local qrencode (require :qrprinter.ffi.qrencode)) ;; -------------------------------------------------------------------------- ;; ;; Helper functions ;; ---------------------------------- ;; ;; Padding (fn generate_sequence [element size] "Return a sequential table of `element` with length `size`." (let [row []] (for [_ 1 size] (table.insert row element)) row)) (fn pad_sequence [sequence element amount] "Return a sequential table containing `sequence` padded with `amount` count of `element`." (let [padded_sequence [(unpack sequence)]] (for [_ 1 amount] (table.insert padded_sequence 1 element) (table.insert padded_sequence element)) padded_sequence)) (fn pad_qr [qr amount] "Return 2D sequential table `qr` padded with `amount` count of white modules." (let [blank_row (generate_sequence false (length qr)) padded_qr (pad_sequence qr blank_row amount)] (icollect [_ row (ipairs padded_qr)] (pad_sequence row false amount)))) ;; ---------------------------------- ;; ;; Printing (fn print_qr [qr black_cell white_cell ?options] "Print 2D sequential table `qr` to stdout. Black modules will be printed as `black_cell`, and white modules will be printed as `white_cell`. `?options` is optional and may contain two keys: - invert: If non-nil, the output colors will be inverted. - padding: Amount of blank modules to print around QR code. If nil, defaults to 2." (let [invert? (?. ?options :invert) padding (or (?. ?options :padding) 2) padded_qr (pad_qr qr padding) [black_cell white_cell] (if invert? [white_cell black_cell] [black_cell white_cell]) row_strings (icollect [_ row (ipairs padded_qr)] (accumulate [row_string "" _ module_black? (ipairs row)] (.. row_string (if module_black? black_cell white_cell))))] (print (table.concat row_strings "\n")))) ;; -------------------------------------------------------------------------- ;; ;; Module functions ;; Some Fennel naming conventions are eschewed here because these are Lua-facing library functions ;; and the names would be mangled during compilation. ;; e.g. ?options -> _3foptions, invert? -> invert_3f (fn encode_string [string] "Encode string `string` into a table representing a QR code. Returns a 2D sequential table of true/false values representing black/white QR modules." (qrencode.encode_string_8_bit string)) (fn print_qr_ascii [qr options] "Print QR code `qr` to stdout using ASCII characters." (let [black_cell " " white_cell "##"] (print_qr qr black_cell white_cell options))) (fn print_qr_ansi [qr options] "Print QR code `qr` to stdout using ANSI escape sequences." (let [black_cell "\27[40m \27[0m" white_cell "\27[47m \27[0m"] (print_qr qr black_cell white_cell options))) (fn print_qr_utf8 [qr options] "Print QR code `qr` to stdout using UTF8 block elements." (let [black_cell " " white_cell "██"] (print_qr qr black_cell white_cell options))) ;; -------------------------------------------------------------------------- ;; ;; Provide qrprinter module {: encode_string : print_qr_ascii : print_qr_ansi : print_qr_utf8 :print_qr print_qr_ansi}