{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TemplateHaskell    #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Diagrams.Backend.PGF.Surface
-- Copyright   :  (c) 2015 Christopher Chalmers
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  diagrams-discuss@googlegroups.com
--
-- A 'Surface' defines how a pgfpicture should be placed and compiled. Surfaces
-- are used for rendering a @.tex@ or @.pdf@ using functions from
-- 'Diagrams.Backend.PGF'.
--
-- Surfaces are also used in 'Diagrams.Backend.PGF.Hbox' for querying
-- envelopes of text.
--
-- Surfaces for Latex, Context and plain Tex are provided and reexported by
-- Diagrams.Backend.PGF. Lenses here allow these to be adjusted as required.
-----------------------------------------------------------------------------

module Diagrams.Backend.PGF.Surface
  ( -- * Surface definition
    Surface(..)
  , TexFormat(..)

    -- * Online rendering with surfaces
  , surfOnlineTex
  , surfOnlineTexIO

    -- * Predefined surfaces
  , latexSurface
  , contextSurface
  , plaintexSurface
  , sampleSurfaceOutput

    -- * Lenses
  , texFormat
  , command
  , arguments
  , pageSize
  , preamble
  , beginDoc
  , endDoc
  ) where

import           Data.ByteString.Builder
import           Data.Hashable           (Hashable (..))
import           Data.Typeable           (Typeable)
import           System.IO.Unsafe
import           System.Texrunner.Online


import           Diagrams.Prelude
import           Prelude

-- | The 'TexFormat' is used to choose the different PGF commands necessary for
--   that format.
data TexFormat = LaTeX | ConTeXt | PlainTeX
  deriving (Int -> TexFormat -> ShowS
[TexFormat] -> ShowS
TexFormat -> [Char]
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [TexFormat] -> ShowS
$cshowList :: [TexFormat] -> ShowS
show :: TexFormat -> [Char]
$cshow :: TexFormat -> [Char]
showsPrec :: Int -> TexFormat -> ShowS
$cshowsPrec :: Int -> TexFormat -> ShowS
Show, ReadPrec [TexFormat]
ReadPrec TexFormat
Int -> ReadS TexFormat
ReadS [TexFormat]
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [TexFormat]
$creadListPrec :: ReadPrec [TexFormat]
readPrec :: ReadPrec TexFormat
$creadPrec :: ReadPrec TexFormat
readList :: ReadS [TexFormat]
$creadList :: ReadS [TexFormat]
readsPrec :: Int -> ReadS TexFormat
$creadsPrec :: Int -> ReadS TexFormat
Read, TexFormat -> TexFormat -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: TexFormat -> TexFormat -> Bool
$c/= :: TexFormat -> TexFormat -> Bool
== :: TexFormat -> TexFormat -> Bool
$c== :: TexFormat -> TexFormat -> Bool
Eq, Typeable)
  -- These names are only capitalised so Context doesn't conflict with
  -- lens's Context.

data Surface = Surface
  { Surface -> TexFormat
_texFormat :: TexFormat -- ^ Format for the PGF commands
  , Surface -> [Char]
_command   :: String    -- ^ System command to be called.
  , Surface -> [[Char]]
_arguments :: [String]  -- ^ Arguments for command.
  , Surface -> Maybe (V2 Int -> [Char])
_pageSize  :: Maybe (V2 Int -> String)
                            -- ^ Command to change page size from dimensions of image.
                            --   (in bp)
  , Surface -> [Char]
_preamble  :: String    -- ^ Preamble for document, should import pgfcore.
  , Surface -> [Char]
_beginDoc  :: String    -- ^ Begin document.
  , Surface -> [Char]
_endDoc    :: String    -- ^ End document.
  }

makeLensesWith (lensRules & generateSignatures .~ False) ''Surface

-- | Format for the PGF commands.
texFormat :: Lens' Surface TexFormat

-- | System command to call for rendering PDFs for 'OnlineTex'.
command :: Lens' Surface String

-- | List of arguments for the 'command'.
arguments :: Lens' Surface [String]

-- | Preamble for the tex document. This should at least import
--   @pgfcore@.
preamble :: Lens' Surface String

-- | Specify the page size for the tex file.
pageSize :: Lens' Surface (Maybe (V2 Int -> String))

-- | Command to begin the document. (This normally doesn't need to
--   change)
beginDoc :: Lens' Surface String

-- | Command to end the document. (This normally doesn't need to
--   change)
endDoc :: Lens' Surface String

-- Predefined surfaces -------------------------------------------------

-- | Default surface for latex files by calling @pdflatex@.
--
-- ==== __Sample output__
--
-- @
-- 'command': pdflatex
--
-- % 'preamble'
-- \documentclass{article}
-- \usepackage{pgfcore}
-- \pagenumbering{gobble}
--
-- % 'pageSize'
-- \pdfpagewidth=100bp
-- \pdfpageheight=80bp
-- \textheight=80bp
-- \pdfhorigin=-76.6bp
-- \pdfvorigin=-52.8bp
--
-- % 'beginDoc'
-- \begin{document}
--
-- \<Latex pgf code\>
--
-- % 'endDoc'
-- \end{document}
-- @
--
latexSurface :: Surface
latexSurface :: Surface
latexSurface = Surface
  { _texFormat :: TexFormat
_texFormat = TexFormat
LaTeX
  , _command :: [Char]
_command   = [Char]
"pdflatex"
  , _arguments :: [[Char]]
_arguments = []
  , _pageSize :: Maybe (V2 Int -> [Char])
_pageSize  = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \(V2 Int
w Int
h) ->
                 [Char]
"\\pdfpagewidth=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
w forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfpageheight=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
h forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\textheight=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
h forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfhorigin=-76.6bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfvorigin=-52.8bp"
  , _preamble :: [Char]
_preamble  = [Char]
"\\documentclass{article}\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\usepackage{pgfcore}\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pagenumbering{gobble}"
  , _beginDoc :: [Char]
_beginDoc  = [Char]
"\\begin{document}"
  , _endDoc :: [Char]
_endDoc    = [Char]
"\\end{document}"
  }

-- | Default surface for latex files by calling @pdflatex@.
--
-- ==== __Sample output__
--
-- @
-- 'command': context --pipe --once
--
-- % 'preamble'
-- \usemodule[pgf]
-- \setuppagenumbering[location=]
--
-- % 'pageSize'
-- \definepapersize[diagram][width=100bp,height=80bp]
-- \setuppapersize[diagram][diagram]
-- \setuplayout
--   [ topspace=0bp
--   , backspace=0bp
--   , header=0bp
--   , footer=0bp
--   , width=100bp
--   , height=80bp
--   ]
--
-- % 'beginDoc'
-- \starttext
--
-- \<Context pgf code\>
--
-- % 'endDoc'
-- \stoptext
-- @
--
contextSurface :: Surface
contextSurface :: Surface
contextSurface = Surface
  { _texFormat :: TexFormat
_texFormat = TexFormat
ConTeXt
  , _command :: [Char]
_command   = [Char]
"context"
  , _arguments :: [[Char]]
_arguments = [[Char]
"--pipe", [Char]
"--once"]
  , _pageSize :: Maybe (V2 Int -> [Char])
_pageSize  = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \(V2 Int
w Int
h) ->
                 [Char]
"\\definepapersize[diagram][width="forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
w forall a. [a] -> [a] -> [a]
++[Char]
"bp,height="forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
h forall a. [a] -> [a] -> [a]
++[Char]
"bp]\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\setuppapersize[diagram][diagram]\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\setuplayout\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  [ topspace=0bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  , backspace=0bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  , header=0bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  , footer=0bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  , width=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
w forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  , height=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
h forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"  ]"
  , _preamble :: [Char]
_preamble  = [Char]
"\\usemodule[pgf]\n" -- pgfcore doesn't work
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\setuppagenumbering[location=]"
  , _beginDoc :: [Char]
_beginDoc  = [Char]
"\\starttext"
  , _endDoc :: [Char]
_endDoc    = [Char]
"\\stoptext"
  }

-- | Default surface for latex files by calling @pdflatex@.
--
-- ==== __Sample output__
--
-- @
-- 'command': pdftex
--
-- % 'preamble'
-- \input eplain
-- \beginpackages
-- \usepackage{color}
-- \endpackages
-- \input pgfcore
-- \def\frac#1#2{{\begingroup #1\endgroup\over #2}}\nopagenumbers
--
-- % 'pageSize'
-- \pdfpagewidth=100bp
-- \pdfpageheight=80bp
-- \pdfhorigin=-20bp
-- \pdfvorigin=0bp
--
-- % 'beginDoc'
--
--
-- <PlainTex pgf code>
--
-- % 'endDoc'
-- \bye
-- @
--
plaintexSurface :: Surface
plaintexSurface :: Surface
plaintexSurface = Surface
  { _texFormat :: TexFormat
_texFormat = TexFormat
PlainTeX
  , _command :: [Char]
_command   = [Char]
"pdftex"
  , _arguments :: [[Char]]
_arguments = []
  , _pageSize :: Maybe (V2 Int -> [Char])
_pageSize  = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \(V2 Int
w Int
h) ->
                 [Char]
"\\pdfpagewidth=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
w forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfpageheight=" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show Int
h forall a. [a] -> [a] -> [a]
++ [Char]
"bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfhorigin=-20bp\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\pdfvorigin=0bp"
  , _preamble :: [Char]
_preamble  = [Char]
"\\input eplain\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\beginpackages\n\\usepackage{color}\n\\endpackages\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\input pgfcore\n"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\def\\frac#1#2{{\\begingroup #1\\endgroup\\over #2}}"
              forall a. [a] -> [a] -> [a]
++ [Char]
"\\nopagenumbers"
  , _beginDoc :: [Char]
_beginDoc  = [Char]
""
  , _endDoc :: [Char]
_endDoc    = [Char]
"\\bye"
  }

-- | Latex is the default surface.
instance Default Surface where
  def :: Surface
def = Surface
latexSurface

sampleSurfaceOutput :: Surface -> String
sampleSurfaceOutput :: Surface -> [Char]
sampleSurfaceOutput Surface
surf = [[Char]] -> [Char]
unlines
  [ [Char]
"command: " forall a. [a] -> [a] -> [a]
++ Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface [Char]
command forall a. [a] -> [a] -> [a]
++ [Char]
" " forall a. [a] -> [a] -> [a]
++ [[Char]] -> [Char]
unwords (Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface [[Char]]
arguments)
  , [Char]
"\n% preamble"
  , Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface [Char]
preamble
  , [Char]
"\n% pageSize"
  , forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall a b. Prism (Maybe a) (Maybe b) a b
_Just forall a b. (a -> b) -> a -> b
$ Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface (Maybe (V2 Int -> [Char]))
pageSize forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. a -> Maybe a
Just (forall a. a -> a -> V2 a
V2 Int
100 Int
80)
  , [Char]
"\n% beginDoc"
  , Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface [Char]
beginDoc
  , [Char]
"\n<" forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> [Char]
show (Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface TexFormat
texFormat) forall a. [a] -> [a] -> [a]
++ [Char]
" pgf code>"
  , [Char]
"\n% endDoc"
  , Surface
surf forall s a. s -> Getting a s a -> a
^. Lens' Surface [Char]
endDoc
  ]

-- OnlineTex functions -------------------------------------------------

-- | Get the result of an OnlineTex using the given surface.
surfOnlineTex :: Surface -> OnlineTex a -> a
surfOnlineTex :: forall a. Surface -> OnlineTex a -> a
surfOnlineTex Surface
surf OnlineTex a
a = forall a. IO a -> a
unsafePerformIO (forall a. Surface -> OnlineTex a -> IO a
surfOnlineTexIO Surface
surf OnlineTex a
a)
{-# NOINLINE surfOnlineTex #-}

-- | Get the result of an OnlineTex using the given surface.
surfOnlineTexIO :: Surface -> OnlineTex a -> IO a
surfOnlineTexIO :: forall a. Surface -> OnlineTex a -> IO a
surfOnlineTexIO Surface
surf = forall a. [Char] -> [[Char]] -> ByteString -> OnlineTex a -> IO a
runOnlineTex (Surface
surfforall s a. s -> Getting a s a -> a
^.Lens' Surface [Char]
command) (Surface
surfforall s a. s -> Getting a s a -> a
^.Lens' Surface [[Char]]
arguments) ByteString
begin
  where
    begin :: ByteString
begin = forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view forall lazy strict. Strict lazy strict => Iso' lazy strict
strict forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
toLazyByteString forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> Builder
stringUtf8
          forall a b. (a -> b) -> a -> b
$ Surface
surf forall s a. s -> Getting a s a -> a
^. (Lens' Surface [Char]
preamble forall a. Semigroup a => a -> a -> a
<> Lens' Surface [Char]
beginDoc)

-- Hashable instances --------------------------------------------------

instance Hashable TexFormat where
  hashWithSalt :: Int -> TexFormat -> Int
hashWithSalt Int
s TexFormat
LaTeX    = Int
s forall a. Hashable a => Int -> a -> Int
`hashWithSalt` (Int
1::Int)
  hashWithSalt Int
s TexFormat
ConTeXt  = Int
s forall a. Hashable a => Int -> a -> Int
`hashWithSalt` (Int
2::Int)
  hashWithSalt Int
s TexFormat
PlainTeX = Int
s forall a. Hashable a => Int -> a -> Int
`hashWithSalt` (Int
3::Int)

instance Eq Surface where
  Surface TexFormat
tf1 [Char]
cm1 [[Char]]
ar1 Maybe (V2 Int -> [Char])
ps1 [Char]
p1 [Char]
bd1 [Char]
ed1 == :: Surface -> Surface -> Bool
== Surface TexFormat
tf2 [Char]
cm2 [[Char]]
ar2 Maybe (V2 Int -> [Char])
ps2 [Char]
p2 [Char]
bd2 [Char]
ed2
    = forall (t :: * -> *). Foldable t => t Bool -> Bool
and
      [ TexFormat
tf1 forall a. Eq a => a -> a -> Bool
== TexFormat
tf2
      , [Char]
cm1 forall a. Eq a => a -> a -> Bool
== [Char]
cm2
      , [[Char]]
ar1 forall a. Eq a => a -> a -> Bool
== [[Char]]
ar2
      , (Maybe (V2 Int -> [Char])
ps1 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. a -> Maybe a
Just (forall a. a -> a -> V2 a
V2 Int
1 Int
2)) forall a. Eq a => a -> a -> Bool
== (Maybe (V2 Int -> [Char])
ps2 forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. a -> Maybe a
Just (forall a. a -> a -> V2 a
V2 Int
1 Int
2))
      , [Char]
p1 forall a. Eq a => a -> a -> Bool
== [Char]
p2
      , [Char]
bd1 forall a. Eq a => a -> a -> Bool
== [Char]
bd2
      , [Char]
ed1 forall a. Eq a => a -> a -> Bool
== [Char]
ed2
      ]

instance Hashable Surface where
  hashWithSalt :: Int -> Surface -> Int
hashWithSalt Int
s (Surface TexFormat
tf [Char]
cm [[Char]]
ar Maybe (V2 Int -> [Char])
ps [Char]
p [Char]
bd [Char]
ed)
    = Int
s                    forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      TexFormat
tf                   forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      [Char]
cm                   forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      [[Char]]
ar                   forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      Maybe (V2 Int -> [Char])
ps forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. a -> Maybe a
Just (forall a. a -> a -> V2 a
V2 Int
1 Int
2) forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      [Char]
p                    forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      [Char]
bd                   forall a. Hashable a => Int -> a -> Int
`hashWithSalt`
      [Char]
ed