packages feed

qr-0.2.0.0: src/Data/QR/Encode.hs

module Data.QR.Encode where

import qualified Codec.Binary.UTF8.String as UTF8
import Data.Char
import Data.Maybe
import Data.Word

import Data.QR.Tables
import Data.QR.Types

countLength :: Version -> Mode -> Int
countLength v = f c
  where
    c | v < 10 = 0 :: Int
      | v < 27 = 1
      | otherwise = 2
    f 0 Numeric = 10
    f 1 Numeric = 12
    f _ Numeric = 14
    f 0 Alpha = 9
    f 1 Alpha = 11
    f _ Alpha = 13
    f 0 Byte = 8
    f _ Byte = 16

count :: Version -> Mode -> Int -> [Bit]
count v m = toBinary (countLength v m)

mode :: Mode -> [Bit]
mode = toBinary 4 . go
  where
    go Numeric = 1 :: Int
    go Alpha = 2
    go Byte = 4

encodeData :: Mode -> String -> (Int, [Bit])
encodeData Numeric xs = (length xs, chunksOf 3 xs >>= encodeChunk)
  where
    encodeChunk c = case reads c of
      [(n, "")] | n >= 0 -> toBinary (bits (length c)) (n :: Int)
      _ -> []
    bits 1 = 4
    bits 2 = 7
    bits _ = 10
encodeData Alpha xs = (length xs, chunksOf 2 xs >>= encodeChunk)
  where
    encodeChunk [x] = toBinary 6 (value x)
    encodeChunk [x, y] = toBinary 11 (value x * 45 + value y)
    encodeChunk _ = []

    value ' ' = 36
    value '$' = 37
    value '%' = 38
    value '*' = 39
    value '+' = 40
    value '-' = 41
    value '.' = 42
    value '/' = 43
    value ':' = 44
    value x
      | isAlpha x = ord (toUpper x) - ord 'A' + 10
      | isDigit x = digitToInt x
      | otherwise = 0
encodeData Byte xs = (length binData, binData >>= toBinary 8)
  where
    binData = UTF8.encode xs

encode :: Version -> Level -> Mode -> String -> [Word8]
encode v l m xs = toWords $ take total $ base ++ pad8 ++ cycle padding
  where
    (size, encoded) = encodeData m xs
    base0 = mode m ++ count v m size ++ encoded
    base = take total $ base0 ++ replicate 4 Z
    total = dataBits v l
    pad8 = replicate ((-(length base)) `mod` 8) Z
    padding = [O,O,O,Z,O,O,Z,Z,Z,Z,Z,O,Z,Z,Z,O]

minimumVersion :: Level -> Mode -> Int -> Maybe Version
minimumVersion l m sz = fmap fst . listToMaybe . dropWhile ((< sz) . snd)
                      . zip [1..] . map (capacity l m)
                      $ [1 .. 40]