diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+Changelog for pdfname
+=====================
+
+0.1
+---
+
+First release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 Andrés Sicard-Ramírez
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+pdfname
+=======
+
+Description
+-----------
+
+Name a PDF file using information (author, year of creation and title)
+from the `pdfinfo` program.
+
+Prerequisites
+--------------
+
+* Glasgow Haskell Compiler ([GHC](https://www.haskell.org/ghc/))
+
+* [cabal-install](http://www.haskell.org/cabal/)
+
+* [pdfinfo](http://linuxcommand.org/man_pages/pdfinfo1.html) Unix
+  program
+
+Installation
+------------
+
+The program can installed with the following commands:
+
+```bash
+$ cabal update
+$ cabal install
+```
+
+Usage
+-----
+
+Just run `pdfname` on your PDF file.
+
+How is the file name chosen?
+---------------------------
+
+Given the author, year of creation and title information extracted
+from the `pdfinfo` program (fields `Author`, `CreationDate` and
+`Title`, respectively) the name of the PDF file will be
+
+```
+author-year.title.pdf
+```
+
+where `author` and `title` are the strings obtained after making
+certain substitutions (e.g. remove whitespace, translate non-ASCCI
+characters and remove TeX/LaTeX specific information) to the
+information shown by the `pdfinfo` program.
+
+Example. Let's suppose that running `pdfinfo` on the file `foo.pdf`
+shows the following (fictional and incomplete) information:
+
+```bash
+$ pdfinfo foo.pdf
+Title:          Introducction to the <TEX>$\lambda$</TEX>-Calculus
+Author:         Per Martin-Löf
+CreationDate:   Fri Apr  9 07:14:01 2010
+```
+
+Now, running `pdfname` on that file will create the new file
+
+```
+/tmp/martin-lof-2010.introduction-to-the-lambda-calculus.pdf
+```
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/pdfname.cabal b/pdfname.cabal
new file mode 100644
--- /dev/null
+++ b/pdfname.cabal
@@ -0,0 +1,65 @@
+name:          pdfname
+version:       0.1
+license:       MIT
+license-file:  LICENSE
+author:        Andrés Sicard-Ramírez
+maintainer:    Andrés Sicard-Ramírez <asr@eafit.edu.co>
+copyright:     Andrés Sicard-Ramírez 2017
+homepage:      https://github.com/asr/pdfname#readme
+bug-reports:   https://github.com/asr/pdfname/issues
+category:      PDF
+build-type:    Simple
+cabal-version: >= 1.10
+synopsis:      Name a PDF file using information from the pdfinfo command
+description:
+  pdfname names a PDF file using the author, year of creation and
+  title information extracted from the pdfinfo program.
+tested-with:   GHC == 8.0.2
+
+extra-source-files: CHANGELOG.md
+                    README.md
+
+executable pdfname
+  main-is:        Main.hs
+  hs-source-dirs: src
+
+  build-depends:  base                 >= 4.7.0.2 && < 4.11
+                , directory            >= 1.2.1.0 && < 1.4
+                , filepath             >= 1.3.0.2 && < 1.5
+                , optparse-applicative >= 0.14.0  && < 0.15
+                , pdfinfo              >= 1.5.4   && < 1.6
+                , text                 >= 1.2.0.4 && < 1.3
+
+  default-language: Haskell2010
+
+  default-extensions:  OverloadedStrings
+                     , UnicodeSyntax
+
+  other-modules:  CreateFile
+                , Options
+                , Paths_pdfname
+                , Substituions
+                , Utilities
+
+  if impl(ghc >= 8.0)
+    ghc-options:  -Wall
+                  -Widentities
+                  -Wincomplete-record-updates
+                  -Wincomplete-uni-patterns
+                  -Wderiving-typeable
+                  -Wmissing-import-lists
+                  -Wmissing-local-signatures
+                  -Wmissing-monadfail-instances
+                  -Wmissing-pattern-synonym-signatures
+                  -Wmonomorphism-restriction
+                  -Wnoncanonical-monad-instances
+                  -Wnoncanonical-monadfail-instances
+                  -Wnoncanonical-monoid-instances
+                  -Wredundant-constraints
+                  -Wsemigroup
+                  -Wunused-binds
+                  -Wunused-type-patterns
+
+  if impl(ghc >= 8.2)
+    ghc-options: -Wcpp-undef
+                 -Wsimplifiable-class-constraints
diff --git a/src/CreateFile.hs b/src/CreateFile.hs
new file mode 100644
--- /dev/null
+++ b/src/CreateFile.hs
@@ -0,0 +1,120 @@
+-- | Creation of the PDF file.
+
+module CreateFile
+  ( createFile
+  , generateFileName
+  ) where
+
+import Data.Char  ( isAscii )
+import Data.Maybe ( fromMaybe )
+
+import Data.Text ( Text )
+import qualified Data.Text as T
+
+import System.Directory ( copyFile )
+import System.FilePath  ( (</>) )
+
+import Text.PDF.Info
+  ( PDFInfo
+  , pdfInfoAuthor
+  , pdfInfoCreationDate
+  , pdfInfoTitle
+  )
+
+------------------------------------------------------------------------------
+-- Local imports
+
+import Options ( outputDir )
+
+import Substituions
+  ( authorSubst
+  , chSubst
+  , titleSubst
+  )
+
+import Utilities
+  ( (+++)
+  , die
+  , replace
+  )
+
+------------------------------------------------------------------------------
+defaultValue ∷ Text
+defaultValue = "xx"
+
+getAuthor ∷ Text → Text
+getAuthor xs =
+  if T.null xs
+  then defaultValue
+  else
+    -- NB that the substituions are not commutative.
+    ( replace chSubst
+      . T.intercalate (T.singleton '-')
+      . map (T.toLower . T.reverse . T.takeWhile (' ' /=) . T.reverse)
+      . T.split (',' ==)
+      . replace authorSubst
+    ) xs
+
+getYear ∷ Text → Text
+getYear xs = if T.null xs then defaultValue else T.take 4 xs
+
+getTitle ∷ Text → Text
+getTitle xs =
+  if T.null xs
+  then defaultValue
+  else
+    -- NB that the substituions are not commutative.
+    ( T.toLower
+      . replace chSubst
+      . replace titleSubst
+    ) xs
+
+generateFileName ∷ PDFInfo → IO FilePath
+generateFileName info = do
+
+  let authorHelper ∷ Text
+      authorHelper = maybe defaultValue getAuthor $ pdfInfoAuthor info
+
+  author ← if validateName authorHelper
+           then return authorHelper
+           else die $ "could not generate the author from "
+                       +++ "`"
+                       +++ fromMaybe "missing author field" (pdfInfoAuthor info)
+                       +++ "`"
+
+  let year ∷ Text
+      year = maybe defaultValue (getYear . T.pack . show ) $ pdfInfoCreationDate info
+
+  let titleHelper ∷ Text
+      titleHelper = maybe defaultValue getTitle $ pdfInfoTitle info
+
+  title ← if validateName titleHelper
+            then return titleHelper
+            else die $ "could not generate the title from "
+                       +++ "`"
+                       +++ fromMaybe "missing title field" (pdfInfoTitle info)
+                       +++ "`"
+
+  let fileName ∷ FilePath
+      fileName = T.unpack $ foldl1 T.append
+                   [ author
+                   , T.singleton '-'
+                   , year
+                   , T.singleton '.'
+                   , title
+                   , ".pdf"
+                   ]
+
+  return fileName
+
+-- TODO (2017-07-04): Is there Data.Text functions instead of
+-- Data.Char functions for implementing this function?
+validateName ∷ Text → Bool
+validateName t = all (== True) $ map isAscii $ T.unpack t
+
+createFile ∷ FilePath → FilePath → IO ()
+createFile f newF =
+  copyFile f outputPath >> putStrLn ("Created " ++ outputPath)
+  where
+  outputPath ∷ FilePath
+  outputPath = outputDir </> newF
diff --git a/src/Main.hs b/src/Main.hs
new file mode 100644
--- /dev/null
+++ b/src/Main.hs
@@ -0,0 +1,66 @@
+-- | pdfname: Name a PDF file using the information from the `pdfinfo`
+-- command.
+
+module Main where
+
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+
+import Options.Applicative ( execParser )
+
+import System.Exit     ( exitFailure )
+import System.FilePath ( (</>) )
+
+import System.IO
+  ( hPrint
+  , stderr
+  )
+
+import Text.PDF.Info ( pdfInfo )
+
+------------------------------------------------------------------------------
+-- Local imports
+
+import CreateFile
+  ( createFile
+  , generateFileName
+  )
+
+import Options
+  ( options
+  , Options( optDryRun
+           , optInputFile
+           )
+  , outputDir
+  )
+
+import Utilities
+  ( (+++)
+  , die
+  , isPDF
+  , unlessM
+  )
+
+------------------------------------------------------------------------------
+
+main ∷ IO ()
+main = do
+  opts ← execParser options
+
+  let file ∷ FilePath
+      file = optInputFile opts
+
+  unlessM (isPDF file) $ die $ T.pack file +++ " is not a PDF file"
+
+  info ← pdfInfo file
+  case info of
+    Right i → do
+      newFile ← generateFileName i
+      if optDryRun opts
+        then putStrLn $ "The full path name will be " ++ outputDir </> newFile
+        else createFile file newFile
+
+    Left  err → do
+      T.hPutStr stderr "pdfInfo exception: "
+      hPrint stderr err
+      exitFailure
diff --git a/src/Options.hs b/src/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/Options.hs
@@ -0,0 +1,65 @@
+-- | Process the command-line arguments.
+
+module Options
+  ( options
+  , Options( Options -- Improve Haddock information.
+           , optDryRun
+           , optInputFile
+           )
+  , outputDir
+  ) where
+
+import Data.Monoid ( (<>) )
+
+import Options.Applicative
+  ( argument
+  , info
+  , infoOption
+  , help
+  , helper
+  , long
+  , metavar
+  , Parser
+  , ParserInfo
+  , short
+  , str
+  , switch
+  )
+
+------------------------------------------------------------------------------
+-- Local imports
+
+import Utilities ( progNameVersion )
+
+------------------------------------------------------------------------------
+-- | Program command-line options.
+data Options = Options
+  { optDryRun    ∷ Bool
+  , optInputFile ∷ FilePath
+  }
+
+pOptDryRun ∷ Parser Bool
+pOptDryRun =
+  switch ( long "dry-run"
+           <> help ("Do not create the PDF file only print which would be "
+                    ++ "its full path name")
+         )
+
+-- | Parse a version flag.
+pOptVersion ∷ Parser (a → a)
+pOptVersion = infoOption progNameVersion $
+  long "version"
+  <> short 'V'
+  <> help "Show version number"
+
+pOptInputFile ∷ Parser FilePath
+pOptInputFile = argument str (metavar "FILE")
+
+pOptions :: Parser Options
+pOptions = Options <$> pOptDryRun <*> pOptInputFile
+
+options ∷ ParserInfo Options
+options = info (helper <*> pOptVersion <*> pOptions) mempty
+
+outputDir ∷ FilePath
+outputDir = "/tmp/"
diff --git a/src/Substituions.hs b/src/Substituions.hs
new file mode 100644
--- /dev/null
+++ b/src/Substituions.hs
@@ -0,0 +1,272 @@
+-- | Substituions.
+
+module Substituions
+  ( authorSubst
+  , chSubst
+  , titleSubst
+  ) where
+
+import Data.Text ( Text )
+
+------------------------------------------------------------------------------
+-- Characters substituions
+
+-- If a new entry is added here, please also add it to the
+-- characters-decimal substituions.
+
+-- | Characters in non-numeric notation.
+chNNSubst ∷ [(Text,Text)]
+chNNSubst =
+  [ ("&ouml;", "o")  -- LATIN SMALL LETTER O WITH DIAERESIS   (ö)
+  , ("&Auml;", "A")  -- LATIN CAPITAL LETTER A WITH DIAERESIS (Ä)
+  , ("&Uuml;", "U")  -- LATIN CAPITAL LETTER U WITH DIAERESIS (Ü)
+  ]
+
+-- If a new entry is added here, please also add it to the
+-- characters-hexadecimal substituions.
+
+-- | Characters substituions in decimal notation.
+chDecSubst ∷ [(Text,Text)]
+chDecSubst =
+  [ ("&#225;",  "a")              -- LATIN SMALL LETTER A WITH ACUTE
+  , ("&#233;",  "e")              -- LATIN SMALL LETTER E WITH ACUTE
+  , ("&#237;",  "i")              -- LATIN SMALL LETTER I WITH ACUTE
+  , ("&#243;",  "o")              -- LATIN SMALL LETTER O WITH ACUTE
+  , ("&#246;",  "o")              -- LATIN SMALL LETTER O WITH DIAERESIS (ö)
+  , ("&#250;",  "u")              -- LATIN SMALL LETTER U WITH ACUTE
+  , ("&#352",   "S")              -- LATIN CAPITAL LETTER S WITH CARON
+  , ("&#353",   "s")              -- LATIN SMALL LETTER S WITH CARON
+  , ("&#955;",  "lambda")         -- GREEK SMALL LETTER LAMDA
+  , ("&#8216;", "")               -- LEFT SINGLE QUOTATION MARK
+  , ("&#8217",  "")               -- RIGHT SINGLE QUOTATION MARK
+  ]
+
+-- | Characters substituions in hexadecimal notation.
+chHexSubst ∷ [(Text,Text)]
+chHexSubst =
+  [ ("&#x00C4;",  "A")              -- LATIN CAPITAL LETTER A WITH DIAERESIS (Ä)
+  , ("&#x00DC;",  "U")              -- LATIN CAPITAL LETTER U WITH DIAERESIS (Ü)
+  , ("&#x00E1;",  "a")              -- LATIN SMALL LETTER A WITH ACUTE
+  , ("&#x00E9;",  "e")              -- LATIN SMALL LETTER E WITH ACUTE
+  , ("&#x00ED;",  "i")              -- LATIN SMALL LETTER I WITH ACUTE
+  , ("&#x00F3;",  "o")              -- LATIN SMALL LETTER O WITH ACUTE
+  , ("&#x00FA;",  "u")              -- LATIN SMALL LETTER U WITH ACUTE
+  , ("&#x00F6;",  "o")              -- LATIN SMALL LETTER O WITH DIAERESIS (ö)
+  , ("&#x00FC;",  "u")              -- LATIN SMALL LETTER U WITH DIAERESIS (ü)
+  , ("&#x0160",   "S")              -- LATIN CAPITAL LETTER S WITH CARON
+  , ("&#x0161",   "s")              -- LATIN SMALL LETTER S WITH CARON
+  , ("&#x012A;",  "I")              -- LATIN CAPITAL LETTER I WITH MACRON
+  , ("&#x012B;",  "I")              -- LATIN SMALL LETTER I WITH MACRON
+  , ("&#x015A;",  "S")              -- LATIN CAPITAL LETTER S WITH ACUTE
+  , ("&#x015B;",  "s")              -- LATIN SMALL LETTER S WITH ACUTE
+  , ("&#x015E;",  "s")              -- LATIN CAPITAL LETTER S WITH CEDILLA
+  , ("&#x015F;",  "s")              -- LATIN SMALL LETTER S WITH CEDILLA
+  , ("&#x012A;",  "I")              -- LATIN CAPITAL LETTER I WITH MACRON
+  , ("&#x012B;",  "i")              -- LATIN SMALL LETTER I WITH MACRON
+  , ("&#x03B1;",  "alpha")          -- GREEK SMALL LETTER ALPHA
+  , ("&#x03B2;",  "beta")           -- GREEK SMALL LETTER BETA
+  , ("&#x03B3;",  "gamma")          -- GREEK SMALL LETTER GAMMA
+  , ("&#x03B4;",  "delta")          -- GREEK SMALL LETTER DELTA
+  , ("&#x03B5;",  "epsilon")        -- GREEK SMALL LETTER EPSILON
+  , ("&#x03B6;",  "zeta")           -- GREEK SMALL LETTER ZETA
+  , ("&#x03B7;",  "eta")            -- GREEK SMALL LETTER ETA
+  , ("&#x03B8;",  "theta")          -- GREEK SMALL LETTER THETA
+  , ("&#x03B9;",  "iota")           -- GREEK SMALL LETTER IOTA
+  , ("&#x03BA;",  "kappa")          -- GREEK SMALL LETTER KAPPA
+  , ("&#x03BB;",  "lambda")         -- GREEK SMALL LETTER LAMDA
+  , ("&#x03BC;",  "mu")             -- GREEK SMALL LETTER MU
+  , ("&#x03BD;",  "nu")             -- GREEK SMALL LETTER NU
+  , ("&#x03BE;",  "xi")             -- GREEK SMALL LETTER XI
+  , ("&#x03BF;",  "omicron")        -- GREEK SMALL LETTER OMICRON
+  , ("&#x03C0;",  "pi")             -- GREEK SMALL LETTER PI
+  , ("&#x03C1;",  "rho")            -- GREEK SMALL LETTER RHO
+  , ("&#x03C2;",  "sigma")          -- GREEK SMALL LETTER FINAL SIGMA
+  , ("&#x03C3;",  "sigma")          -- GREEK SMALL LETTER SIGMA
+  , ("&#x03C4;",  "tau")            -- GREEK SMALL LETTER TAU
+  , ("&#x03C5;",  "upsilon")        -- GREEK SMALL LETTER UPSILON
+  , ("&#x03C6;",  "phi")            -- GREEK SMALL LETTER PHI
+  , ("&#x03C7;",  "chi")            -- GREEK SMALL LETTER CHI
+  , ("&#x03C8;",  "psi")            -- GREEK SMALL LETTER PSI
+  , ("&#x03C9;",  "omega")          -- GREEK SMALL LETTER OMEGA
+  , ("&#x2010;",  "-")              -- HYPHEN
+  , ("&#x2013;",  "-")              -- EN DASH
+  , ("&#x2014;",  ".")              -- EM DAS
+  , ("&#x2018;",  "")               -- LEFT SINGLE QUOTATION MARK
+  , ("&#x2019;",  "")               -- RIGHT SINGLE QUOTATION MARK
+  , ("&#x201A;",  "")               -- SINGLE LOW-9 QUOTATION MAR
+  , ("&#x201C;",  "")               -- LEFT DOUBLE QUOTATION MARK
+  , ("&#x201D;",  "")               -- RIGHT DOUBLE QUOTATION MARK
+  , ("&#x201E;",  "")               -- DOUBLE LOW-9 QUOTATION MARK
+  , ("&#x2020;",  "dagger")         -- DAGGER
+  , ("&#x2021;",  "dagger-dagger")  -- DOUBLE DAGGER
+  , ("&#x2022;",  "")               -- BULLET
+  , ("&#x2026;",  "")               -- HORIZONTAL ELLIPSIS
+  , ("&#x2283;",  "")               -- SUPERSET OF
+  , ("&#x231D;",  "")               -- TOP RIGHT CORNER
+  , ("&#x02010;", "-")              -- HYPHEN
+  ]
+
+-- | Characters substituions in Unicode notation.
+chUnicodeSubst ∷ [(Text,Text)]
+chUnicodeSubst =
+  [ ("\r",          "")         -- U+000D CARRIAGE RETURN (CR)
+  , (" ",           "-")        -- U+0020 SPACE
+  , ("!",           "")         -- U+0021 EXCLAMATION MARK
+  , ("\"",          "")         -- U+0022 QUOTATION MARK
+  , ("#",           "")         -- U+0023 NUMBER SIGN
+  , ("$",           "")         -- U+0024 DOLLAR SIGN
+  , ("&",           "")         -- U+0026 AMPERSAND
+  , ("'",           "")         -- U+0027 APOSTROPHE
+  , ("(",           "")         -- U+0028 LEFT PARENTHESIS
+  , (")",           "")         -- U+0029 RIGHT PARENTHESIS
+  , ("*",           "")         -- U+002A ASTERISK
+  , ("+",           "")         -- U+002B PLUS SIGN
+  , (",",           "")         -- U+002C COMMA
+  , ("/",           "-")        -- U+002F SOLIDUS
+  , ("²",           "2")        -- U+00B2 SUPERSCRIPT TWO
+  , ("³",           "3")        -- U+00B3 SUPERSCRIPT THREE
+  , ("¹",           "1")        -- U+00B9 SUPERSCRIPT ONE
+  , (":",           ".")        -- U+003A COLON
+  , (";",           ".")        -- U+003B SEMICOLON
+  , ("<",           "")         -- U+003C LESS-THAN SIGN
+  , ("=",           "")         -- U+003D EQUALS SIGN
+  , (">",           "")         -- U+003E GREATER-THAN SIGN
+  , ("?",           "")         -- U+003F QUESTION MARK
+  , ("@",           "")         -- U+0040 COMMERCIAL AT
+  , ("[",           "")         -- U+005B LEFT SQUARE BRACKET
+  , ("\\",          "")         -- U+005C REVERSE SOLIDUS
+  , ("]",           "")         -- U+005D RIGHT SQUARE BRACKET
+  , ("_",           "-")        -- U+005F LOW LINE
+  , ("`",           "")         -- U+0060 GRAVE ACCENT
+  , ("|",           "")         -- U+007C VERTICAL LINE
+  , ("¡",           "")         -- U+00A1 INVERTED EXCLAMATION MARK
+  , ("¬",           "")         -- U+00AC NOT SIGN
+  , ("À",           "A")        -- U+00C0 LATIN CAPITAL LETTER A WITH GRAVE
+  , ("Á",           "A")        -- U+00C1 LATIN CAPITAL LETTER A WITH ACUTE
+  , ("Ã",           "A")        -- U+00C3 LATIN CAPITAL LETTER A WITH TILDE
+  , ("Æ",           "E")        -- U+00C6 LATIN CAPITAL LETTER AE
+  , ("É",           "E")        -- U+00C9 LATIN CAPITAL LETTER E WITH ACUTE
+  , ("Í",           "I")        -- U+00CD LATIN CAPITAL LETTER I WITH ACUTE
+  , ("Ñ",           "N")        -- U+00D1 LATIN CAPITAL LETTER N WITH TILDE
+  , ("Ó",           "O")        -- U+00D3 LATIN CAPITAL LETTER O WITH ACUTE
+  , ("Ú",           "U")        -- U+00DA LATIN CAPITAL LETTER U WITH ACUTE
+  , ("Ö",           "O")        -- U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS
+  , ("×",           "")         -- U+00D7 MULTIPLICATION SIGN
+  , ("à",           "a")        -- U+00E0 LATIN SMALL LETTER A WITH GRAVE
+  , ("á",           "a")        -- U+00E1 LATIN SMALL LETTER A WITH ACUTE
+  , ("â",           "a")        -- U+00E2 LATIN SMALL LETTER A CIRCUMFLEX
+  , ("ã",           "a")        -- U+00E3 LATIN SMALL LETTER A WITH TILDE
+  , ("ä",           "a")        -- U+00E4 LATIN SMALL LETTER A WITH DIAERESIS
+  , ("æ",           "e")        -- U+00E6 LATIN SMALL LETTER AE
+  , ("ç",           "c")        -- U+00E7 LATIN SMALL LETTER C WITH CEDILLA
+  , ("é",           "e")        -- U+00E9 LATIN SMALL LETTER E WITH ACUTE
+  , ("í",           "i")        -- U+00ED LATIN SMALL LETTER I WITH ACUTE
+  , ("ñ",           "n")        -- U+00F1 LATIN SMALL LETTER N WITH TILDE
+  , ("ò",           "o")        -- U+00F2 LATIN SMALL LETTER O WITH GRAVE
+  , ("ó",           "o")        -- U+00F3 LATIN SMALL LETTER O WITH ACUTE
+  , ("ö",           "o")        -- U+00F6 LATIN SMALL LETTER O WITH DIAERESIS
+  , ("ø",           "o")        -- U+00F8 LATIN SMALL LETTER O WITH STROKE
+  , ("ú",           "u")        -- U+00FA LATIN SMALL LETTER U WITH ACUTE
+  , ("ü",           "u")        -- U+00FC LATIN SMALL LETTER U WITH DIAERESIS
+  , ("þ",           "t")        -- U+00FE LATIN SMALL LETTER THORN
+  , ("ÿ",           "y")        -- U+00FF LATIN SMALL LETTER Y WITH DIAERESIS
+  , ("ć",           "c")        -- U+0107 LATIN SMALL LETTER C WITH ACUTE
+  , ("č",           "c")        -- U+010D LATIN SMALL LETTER C WITH CARON
+  , ("Ł",           "L")        -- U+0141 LATIN CAPITAL LETTER L WITH STROKE
+  , ("ņ",           "n")        -- U+0146 LATIN SMALL LETTER N WITH CEDILLA
+  , ("ř",           "r")        -- U+0159 LATIN SMALL LETTER R WITH CARON
+  , ("š",           "s")        -- U+0161 LATIN SMALL LETTER S WITH CARON
+  , ("ū",           "u")        -- U+016B LATIN SMALL LETTER U WITH MACRON
+  , ("Ÿ",           "Y")        -- U+0178 LATIN CAPITAL LETTER Y WITH DIAERESIS
+  , ("Ω",           "Omega")    -- U+03A9 GREEK CAPITAL LETTER OMEGA
+  , ("α",           "alpha")    -- U+03B1 GREEK SMALL LETTER ALPHA
+  , ("β",           "beta")     -- U+03B2 GREEK SMALL LETTER BETA
+  , ("γ",           "gamma")    -- U+03B3 GREEK SMALL LETTER GAMMA
+  , ("δ",           "delta")    -- U+03B4 GREEK SMALL LETTER DELTA
+  , ("ε",           "epsilon")  -- U+03B5 GREEK SMALL LETTER EPSILON
+  , ("ζ",           "zeta")     -- U+03B6 GREEK SMALL LETTER ZETA
+  , ("η",           "eta")      -- U+03B7 GREEK SMALL LETTER ETA
+  , ("θ",           "theta")    -- U+03B8 GREEK SMALL LETTER THETA
+  , ("ι",           "iota")     -- U+03B9 GREEK SMALL LETTER IOTA
+  , ("κ",           "kappa")    -- U+03BA GREEK SMALL LETTER KAPPA
+  , ("λ",           "lambda")   -- U+03BB GREEK SMALL LETTER LAMDA
+  , ("μ",           "mu")       -- U+03BC GREEK SMALL LETTER MU
+  , ("ν",           "nu")       -- U+03BD GREEK SMALL LETTER NU
+  , ("ξ",           "xi")       -- U+03BE GREEK SMALL LETTER ZI
+  , ("ο",           "omicron")  -- U+03BF GREEK SMALL LETTER OMICRON
+  , ("π",           "pi")       -- U+03C0 GREEK SMALL LETTER PI
+  , ("ρ",           "rho")      -- U+03C1 GREEK SMALL LETTER RHO
+  , ("σ",           "sigma")    -- U+03C3 GREEK SMALL LETTER SIGMA
+  , ("ς",           "sigma")    -- U+03C2 GREEK SMALL LETTER FINAL SIGMA
+  , ("τ",           "tau")      -- U+03C4 GREEK SMALL LETTER TAU
+  , ("υ",           "upsilon")  -- U+03C5 GREEK SMALL LETTER UPSILON
+  , ("φ",           "phi")      -- U+03C6 GREEK SMALL LETTER PHI
+  , ("χ",           "chi")      -- U+03C7 GREEK SMALL LETTER CHI
+  , ("ψ",           "psi")      -- U+03C8 GREEK SMALL LETTER PSI
+  , ("ω",           "omega")    -- U+03C9 GREEK SMALL LETTER OMEGA
+  , ("–",           "-")        -- U+2013 EN DASH
+  , ("—",           "-")        -- U+2014 EM DASH
+  , ("‘",           "")         -- U+2018 LEFT SINGLE QUOTATION MARK
+  , ("’",           "")         -- U+2019 RIGHT SINGLE QUOTATION MARK
+  , ("‡",           "")         -- U+2021 DOUBLE DAGGER
+  , ("™",           "")         -- U+2122 TRADE MARK SIGN
+  , ("�",          "")         -- U+FFFD REPLACEMENT CHARACTER
+  ]
+
+-- | All the characters substituions.
+-- NB that the substituions are not commutative.
+chSubst ∷ [(Text, Text)]
+chSubst =  chHexSubst ++ chNNSubst ++ chDecSubst ++ chUnicodeSubst
+
+------------------------------------------------------------------------------
+-- Author substituions
+
+authorSubst ∷ [(Text, Text)]
+authorSubst =
+  [ (", ",   ",")
+  , (" and", ",")
+  -- See Issue #1.
+  , ("Ã\x00AD", "i")  -- U+00C3 and U+00AD
+  -- See Issue #1.
+  , ("Ã¡",      "a")  -- U+00C3 and U+00A1
+  , ("Mcbride", "McBride")
+  ]
+
+------------------------------------------------------------------------------
+-- Title substituions
+
+-- These substituions should be done before converting to lower case.
+titleSubst ∷ [(Text,Text)]
+titleSubst =
+  [ ("<Emphasis Type=\"BoldItalic\">P </Emphasis>",       "P")
+  , ("<Emphasis Type=\"Italic\">0 </Emphasis>",           "0")
+  , ("<Emphasis Type=\"Italic\">C</Emphasis>",            "C")
+  , ("<Emphasis Type=\"Italic\">CC</Emphasis>",           "CC")
+  , ("<Emphasis Type=\"Italic\">I </Emphasis>",           "I")
+  , ("<Emphasis Type=\"Italic\">J</Emphasis>",            "J")
+  , ("<Emphasis Type=\"Italic\">Modus ponens</Emphasis>", "Modus ponens")
+  , ("<Emphasis Type=\"Italic\">P </Emphasis>",           "P")
+  , ("<Emphasis Type=\"Italic\">really </Emphasis>",      "really")
+  , ("<Emphasis Type=\"Italic\">S-P</Emphasis>",          "S-P")
+  , ("<Subscript>3</Subscript>",                          "3")
+  -- The whitespace around `+` is not the standard one.
+  -- TODO (2017-07-04): Added test case.
+  , ("<Superscript> + </Superscript>",                    "plus")
+  , ("<Subscript>&#x03C9;</Subscript>",                   "omega")
+  , ("<TEX>$\\alpha$</TEX>",                              "alpha")
+  , ("<TEX>$\\beta$</TEX>",                               "beta")
+  , ("<TEX>$\\gamma$</TEX>",                              "gamma")
+  , ("<TEX>$\\epsilon$</TEX>",                            "epsilon")
+  , ("<TEX>$\\eta$</TEX>",                                "eta")
+  -- See Issue #2.
+  , ("<TEX>$\\lambda$</TEX>",                             "lambda")
+  , ("<TEX>$\\pi$</TEX>",                                 "pi")
+  , ("<TEX>$\\omega$</TEX>",                              "omega")
+  , ("<TEX>{\\sc Coq}</TEX>",                             "Coq")
+  , ("<TEX>{\\sf Haskell}</TEX>:",                        "Haskell")
+  , ("<TEX>{\\sc QuickSpec}</TEX>:",                      "QuickSpec")
+  , ("<TEX>{\\sc QuodLibet}</TEX>!",                      "QuodLibet")
+  , ("<TEX>{\\sc Vampire}</TEX>",                         "Vampire")
+  , ("Å›", "s")  -- U+00C5 and U+203A
+  , ("Ã¶", "")   -- U+00C3 and U+00B6
+  ]
diff --git a/src/Utilities.hs b/src/Utilities.hs
new file mode 100644
--- /dev/null
+++ b/src/Utilities.hs
@@ -0,0 +1,85 @@
+-- | Utilities.
+
+module Utilities
+ ( (+++)
+ , die
+ , ifM
+ , isPDF
+ , progNameVersion
+ , replace
+ , unless_
+ , unlessM
+ , upperFirst
+ ) where
+
+import Data.Char ( toUpper )
+
+import Data.Text ( Text )
+import qualified Data.Text as T
+import qualified Data.Text.IO as T
+
+import Data.Version ( showVersion )
+
+import Control.Monad ( replicateM, unless, void )
+
+import Paths_pdfname ( version )
+
+import System.Environment ( getProgName )
+import System.Exit        ( exitFailure )
+
+import System.IO
+  ( IOMode(ReadMode)
+  , hGetChar
+  , stderr
+  , withBinaryFile
+  )
+
+------------------------------------------------------------------------------
+-- Utilities from the Agda library
+
+ifM ∷ Monad m ⇒ m Bool → m a → m a → m a
+ifM c m m' = do
+  b ← c
+  if b then m else m'
+
+-- | @unless_@ is just @Control.Monad.unless@ with a more general type.
+unless_ ∷ Monad m ⇒ Bool → m a → m ()
+unless_ b m = unless b $ void m
+
+unlessM ∷ Monad m ⇒ m Bool → m a → m ()
+unlessM c m = c >>= (`unless_` m)
+
+------------------------------------------------------------------------------
+-- Utilities
+
+die ∷ Text → IO a
+die err = do
+  progName ← getProgName
+  T.hPutStrLn stderr (T.pack progName +++ ": " +++ err)
+  exitFailure
+
+replace ∷ [(Text,Text)] → Text → Text
+replace xs ys = foldl (flip (uncurry T.replace)) ys xs
+
+-- | Uppercase the first character of a text.
+upperFirst ∷ Text → Text
+upperFirst xs =
+  case T.uncons xs of
+    Nothing        → T.empty
+    Just (x', xs') → T.cons (toUpper x') (T.toLower xs')
+
+-- | Append synonymous.
+(+++) ∷ Text → Text → Text
+(+++) = T.append
+
+-- | Return version information.
+-- TODO (2017-07-06): To use `getProgName`.
+progNameVersion ∷ String
+progNameVersion = "pdfname" ++ " version " ++ showVersion version
+
+isPDF ∷ FilePath → IO Bool
+isPDF f =
+  ifM ((==) "%PDF" <$> withBinaryFile f ReadMode (replicateM 4 . hGetChar))
+      (return True)
+      (return False)
+
