{-# LANGUAGE CPP, ForeignFunctionInterface #-} -- | Haskell bindings for libqrencode. -- -- Libqrencode is a C library for encoding data in a QR Code symbol, a kind of 2D symbology -- that can be scanned by handy terminals such as a mobile phone with CCD. The capacity of -- QR Code is up to 7000 digits or 4000 characters, and is highly robust. module Data.QRCode (encodeByteString, encodeString, getQRCodeVersion, getQRCodeWidth, getQRCodeString, toMatrix, QREncodeLevel (..), QREncodeMode (..)) where import Control.Monad import Data.ByteString (ByteString, unpack, useAsCString, packCStringLen) import qualified Data.ByteString as BS import Data.Maybe import Foreign import Foreign.C.Error import Foreign.C.String import Foreign.C.Types import Foreign.Storable #include data QREncodeLevel = QR_ECLEVEL_L | QR_ECLEVEL_M | QR_ECLEVEL_Q | QR_ECLEVEL_H data QREncodeMode = QR_MODE_NUM -- ^ Numeric mode | QR_MODE_AN -- ^ Alphabet-numeric mode | QR_MODE_EIGHT -- ^ 8-bit data mode | QR_MODE_KANJI -- ^ Kanji (shift-jis) mode convertQREncodeLevel :: QREncodeLevel -> CInt convertQREncodeLevel QR_ECLEVEL_L = #const QR_ECLEVEL_L convertQREncodeLevel QR_ECLEVEL_M = #const QR_ECLEVEL_M convertQREncodeLevel QR_ECLEVEL_Q = #const QR_ECLEVEL_Q convertQREncodeLevel QR_ECLEVEL_H = #const QR_ECLEVEL_H convertQREncodeMode :: QREncodeMode -> CInt convertQREncodeMode QR_MODE_NUM = #const QR_MODE_NUM convertQREncodeMode QR_MODE_AN = #const QR_MODE_AN convertQREncodeMode QR_MODE_EIGHT = #const QR_MODE_8 convertQREncodeMode QR_MODE_KANJI = #const QR_MODE_KANJI data QRcode = QRcode { getQRCodeVersion :: Int, getQRCodeWidth :: Int, getQRCodeString :: ByteString } deriving (Show, Read) data QRcodeStruct = QRcodeStruct { c_version :: CInt, c_width :: CInt, c_data :: CString } deriving (Show) #let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t (y__); }, y__) instance Storable QRcodeStruct where alignment _ = #{alignment QRcode} sizeOf _ = #{size QRcode} peek ptr = do version <- #{peek QRcode, version} ptr width <- #{peek QRcode, width} ptr data' <- #{peek QRcode, data} ptr return $ QRcodeStruct version width data' poke ptr (QRcodeStruct version width data') = do #{poke QRcode, version} ptr version #{poke QRcode, width} ptr width #{poke QRcode, data} ptr data' foreign import ccall safe "QRcode_encodeString" c_encodeString :: CString -- string -> CInt -- version -> CInt -- level -> CInt -- hint -> CInt -- casesensitive -> IO (Ptr QRcodeStruct) foreign import ccall unsafe "QRcode_free" c_free :: Ptr QRcodeStruct -> IO () -- | create a QR code from a ByteString encodeByteString :: ByteString -- ^ String to encode -> Maybe Int -- ^ Version (auto if Nothing) -> QREncodeLevel -- ^ Encode Level -> QREncodeMode -- ^ Encode Mode -> Bool -- ^ Case-sensative -> IO QRcode encodeByteString str version level mode casesensitive = do when (BS.null str) $ error "empty bytestring provided" useAsCString str $ \s-> encoder s version level mode casesensitive -- | create a QR code from a String encodeString :: String -- ^ String to encode -> Maybe Int -- ^ Version (auto if Nothing) -> QREncodeLevel -- ^ Encode Level -> QREncodeMode -- ^ Encode Mode -> Bool -- ^ Case-sensative -> IO QRcode encodeString str version level mode casesensitive = do when (null str) $ error "empty string provided" newCAString str >>= \s-> encoder s version level mode casesensitive encoder :: CString -> Maybe Int -> QREncodeLevel -> QREncodeMode -> Bool -> IO QRcode encoder cstr ver level mode casesensitive = do let l = convertQREncodeLevel level let m = convertQREncodeMode mode c_qrptr <- throwErrnoIfNull "haskell-qrencode/QRcode_encodeString" $ c_encodeString cstr (fromIntegral $ fromMaybe 0 ver) l m (b2i casesensitive) c_qr <- peek c_qrptr let version = fromIntegral (c_version c_qr) let width = fromIntegral (c_width c_qr) str <- packCStringLen (c_data c_qr, width * width) c_free c_qrptr return (QRcode version width str) where b2i True = 1 b2i False = 0 -- | Convert a QRcode to a matrix of ones and zeros (1 = On, 0 = Off) toMatrix :: QRcode -> [[Word8]] toMatrix (QRcode _ width str) = regroup . map tobin . unpack $ str where tobin c = c .&. 1 regroup [] = [] regroup xs = let ~(this, rest) = splitAt width xs in this : regroup rest