{-# 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 = forall a. IsString a => String -> a
fromString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show
 renderBuilder = Text -> Builder
Builder.fromText forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Render a => a -> Text
render

renderDefault :: Render a => a -> Text
renderDefault :: forall a. Render a => a -> Text
renderDefault = Text -> Text
toStrict forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.toLazyText forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 :: forall a. Render a => [a] -> Text
renderAppend = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Render a => a -> Text
render

renderAppendBuilder :: Render a => [a] -> Builder
renderAppendBuilder :: forall a. Render a => [a] -> Builder
renderAppendBuilder = forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap 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 :: forall a. Render a => Char -> [a] -> Text
renderChars Char
c = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a] -> [a]
intersperse (forall a. IsString a => String -> a
fromString [Char
c]) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Render a => a -> Text
render

renderCharsBuilder :: Render a => Char -> [a] -> Builder
renderCharsBuilder :: forall a. Render a => Char -> [a] -> Builder
renderCharsBuilder Char
c = forall a. Monoid a => [a] -> a
mconcat forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. a -> [a] -> [a]
intersperse (Char -> Builder
Builder.singleton Char
c) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap 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 :: forall a. Render a => [a] -> Text
renderCommas = forall a. Render a => Char -> [a] -> Text
renderChars Char
','

renderCommasBuilder :: Render a => [a] -> Builder
renderCommasBuilder :: forall a. Render a => [a] -> Builder
renderCommasBuilder = 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 :: forall a. Render a => String -> a -> IO ()
renderFile String
f = String -> ByteString -> IO ()
B.writeFile String
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. 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 = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ByteString -> Text
decodeUtf8 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 :: forall a l. (Render a, LaTeXC l) => a -> l
rendertex = forall l. LaTeXC l => LaTeX -> l
fromLaTeX forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> LaTeX
TeXRaw forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Render a => a -> Text
render

-- Render instances

instance Render Measure where
 render :: Measure -> Text
render (Pt Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"pt"
 render (Mm Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"mm"
 render (Cm Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"cm"
 render (In Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"in"
 render (Ex Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"ex"
 render (Em Double
x) = forall a. Render a => a -> Text
render Double
x forall a. Semigroup a => a -> a -> a
<> Text
"em"
 render (CustomMeasure LaTeX
x) = 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
"\\" forall a. Semigroup a => a -> a -> a
<> forall a. IsString a => String -> a
fromString String
name forall a. Semigroup a => a -> a -> a
<> Builder
"{}"
  renderBuilder (TeXComm String
name [TeXArg]
args) =
      Builder
"\\"
   forall a. Semigroup a => a -> a -> a
<> forall a. IsString a => String -> a
fromString String
name
   forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderAppendBuilder [TeXArg]
args
  renderBuilder (TeXCommS String
name) = Builder
"\\" forall a. Semigroup a => a -> a -> a
<> forall a. IsString a => String -> a
fromString String
name
  
  renderBuilder (TeXEnv String
name [TeXArg]
args LaTeX
c) =
      Builder
"\\begin{"
   forall a. Semigroup a => a -> a -> a
<> forall a. IsString a => String -> a
fromString String
name
   forall a. Semigroup a => a -> a -> a
<> Builder
"}"
   forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderAppendBuilder [TeXArg]
args
   forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
c
   forall a. Semigroup a => a -> a -> a
<> Builder
"\\end{"
   forall a. Semigroup a => a -> a -> a
<> forall a. IsString a => String -> a
fromString String
name
   forall a. Semigroup a => a -> a -> a
<> Builder
"}"

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

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

  renderBuilder (TeXBraces LaTeX
l) = Builder
"{" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
l 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 forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Text]
xs then Builder
"%\n"
                 else Text -> Builder
Builder.fromText forall a b. (a -> b) -> a -> b
$ [Text] -> Text
Data.Text.unlines forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text
"%" forall a. Semigroup a => a -> a -> a
<>) [Text]
xs

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

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

instance Render TeXArg where
 renderBuilder :: TeXArg -> Builder
renderBuilder (FixArg LaTeX
l) = Builder
"{" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
l forall a. Semigroup a => a -> a -> a
<> Builder
"}"
 renderBuilder (OptArg LaTeX
l) = Builder
"[" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
l forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 renderBuilder (MOptArg []) = forall a. Monoid a => a
mempty
 renderBuilder (MOptArg [LaTeX]
ls) = Builder
"[" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 renderBuilder (SymArg LaTeX
l) = Builder
"<" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
l forall a. Semigroup a => a -> a -> a
<> Builder
">"
 renderBuilder (MSymArg []) = forall a. Monoid a => a
mempty
 renderBuilder (MSymArg [LaTeX]
ls) = Builder
"<" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls forall a. Semigroup a => a -> a -> a
<> Builder
">"
 renderBuilder (ParArg LaTeX
l) = Builder
"(" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => a -> Builder
renderBuilder LaTeX
l forall a. Semigroup a => a -> a -> a
<> Builder
")"
 renderBuilder (MParArg []) = forall a. Monoid a => a
mempty
 renderBuilder (MParArg [LaTeX]
ls) = Builder
"(" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderCommasBuilder [LaTeX]
ls forall a. Semigroup a => a -> a -> a
<> Builder
")"
 render :: TeXArg -> Text
render = 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 :: forall a. RealFloat a => a -> String
showFloat a
x = forall a. RealFloat a => Maybe Int -> a -> ShowS
showFFloat (forall a. a -> Maybe a
Just Int
5) a
x []

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

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

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

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

instance Render Word8 where
  renderBuilder :: Word8 -> Builder
renderBuilder = forall a. Integral a => a -> Builder
Builder.decimal
  render :: Word8 -> Text
render = 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
"[" forall a. Semigroup a => a -> a -> a
<> forall a. Render a => [a] -> Builder
renderCommasBuilder [a]
xs forall a. Semigroup a => a -> a -> a
<> Builder
"]"
 render :: [a] -> Text
render = forall a. Render a => a -> Text
renderDefault