-- | A few functions to deal with the image itself module Data.QRCodes.Image ( -- * Functions to convert to JuicyPixels `Image` bsToImg , objToImg -- * Functions to sign and convert to `Image` , bsToImgSec , objToImgSec -- * Functions to sign with user-supplied key and yield an `Image` , bsToImgSec' , objToImgSec' ) where import Codec.Picture.Types as T import Control.Monad import Crypto.PubKey.RSA import Data.Binary import qualified Data.ByteString as BS import Data.ByteString.Lazy (toStrict) import Data.QRCode import Data.QRCodes.Signature import Data.QRCodes.Utils import qualified Data.Vector.Storable as V import Data.Word (Word8) import Prelude as P -- | Creates a signed QR code from a strict bytestring and path to keyfile/path where the keyfile should be generated, yielding a JuicyPixels `Image`. -- Note that QR codes may only contain a small number of characters, so encrypting can sometimes make an object too big to encode. -- -- > bsToImgSec (BS.pack "hello") ".key.hk" bsToImgSec :: BS.ByteString -> FilePath -> IO (T.Image Word8) bsToImgSec string keyfile = bsToImg =<< (fmap preserveUpper . flip mkSigFile keyfile) string -- | Sign a byteString with a given key -- -- > bsToImgSec' (BS.pack "str") (generate 256 0x10001) bsToImgSec' :: BS.ByteString -> (PublicKey, PrivateKey) -> IO (T.Image Word8) bsToImgSec' string key = bsToImg =<< (fmap preserveUpper . flip mkSig key) string -- | Encode an object as a JuicyPixels `Image` with a key in a given file. objToImgSec :: (Binary a) => a -> FilePath -> IO (T.Image Word8) objToImgSec obj = bsToImgSec (toStrict $ encode obj) -- | Encode an object as a JuicyPixels `Image` with a key. objToImgSec' :: (Binary a) => a -> (PublicKey, PrivateKey) -> IO (T.Image Word8) objToImgSec' obj = bsToImgSec' (toStrict $ encode obj) -- | Create a JuicyPixels `Image` from a `ByteString` bsToImg :: BS.ByteString -> IO (T.Image Word8) bsToImg input = do smallMatrix <- toMatrix <$> encodeByteString input Nothing QR_ECLEVEL_H QR_MODE_EIGHT False let qrMatrix = fattenList 8 $ map (fattenList 8) smallMatrix --consider using repa here, with the `onImg` thing with JuicyPixels pure $ encodePng qrMatrix -- | Encode an object as a JuicyPixels `Image` objToImg :: (Binary a) => a -> IO (T.Image Word8) objToImg obj = let input = toStrict $ encode obj in bsToImg input -- | Encode a JuicyPixels `Image` given a matrix encodePng :: [[Word8]] -> T.Image Word8 encodePng matrix = Image dim dim vector where dim = P.length matrix vector = V.map ((*255) . swapWord) $ V.fromList $ join matrix -- | To help scale the image up, e.g. -- -- > fattenList 8 $ (map fattenList 8) smallMatrix -- -- to scale @smallMatrix :: [[Word8]]@ by a factor of 8 fattenList :: Int -> [a] -> [a] fattenList = (=<<) . replicate