diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,19 @@
+# Changelog
+
+`xml-prettify-text` uses [PVP Versioning][1].
+The changelog is available [on GitHub][2].
+
+## 1.0.0.1
+
+* Use [`with-utf8`](https://hackage.haskell.org/package/with-utf8) to fix encoding error in command-line app.
+
+## 1.0.0.0
+
+* Initial release version.
+
+## 0.0.0.0
+
+* Initially created.
+
+[1]: https://pvp.haskell.org
+[2]: https://github.com/MrcJkb/xml-prettify-text/releases
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,46 @@
+# xml-prettify-text
+
+XML pretty printer based on [xml-prettify](https://github.com/rosenbergdm/xml-prettify) by David M. Rosenberg. Modified to work with `Text` instead of `String` and tuned for improved performance.
+
+[![Build status](https://img.shields.io/travis/MrcJkb/xml-prettify-text.svg?logo=travis)](https://app.travis-ci.com/MrcJkb/xml-prettify-text)
+[![Hackage](https://img.shields.io/hackage/v/xml-prettify-text.svg?logo=haskell)](https://hackage.haskell.org/package/xml-prettify-text)
+[![Stackage Lts](http://stackage.org/package/xml-prettify-text/badge/lts)](http://stackage.org/lts/package/xml-prettify-text)
+[![Stackage Nightly](http://stackage.org/package/xml-prettify-text/badge/nightly)](http://stackage.org/nightly/package/xml-prettify-text)
+[![GPL-2.0-only license](https://img.shields.io/badge/license-GPL--2.0--only-blue.svg)](LICENSE)
+
+## Usage ##
+```bash
+Usage: xml-prettify ((-f|--file FILE_NAME) | (-t|--text XML_TEXT))
+                    [(-o|--out FILE_NAME) | (-c|--console)]
+                    [--indent-style <TAB | SPACE SIZE>] [--eol <LF | CR | CRLF>]
+  Pretty-prints XML text
+
+Available options:
+  -f,--file FILE_NAME      XML file to pretty-print
+  -t,--text XML_TEXT       XML text to pretty-print
+  -o,--out FILE_NAME       XML file to pretty-print to
+  -c,--console             Output the pretty-printed XML to the console
+  --indent-style <TAB | SPACE SIZE>
+                           The indent style (TAB or SPACE
+                           INDENT_SIZE) (default: SPACE 2)
+  --eol <LF | CR | CRLF>   The line-break style: Line Feed (LF), Carriage Return
+                           (CR), or both (CRLF) (default: LF)
+  -h,--help                Show this help text
+  ```
+
+## Motivation ##
+Why re-implement [xml-prettify](https://github.com/rosenbergdm/xml-prettify)? 
+- I am not satisfied with the [Text.PrettyPrint](https://hackage.haskell.org/package/pretty-1.1.3.6/docs/Text-PrettyPrint.html#t:Doc) output.
+- `xml-prettify` produces good output, but with poor performance.
+- `xml-prettify` appears not to be maintained (I haven't been able to use it as a library in modern projects).
+- Some more configurability (see TODOs) would be great.
+- A good project for me to practise profiling and benchmarking in Haskell.
+
+## TODO ##
+- [x] Port xml-prettify for use with Text
+- [x] Refactor for better readability
+- [x] Implement golden tests
+- [x] Profile, benchmark & squeeze performance
+- [x] Add options for specifying newlines, indent-style, indent-size, etc.
+- [x] Create cli app
+
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,135 @@
+{-# LANGUAGE RecordWildCards #-}
+
+module Main (main) where
+
+import Control.Exception
+import qualified Data.Text.IO as TIO
+import Options.Applicative as Opt
+import System.IO.Error (ioeGetFileName, isDoesNotExistError)
+import Text.XML.Prettify
+import Prelude
+import GHC.IO.Exception (ExitCode)
+import Data.Maybe (fromMaybe)
+import Control.Monad.IO.Class (liftIO)
+import Main.Utf8 (withUtf8)
+
+main :: IO ()
+main = withUtf8 $
+  (execParser options >>= runMainWithConfig)
+    `catches` [ Handler parserExit,
+                Handler printIOError,
+                Handler printError
+              ]
+  where
+    options =
+      info
+        (mkOptions <**> helper)
+        ( fullDesc
+            <> progDesc
+              "Pretty-prints XML text"
+        )
+    parserExit :: ExitCode -> IO ()
+    parserExit _ = pure ()
+    printIOError :: IOException -> IO ()
+    printIOError e
+      | isDoesNotExistError e = do
+        let mbfn = ioeGetFileName e
+        putStrLn $ "File " ++ fromMaybe "" mbfn ++ " not found."
+      | otherwise = putStrLn $ "I/O error: " ++ show e
+    printError :: SomeException -> IO ()
+    printError = print
+
+runMainWithConfig :: Options -> IO ()
+runMainWithConfig Options {..} = do
+  uglyXml <- case inputOption of
+    (FromFile filePath) -> TIO.readFile filePath
+    (FromText xmlText) -> pure xmlText
+  let prettyXml = prettyPrintXml prettifyOptions uglyXml
+  liftIO $ case outputOption of
+    (ToFile filePath) -> TIO.writeFile filePath prettyXml
+    _ -> TIO.putStrLn prettyXml
+
+data Options = Options
+  { inputOption :: InputOption,
+    outputOption :: OutputOption,
+    prettifyOptions :: PrettifyOpts
+  }
+
+data InputOption = FromFile FilePath | FromText XmlText
+
+data OutputOption = ToFile FilePath | ToConsole
+
+mkOptions :: Parser Options
+mkOptions = Options <$> inputOpt <*> outputOpt <*> prettifyOpts
+  where
+    inputFile :: Parser FilePath
+    inputFile =
+      strOption
+        ( long "file"
+            <> short 'f'
+            <> metavar "FILE_NAME"
+            <> help "XML file to pretty-print"
+        )
+    inputText :: Parser XmlText
+    inputText =
+      strOption
+        ( long "text"
+            <> short 't'
+            <> metavar "XML_TEXT"
+            <> help "XML text to pretty-print"
+        )
+    inputOpt = (FromFile <$> inputFile) <|> (FromText <$> inputText)
+    outputFile :: Parser FilePath
+    outputFile =
+      strOption
+        ( long "out"
+            <> short 'o'
+            <> metavar "FILE_NAME"
+            <> help "XML file to pretty-print to"
+        )
+    outputToFile = ToFile <$> outputFile
+    outputToConsole :: Parser OutputOption
+    outputToConsole =
+      flag
+        ToConsole
+        ToConsole
+        ( long "console"
+            <> short 'c'
+            <> help "Output the pretty-printed XML to the console"
+        )
+    outputOpt :: Parser OutputOption
+    outputOpt = outputToFile <|> outputToConsole
+    eol :: Parser EndOfLine
+    eol =
+      Opt.option
+        auto
+        ( long "eol"
+            <> help "The line-break style: Line Feed (LF), Carriage Return (CR), or both (CRLF)"
+            <> showDefault
+            <> value LF
+            <> metavar "<LF | CR | CRLF>"
+        )
+    identStyle :: Parser IndentStyle
+    identStyle =
+      Opt.option
+        auto
+        ( long "indent-style"
+            <> help "The indent style (TAB or SPACE INDENT_SIZE)"
+            <> showDefault
+            <> value (SPACE 2)
+            <> metavar "<TAB | SPACE SIZE>"
+        )
+    prettifyOpts :: Parser PrettifyOpts
+    prettifyOpts = PrettifyOpts <$> identStyle <*> eol
+
+-- Simulates repeated uses for profiling
+-- profileSimulate :: IO ()
+-- profileSimulate = do
+--   uglyXml <- TIO.readFile uglyXmlFile
+--   let uglyXmlList = replicate nRuns uglyXml
+--   let prettyXmlList = map prettyPrintXmlDefault uglyXmlList
+--   pure (deepseq prettyXmlList "Done") >>= putStrLn
+--   where
+--     uglyXmlFile :: FilePath
+--     uglyXmlFile = "data/tests/prettyPrintTest.xml"
+--     nRuns = 10000
diff --git a/benchmark/Main.hs b/benchmark/Main.hs
new file mode 100644
--- /dev/null
+++ b/benchmark/Main.hs
@@ -0,0 +1,7 @@
+module Main (main) where
+
+import Gauge.Main
+
+
+main :: IO ()
+main = defaultMain [bench "const" (whnf const ())]
diff --git a/data/tests/prettyPrintTest.out.golden b/data/tests/prettyPrintTest.out.golden
new file mode 100644
--- /dev/null
+++ b/data/tests/prettyPrintTest.out.golden
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<root>
+  <standaloneTagTest1/>
+  <!--comment-->
+  <cent>
+    -2036987824
+  </cent>
+  <advice>
+    -382485876
+  </advice>
+  <mood>
+    -1255836683.6094828
+  </mood>
+  <fewer>
+    willing
+  </fewer>
+  <red>
+    <wrote>
+      -2045291330.9269004
+    </wrote>
+    <lay>
+      1089968638.5407443
+    </lay>
+    <folks>
+      transportation
+    </folks>
+    <middle>
+      <southern>
+        596529693.8457294
+      </southern>
+      <coat>
+        that
+      </coat>
+      <beyond>
+        231916564.58609104
+      </beyond>
+      <space>
+        -1304314384.5063148
+      </space>
+      <slight>
+        0.9399357
+      </slight>
+      <draw>
+        bring
+      </draw>
+    </middle>
+    <replied>
+      would
+    </replied>
+    <clay>
+      <get>
+        1785527806.6880274
+      </get>
+      <pleasure>
+        mud
+      </pleasure>
+      <drawn>
+        380940042.21293926
+      </drawn>
+      <growth>
+        bound
+      </growth>
+      <under>
+        jack
+      </under>
+      <ask>
+        1128386694.0411663
+      </ask>
+    </clay>
+  </red>
+  <many>
+    pot
+  </many>
+</root>
diff --git a/data/tests/prettyPrintTest.xml b/data/tests/prettyPrintTest.xml
new file mode 100644
--- /dev/null
+++ b/data/tests/prettyPrintTest.xml
@@ -0,0 +1,1 @@
+<?xml version="1.0" encoding="UTF-8"?><root><standaloneTagTest1/><!--comment--><cent>-2036987824</cent><advice>-382485876</advice><mood>-1255836683.6094828</mood><fewer>willing</fewer><red><wrote>-2045291330.9269004</wrote><lay>1089968638.5407443</lay><folks>transportation</folks><middle><southern>596529693.8457294</southern><coat>that</coat><beyond>231916564.58609104</beyond><space>-1304314384.5063148</space><slight>0.9399357</slight><draw>bring</draw></middle><replied>would</replied><clay><get>1785527806.6880274</get><pleasure>mud</pleasure><drawn>380940042.21293926</drawn><growth>bound</growth><under>jack</under><ask>1128386694.0411663</ask></clay></red><many>pot</many></root>
diff --git a/src/Text/XML/Prettify.hs b/src/Text/XML/Prettify.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/XML/Prettify.hs
@@ -0,0 +1,132 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE OverloadedStrings #-}
+-----------------------------------------------------------------------------
+-- Based on Text.XML.Prettify by David M. Rosenberg
+-- Copyright    : (c) 2010 David M. Rosenberg
+-- License      : BSD3
+-----------------------------------------------------------------------------
+-- Module        :  Text.XML.Prettify
+--
+-- Maintained by :  Marc Jakobi, 2021-09-09
+-- Modifications:
+--                 - Update to Haskell 2010
+--                 - Replace String with Data.Text
+--                 - Encapsulate internals of module
+--                 - Tweak performance
+--
+-- License       :  GPL2
+--
+-- Description   :  Pretty-print XML Text
+-----------------------------------------------------------------------------
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Text.XML.Prettify
+  ( XmlText,
+    prettyPrintXml,
+    prettyPrintXmlDefault,
+    module Text.XML.Prettify.Options,
+  )
+where
+
+import Control.Monad.Reader
+import Data.Char (isSpace)
+import qualified Data.Text as T
+import Text.XML.Prettify.Options
+import TextShow
+import Prelude
+
+type Prettify = Reader PrettifyOpts
+
+-- | Pretty-print an XML text with the default options:
+-- EndOfLine: LF
+-- Indent style: 2 spaces
+-- Returns: The pretty-printed XML
+prettyPrintXmlDefault :: XmlText -> XmlText
+prettyPrintXmlDefault =
+  prettyPrintXml
+    PrettifyOpts
+      { endOfLine = LF,
+        indentStyle = SPACE 2
+      }
+
+-- | Pretty-print an XML text
+-- opts: The output options
+-- xmlText: xml text (e.g. on one line)
+-- Returnms: The pretty-printed XML
+prettyPrintXml :: PrettifyOpts -> XmlText -> XmlText
+prettyPrintXml opts xmlText = runReader (printAllTags tags) opts
+  where
+    tags = inputToTags $ T.concat $ T.lines xmlText
+
+data TagType = IncTagType | DecTagType | StandaloneTagType
+  deriving stock (Ord, Eq, Enum)
+
+type XmlText = T.Text
+
+type TagContent = T.Text
+
+type OneLineXmlText = XmlText
+
+data XmlTag = XmlTag TagContent TagType
+  deriving stock (Ord, Eq)
+
+inputToTags :: OneLineXmlText -> [XmlTag]
+inputToTags "" = []
+inputToTags xmlText = xtag : inputToTags xmlText'
+  where
+    (xtag, xmlText') = lexOne xmlText
+
+lexOne :: OneLineXmlText -> (XmlTag, OneLineXmlText)
+lexOne xmlText = case nextCharacter of
+  ' ' -> (XmlTag "" StandaloneTagType, "")
+  '<' -> lexOneTag xmlText
+  _ -> lexNonTagged xmlText
+  where
+    nextWord = getWord xmlText
+    nextCharacter = T.head $ nextWord <> " "
+
+lexNonTagged :: OneLineXmlText -> (XmlTag, OneLineXmlText)
+lexNonTagged xmlText = (XmlTag tagContent tagType, remaining)
+  where
+    nextWord = getWord xmlText
+    (tagContent, remaining) = T.break (== '<') nextWord
+    tagType = StandaloneTagType
+
+getWord :: T.Text -> T.Text
+getWord = T.dropWhile isSpace
+
+lexOneTag :: OneLineXmlText -> (XmlTag, OneLineXmlText)
+lexOneTag xmlText = (XmlTag tagContent tagType, res)
+  where
+    afterTagStart = T.dropWhile (/= '<') xmlText
+    (tagContent', remaining) = T.span (/= '>') afterTagStart
+    tagContent = tagContent' <> (T.singleton . T.head) remaining
+    res = T.tail remaining
+    tagType = case (T.index tagContent 1, T.index tagContent (T.length tagContent - 2)) of
+      ('/', _) -> DecTagType
+      (_, '/') -> StandaloneTagType
+      ('!', _) -> StandaloneTagType
+      ('?', _) -> StandaloneTagType
+      (_, _) -> IncTagType
+
+printTag :: Int -> XmlTag -> Prettify (Int, XmlText)
+printTag tagIdent (XmlTag content tagType) = do
+  identStyle <- asks indentStyle
+  let identText = showt identStyle
+  let (contentIdent, nextTagIdent) = case tagType of
+        IncTagType -> (tagIdent, tagIdent + 1)
+        DecTagType -> (tagIdent - 1, tagIdent - 1)
+        _ -> (tagIdent, tagIdent)
+  let outText = T.replicate contentIdent identText <> content
+  pure (nextTagIdent, outText)
+
+printAllTags :: [XmlTag] -> Prettify XmlText
+printAllTags = printTags 0
+
+printTags :: Int -> [XmlTag] -> Prettify XmlText
+printTags _ [] = pure ""
+printTags ident (tag : tags) = do
+  eol <- asks endOfLine
+  (nextTagIdent, tagText) <- printTag ident tag
+  remainingTagText <- printTags nextTagIdent tags
+  pure $ T.intercalate (showt eol) [tagText, remainingTagText]
diff --git a/src/Text/XML/Prettify/Options.hs b/src/Text/XML/Prettify/Options.hs
new file mode 100644
--- /dev/null
+++ b/src/Text/XML/Prettify/Options.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+{-# OPTIONS_GHC -Wno-missing-export-lists #-}
+
+module Text.XML.Prettify.Options where
+
+import TextShow
+import Prelude
+
+-- | The indent size
+type IndentSize = Int
+
+-- | The indent style, either `Tab` or `Space` with a given indent size
+data IndentStyle = TAB | SPACE IndentSize
+  deriving stock (Eq, Read, Show)
+
+-- | The line break style:
+-- Line Feed (LF), Carriage Return (CR),
+-- or both (CRLF)
+data EndOfLine = LF | CR | CRLF
+  deriving stock (Eq, Read, Show)
+
+-- | The options for the Prettify module
+data PrettifyOpts = PrettifyOpts
+  { -- | The indent style, either `Tab` or `Space` with a given indent size
+    indentStyle :: IndentStyle,
+    -- | The line break style:
+    -- Line Feed (LF), Carriage Return (CR),
+    -- or both (CRLF)
+    endOfLine :: EndOfLine
+  }
+
+instance TextShow EndOfLine where
+  showb LF = "\n"
+  showb CR = "\r"
+  showb _ = foldMap showb [CR, LF]
+
+instance TextShow IndentStyle where
+  showb TAB = "\t"
+  showb (SPACE indentSize) = mconcat $ replicate indentSize showbSpace
diff --git a/test/PrettyPrintGoldenTest.hs b/test/PrettyPrintGoldenTest.hs
new file mode 100644
--- /dev/null
+++ b/test/PrettyPrintGoldenTest.hs
@@ -0,0 +1,36 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module PrettyPrintGoldenTest
+  ( prettyPrintGoldentTest,
+  )
+where
+
+import qualified Data.Text.IO as TIO
+import System.FilePath (normalise, replaceExtension, takeBaseName)
+import Test.Tasty
+import Test.Tasty.Golden
+import Text.XML.Prettify
+
+prettyPrintGoldentTest :: IO [TestTree]
+prettyPrintGoldentTest = sequence [goldenPrettyPrint]
+
+goldenPrettyPrint :: IO TestTree
+goldenPrettyPrint =
+  testGroup "Pretty print XML" . map createTest
+    <$> findByExtension [".xml"] testsDir
+  where
+    testsDir = normalise "data/tests"
+
+createTest :: [Char] -> TestTree
+createTest testFile =
+  goldenVsFile
+    (takeBaseName testFile)
+    goldenf
+    outf
+    testAction
+  where
+    goldenf = replaceExtension testFile ".out.golden"
+    outf = replaceExtension testFile ".out"
+    testAction = do
+      prettyPrintedXml <- prettyPrintXmlDefault <$> TIO.readFile testFile
+      TIO.writeFile outf prettyPrintedXml
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,14 @@
+module Main (main) where
+
+import PrettyPrintGoldenTest
+import Test.Tasty
+
+main :: IO ()
+main = do
+  goldens <- prettyPrintGoldentTest
+  defaultMain
+    ( testGroup
+        "All tests"
+        [ testGroup "Golden tests" goldens
+        ]
+    )
diff --git a/xml-prettify-text.cabal b/xml-prettify-text.cabal
new file mode 100644
--- /dev/null
+++ b/xml-prettify-text.cabal
@@ -0,0 +1,92 @@
+cabal-version:       2.4
+name:                xml-prettify-text
+version:             1.0.0.1
+synopsis:            XML pretty printer
+description:         XML pretty printer based on xml-prettify by David M. Rosenberg. Modified to work with Text instead of String and tuned for improved performance.
+homepage:            https://github.com/MrcJkb/xml-prettify-text
+bug-reports:         https://github.com/MrcJkb/xml-prettify-text/issues
+license:             GPL-2.0-only
+license-file:        LICENSE
+author:              Marc Jakobi
+maintainer:          Marc Jakobi <mrcjkb89@outlook.com>
+copyright:           2021 Marc Jakobi
+category:            Efficient XML pretty-printer
+build-type:          Simple
+extra-doc-files:     README.md
+                     CHANGELOG.md
+tested-with:         GHC == 8.8.3
+data-files:
+    data/tests/prettyPrintTest.out.golden
+    data/tests/prettyPrintTest.xml
+
+source-repository head
+  type:                git
+  location:            https://github.com/MrcJkb/xml-prettify-text.git
+common common-options
+  build-depends:       base >= 4.14.3.0 && < 5
+                     , protolude >= 0.3.0 && < 0.4
+  
+  mixins:              base hiding (Prelude)
+                     , protolude (Protolude as Prelude)
+  ghc-options:         -Wall
+                        -Wcompat
+                        -Widentities
+                        -Wincomplete-uni-patterns
+                        -Wincomplete-record-updates
+  if impl(ghc >= 8.0)
+    ghc-options:       -Wredundant-constraints
+  if impl(ghc >= 8.2)
+    ghc-options:       -fhide-source-paths
+  if impl(ghc >= 8.4)
+    ghc-options:       -Wmissing-export-lists
+                       -Wpartial-fields
+  if impl(ghc >= 8.8)
+    ghc-options:       -Wmissing-deriving-strategies
+
+  default-language:    Haskell2010
+library
+  import:              common-options
+  hs-source-dirs:      src
+  exposed-modules:     Text.XML.Prettify
+                     , Text.XML.Prettify.Options
+  build-depends:       base >= 4.14.3.0 && < 5
+                     , text >= 1.2.4.1 && < 1.3
+                     , text-show >= 3.9.2 && < 3.10
+                     , mtl >= 2.2.2 && < 2.3
+executable xml-prettify
+  import:              common-options
+  hs-source-dirs:      app
+  main-is:             Main.hs
+  build-depends:       xml-prettify-text
+                     , text >= 1.2.4.1 && < 1.3
+                     , optparse-applicative >= 0.16.1.0 && < 0.17
+                     , with-utf8 >= 1.0.2.3 && < 1.1
+  ghc-options:         -threaded
+                         -rtsopts
+                         -with-rtsopts=-N
+test-suite xml-prettify-text-test
+  import:              common-options
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  other-modules:       
+                       PrettyPrintGoldenTest
+  build-depends:       xml-prettify-text
+                     , tasty >= 1.4.2 && < 1.5
+                     , tasty-golden >= 2.3.4 && < 2.4
+                     , text >= 1.2.4.1 && < 1.3
+                     , filepath >= 1.4.2.1 && < 1.5
+  ghc-options:         -threaded
+                        -rtsopts
+                        -with-rtsopts=-N
+
+benchmark xml-prettify-text-benchmark
+  import:              common-options
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      benchmark
+  main-is:             Main.hs
+  build-depends:       gauge
+                     , xml-prettify-text
+  ghc-options:         -threaded
+                         -rtsopts
+                         -with-rtsopts=-N
