diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2016-2018, John MacFarlane.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * The names of the contributors may not be used to endorse or
+      promote products derived from this software without specific
+      prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,5 @@
+# skylighting-format-ansi
+
+This package provides functions to render syntax-highlighting
+as ANSI text.
+
diff --git a/skylighting-format-latex.cabal b/skylighting-format-latex.cabal
new file mode 100644
--- /dev/null
+++ b/skylighting-format-latex.cabal
@@ -0,0 +1,36 @@
+name:                skylighting-format-latex
+version:             0.1
+synopsis:            LaTeX formatter for skylighting syntax highlighting library
+description:         This module allows tokens produced by skylighting-core
+                     to be rendered as LaTeX macros.
+homepage:            https://github.com/jgm/skylighting
+license:             BSD3
+license-file:        LICENSE
+author:              John MacFarlane
+maintainer:          jgm@berkeley.edu
+copyright:           (C) 2016-2022 John MacFarlane
+category:            Text
+build-type:          Simple
+extra-source-files:  README.md
+
+cabal-version:       >=1.10
+
+source-repository head
+  type:              git
+  location:          https://github.com/jgm/skylighting.git
+
+library
+  exposed-modules:     Skylighting.Format.LaTeX
+  other-extensions:    CPP
+  build-depends:       base >= 4.8 && < 5.0,
+                       skylighting-core,
+                       text,
+                       containers
+  hs-source-dirs:      src
+  ghc-prof-options:    -fprof-auto-exported
+  default-language:    Haskell2010
+  ghc-options:         -Wall
+  if impl(ghc >= 8.4)
+    ghc-options:       -fhide-source-paths
+  if impl(ghc >= 8.10)
+    ghc-options:      -Wunused-packages
diff --git a/src/Skylighting/Format/LaTeX.hs b/src/Skylighting/Format/LaTeX.hs
new file mode 100644
--- /dev/null
+++ b/src/Skylighting/Format/LaTeX.hs
@@ -0,0 +1,152 @@
+{-# LANGUAGE CPP                 #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Skylighting.Format.LaTeX (
+         formatLaTeXInline
+       , formatLaTeXBlock
+       , styleToLaTeX
+       ) where
+
+import Control.Monad (mplus)
+import Data.Char (isSpace)
+import Data.List (sort)
+import qualified Data.Map as Map
+import Data.Text (Text)
+import qualified Data.Text as Text
+import Skylighting.Types
+import Text.Printf
+#if !MIN_VERSION_base(4,11,0)
+import Data.Semigroup
+#endif
+
+formatLaTeX :: Bool -> [SourceLine] -> Text
+formatLaTeX inline = Text.intercalate (Text.singleton '\n')
+                       . map (sourceLineToLaTeX inline)
+
+-- | Formats tokens as LaTeX using custom commands inside
+-- @|@ characters. Assumes that @|@ is defined as a short verbatim
+-- command by the macros produced by 'styleToLaTeX'.
+-- A @KeywordTok@ is rendered using @\\KeywordTok{..}@, and so on.
+formatLaTeXInline :: FormatOptions -> [SourceLine] -> Text
+formatLaTeXInline _opts ls = "\\VERB|" <> formatLaTeX True ls <> "|"
+
+sourceLineToLaTeX :: Bool -> SourceLine -> Text
+sourceLineToLaTeX inline = mconcat . map (tokenToLaTeX inline)
+
+tokenToLaTeX :: Bool -> Token -> Text
+tokenToLaTeX inline (NormalTok, txt)
+  | Text.all isSpace txt = escapeLaTeX inline txt
+tokenToLaTeX inline (toktype, txt)   = Text.cons '\\'
+  (Text.pack (show toktype) <> "{" <> escapeLaTeX inline txt <> "}")
+
+escapeLaTeX :: Bool -> Text -> Text
+escapeLaTeX inline = Text.concatMap escapeLaTeXChar
+  where escapeLaTeXChar c =
+         case c of
+           '\\' -> "\\textbackslash{}"
+           '{'  -> "\\{"
+           '}'  -> "\\}"
+           '|' | inline -> "\\VerbBar{}" -- used in inline verbatim
+           '_'  -> "\\_"
+           '&'  -> "\\&"
+           '%'  -> "\\%"
+           '#'  -> "\\#"
+           '`'  -> "\\textasciigrave{}"
+           '\'' -> "\\textquotesingle{}"
+           '-'  -> "{-}" -- prevent ligatures
+           '~'  -> "\\textasciitilde{}"
+           '^'  -> "\\^{}"
+           '>'  -> "\\textgreater{}"
+           '<'  -> "\\textless{}"
+           _    -> Text.singleton c
+
+-- LaTeX
+
+-- | Format tokens as a LaTeX @Highlighting@ environment inside a
+-- @Shaded@ environment.  @Highlighting@ and @Shaded@ are
+-- defined by the macros produced by 'styleToLaTeX'.  @Highlighting@
+-- is a verbatim environment using @fancyvrb@; @\\@, @{@, and @}@
+-- have their normal meanings inside this environment, so that
+-- formatting commands work.  @Shaded@ is either nothing
+-- (if the style's background color is default) or a @snugshade@
+-- environment from @framed@, providing a background color
+-- for the whole code block, even if it spans multiple pages.
+formatLaTeXBlock :: FormatOptions -> [SourceLine] -> Text
+formatLaTeXBlock opts ls = Text.unlines
+  ["\\begin{Shaded}"
+  ,"\\begin{Highlighting}[" <>
+   (if numberLines opts
+       then "numbers=left," <>
+            (if startNumber opts == 1
+                then ""
+                else ",firstnumber=" <>
+                     Text.pack (show (startNumber opts))) <> ","
+       else Text.empty) <> "]"
+  ,formatLaTeX False ls
+  ,"\\end{Highlighting}"
+  ,"\\end{Shaded}"]
+
+-- | Converts a 'Style' to a set of LaTeX macro definitions,
+-- which should be placed in the document's preamble.
+-- Note: default LaTeX setup doesn't allow boldface typewriter font.
+-- To make boldface work in styles, you need to use a different typewriter
+-- font. This will work for computer modern:
+--
+-- > \DeclareFontShape{OT1}{cmtt}{bx}{n}{<5><6><7><8><9><10><10.95><12><14.4><17.28><20.74><24.88>cmttb10}{}
+--
+-- Or, with xelatex:
+--
+-- > \usepackage{fontspec}
+-- > \setmainfont[SmallCapsFont={* Caps}]{Latin Modern Roman}
+-- > \setsansfont{Latin Modern Sans}
+-- > \setmonofont[SmallCapsFont={Latin Modern Mono Caps}]{Latin Modern Mono Light}
+--
+styleToLaTeX :: Style -> Text
+styleToLaTeX f = Text.unlines $
+  [ "\\usepackage{color}"
+  , "\\usepackage{fancyvrb}"
+  , "\\newcommand{\\VerbBar}{|}"
+  , "\\newcommand{\\VERB}{\\Verb[commandchars=\\\\\\{\\}]}"
+  , "\\DefineVerbatimEnvironment{Highlighting}{Verbatim}{commandchars=\\\\\\{\\}}"
+  , "% Add ',fontsize=\\small' for more characters per line"
+  ] ++
+  (case backgroundColor f of
+        Nothing          -> ["\\newenvironment{Shaded}{}{}"]
+        Just (RGB r g b) -> ["\\usepackage{framed}"
+                            ,Text.pack
+                              (printf "\\definecolor{shadecolor}{RGB}{%d,%d,%d}" r g b)
+                            ,"\\newenvironment{Shaded}{\\begin{snugshade}}{\\end{snugshade}}"])
+  ++ sort (map (macrodef (defaultColor f) (Map.toList (tokenStyles f)))
+            (enumFromTo KeywordTok NormalTok))
+
+macrodef :: Maybe Color -> [(TokenType, TokenStyle)] -> TokenType -> Text
+macrodef defaultcol tokstyles tokt = "\\newcommand{\\"
+  <> Text.pack (show tokt)
+  <> "}[1]{"
+  <> Text.pack (co . ul . bf . it . bg $ "#1")
+  <> "}"
+  where tokf = case lookup tokt tokstyles of
+                     Nothing -> defStyle
+                     Just x  -> x
+        ul x = if tokenUnderline tokf
+                  then "\\underline{" <> x <> "}"
+                  else x
+        it x = if tokenItalic tokf
+                  then "\\textit{" <> x <> "}"
+                  else x
+        bf x = if tokenBold tokf
+                  then "\\textbf{" <> x <> "}"
+                  else x
+        bcol = fromColor `fmap` tokenBackground tokf
+                  :: Maybe (Double, Double, Double)
+        bg x = case bcol of
+                    Nothing        -> x
+                    Just (r, g, b) ->
+                       printf "\\colorbox[rgb]{%0.2f,%0.2f,%0.2f}{%s}" r g b x
+        col  = fromColor `fmap` (tokenColor tokf `mplus` defaultcol)
+                  :: Maybe (Double, Double, Double)
+        co x = case col of
+                    Nothing        -> x
+                    Just (r, g, b) ->
+                        printf "\\textcolor[rgb]{%0.2f,%0.2f,%0.2f}{%s}" r g b x
+
