diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,10 +2,10 @@
 This is a library to generate `.png` files from QR codes.
 
 ##Dependencies
-The library depends on the C library [https://github.com/fukuchi/libqrencode](libqrencode) which you will need to install separately.
+The library depends on the C library [https://github.com/fukuchi/libqrencode](libqrencode) which you will need to install separately, as well as the C library `Zbar` from [here](https://github.com/ZBar/ZBar). You should also be able to get them from your distro. 
 
 ##Usage
-The library exports main functions - `createQRCode` and `byteStringToQR` - and their secured/signed versions. The first takes any object that is an instance of `ToJSON` and writes an image to file, while the second takes a (strict) bytestring and writes it to file.
+The library exports three main functions - `createQRCode`, `readQRString`, and `byteStringToQR` - and their secured/signed versions. The first takes any object that is an instance of `ToJSON` and writes an image to file, while the second takes filepath pointing to an image and returns the text in the QR code. The third takes a (strict) bytestring and writes it to file.
 
 ##Executable
 
@@ -20,6 +20,6 @@
 
 ###Use
 
-Compiling will generate an executable called `QRPipe` which reads from `stdin` and outputs a file as the second argument, e.g.
+Compiling will generate an executable called `qrpipe` which reads from `stdin` and outputs a file as the second argument, e.g.
 
 ```echo 'My name is:" | qrpipe "nametag.png"```
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,11 +1,6 @@
 {-#LANGUAGE OverloadedStrings #-}
 
-import Data.QRCodes
-import qualified Data.ByteString as B
-import System.Environment (getArgs)
+import Data.QRCodes.Exe (exec)
 
 main :: IO ()
-main = do
-    pipeIn <- B.getContents
-    filepath <- fmap (flip (!!) 0) getArgs
-    byteStringToQRSec pipeIn filepath
+main = exec
diff --git a/qr-imager.cabal b/qr-imager.cabal
--- a/qr-imager.cabal
+++ b/qr-imager.cabal
@@ -1,5 +1,5 @@
 name:                qr-imager
-version:             0.1.0.1
+version:             0.1.2.0
 synopsis:            Library to generate QR codes from bytestrings and objects
 description:         Please see README.md
 homepage:            https://github.com/vmchale/QRImager#readme
@@ -17,6 +17,10 @@
 library
   hs-source-dirs:      src
   exposed-modules:     Data.QRCodes
+                     , Data.QRCodes.Exe
+  other-modules:       Data.QRCodes.Image
+                     , Data.QRCodes.Signature
+                     , Data.QRCodes.Utils
   build-depends:       base >= 4.7 && < 5
                      , aeson
                      , JuicyPixels
@@ -27,12 +31,15 @@
                      , jose-jwt
                      , directory
                      , haskell-qrencode
+                     , process
+                     , MissingH
+                     , optparse-applicative
   default-language:    Haskell2010
 
 executable qrpipe
   hs-source-dirs:      app
   main-is:             Main.hs
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O3
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -O2
   build-depends:       base
                      , qr-imager
                      , bytestring
diff --git a/src/Data/QRCodes.hs b/src/Data/QRCodes.hs
--- a/src/Data/QRCodes.hs
+++ b/src/Data/QRCodes.hs
@@ -6,64 +6,32 @@
                     , createQRCode
                     , byteStringToQR
                     , byteStringToQRSec
+                    , readQRString
+                    , readQRStrSec
                     ) where
 
 import Data.Aeson
 import Data.QRCode
-import Codec.Picture.Types as T
 import Codec.Picture.Png (writePng)
-import Data.Word (Word8)
-import qualified Data.Vector.Storable as V
 import Data.ByteString.Lazy (toStrict)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as BSL
-import Data.List (replicate)
+import qualified Data.ByteString.Char8 as BS
 import Data.Char (toLower)
-import Prelude as P
-import Crypto.PubKey.RSA as Cr
-import Jose.Jws
-import System.Directory (doesFileExist)
 import Control.Lens.Tuple
 import Control.Lens (view)
-import Jose.Jwt (unJwt, JwtError)
-import Jose.Jwa (JwsAlg (RS512))
-import Data.Either (either)
-import Data.Bits ((.&.))
 import Control.Applicative ((<$>))
-
--- | Verify a signed token
-checkSig :: BS.ByteString -> IO (Either JwtError BS.ByteString)
-checkSig tok = do
-    key <- read <$> readFile ".key.hk" :: IO (Cr.PublicKey, Cr.PrivateKey)
-    let jws = rsaDecode (view _1 key) tok
-    return $ fmap (view _2) jws
-
--- | Sign a token
-mkSig :: BS.ByteString -> IO BS.ByteString
-mkSig string = do
-    switch <- doesFileExist ".key.hk"
-    if not switch then do
-        putStrLn "generating key..."
-        key <- Cr.generate 512 0x10001
-        writeFile ".key.hk" (show key)
-    else
-        return ()
-    key' <- read <$> readFile ".key.hk" :: IO (Cr.PublicKey, Cr.PrivateKey)
-    signedToken <- rsaEncode RS512 (view _2 key') string
-    let signed = fmap unJwt signedToken
-    liftEither id (return <$> signed)
+import System.Process
+import Data.QRCodes.Utils
+import Data.QRCodes.Signature
+import Data.QRCodes.Image
 
 -- | Creates a signed QR code from a strict bytestring
 byteStringToQRSec :: BS.ByteString -> FilePath -> IO ()
-byteStringToQRSec string filepath = (flip byteStringToQR filepath) =<< (mkSig string)
+byteStringToQRSec string filepath = (flip byteStringToQR filepath) =<< (((fmap preserveUpper) . mkSig) string)
 
 -- | Creates a signed QR code from an object that is part of the ToJSON class
 createSecureQRCode :: (ToJSON a) => a -> FilePath -> IO ()
 createSecureQRCode object = byteStringToQRSec (toStrict $ encode object)
 
-liftEither :: (Show b, Monad m) => (t -> m a) -> Either b t -> m a
-liftEither = either (error . show)
-
 -- | Creates a QR code from an object that is part of the ToJSON class
 createQRCode :: (ToJSON a) => a -> FilePath -> IO ()
 createQRCode object filepath = let input = toStrict $ encode object in byteStringToQR input filepath
@@ -72,17 +40,15 @@
 byteStringToQR :: BS.ByteString -> FilePath -> IO ()
 byteStringToQR input filepath = do
     smallMatrix <- toMatrix <$> encodeByteString input Nothing QR_ECLEVEL_H QR_MODE_EIGHT False
-    let qrMatrix = fattenList 8 $ P.map (fattenList 8) smallMatrix
+    let qrMatrix = fattenList 8 $ map (fattenList 8) smallMatrix
     writePng filepath (encodePng qrMatrix)
 
-encodePng :: [[Word8]] -> T.Image Word8
-encodePng matrix = Image dim dim vector
-    where dim    = P.length matrix
-          vector = V.map ((*255) . swapWord) $ V.fromList $ P.concat matrix
-
-fattenList :: Int -> [a] -> [a]
-fattenList i l = P.concat $ P.foldr ((:) . (P.replicate i)) [] l
+-- | given a filepath, read the QR code as a string in all lowercase
+readQRString :: FilePath -> IO String
+readQRString filepath = (map toLower) . init . (drop 8 . view _2) <$> readCreateProcessWithExitCode (shell $ "zbarimg " ++ filepath) ""
 
-swapWord :: Word8 -> Word8
-swapWord 1 = 0
-swapWord 0 = 1
+-- | given a filepath pointing to a QR code, get the contents & verify signature
+readQRStrSec :: FilePath -> IO String
+readQRStrSec filepath = do
+    enc <- (map toLower) . init . (drop 8) . (view _2) <$> readCreateProcessWithExitCode (shell $ "zbarimg " ++ filepath) ""
+    (fmap $ liftEither show) . checkSig . resolveUpper $ (BS.pack) enc
diff --git a/src/Data/QRCodes/Exe.hs b/src/Data/QRCodes/Exe.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/QRCodes/Exe.hs
@@ -0,0 +1,58 @@
+module Data.QRCodes.Exe (exec) where
+
+import Options.Applicative
+import qualified Data.ByteString as B
+import System.Environment (getArgs) --fix soon!
+import Data.QRCodes
+
+data Prog = Prog { cmd     :: Com
+                 , secured :: Bool
+                 , verify  :: Bool
+                 , file    :: String} --whether to verify the encoding worked
+
+data Com = Input | Output--whether to be writing *to* file or reading *from it
+
+exec :: IO ()
+exec = execParser full >>= act
+    where 
+        full = info (helper <*> program)
+            ( fullDesc
+            <> progDesc "Read/Write QR Codes with files"
+            <> header "qrpipe - QR utilities made in Haskell" )
+
+act :: Prog -> IO ()
+act (Prog Output True True filepath) = do
+    pipeIn <- B.getContents
+    byteStringToQRSec pipeIn filepath
+    readQRStrSec filepath >>= print
+act (Prog Output False True filepath) = do
+    pipeIn <- B.getContents
+    byteStringToQR pipeIn filepath
+    readQRString filepath >>= print
+act (Prog Output True False filepath) = do
+    pipeIn <- B.getContents
+    byteStringToQRSec pipeIn filepath
+act (Prog Output False False filepath) = do
+    pipeIn <- B.getContents
+    byteStringToQR pipeIn filepath
+act (Prog Input True _ filepath) = do
+    readQRStrSec filepath >>= print
+act (Prog Input False _ filepath) = do
+    readQRString filepath >>= print
+
+program :: Parser Prog
+program = Prog
+    <$> subparser
+        ( command "write" (info (pure Output)
+            ( progDesc "Create a QR Code from stdin" ))
+        <> command "read" (info (pure Input)
+            ( progDesc "Read a QR code from file" )))
+    <*> switch
+        ( long "signed"
+        <> short 's'
+        <> help "Whether to sign the QR code" )
+    <*> switch
+        ( long "verify"
+        <> short 'v'
+        <> help "Attempt to read the resultant file?" )
+    <*> argument str (metavar "FILE")
diff --git a/src/Data/QRCodes/Image.hs b/src/Data/QRCodes/Image.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/QRCodes/Image.hs
@@ -0,0 +1,18 @@
+module Data.QRCodes.Image where
+
+import Data.Word (Word8)
+import Codec.Picture.Types as T
+import Prelude as P
+import qualified Data.Vector.Storable as V
+
+encodePng :: [[Word8]] -> T.Image Word8
+encodePng matrix = Image dim dim vector
+    where dim    = P.length matrix
+          vector = V.map ((*255) . swapWord) $ V.fromList $ P.concat matrix
+
+fattenList :: Int -> [a] -> [a]
+fattenList i l = P.concat $ P.foldr ((:) . (P.replicate i)) [] l
+
+swapWord :: Word8 -> Word8
+swapWord 1 = 0
+swapWord 0 = 1
diff --git a/src/Data/QRCodes/Signature.hs b/src/Data/QRCodes/Signature.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/QRCodes/Signature.hs
@@ -0,0 +1,32 @@
+module Data.QRCodes.Signature where
+
+import Jose.Jws
+import qualified Data.ByteString.Char8 as BS
+import Jose.Jwt (unJwt, JwtError)
+import Crypto.PubKey.RSA as Cr
+import Control.Lens
+import System.Directory
+import Jose.Jwa (JwsAlg (RS256))
+import Data.QRCodes.Utils
+
+-- | Verify a signed token
+checkSig :: BS.ByteString -> IO (Either JwtError BS.ByteString)
+checkSig tok = do
+    key <- read <$> readFile ".key.hk" :: IO (Cr.PublicKey, Cr.PrivateKey)
+    let jws = rsaDecode (view _1 key) tok
+    return $ fmap (view _2) jws
+
+-- | Sign a token (note that we map the string to all lowercase before signing since QR codes do not distinguish case once read)
+mkSig :: BS.ByteString -> IO BS.ByteString
+mkSig string = do
+    switch <- doesFileExist ".key.hk"
+    if not switch then do
+        putStrLn "generating key..."
+        key <- Cr.generate 256 0x10001
+        writeFile ".key.hk" (show key)
+    else
+        return ()
+    key' <- read <$> readFile ".key.hk" :: IO (Cr.PublicKey, Cr.PrivateKey)
+    signedToken <- rsaEncode RS256 (view _2 key') string
+    let signed = fmap unJwt signedToken
+    liftEither id (return <$> signed)
diff --git a/src/Data/QRCodes/Utils.hs b/src/Data/QRCodes/Utils.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/QRCodes/Utils.hs
@@ -0,0 +1,25 @@
+module Data.QRCodes.Utils where
+
+import qualified Data.ByteString.Char8 as BS
+import Data.Char (toLower, toUpper)
+import Data.List.Utils (replace)
+
+-- | function applied to byteStrings before saving to QR code so that uppercase/lowercase signatures can be preserverd
+preserveUpper :: BS.ByteString -> BS.ByteString
+preserveUpper = lift pU
+    where pU = concatMap (\c -> if c `elem` ['A'..'Z'] then ((toLower c) : "---") else return c)
+
+-- | resolve coded string to string with uppercase
+resolveUpper :: BS.ByteString -> BS.ByteString
+resolveUpper = lift rU
+    where rU = foldr (.) id (map (\s -> replace (s : "---") ((pure . toUpper) s)) ['a'..'z'])
+
+--kind of hacky and could be fixed in all likelihood
+lift :: (String -> String) -> (BS.ByteString -> BS.ByteString)
+lift f = BS.pack . f . BS.unpack
+
+--liftIO = either (show) (((++) "success! ") . show)
+
+liftEither :: (Show b, Monad m) => (t -> m a) -> Either b t -> m a
+liftEither = either (error . show)
+
