{-# LANGUAGE OverloadedStrings #-}

-- | The final purpose of this module is to render a Text value
--   from a 'LaTeX' value. The interface is abstracted via a typeclass
--   so you can cast to 'Text' other types as well. Also, some other
--   handy 'Text'-related functions are defined.
module Text.LaTeX.Base.Render
 ( -- * Re-exports
   Text
 , module Data.String
   -- * Render class
 , Render (..)
 , renderAppend
 , renderChars
 , renderCommas
 , renderFile
 , rendertex
   -- * Reading files
 , readFileTex
   -- * Util
 , showFloat
   ) where

import Text.LaTeX.Base.Syntax
import Text.LaTeX.Base.Class
import Data.String
import Data.List (intersperse)
import qualified Data.ByteString as B
import Data.Word (Word8)
import Numeric (showFFloat)
import Data.Text (Text,lines,unlines)
import Data.Text.Encoding
import Data.Text.Lazy (toStrict)
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Text.Lazy.Builder.Int as Builder
import qualified Data.Text.Lazy.Builder.RealFloat as Builder

-- | Class of values that can be transformed to 'Text'.
-- You mainly will use this to obtain the 'Text' output
-- of a 'LaTeX' value. If you are going to write the result
-- in a file, consider to use 'renderFile'.
--
-- Consider also to use 'rendertex' to get 'Render'able values
-- into 'LaTeX' blocks.
--
-- If you want to make a type instance of 'Render' and you already
-- have a 'Show' instance, you can use the default instance.
--
-- > render = fromString . show
--
class Show a => Render a where
 render :: a -> Text
 renderBuilder :: a -> Builder
 --
 render = String -> Text
forall a. IsString a => String -> a
fromString (String -> Text) -> (a -> String) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> String
forall a. Show a => a -> String
show
 renderBuilder = Text -> Builder
Builder.fromText (Text -> Builder) -> (a -> Text) -> a -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. Render a => a -> Text
render

renderDefault :: Render a => a -> Text
renderDefault :: a -> Text
renderDefault = Text -> Text
toStrict (Text -> Text) -> (a -> Text) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.toLazyText (Builder -> Text) -> (a -> Builder) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Builder
forall a. Render a => a -> Builder
renderBuilder

-- | This instance escapes LaTeX reserved characters.
instance Render Text where
 render :: Text -> Text
render = Text -> Text
protectText

-- | Render every element of a list and append results.
renderAppend :: Render a => [a] -> Text
renderAppend :: [a] -> Text
renderAppend = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text) -> ([a] -> [Text]) -> [a] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Text) -> [a] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Text
forall a. Render a => a -> Text
render

renderAppendBuilder :: Render a => [a] -> Builder
renderAppendBuilder :: [a] -> Builder
renderAppendBuilder = (a -> Builder) -> [a] -> Builder
forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap a -> Builder
forall a. Render a => a -> Builder
renderBuilder

-- | Render every element of a list and append results,
--   separated by the given 'Char'.
renderChars :: Render a => Char -> [a] -> Text
renderChars :: Char -> [a] -> Text
renderChars Char
c = [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat ([Text] -> Text) -> ([a] -> [Text]) -> [a] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
intersperse (String -> Text
forall a. IsString a => String -> a
fromString [Char
c]) ([Text] -> [Text]) -> ([a] -> [Text]) -> [a] -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Text) -> [a] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Text
forall a. Render a => a -> Text
render

renderCharsBuilder :: Render a => Char -> [a] -> Builder
renderCharsBuilder :: Char -> [a] -> Builder
renderCharsBuilder Char
c = [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> ([a] -> [Builder]) -> [a] -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
intersperse (Char -> Builder
Builder.singleton Char
c) ([Builder] -> [Builder]) -> ([a] -> [Builder]) -> [a] -> [Builder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Builder) -> [a] -> [Builder]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> Builder
forall a. Render a => a -> Builder
renderBuilder

-- | Render every element of a list and append results,
--   separated by commas.
renderCommas :: Render a => [a] -> Text
renderCommas :: [a] -> Text
renderCommas = Char -> [a] -> Text
forall a. Render a => Char -> [a] -> Text
renderChars Char
','

renderCommasBuilder :: Render a => [a] -> Builder
renderCommasBuilder :: [a] -> Builder
renderCommasBuilder = Char -> [a] -> Builder
forall a. Render a => Char -> [a] -> Builder
renderCharsBuilder Char
','

-- | Use this function to render a 'LaTeX' (or another
--   one in the 'Render' class) value directly
--   in a file.
renderFile :: Render a => FilePath -> a -> IO ()
renderFile :: String -> a -> IO ()
renderFile String
f = String -> ByteString -> IO ()
B.writeFile String
f (ByteString -> IO ()) -> (a -> ByteString) -> a -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 (Text -> ByteString) -> (a -> Text) -> a -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. Render a => a -> Text
render

-- | If you are going to insert the content of a file
-- in your 'LaTeX' data, use this function to ensure
-- your encoding is correct.
readFileTex :: FilePath -> IO Text
readFileTex :: String -> IO Text
readFileTex = (ByteString -> Text) -> IO ByteString -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8 (IO ByteString -> IO Text)
-> (String -> IO ByteString) -> String -> IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> IO ByteString
B.readFile

-- | If you can transform a value to 'Text', you can
--   insert that 'Text' in your 'LaTeX' code.
--   That is what this function does.
--
-- /Warning: /'rendertex'/ does not escape LaTeX reserved characters./
-- /Use /'protectText'/ to escape them./
rendertex :: (Render a,LaTeXC l) => a -> l
rendertex :: a -> l
rendertex = LaTeX -> l
forall l. LaTeXC l => LaTeX -> l
fromLaTeX (LaTeX -> l) -> (a -> LaTeX) -> a -> l
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> LaTeX
TeXRaw (Text -> LaTeX) -> (a -> Text) -> a -> LaTeX
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Text
forall a. Render a => a -> Text
render

-- Render instances

instance Render Measure where
 render :: Measure -> Text
render (Pt Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"pt"
 render (Mm Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"mm"
 render (Cm Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"cm"
 render (In Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"in"
 render (Ex Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"ex"
 render (Em Double
x) = Double -> Text
forall a. Render a => a -> Text
render Double
x Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"em"
 render (CustomMeasure LaTeX
x) = LaTeX -> Text
forall a. Render a => a -> Text
render LaTeX
x

-- LaTeX instances

instance Render LaTeX where
  
  renderBuilder :: LaTeX -> Builder
renderBuilder (TeXRaw Text
t) = Text -> Builder
Builder.fromText Text
t
  
  renderBuilder (TeXComm String
name []) = Builder
"\\" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString String
name Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"{}"
  renderBuilder (TeXComm String
name [TeXArg]
args) =
      Builder
"\\"
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString String
name
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [TeXArg] -> Builder
forall a. Render a => [a] -> Builder
renderAppendBuilder [TeXArg]
args
  renderBuilder (TeXCommS String
name) = Builder
"\\" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString String
name
  
  renderBuilder (TeXEnv String
name [TeXArg]
args LaTeX
c) =
      Builder
"\\begin{"
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString String
name
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [TeXArg] -> Builder
forall a. Render a => [a] -> Builder
renderAppendBuilder [TeXArg]
args
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
c
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\end{"
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> String -> Builder
forall a. IsString a => String -> a
fromString String
name
   Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"

  renderBuilder (TeXMath MathType
Dollar LaTeX
l) = Builder
"$" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"$"
  renderBuilder (TeXMath MathType
DoubleDollar LaTeX
l) = Builder
"$$" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"$$"
  renderBuilder (TeXMath MathType
Square LaTeX
l) = Builder
"\\[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\]"
  renderBuilder (TeXMath MathType
Parentheses LaTeX
l) = Builder
"\\(" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\)"

  renderBuilder (TeXLineBreak Maybe Measure
m Bool
b) = Builder
"\\\\" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder -> (Measure -> Builder) -> Maybe Measure -> Builder
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Builder
forall a. Monoid a => a
mempty (\Measure
x -> Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Measure -> Builder
forall a. Render a => a -> Builder
renderBuilder Measure
x Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]") Maybe Measure
m Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ( if Bool
b then Builder
"*" else Builder
forall a. Monoid a => a
mempty )

  renderBuilder (TeXBraces LaTeX
l) = Builder
"{" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"

  renderBuilder (TeXComment Text
c) =
   let xs :: [Text]
xs = Text -> [Text]
Data.Text.lines Text
c
   in if [Text] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
xs then Builder
"%\n"
                 else Text -> Builder
Builder.fromText (Text -> Builder) -> Text -> Builder
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
Data.Text.unlines ([Text] -> Text) -> [Text] -> Text
forall a b. (a -> b) -> a -> b
$ (Text -> Text) -> [Text] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text
"%" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<>) [Text]
xs

  renderBuilder (TeXSeq LaTeX
l1 LaTeX
l2) = LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l2
  renderBuilder LaTeX
TeXEmpty = Builder
forall a. Monoid a => a
mempty

  render :: LaTeX -> Text
render = LaTeX -> Text
forall a. Render a => a -> Text
renderDefault

instance Render TeXArg where
 renderBuilder :: TeXArg -> Builder
renderBuilder (FixArg LaTeX
l) = Builder
"{" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"}"
 renderBuilder (OptArg LaTeX
l) = Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 renderBuilder (MOptArg []) = Builder
forall a. Monoid a => a
mempty
 renderBuilder (MOptArg [LaTeX]
ls) = Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [LaTeX] -> Builder
forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 renderBuilder (SymArg LaTeX
l) = Builder
"<" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
">"
 renderBuilder (MSymArg []) = Builder
forall a. Monoid a => a
mempty
 renderBuilder (MSymArg [LaTeX]
ls) = Builder
"<" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [LaTeX] -> Builder
forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
">"
 renderBuilder (ParArg LaTeX
l) = Builder
"(" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> LaTeX -> Builder
forall a. Render a => a -> Builder
renderBuilder LaTeX
l Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
")"
 renderBuilder (MParArg []) = Builder
forall a. Monoid a => a
mempty
 renderBuilder (MParArg [LaTeX]
ls) = Builder
"(" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [LaTeX] -> Builder
forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
")"
 render :: TeXArg -> Text
render = TeXArg -> Text
forall a. Render a => a -> Text
renderDefault

-- Other instances

-- | Show a signed floating number using standard decimal notation using 5 decimals.
showFloat :: RealFloat a => a -> String
showFloat :: a -> String
showFloat a
x = Maybe Int -> a -> ShowS
forall a. RealFloat a => Maybe Int -> a -> ShowS
showFFloat (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
5) a
x []

instance Render Int where
  renderBuilder :: Int -> Builder
renderBuilder = Int -> Builder
forall a. Integral a => a -> Builder
Builder.decimal
  render :: Int -> Text
render = Int -> Text
forall a. Render a => a -> Text
renderDefault

instance Render Integer where
  renderBuilder :: Integer -> Builder
renderBuilder = Integer -> Builder
forall a. Integral a => a -> Builder
Builder.decimal
  render :: Integer -> Text
render = Integer -> Text
forall a. Render a => a -> Text
renderDefault

instance Render Float where
  renderBuilder :: Float -> Builder
renderBuilder = FPFormat -> Maybe Int -> Float -> Builder
forall a. RealFloat a => FPFormat -> Maybe Int -> a -> Builder
Builder.formatRealFloat FPFormat
Builder.Fixed (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
5)
  render :: Float -> Text
render = Float -> Text
forall a. Render a => a -> Text
renderDefault

instance Render Double where
  renderBuilder :: Double -> Builder
renderBuilder = FPFormat -> Maybe Int -> Double -> Builder
forall a. RealFloat a => FPFormat -> Maybe Int -> a -> Builder
Builder.formatRealFloat FPFormat
Builder.Fixed (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
5)
  render :: Double -> Text
render = Double -> Text
forall a. Render a => a -> Text
renderDefault

instance Render Word8 where
  renderBuilder :: Word8 -> Builder
renderBuilder = Word8 -> Builder
forall a. Integral a => a -> Builder
Builder.decimal
  render :: Word8 -> Text
render = Word8 -> Text
forall a. Render a => a -> Text
renderDefault

-- | 'Render' instance for 'Bool'. It satisfies @render True = "true"@ and @render False = "false"@.
instance Render Bool where
  render :: Bool -> Text
render Bool
True = Text
"true"
  render Bool
_ = Text
"false"

instance Render a => Render [a] where
 renderBuilder :: [a] -> Builder
renderBuilder [a]
xs = Builder
"[" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> [a] -> Builder
forall a. Render a => [a] -> Builder
renderCommasBuilder [a]
xs Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 render :: [a] -> Text
render = [a] -> Text
forall a. Render a => a -> Text
renderDefault