1
mirror of https://gitlab.com/jessieh/qrprinter.git synced 2025-04-12 12:11:45 +00:00

Compare commits

...

13 Commits
1.0-0 ... main

19 changed files with 527 additions and 10 deletions

5
.busted Normal file
View File

@ -0,0 +1,5 @@
return {
default = {
coverage = true
}
}

8
.gitignore vendored
View File

@ -6,4 +6,10 @@
.\#*
# Build folder
build/
build/
# Luarocks source tree files
*.src.rock
# luacov report files
luacov.*

12
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,12 @@
image: jessiehildebrandt/luadev:luajit
test:
script:
- apk add libqrencode-dev
- luarocks build
- luarocks test
- luacov
- cat luacov.report.out
artifacts:
paths:
- luacov.report.out

4
.luacov Normal file
View File

@ -0,0 +1,4 @@
modules = {
["qrprinter"] = "build/qrprinter.lua",
["qrprinter.ffi.qrencode"] = "build/ffi/qrencode.lua",
}

View File

@ -4,7 +4,7 @@
SRC_DIR = src
BUILD_DIR = build
FENNEL = ./fennel-0.10.0-x86_64
FENNEL = ./bin/fennel-0.10.0-x86_64
fnl_files := $(shell find $(SRC_DIR)/ -type f -name '*.fnl')
lua_files := $(patsubst $(SRC_DIR)/%.fnl, $(BUILD_DIR)/%.lua, $(fnl_files))
@ -13,12 +13,13 @@ lua_files := $(patsubst $(SRC_DIR)/%.fnl, $(BUILD_DIR)/%.lua, $(fnl_files))
all: $(lua_files)
install:
@echo "Leaving installation up to luarocks..."
$(BUILD_DIR)/%.lua: $(SRC_DIR)/%.fnl
mkdir -p "$(@D)"
$(FENNEL) --compile $< > $@
install:
@echo "Leaving installation up to luarocks..."
clean:
rm -rf $(BUILD_DIR)
rm -f *.src.rock

View File

@ -2,16 +2,46 @@
A wrapper for `libqrencode` that provides an easy way of encoding and printing strings as QR codes from Lua.
**This uses LuaJIT's FFI library, and so will not run in PUC Lua implementations.**
Written in [Fennel](https://fennel-lang.org).
[![Test Status](https://gitlab.com/jessieh/qrprinter/badges/main/pipeline.svg)](https://gitlab.com/jessieh/qrprinter/-/pipelines) [![Code Coverage](https://gitlab.com/jessieh/qrprinter/badges/main/coverage.svg)](https://gitlab.com/jessieh/qrprinter/-/jobs)
## Table of Contents
- [Installation](#installation)
- [Example Usage](#example-usage)
- [Documentation](#documentation)
- [Encoding](#encoding)
- [qrprinter.encode_string](#qrprinterencode_string)
- [Printing](#config-structure)
- [qrprinter.print_qr](#qrprinterprint_qr)
- [qrprinter.print_qr_ascii](#qrprinterprint_qr_ascii)
- [qrprinter.print_qr_ansi](#qrprinterprint_qr_ansi)
- [qrprinter.print_qr_utf8](#qrprinterprint_qr_utf8)
- [Testing](#testing)
## Installation
Install dependencies:
```bash
# Debian/Ubuntu:
apt install libqrencode-dev
# Alpine:
apk add gcompat libqrencode-dev
```
Install with [luarocks](https://luarocks.org):
```bash
luarocks install qrprinter
```
For a list of available versions, see the [luarocks module page](https://luarocks.org/modules/jessieh/qrprinter).
## Example Usage
<img src="../../raw/assets/demo.png" width=500>
@ -24,9 +54,8 @@ luarocks install qrprinter
```
qrprinter.encode_string( string )
```
Encodes `string` into a table representing a QR code.
Returns a 2D sequential table of true/false values representing black/white QR modules.
Encodes `string` into a table representing a QR code.\
Returns a two-dimensional sequential table of true/false values representing black/white QR modules.
### Printing
@ -58,3 +87,15 @@ Print `qr` to `stdout` using ANSI escape sequences.
qrprinter.print_qr_utf8( qr, [options] )
```
Print `qr` to `stdout` using UTF8 block elements.
## Testing
The Fennel code needs to be compiled before tests can be run:
```bash
# Build the project:
luarocks build
# Now you can run tests:
luarocks test
```

View File

@ -0,0 +1,36 @@
package = 'qrprinter'
version = '1.0-1'
supported_platforms = {'linux'}
source = {
url = "git+https://gitlab.com/jessieh/qrprinter",
tag = "1.0-1"
}
description = {
summary = "Encode and print strings as QR codes.",
detailed = "qrprinter wraps the libqrencode C library to provide an easy way of encoding and printing strings as QR codes from Lua.",
homepage = "https://gitlab.com/jessieh/qrprinter",
maintainer = "Jessie Hildebrandt <jessieh@jessieh.net>",
license = "GPLv3"
}
dependencies = {
"lua >= 5.1",
}
external_dependencies = {
LIBQRENCODE = {
header = "qrencode.h"
}
}
build = {
type = "make",
makefile = "Makefile",
build_variables = {
-- This suppresses luarocks' CFLAGS warning-- we don't actually use this
CFLAGS = "$(CFLAGS)"
},
install = {
lua = {
["qrprinter"] = "build/qrprinter.lua",
["qrprinter.ffi.qrencode"] = "build/ffi/qrencode.lua"
}
}
}

View File

@ -0,0 +1,45 @@
package = 'qrprinter'
version = '1.1-0'
rockspec_format = '3.0'
supported_platforms = {'linux'}
source = {
url = "git+https://gitlab.com/jessieh/qrprinter",
tag = "1.1-0"
}
description = {
summary = "Encode and print strings as QR codes.",
detailed = "qrprinter wraps the libqrencode C library using LuaJIT's FFI to provide an easy way of encoding and printing strings as QR codes from Lua.",
homepage = "https://gitlab.com/jessieh/qrprinter",
maintainer = "Jessie Hildebrandt <jessieh@jessieh.net>",
license = "GPLv3"
}
dependencies = {
"lua >= 5.1",
"luajit >= 2.0"
}
external_dependencies = {
LIBQRENCODE = {
header = "qrencode.h"
}
}
test = {
type = 'busted'
}
test_dependencies = {
'busted = 2.0.0-1',
'luacov = 0.15.0-1'
}
build = {
type = "make",
makefile = "Makefile",
build_variables = {
-- This suppresses luarocks' CFLAGS warning-- we don't actually use this
CFLAGS = "$(CFLAGS)"
},
install = {
lua = {
["qrprinter"] = "build/qrprinter.lua",
["qrprinter.ffi.qrencode"] = "build/ffi/qrencode.lua"
}
}
}

View File

@ -0,0 +1,97 @@
-- spec/encode_string_spec.lua
-- Specifies tests for qrprinter.encode_string functionality
--------------------------------------------------------------------------------
-- Disable LuaJIT compiler
-- LuaJIT's optimizations can frob luacov's coverage reports
require( 'jit' ).off()
--------------------------------------------------------------------------------
-- Module imports
qrprinter = require( 'qrprinter' )
--------------------------------------------------------------------------------
-- Test context
describe( 'qrprinter.encode_string functionality', function ()
----------------------------------------
-- Test definitions
it( 'returns nil for empty strings', function()
local expected_value = nil
local empty_string = ''
assert.are.same( expected_value, qrprinter.encode_string( empty_string ) )
end )
it( 'encodes alphanumeric strings', function()
local expected_value = {
{ true, true, true, true, true, true, true, false, false, true, false, false, true, false, true, true, true, true, true, true, true },
{ true, false, false, false, false, false, true, false, true, false, false, true, false, false, true, false, false, false, false, false, true },
{ true, false, true, true, true, false, true, false, false, true, false, false, false, false, true, false, true, true, true, false, true },
{ true, false, true, true, true, false, true, false, true, false, false, true, false, false, true, false, true, true, true, false, true },
{ true, false, true, true, true, false, true, false, false, false, true, true, true, false, true, false, true, true, true, false, true },
{ true, false, false, false, false, false, true, false, true, true, true, false, true, false, true, false, false, false, false, false, true },
{ true, true, true, true, true, true, true, false, true, false, true, false, true, false, true, true, true, true, true, true, true },
{ false, false, false, false, false, false, false, false, false, false, true, true, true, false, false, false, false, false, false, false, false },
{ true, true, true, true, true, false, true, true, true, true, false, false, true, true, false, true, false, true, false, true, false },
{ false, false, true, true, false, false, false, false, false, true, false, false, true, false, false, true, false, false, true, false, true },
{ true, false, false, false, false, true, true, false, false, false, true, true, false, true, false, false, true, false, false, true, false },
{ true, true, true, true, true, false, false, false, false, true, false, false, false, false, false, true, true, true, true, true, false },
{ true, false, false, false, true, true, true, true, false, false, true, true, false, true, false, false, true, false, false, false, false },
{ false, false, false, false, false, false, false, false, true, true, true, true, true, true, true, true, false, false, true, false, true },
{ true, true, true, true, true, true, true, false, true, true, false, false, true, false, true, true, false, true, false, true, false },
{ true, false, false, false, false, false, true, false, false, false, true, true, true, true, true, false, true, false, true, false, true },
{ true, false, true, true, true, false, true, false, true, false, true, false, true, false, false, true, false, true, false, true, false },
{ true, false, true, true, true, false, true, false, true, true, false, false, true, false, false, false, true, false, true, false, false },
{ true, false, true, true, true, false, true, false, true, false, true, true, false, true, false, true, false, true, true, false, false },
{ true, false, false, false, false, false, true, false, true, false, true, false, false, false, false, true, true, false, true, false, false },
{ true, true, true, true, true, true, true, false, true, true, true, true, false, true, false, true, false, true, false, true, false }
}
local test_string = 'abc123'
assert.are.same( expected_value, qrprinter.encode_string( test_string ) )
end )
it( 'encodes UTF-8 strings', function()
local expected_value = {
{ true, true, true, true, true, true, true, false, false, false, true, false, true, false, true, true, true, true, true, true, true },
{ true, false, false, false, false, false, true, false, false, false, false, false, true, false, true, false, false, false, false, false, true },
{ true, false, true, true, true, false, true, false, true, false, true, false, false, false, true, false, true, true, true, false, true },
{ true, false, true, true, true, false, true, false, false, false, false, false, true, false, true, false, true, true, true, false, true },
{ true, false, true, true, true, false, true, false, false, true, false, true, true, false, true, false, true, true, true, false, true },
{ true, false, false, false, false, false, true, false, false, true, true, true, false, false, true, false, false, false, false, false, true },
{ true, true, true, true, true, true, true, false, true, false, true, false, true, false, true, true, true, true, true, true, true },
{ false, false, false, false, false, false, false, false, true, false, true, false, false, false, false, false, false, false, false, false, false },
{ true, true, true, false, true, true, true, true, true, false, true, false, true, true, true, false, false, false, true, false, false },
{ false, false, true, false, false, true, false, true, false, true, false, true, false, false, false, false, true, false, true, false, false },
{ false, true, true, false, true, false, true, false, true, true, false, true, false, true, true, false, false, true, false, false, false },
{ false, false, true, false, false, false, false, true, true, true, true, true, true, false, true, true, false, true, true, true, false },
{ true, true, true, false, true, true, true, false, false, false, false, true, false, true, false, true, true, true, true, false, true },
{ false, false, false, false, false, false, false, false, true, true, false, true, false, true, false, false, false, true, false, false, true },
{ true, true, true, true, true, true, true, false, true, true, true, false, true, false, true, false, false, false, false, true, false },
{ true, false, false, false, false, false, true, false, true, true, false, false, true, true, true, false, true, true, true, true, false },
{ true, false, true, true, true, false, true, false, true, false, false, true, true, true, true, true, false, true, true, true, false },
{ true, false, true, true, true, false, true, false, false, true, false, false, true, false, true, false, false, true, true, true, false },
{ true, false, true, true, true, false, true, false, true, false, true, false, false, true, false, false, false, true, true, false, true },
{ true, false, false, false, false, false, true, false, true, true, false, true, false, true, true, true, false, false, true, true, false },
{ true, true, true, true, true, true, true, false, true, true, true, true, true, true, true, false, true, true, false, true, true }
}
local test_string = '💙🔵🔷'
assert.are.same( expected_value, qrprinter.encode_string( test_string ) )
end )
end )

View File

@ -0,0 +1,25 @@
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         

View File

@ -0,0 +1,25 @@
##################################################
##################################################
#### #### #### ## ####
#### ########## ## #### #### ########## ####
#### ## ## #### ######## ## ## ####
#### ## ## ## #### #### ## ## ####
#### ## ## ###### ## ## ## ####
#### ########## ## ## ## ########## ####
#### ## ## ## ## ####
######################## ####################
#### ## #### ## ## ## ######
######## ########## #### #### #### ## ####
#### ######## ###### ## #### #### ######
#### ######## ########## ######
#### ###### #### ## #### ############
#################### #### ## ####
#### ## #### ## ## ## ######
#### ########## ###### ## ## ## ####
#### ## ## ## ## ## #### ## ## ######
#### ## ## ## #### ###### ## ########
#### ## ## ## ## ## ## ## ########
#### ########## ## ## ######## ## ########
#### ## ## ## ## ## ######
##################################################
##################################################

View File

@ -0,0 +1,25 @@
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         
                         

View File

@ -0,0 +1,21 @@
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     
                     

View File

@ -0,0 +1,27 @@
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           
                           

View File

@ -0,0 +1,25 @@
██████████████████████████████████████████████████
██████████████████████████████████████████████████
████ ████ ████ ██ ████
████ ██████████ ██ ████ ████ ██████████ ████
████ ██ ██ ████ ████████ ██ ██ ████
████ ██ ██ ██ ████ ████ ██ ██ ████
████ ██ ██ ██████ ██ ██ ██ ████
████ ██████████ ██ ██ ██ ██████████ ████
████ ██ ██ ██ ██ ████
████████████████████████ ████████████████████
████ ██ ████ ██ ██ ██ ██████
████████ ██████████ ████ ████ ████ ██ ████
████ ████████ ██████ ██ ████ ████ ██████
████ ████████ ██████████ ██████
████ ██████ ████ ██ ████ ████████████
████████████████████ ████ ██ ████
████ ██ ████ ██ ██ ██ ██████
████ ██████████ ██████ ██ ██ ██ ████
████ ██ ██ ██ ██ ██ ████ ██ ██ ██████
████ ██ ██ ██ ████ ██████ ██ ████████
████ ██ ██ ██ ██ ██ ██ ██ ████████
████ ██████████ ██ ██ ████████ ██ ████████
████ ██ ██ ██ ██ ██ ██████
██████████████████████████████████████████████████
██████████████████████████████████████████████████

116
spec/print_qr_spec.lua Normal file
View File

@ -0,0 +1,116 @@
-- spec/encode_string_spec.lua
-- Specifies tests for qrprinter.encode_string functionality
--------------------------------------------------------------------------------
-- Disable LuaJIT compiler
-- LuaJIT's optimizations can frob luacov's coverage reports
require( 'jit' ).off()
--------------------------------------------------------------------------------
-- Module imports
qrprinter = require( 'qrprinter' )
--------------------------------------------------------------------------------
-- Helper functions
local function read_file_into_string( path )
-- Open file at `path`
local file = io.open( path, 'r' )
if not file then
error( 'Error opening file at ' .. path .. ' during test.' )
end
-- Read file contents
local content = file:read( '*a' )
-- Remove terminating newline
local last_char = string.sub( content, -1 )
if last_char == '\n' then
content = string.sub( content, 1, -2 )
end
file:close()
return content
end
--------------------------------------------------------------------------------
-- Test context
describe( 'qrprinter.print_qr functionality', function ()
----------------------------------------
-- Test-wide values
local test_qr = qrprinter.encode_string( 'abc123' )
----------------------------------------
-- Replace print with mock function that returns the provided string
setup( function()
_G.print = function( msg )
return msg
end
end )
----------------------------------------
-- Test definitions
it( 'aliases print_qr to print_qr_ansi', function()
assert.are.same( qrprinter.print_qr( test_qr ), qrprinter.print_qr_ansi( test_qr ) )
end )
it( 'prints QR codes using ASCII characters', function()
local expected_output = read_file_into_string( 'spec/expected_output/print_qr_ascii.txt' )
assert.are.same( expected_output, qrprinter.print_qr_ascii( test_qr ) )
end )
it( 'prints QR codes using ANSI escape sequences', function()
local expected_output = read_file_into_string( 'spec/expected_output/print_qr_ansi.txt' )
assert.are.same( expected_output, qrprinter.print_qr_ansi( test_qr ) )
end )
it( 'prints QR codes using UTF8 block sequences', function()
local expected_output = read_file_into_string( 'spec/expected_output/print_qr_utf8.txt' )
assert.are.same( expected_output, qrprinter.print_qr_utf8( test_qr ) )
end )
it( 'adjusts padding of the output when the "padding" option is provided', function()
local padding_0_expected_output = read_file_into_string( 'spec/expected_output/print_qr_padding_0.txt' )
local padding_3_expected_output = read_file_into_string( 'spec/expected_output/print_qr_padding_3.txt' )
assert.are.same( padding_0_expected_output, qrprinter.print_qr( test_qr, { padding = 0 } ) )
assert.are.same( padding_3_expected_output, qrprinter.print_qr( test_qr, { padding = 3 } ) )
end )
it( 'inverts the colors of the output when the "invert" option is set', function()
local expected_output = read_file_into_string( 'spec/expected_output/print_qr_invert_true.txt' )
assert.are.same( expected_output, qrprinter.print_qr( test_qr, { invert = true } ) )
end )
end )

View File

@ -48,7 +48,10 @@ typedef enum {
} QRecLevel;
/* Encode 8-bit string as QR code object */
QRcode* QRcode_encodeString8bit( const char *string, int version, QRecLevel level );
QRcode* QRcode_encodeString8bit( const char* string, int version, QRecLevel level );
/* Free QR code object from memory */
void QRcode_free( QRcode* qrcode );
")
@ -78,6 +81,7 @@ Returns a 2D sequential table of true/false values representing black/white QR m
module_black? (= (% module_data 2) 1)]
(table.insert row_table module_black?)))
(table.insert qrcode_table row_table)))
(ffi_qrencode.QRcode_free qrcode)
qrcode_table))
}

View File

@ -81,7 +81,9 @@ Black modules will be printed as `black_cell`, and white modules will be printed
(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))
(if (~= (string.len string) 0)
(qrencode.encode_string_8_bit string)
nil))
(fn print_qr_ascii [qr options]
"Print QR code `qr` to stdout using ASCII characters."