diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for pdftotext
 
+## 0.1.0.0 -- 2020-06-18
+
+* Added executable `pdftotext.hs`
+* Removed `xml-conduit` flag, it was bad idea, sorry for that
+
 ## 0.0.2.0 -- 2020-06-11
 
 * Added PDF document properties (author, title etc.)
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -14,11 +14,38 @@
   T.putStrLn $ pdftotext Physical pdf
 ```
 
-## Flags
+## Executable
 
-### `xml-conduit`
+`pdftotext` comes with executable program `pdftotext.hs` which can print text extracted from PDF and basic information from the document.
 
-`pdftotext` can extract properties from PDF document. One of them is metadata which is in form of XML document. If `xml-conduit` flag is set then the metadata is parsed using `xml-conduit` package, otherwise provided as text.
+```shell
+$> pdftotext.hs info test/simple.pdf
+File      : test/simple.pdf
+Pages     : 4
+Properties
+  Title   : Simple document for testing
+  Author  : G. Eyaeb
+  Subject : Testing
+  Creator : pdflatex
+  Producer: LaTeX with hyperref
+  Keywords: haskell,pdf
+```
+
+```shell
+$> pdftotext.hs text --pages 1,4 test/simple.pdf
+Simple document for testing
+
+                  deserve neither
+liberty nor safety.
+```
+
+See help for more information:
+
+```shell
+$> pdftotext.hs --help
+$> pdftotext.hs text --help
+$> pdftotext.hs info --help
+```
 
 ## Internals
 
diff --git a/cbits/poppler.cc b/cbits/poppler.cc
--- a/cbits/poppler.cc
+++ b/cbits/poppler.cc
@@ -3,6 +3,36 @@
 #include <iostream>
 #include <string.h>
 
+/*
+
+Failed attempt to make poppler quiet
+====================================
+
+
+
+#include "Error.h"
+#include "GlobalParams.h"
+
+static void errorcb(ErrorCategory, Goffset pos, const char *msg) {
+  ...
+}
+
+setErrorCallback(errorcb);
+globalParams = std::make_unique<GlobalParams>();
+globalParams->setErrQuiet(true);
+
+
+
+Do not forget to add poppler to pkgconfig-depends.
+
+Neither setting error callback nor setting global param makes
+poppler quiet. It seems that the errQuiet flag is set
+and the changes are visible inside poppler.cc, however
+they seem not to be visible by poppler library. I probably
+do something wrong.
+
+*/
+
 extern "C" {
 
   std::string* to_stdstring(poppler::ustring ust) {
diff --git a/cli/Cli.hs b/cli/Cli.hs
new file mode 100644
--- /dev/null
+++ b/cli/Cli.hs
@@ -0,0 +1,178 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+
+module Main (main) where
+
+import Data.Aeson ((.=), ToJSON (..), defaultOptions, genericToJSON, object)
+import Data.Aeson.Text (encodeToLazyText)
+import Data.Bifunctor (first)
+import Data.List (sort)
+import Data.Maybe (catMaybes)
+import Data.Range (Range, fromRanges, intersection, lbi)
+import Data.Range.Parser (parseRanges)
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+import qualified Data.Text.Lazy.IO as TL
+import Options.Applicative
+import Pdftotext.Internal
+import qualified Text.PrettyPrint.ANSI.Leijen as P
+
+data Command
+  = PrintText PrintTextOptions
+  | Info InfoOptions
+
+data PrintTextOptions = PrintTextOptions
+  { prtPages :: [Range Int],
+    prtOutfile :: Maybe FilePath,
+    prtSeparate :: Bool,
+    prtColor :: Bool,
+    prtViewer :: Bool,
+    prtFile :: FilePath
+  }
+  deriving (Show)
+
+data InfoFormat = JsonFormat | PlainFormat deriving (Eq, Show)
+
+data InfoOptions = InfoOptions
+  { infFormat :: InfoFormat,
+    infFile :: FilePath
+  }
+  deriving (Show)
+
+data Information = Information
+  { iProperties :: Properties,
+    iFile :: FilePath,
+    iPages :: Int
+  }
+  deriving (Show)
+
+instance ToJSON Information where
+  toJSON Information {..} =
+    object
+      [ "file" .= iFile,
+        "pages" .= iPages,
+        "properties" .= genericToJSON defaultOptions iProperties
+      ]
+
+main :: IO ()
+main =
+  customExecParser
+    (prefs $ showHelpOnEmpty <> showHelpOnError)
+    ( info
+        (commandParser <**> helper)
+        (fullDesc <> progDesc "Extract text from PDF")
+    )
+    >>= \case
+      PrintText opts -> printText opts
+      Info opts -> printInfo opts
+
+commandParser :: Parser Command
+commandParser =
+  hsubparser
+    ( command "text" (info printOptions (progDesc "Print extracted text" <> footer "RANGE: -3,5,7-12,15,20-"))
+        <> command "info" (info infoOptions (progDesc "Show information about document"))
+    )
+
+infoOptions :: Parser Command
+infoOptions =
+  fmap Info $
+    InfoOptions
+      <$> option
+        format
+        ( long "format"
+            <> short 'f'
+            <> help "Output format (plain, json)"
+            <> value PlainFormat
+            <> completeWith ["plain", "json"]
+        )
+      <*> strArgument
+        ( metavar "FILE"
+            <> help "PDF file"
+            <> completer (bashCompleter "file")
+        )
+  where
+    format =
+      eitherReader \case
+        "json" -> Right JsonFormat
+        "plain" -> Right PlainFormat
+        f -> Left $ f ++ " is not a valid output format, use one of: plain, json"
+
+printOptions :: Parser Command
+printOptions =
+  fmap PrintText $
+    PrintTextOptions
+      <$> option
+        range
+        ( long "pages"
+            <> short 'p'
+            <> help "Range of pages to process"
+            <> metavar "RANGE"
+            <> value []
+        )
+      <*> pure Nothing -- switch (metavar "FILE" <> long "output" <> short 'o' <> help "Write output to file")
+      <*> pure False -- switch (long "separate" <> help "Separate pages")
+      <*> pure False -- switch (long "color" <> short "c" <> help "Use colors")
+      <*> pure False -- switch (long "viewer" <> short "v" <> help "Use internal viewer")
+      <*> strArgument
+        ( metavar "FILE"
+            <> help "PDF file"
+            <> completer (bashCompleter "file")
+        )
+  where
+    range = eitherReader (first show . parseRanges)
+
+printText :: PrintTextOptions -> IO ()
+printText PrintTextOptions {..} = do
+  f <- openFile prtFile
+  case f of
+    Just d -> do
+      pageNo <- pagesTotalIO d
+      pages <- mapM (flip pageIO d) (pageList pageNo prtPages)
+      txt <- mapM (pageTextIO Physical) (catMaybes pages)
+      T.putStrLn (T.concat txt)
+    _ -> putStrLn $ prtFile ++ " is not a valid PDF document"
+
+pageList :: Int -> [Range Int] -> [Int]
+pageList total [] = [1 .. total]
+pageList total ranges =
+  sort
+    $ filter (<= total)
+    $ take total
+    $ fromRanges
+    $ intersection [lbi 1] ranges
+
+printInfo :: InfoOptions -> IO ()
+printInfo InfoOptions {..} = do
+  f <- openFile infFile
+  case f of
+    Just d -> do
+      p <- propertiesIO d
+      pageno <- pagesTotalIO d
+      let i = Information p infFile pageno
+      case infFormat of
+        JsonFormat -> printInfoJson i
+        PlainFormat -> printInfoPlain i
+    _ -> putStrLn $ infFile ++ " is not a valid PDF document"
+
+printInfoJson :: Information -> IO ()
+printInfoJson p = TL.putStrLn (encodeToLazyText $ toJSON p)
+
+{- ORMOLU_DISABLE -}
+printInfoPlain :: Information -> IO ()
+printInfoPlain Information{..} =
+  P.putDoc $
+    P.text "File      :" P.<+> P.text iFile P.<$>
+    P.text "Pages     :" P.<+> P.text (show iPages) P.<$>
+    P.text "Properties" P.<$>
+      P.indent 2 (
+          P.text "Title   :" P.<+> P.text (maybe "" T.unpack title)    P.<$>
+          P.text "Author  :" P.<+> P.text (maybe "" T.unpack author)   P.<$>
+          P.text "Subject :" P.<+> P.text (maybe "" T.unpack subject)  P.<$>
+          P.text "Creator :" P.<+> P.text (maybe "" T.unpack creator)  P.<$>
+          P.text "Producer:" P.<+> P.text (maybe "" T.unpack producer) P.<$>
+          P.text "Keywords:" P.<+> P.text (maybe "" T.unpack keywords)
+        ) P.<> P.hardline
+  where Properties{..} = iProperties
+{- ORMOLU_ENABLE -}
diff --git a/pdftotext.cabal b/pdftotext.cabal
--- a/pdftotext.cabal
+++ b/pdftotext.cabal
@@ -1,7 +1,7 @@
 cabal-version:       >=1.10
 
 name:                pdftotext
-version:             0.0.2.0
+version:             0.1.0.0
 synopsis:            Extracts text from PDF using poppler
 description:         The @pdftotext@ package provides functions for extraction of plain text from PDF documents. It uses C++ library [Poppler](https://poppler.freedesktop.org/), which is required to be installed in the system. Output of Haskell @pdftotext@ library is identical to output of Poppler's tool @pdftotext@.
 homepage:            https://sr.ht/~geyaeb/haskell-pdftotext/
@@ -18,10 +18,6 @@
 source-repository head
   type:                mercurial
   location:            https://hg.sr.ht/~geyaeb/haskell-pdftotext
-
-flag xml-conduit
-  description:         Parse metadata of PDF document properties using xml-conduit
-  default:             False
   
 library
   exposed-modules:     Pdftotext
@@ -38,9 +34,19 @@
                      , cbits/stdstring.cc
   extra-libraries:     stdc++
   pkgconfig-depends:   poppler-cpp
-  if flag(xml-conduit)
-     build-depends:    xml-conduit == 1.8.*
-     cpp-options:      -DXMLC
+
+executable pdftotext.hs
+  build-depends:       base >= 4.11 && < 5
+                     , aeson >= 1.4 && < 1.6
+                     , ansi-wl-pprint == 0.6.*
+                     , optparse-applicative == 0.15.*
+                     , pdftotext
+                     , range == 0.3.*
+                     , text == 1.2.*
+  hs-source-dirs:      cli
+  main-is:             Cli.hs
+  ghc-options:         -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wcompat -Widentities -Wredundant-constraints -fhide-source-paths -Wmissing-export-lists -Wpartial-fields
+  default-language:    Haskell2010
 
 test-suite pdftotext-test
   default-language:    Haskell2010
diff --git a/src/Pdftotext.hs b/src/Pdftotext.hs
--- a/src/Pdftotext.hs
+++ b/src/Pdftotext.hs
@@ -20,10 +20,6 @@
 >   Just pdf <- openFile "path/to/file.pdf"
 >   T.putStrLn $ pdftotext Physical pdf
 
-=== Flags
-
-* @xml-conduit@ – 'metadata' of PDF document properties is parsed as XML, otherwise remains as text
-
 -}
 {- ORMOLU_ENABLE -}
 module Pdftotext
diff --git a/src/Pdftotext/Internal.hs b/src/Pdftotext/Internal.hs
--- a/src/Pdftotext/Internal.hs
+++ b/src/Pdftotext/Internal.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE BlockArguments #-}
-{-# LANGUAGE CPP #-}
+{-# LANGUAGE DeriveGeneric #-}
 
 {- ORMOLU_DISABLE -}
 {-|
@@ -42,34 +42,24 @@
 import qualified Data.Text as T
 import Foreign (ForeignPtr, newForeignPtr, nullPtr, withForeignPtr)
 import Foreign.C (withCString)
+import GHC.Generics
 import Pdftotext.Foreign
 
-#ifdef XMLC
-import qualified Text.XML as X
-import qualified Data.Text.Lazy as TL
-#endif
-
 newtype Document = Document (ForeignPtr Poppler_Document)
 
 -- | Document properties.
 --
--- If flag @xml-conduit@ is set, 'metadata' is of type @Maybe Text.XML.Document@.
---
 -- @since 0.0.2.0
 data Properties = Properties
   { author :: Maybe T.Text,
     creator :: Maybe T.Text,
     keywords :: Maybe T.Text,
-#ifdef XMLC
-    metadata :: Maybe X.Document,
-#else
     metadata :: Maybe T.Text,
-#endif
     producer :: Maybe T.Text,
     subject :: Maybe T.Text,
     title :: Maybe T.Text
   }
-  deriving (Show)
+  deriving (Show, Generic)
 
 data Page = Page
   { -- | Number of this page in original document.
@@ -148,6 +138,7 @@
         None -> 2
 
 -- | Extract properties from the document.
+--
 -- @since 0.0.2.0
 propertiesIO :: Document -> IO Properties
 propertiesIO (Document docptr) = withForeignPtr docptr \doc -> do
@@ -158,22 +149,12 @@
   p <- asText $ ffiDocumentProducer doc
   s <- asText $ ffiDocumentSubject doc
   t <- asText $ ffiDocumentTitle doc
-
-#ifdef XMLC
-  return $ Properties (f a) (f c) (f k) (xml m) (f p) (f s) (f t)
-  where
-    xml x =
-      if T.null x
-      then Nothing
-      else either (const Nothing) Just $ X.parseText X.def (TL.fromStrict x)
-#else
   return $ Properties (f a) (f c) (f k) (f m) (f p) (f s) (f t)
   where
-#endif
     f x =
       if T.null x
-      then Nothing
-      else Just x
+        then Nothing
+        else Just x
 
 -- | Extract text from PDF document with given 'Layout'.
 pdftotextIO :: Layout -> Document -> IO T.Text
